diff --git a/lib/common/widgets/image/image_save.dart b/lib/common/widgets/image/image_save.dart index aec0327bb..e3202d5f6 100644 --- a/lib/common/widgets/image/image_save.dart +++ b/lib/common/widgets/image/image_save.dart @@ -2,7 +2,7 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/button/icon_button.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/http/user.dart'; -import 'package:PiliPlus/utils/download.dart'; +import 'package:PiliPlus/utils/image_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -111,14 +111,14 @@ void imageSaveDialog({ tooltip: '分享', onPressed: () { SmartDialog.dismiss(); - DownloadUtils.onShareImg(cover!); + ImageUtil.onShareImg(cover!); }, icon: Icons.share, ), iconBtn( tooltip: '保存封面图', onPressed: () async { - bool saveStatus = await DownloadUtils.downloadImg( + bool saveStatus = await ImageUtil.downloadImg( context, [cover!], ); diff --git a/lib/common/widgets/image/network_img_layer.dart b/lib/common/widgets/image/network_img_layer.dart index bb5bda34f..565e8f137 100644 --- a/lib/common/widgets/image/network_img_layer.dart +++ b/lib/common/widgets/image/network_img_layer.dart @@ -1,14 +1,14 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/models/common/image_type.dart'; import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/image_util.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; class NetworkImgLayer extends StatelessWidget { const NetworkImgLayer({ super.key, - this.src, + required this.src, required this.width, this.height, this.type = ImageType.def, @@ -64,7 +64,7 @@ class NetworkImgLayer extends StatelessWidget { memCacheHeight = height.cacheSize(context); } return CachedNetworkImage( - imageUrl: Utils.thumbnailImgUrl(src, quality), + imageUrl: ImageUtil.thumbnailUrl(src, quality), width: width, height: height, memCacheWidth: memCacheWidth, diff --git a/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart b/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart index dfa7f2616..ec39ac8ff 100644 --- a/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart +++ b/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart @@ -4,8 +4,8 @@ import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactive_vi as custom; import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactive_viewer_boundary.dart'; import 'package:PiliPlus/models/common/image_preview_type.dart'; -import 'package:PiliPlus/utils/download.dart'; import 'package:PiliPlus/utils/extension.dart'; +import 'package:PiliPlus/utils/image_util.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; @@ -248,7 +248,7 @@ class _InteractiveviewerGalleryState extends State String _getActualUrl(String url) { return _quality != 100 - ? Utils.thumbnailImgUrl(url, _quality) + ? ImageUtil.thumbnailUrl(url, _quality) : url.http2https; } @@ -367,7 +367,7 @@ class _InteractiveviewerGalleryState extends State final item = widget.sources[currentIndex.value]; return [ PopupMenuItem( - onTap: () => DownloadUtils.onShareImg(item.url), + onTap: () => ImageUtil.onShareImg(item.url), child: const Text("分享图片"), ), PopupMenuItem( @@ -375,7 +375,7 @@ class _InteractiveviewerGalleryState extends State child: const Text("复制链接"), ), PopupMenuItem( - onTap: () => DownloadUtils.downloadImg( + onTap: () => ImageUtil.downloadImg( this.context, [item.url], ), @@ -383,7 +383,7 @@ class _InteractiveviewerGalleryState extends State ), if (widget.sources.length > 1) PopupMenuItem( - onTap: () => DownloadUtils.downloadImg( + onTap: () => ImageUtil.downloadImg( this.context, widget.sources.map((item) => item.url).toList(), ), @@ -392,7 +392,7 @@ class _InteractiveviewerGalleryState extends State if (item.sourceType == SourceType.livePhoto) PopupMenuItem( onTap: () { - DownloadUtils.downloadLivePhoto( + ImageUtil.downloadLivePhoto( context: this.context, url: item.url, liveUrl: item.liveUrl!, @@ -433,7 +433,7 @@ class _InteractiveviewerGalleryState extends State return CachedNetworkImage( fadeInDuration: Duration.zero, fadeOutDuration: Duration.zero, - imageUrl: Utils.thumbnailImgUrl(item.url, widget.quality), + imageUrl: ImageUtil.thumbnailUrl(item.url, widget.quality), ); }, ), @@ -510,7 +510,7 @@ class _InteractiveviewerGalleryState extends State ListTile( onTap: () { Get.back(); - DownloadUtils.onShareImg(item.url); + ImageUtil.onShareImg(item.url); }, dense: true, title: const Text('分享', style: TextStyle(fontSize: 14)), @@ -526,7 +526,7 @@ class _InteractiveviewerGalleryState extends State ListTile( onTap: () { Get.back(); - DownloadUtils.downloadImg( + ImageUtil.downloadImg( this.context, [item.url], ); @@ -538,7 +538,7 @@ class _InteractiveviewerGalleryState extends State ListTile( onTap: () { Get.back(); - DownloadUtils.downloadImg( + ImageUtil.downloadImg( this.context, widget.sources.map((item) => item.url).toList(), ); @@ -550,7 +550,7 @@ class _InteractiveviewerGalleryState extends State ListTile( onTap: () { Get.back(); - DownloadUtils.downloadLivePhoto( + ImageUtil.downloadLivePhoto( context: this.context, url: item.url, liveUrl: item.liveUrl!, diff --git a/lib/common/widgets/pendant_avatar.dart b/lib/common/widgets/pendant_avatar.dart index 493c8652e..c6623711e 100644 --- a/lib/common/widgets/pendant_avatar.dart +++ b/lib/common/widgets/pendant_avatar.dart @@ -2,8 +2,8 @@ import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/models/common/avatar_badge_type.dart'; import 'package:PiliPlus/models/common/image_type.dart'; import 'package:PiliPlus/utils/extension.dart'; +import 'package:PiliPlus/utils/image_util.dart'; import 'package:PiliPlus/utils/storage.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -62,7 +62,7 @@ class PendantAvatar extends StatelessWidget { child: CachedNetworkImage( width: size * 1.75, height: size * 1.75, - imageUrl: Utils.thumbnailImgUrl(garbPendantImage), + imageUrl: ImageUtil.thumbnailUrl(garbPendantImage), ), ), ), diff --git a/lib/common/widgets/stat/stat.dart b/lib/common/widgets/stat/stat.dart index efa79c6ed..65fa2216b 100644 --- a/lib/common/widgets/stat/stat.dart +++ b/lib/common/widgets/stat/stat.dart @@ -1,5 +1,5 @@ import 'package:PiliPlus/models/common/stat_type.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:flutter/material.dart'; class StatWidget extends StatelessWidget { @@ -39,7 +39,7 @@ class StatWidget extends StatelessWidget { color: color, ), Text( - Utils.numFormat(value), + NumUtil.numFormat(value), style: TextStyle(fontSize: 12, color: color), ), ], diff --git a/lib/common/widgets/video_card/video_card_h.dart b/lib/common/widgets/video_card/video_card_h.dart index accdf4447..0e67a30c9 100644 --- a/lib/common/widgets/video_card/video_card_h.dart +++ b/lib/common/widgets/video_card/video_card_h.dart @@ -11,6 +11,8 @@ import 'package:PiliPlus/models/common/stat_type.dart'; import 'package:PiliPlus/models/model_hot_video_item.dart'; import 'package:PiliPlus/models/model_video.dart'; import 'package:PiliPlus/models/search/result.dart'; +import 'package:PiliPlus/utils/date_util.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; @@ -138,7 +140,7 @@ class VideoCardH extends StatelessWidget { PBadge( text: progress == -1 ? '已看完' - : '${Utils.timeFormat(progress)}/${Utils.timeFormat(videoItem.duration)}', + : '${DurationUtil.formatDuration(progress)}/${DurationUtil.formatDuration(videoItem.duration)}', right: 6, bottom: 8, type: PBadgeType.gray, @@ -155,7 +157,8 @@ class VideoCardH extends StatelessWidget { ) ] else if (videoItem.duration > 0) PBadge( - text: Utils.timeFormat(videoItem.duration), + text: DurationUtil.formatDuration( + videoItem.duration), right: 6.0, bottom: 6.0, type: PBadgeType.gray, @@ -194,9 +197,7 @@ class VideoCardH extends StatelessWidget { Widget content(BuildContext context) { final theme = Theme.of(context); - String pubdate = showPubdate - ? Utils.dateFormat(videoItem.pubdate!, formatType: 'day') - : ''; + String pubdate = showPubdate ? DateUtil.dateFormat(videoItem.pubdate!) : ''; if (pubdate != '') pubdate += ' '; return Expanded( child: Column( diff --git a/lib/common/widgets/video_card/video_card_v.dart b/lib/common/widgets/video_card/video_card_v.dart index 646f33f39..2443a30e2 100644 --- a/lib/common/widgets/video_card/video_card_v.dart +++ b/lib/common/widgets/video_card/video_card_v.dart @@ -7,15 +7,17 @@ import 'package:PiliPlus/common/widgets/video_popup_menu.dart'; import 'package:PiliPlus/http/search.dart'; import 'package:PiliPlus/models/common/badge_type.dart'; import 'package:PiliPlus/models/common/stat_type.dart'; -import 'package:PiliPlus/models/home/rcmd/result.dart'; import 'package:PiliPlus/models/model_rec_video_item.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; +import 'package:PiliPlus/utils/date_util.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:intl/intl.dart'; // 视频卡片 - 垂直布局 class VideoCardV extends StatelessWidget { @@ -129,7 +131,8 @@ class VideoCardV extends StatelessWidget { right: 7, size: PBadgeSize.small, type: PBadgeType.gray, - text: Utils.timeFormat(videoItem.duration), + text: + DurationUtil.formatDuration(videoItem.duration), ) ], ); @@ -229,6 +232,9 @@ class VideoCardV extends StatelessWidget { ); } + static final shortFormat = DateFormat('M-d'); + static final longFormat = DateFormat('yy-M-d'); + Widget videoStat(BuildContext context, ThemeData theme) { return Row( children: [ @@ -248,29 +254,36 @@ class VideoCardV extends StatelessWidget { Text.rich( maxLines: 1, TextSpan( - style: TextStyle( - fontSize: theme.textTheme.labelSmall!.fontSize, - color: theme.colorScheme.outline.withValues(alpha: 0.8), - ), - text: Utils.formatTimestampToRelativeTime(videoItem.pubdate)), - ), - const SizedBox(width: 2), - ] else if (videoItem is RecVideoItemAppModel && - videoItem.desc != null && - videoItem.desc!.contains(' · ')) ...[ - const Spacer(), - Text.rich( - maxLines: 1, - TextSpan( - style: TextStyle( - fontSize: theme.textTheme.labelSmall!.fontSize, - color: theme.colorScheme.outline.withValues(alpha: 0.8), - ), - text: Utils.shortenChineseDateString( - videoItem.desc!.split(' · ').last)), + style: TextStyle( + fontSize: theme.textTheme.labelSmall!.fontSize, + color: theme.colorScheme.outline.withValues(alpha: 0.8), + ), + text: DateUtil.dateFormat( + videoItem.pubdate, + shortFormat: shortFormat, + longFormat: longFormat, + ), + ), ), const SizedBox(width: 2), ] + // deprecated + // else if (videoItem is RecVideoItemAppModel && + // videoItem.desc != null && + // videoItem.desc!.contains(' · ')) ...[ + // const Spacer(), + // Text.rich( + // maxLines: 1, + // TextSpan( + // style: TextStyle( + // fontSize: theme.textTheme.labelSmall!.fontSize, + // color: theme.colorScheme.outline.withValues(alpha: 0.8), + // ), + // text: Utils.shortenChineseDateString( + // videoItem.desc!.split(' · ').last)), + // ), + // const SizedBox(width: 2), + // ] ], ); } diff --git a/lib/grpc/grpc_repo.dart b/lib/grpc/grpc_repo.dart index 85173874d..71b330a9e 100644 --- a/lib/grpc/grpc_repo.dart +++ b/lib/grpc/grpc_repo.dart @@ -12,6 +12,7 @@ import 'package:PiliPlus/grpc/google/rpc/status.pb.dart'; import 'package:PiliPlus/http/constants.dart'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/login_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/utils.dart'; @@ -77,7 +78,7 @@ class GrpcRepo { static const _phone = 'phone'; static final _buvid = LoginUtils.buvid; - static final _traceId = Utils.genTraceId(); + static final _traceId = IdUtils.genTraceId(); static final _sessionId = Utils.generateRandomString(8); static void updateHeaders(String? accessKey) { diff --git a/lib/http/api.dart b/lib/http/api.dart index 05e939020..478b5211f 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -890,4 +890,6 @@ class Api { static const String coinLog = '/x/member/web/coin/log'; static const String dynTopicRcmd = '/x/topic/web/dynamic/rcmd'; + + static const String matchInfo = '/x/esports/match/info'; } diff --git a/lib/http/fav.dart b/lib/http/fav.dart index 746e5acd6..318ba81e4 100644 --- a/lib/http/fav.dart +++ b/lib/http/fav.dart @@ -11,8 +11,8 @@ import 'package:PiliPlus/models_new/fav/fav_pgc/data.dart'; import 'package:PiliPlus/models_new/fav/fav_topic/data.dart'; import 'package:PiliPlus/models_new/space/space_fav/data.dart'; import 'package:PiliPlus/models_new/sub/sub_detail/data.dart'; +import 'package:PiliPlus/utils/app_sign.dart'; import 'package:PiliPlus/utils/storage.dart' show Accounts; -import 'package:PiliPlus/utils/utils.dart'; import 'package:dio/dio.dart'; class FavHttp { @@ -379,7 +379,7 @@ class FavHttp { 'sort': sort.join(','), 'csrf': Accounts.main.csrf, }; - Utils.appSign(data); + AppSign.appSign(data); var res = await Request().post( Api.sortFavFolder, data: data, @@ -403,7 +403,7 @@ class FavHttp { 'sort': sort.join(','), 'csrf': Accounts.main.csrf, }; - Utils.appSign(data); + AppSign.appSign(data); var res = await Request().post( Api.sortFav, data: data, diff --git a/lib/http/live.dart b/lib/http/live.dart index 557f934aa..869617af3 100644 --- a/lib/http/live.dart +++ b/lib/http/live.dart @@ -14,8 +14,8 @@ import 'package:PiliPlus/models_new/live/live_room_info_h5/data.dart'; import 'package:PiliPlus/models_new/live/live_room_play_info/data.dart'; import 'package:PiliPlus/models_new/live/live_search/data.dart'; import 'package:PiliPlus/models_new/live/live_second_list/data.dart'; +import 'package:PiliPlus/utils/app_sign.dart'; import 'package:PiliPlus/utils/storage.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'package:PiliPlus/utils/wbi_sign.dart'; import 'package:dio/dio.dart'; @@ -181,7 +181,7 @@ class LiveHttp { 'statistics': Constants.statistics, 'ts': DateTime.now().millisecondsSinceEpoch ~/ 1000, }; - Utils.appSign( + AppSign.appSign( params, Constants.appKey, Constants.appSec, @@ -249,7 +249,7 @@ class LiveHttp { 'statistics': Constants.statistics, 'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(), }; - Utils.appSign( + AppSign.appSign( params, Constants.appKey, Constants.appSec, @@ -282,7 +282,7 @@ class LiveHttp { 'statistics': Constants.statistics, 'ts': DateTime.now().millisecondsSinceEpoch ~/ 1000, }; - Utils.appSign( + AppSign.appSign( params, Constants.appKey, Constants.appSec, @@ -317,7 +317,7 @@ class LiveHttp { 'statistics': Constants.statistics, 'ts': DateTime.now().millisecondsSinceEpoch ~/ 1000, }; - Utils.appSign( + AppSign.appSign( params, Constants.appKey, Constants.appSec, @@ -355,7 +355,7 @@ class LiveHttp { 'statistics': Constants.statistics, 'ts': DateTime.now().millisecondsSinceEpoch ~/ 1000, }; - Utils.appSign( + AppSign.appSign( data, Constants.appKey, Constants.appSec, @@ -396,7 +396,7 @@ class LiveHttp { 'statistics': Constants.statistics, 'ts': DateTime.now().millisecondsSinceEpoch ~/ 1000, }; - Utils.appSign( + AppSign.appSign( params, Constants.appKey, Constants.appSec, @@ -438,7 +438,7 @@ class LiveHttp { 'ts': DateTime.now().millisecondsSinceEpoch ~/ 1000, 'type': type.name, }; - Utils.appSign( + AppSign.appSign( params, Constants.appKey, Constants.appSec, diff --git a/lib/http/login.dart b/lib/http/login.dart index 866bc1c30..8561acc9f 100644 --- a/lib/http/login.dart +++ b/lib/http/login.dart @@ -5,6 +5,7 @@ import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/models/login/model.dart'; import 'package:PiliPlus/utils/accounts/account.dart'; +import 'package:PiliPlus/utils/app_sign.dart'; import 'package:PiliPlus/utils/login_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:crypto/crypto.dart'; @@ -34,7 +35,7 @@ class LoginHttp { 'platform': 'android', 'mobi_app': 'android_hd', }; - Utils.appSign(params); + AppSign.appSign(params); var res = await Request().post(Api.getTVCode, queryParameters: params); if (res.data['code'] == 0) { @@ -50,7 +51,7 @@ class LoginHttp { 'local_id': '0', 'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(), }; - Utils.appSign(params); + AppSign.appSign(params); var res = await Request().post(Api.qrcodePoll, queryParameters: params); return { 'status': res.data['code'] == 0, @@ -116,7 +117,7 @@ class LoginHttp { 'tel': tel, 'ts': (timestamp ~/ 1000).toString(), }; - Utils.appSign(data); + AppSign.appSign(data); var res = await Request().post( Api.appSmsCode, @@ -159,7 +160,7 @@ class LoginHttp { // 'statistics': Constants.statistics, // 'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(), // }; - // String sign = Utils.appSign( + // String sign = AppSign.appSign( // params, // Constants.appKey, // Constants.appSec, @@ -224,7 +225,7 @@ class LoginHttp { 'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(), 'username': username, }; - Utils.appSign(data); + AppSign.appSign(data); var res = await Request().post( Api.loginByPwdApi, data: data, @@ -289,7 +290,7 @@ class LoginHttp { 'tel': tel, 'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(), }; - Utils.appSign(data); + AppSign.appSign(data); var res = await Request().post( Api.logInByAppSms, data: data, @@ -369,7 +370,7 @@ class LoginHttp { if (geeValidate != null) 'gee_validate': geeValidate, if (recaptchaToken != null) 'recaptcha_token': recaptchaToken, }; - Utils.appSign(data); + AppSign.appSign(data); var res = await Request().post( Api.safeCenterSmsCode, data: data, @@ -409,7 +410,7 @@ class LoginHttp { 'source': source, 'captcha_key': captchaKey, }; - Utils.appSign(data); + AppSign.appSign(data); var res = await Request().post( Api.safeCenterSmsVerify, data: data, @@ -455,7 +456,7 @@ class LoginHttp { // 'statistics': Constants.statistics, 'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(), }; - Utils.appSign(data); + AppSign.appSign(data); var res = await Request().post( Api.oauth2AccessToken, data: data, diff --git a/lib/http/match.dart b/lib/http/match.dart new file mode 100644 index 000000000..0a5ebc670 --- /dev/null +++ b/lib/http/match.dart @@ -0,0 +1,22 @@ +import 'package:PiliPlus/http/api.dart'; +import 'package:PiliPlus/http/init.dart'; +import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/models_new/match/match_info/contest.dart'; +import 'package:PiliPlus/models_new/match/match_info/data.dart'; + +class MatchHttp { + static Future> matchInfo(dynamic cid) async { + var res = await Request().get( + Api.matchInfo, + queryParameters: { + 'cid': cid, + 'platform': 2, + }, + ); + if (res.data['code'] == 0) { + return Success(MatchInfoData.fromJson(res.data['data']).contest); + } else { + return Error(res.data['message']); + } + } +} diff --git a/lib/models/home/rcmd/result.dart b/lib/models/home/rcmd/result.dart index 65ede2e17..13004f488 100644 --- a/lib/models/home/rcmd/result.dart +++ b/lib/models/home/rcmd/result.dart @@ -1,7 +1,7 @@ import 'package:PiliPlus/models/model_rec_video_item.dart'; import 'package:PiliPlus/models/model_video.dart'; import 'package:PiliPlus/utils/id_utils.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/num_util.dart'; class RecVideoItemAppModel extends BaseRecVideoItemModel { int? get id => aid; @@ -26,7 +26,7 @@ class RecVideoItemAppModel extends BaseRecVideoItemModel { // json['top_rcmd_reason']; if (rcmdReason != null && rcmdReason!.contains('赞')) { // 有时能在推荐原因里获得点赞数 - (stat as RcmdStat).like = Utils.parseNum(rcmdReason!); + (stat as RcmdStat).like = NumUtil.parseNum(rcmdReason!); } // 由于app端api并不会直接返回与owner的关注状态 // 所以借用推荐原因是否为“已关注”、“新关注”判别关注状态,从而与web端接口等效 @@ -53,8 +53,8 @@ class RecVideoItemAppModel extends BaseRecVideoItemModel { class RcmdStat extends BaseStat { RcmdStat.fromJson(Map json) { - view = Utils.parseNum(json["cover_left_text_1"] ?? ''); - danmu = Utils.parseNum(json["cover_left_text_2"] ?? ''); + view = NumUtil.parseNum(json["cover_left_text_1"] ?? ''); + danmu = NumUtil.parseNum(json["cover_left_text_2"] ?? ''); } } diff --git a/lib/models/search/result.dart b/lib/models/search/result.dart index a8e1d3ea5..db6563980 100644 --- a/lib/models/search/result.dart +++ b/lib/models/search/result.dart @@ -1,8 +1,8 @@ import 'package:PiliPlus/models/model_owner.dart'; import 'package:PiliPlus/models/model_video.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/em.dart'; import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/utils.dart'; abstract class SearchNumData { SearchNumData({ @@ -90,7 +90,7 @@ class SearchVideoItemModel extends BaseVideoItemModel { cover = (json['pic'] as String?)?.http2https; pubdate = json['pubdate']; ctime = json['senddate']; - duration = Utils.duration(json['duration']); + duration = DurationUtil.parseDuration(json['duration']); owner = SearchOwner.fromJson(json); stat = SearchStat.fromJson(json); } diff --git a/lib/models_new/match/match_info/contest.dart b/lib/models_new/match/match_info/contest.dart new file mode 100644 index 000000000..99ee58c7a --- /dev/null +++ b/lib/models_new/match/match_info/contest.dart @@ -0,0 +1,215 @@ +import 'package:PiliPlus/models_new/match/match_info/home_away.dart'; +import 'package:PiliPlus/models_new/match/match_info/season.dart'; +import 'package:PiliPlus/models_new/match/match_info/success_teaminfo.dart'; +import 'package:PiliPlus/models_new/match/match_info/team.dart'; + +class MatchContest { + int? id; + String? gameStage; + int? stime; + int? etime; + int? homeId; + int? awayId; + int? homeScore; + int? awayScore; + int? liveRoom; + int? aid; + int? collection; + String? collectionBvid; + int? gameState; + String? dic; + String? ctime; + String? mtime; + int? status; + int? sid; + int? mid; + Season? season; + MatchTeam? homeTeam; + MatchTeam? awayTeam; + int? special; + int? successTeam; + SuccessTeaminfo? successTeaminfo; + String? specialName; + String? specialTips; + String? specialImage; + String? playback; + String? collectionUrl; + String? liveUrl; + int? dataType; + int? matchId; + int? guessType; + int? guessShow; + String? bvid; + String? gameStage1; + String? gameStage2; + int? liveStatus; + int? livePopular; + String? liveCover; + int? pushSwitch; + String? liveTitle; + int? seriesId; + int? contestStatus; + int? contestFreeze; + int? startTime; + int? endTime; + String? title; + String? playBack; + int? seasonId; + int? isSub; + int? isGuess; + HomeAway? home; + HomeAway? away; + dynamic series; + String? prospect; + String? afterContestVideo; + int? homeSmallScore; + int? awaySmallScore; + String? watchPoint; + String? watchPointIcon; + dynamic hottestPlayer; + + MatchContest({ + this.id, + this.gameStage, + this.stime, + this.etime, + this.homeId, + this.awayId, + this.homeScore, + this.awayScore, + this.liveRoom, + this.aid, + this.collection, + this.collectionBvid, + this.gameState, + this.dic, + this.ctime, + this.mtime, + this.status, + this.sid, + this.mid, + this.season, + this.homeTeam, + this.awayTeam, + this.special, + this.successTeam, + this.successTeaminfo, + this.specialName, + this.specialTips, + this.specialImage, + this.playback, + this.collectionUrl, + this.liveUrl, + this.dataType, + this.matchId, + this.guessType, + this.guessShow, + this.bvid, + this.gameStage1, + this.gameStage2, + this.liveStatus, + this.livePopular, + this.liveCover, + this.pushSwitch, + this.liveTitle, + this.seriesId, + this.contestStatus, + this.contestFreeze, + this.startTime, + this.endTime, + this.title, + this.playBack, + this.seasonId, + this.isSub, + this.isGuess, + this.home, + this.away, + this.series, + this.prospect, + this.afterContestVideo, + this.homeSmallScore, + this.awaySmallScore, + this.watchPoint, + this.watchPointIcon, + this.hottestPlayer, + }); + + factory MatchContest.fromJson(Map json) => MatchContest( + id: json['id'] as int?, + gameStage: json['game_stage'] as String?, + stime: json['stime'] as int?, + etime: json['etime'] as int?, + homeId: json['home_id'] as int?, + awayId: json['away_id'] as int?, + homeScore: json['home_score'] as int?, + awayScore: json['away_score'] as int?, + liveRoom: json['live_room'] as int?, + aid: json['aid'] as int?, + collection: json['collection'] as int?, + collectionBvid: json['collection_bvid'] as String?, + gameState: json['game_state'] as int?, + dic: json['dic'] as String?, + ctime: json['ctime'] as String?, + mtime: json['mtime'] as String?, + status: json['status'] as int?, + sid: json['sid'] as int?, + mid: json['mid'] as int?, + season: json['season'] == null + ? null + : Season.fromJson(json['season'] as Map), + homeTeam: json['home_team'] == null + ? null + : MatchTeam.fromJson(json['home_team'] as Map), + awayTeam: json['away_team'] == null + ? null + : MatchTeam.fromJson(json['away_team'] as Map), + special: json['special'] as int?, + successTeam: json['success_team'] as int?, + successTeaminfo: json['success_teaminfo'] == null + ? null + : SuccessTeaminfo.fromJson( + json['success_teaminfo'] as Map), + specialName: json['special_name'] as String?, + specialTips: json['special_tips'] as String?, + specialImage: json['special_image'] as String?, + playback: json['playback'] as String?, + collectionUrl: json['collection_url'] as String?, + liveUrl: json['live_url'] as String?, + dataType: json['data_type'] as int?, + matchId: json['match_id'] as int?, + guessType: json['guess_type'] as int?, + guessShow: json['guess_show'] as int?, + bvid: json['bvid'] as String?, + gameStage1: json['game_stage1'] as String?, + gameStage2: json['game_stage2'] as String?, + liveStatus: json['live_status'] as int?, + livePopular: json['live_popular'] as int?, + liveCover: json['live_cover'] as String?, + pushSwitch: json['push_switch'] as int?, + liveTitle: json['live_title'] as String?, + seriesId: json['series_id'] as int?, + contestStatus: json['contest_status'] as int?, + contestFreeze: json['contest_freeze'] as int?, + startTime: json['start_time'] as int?, + endTime: json['end_time'] as int?, + title: json['title'] as String?, + playBack: json['play_back'] as String?, + seasonId: json['season_id'] as int?, + isSub: json['is_sub'] as int?, + isGuess: json['is_guess'] as int?, + home: json['home'] == null + ? null + : HomeAway.fromJson(json['home'] as Map), + away: json['away'] == null + ? null + : HomeAway.fromJson(json['away'] as Map), + series: json['series'] as dynamic, + prospect: json['prospect'] as String?, + afterContestVideo: json['after_contest_video'] as String?, + homeSmallScore: json['home_small_score'] as int?, + awaySmallScore: json['away_small_score'] as int?, + watchPoint: json['watch_point'] as String?, + watchPointIcon: json['watch_point_icon'] as String?, + hottestPlayer: json['hottest_player'] as dynamic, + ); +} diff --git a/lib/models_new/match/match_info/data.dart b/lib/models_new/match/match_info/data.dart new file mode 100644 index 000000000..16fc482f0 --- /dev/null +++ b/lib/models_new/match/match_info/data.dart @@ -0,0 +1,13 @@ +import 'package:PiliPlus/models_new/match/match_info/contest.dart'; + +class MatchInfoData { + MatchContest? contest; + + MatchInfoData({this.contest}); + + factory MatchInfoData.fromJson(Map json) => MatchInfoData( + contest: json['contest'] == null + ? null + : MatchContest.fromJson(json['contest'] as Map), + ); +} diff --git a/lib/models_new/match/match_info/home_away.dart b/lib/models_new/match/match_info/home_away.dart new file mode 100644 index 000000000..8e3edafd5 --- /dev/null +++ b/lib/models_new/match/match_info/home_away.dart @@ -0,0 +1,41 @@ +class HomeAway { + int? id; + String? icon; + String? name; + int? wins; + String? region; + int? regionId; + int? externalTeamId; + String? divisionName; + String? divisionLogo; + dynamic playerGradeDetail; + bool? isSuccessTeam; + + HomeAway({ + this.id, + this.icon, + this.name, + this.wins, + this.region, + this.regionId, + this.externalTeamId, + this.divisionName, + this.divisionLogo, + this.playerGradeDetail, + this.isSuccessTeam, + }); + + factory HomeAway.fromJson(Map json) => HomeAway( + id: json['id'] as int?, + icon: json['icon'] as String?, + name: json['name'] as String?, + wins: json['wins'] as int?, + region: json['region'] as String?, + regionId: json['region_id'] as int?, + externalTeamId: json['ExternalTeamId'] as int?, + divisionName: json['division_name'] as String?, + divisionLogo: json['division_logo'] as String?, + playerGradeDetail: json['player_grade_detail'] as dynamic, + isSuccessTeam: json['is_success_team'] as bool?, + ); +} diff --git a/lib/models_new/match/match_info/season.dart b/lib/models_new/match/match_info/season.dart new file mode 100644 index 000000000..50eecd54b --- /dev/null +++ b/lib/models_new/match/match_info/season.dart @@ -0,0 +1,83 @@ +class Season { + int? id; + int? mid; + String? title; + String? subTitle; + int? stime; + int? etime; + String? sponsor; + String? logo; + String? dic; + int? status; + int? ctime; + int? mtime; + int? rank; + int? isApp; + String? url; + String? dataFocus; + String? focusUrl; + int? leidaSid; + int? gameType; + String? searchImage; + int? syncPlatform; + String? centreLogo; + int? centreStatus; + String? centrePcLogo; + int? seasonType; + + Season({ + this.id, + this.mid, + this.title, + this.subTitle, + this.stime, + this.etime, + this.sponsor, + this.logo, + this.dic, + this.status, + this.ctime, + this.mtime, + this.rank, + this.isApp, + this.url, + this.dataFocus, + this.focusUrl, + this.leidaSid, + this.gameType, + this.searchImage, + this.syncPlatform, + this.centreLogo, + this.centreStatus, + this.centrePcLogo, + this.seasonType, + }); + + factory Season.fromJson(Map json) => Season( + id: json['id'] as int?, + mid: json['mid'] as int?, + title: json['title'] as String?, + subTitle: json['sub_title'] as String?, + stime: json['stime'] as int?, + etime: json['etime'] as int?, + sponsor: json['sponsor'] as String?, + logo: json['logo'] as String?, + dic: json['dic'] as String?, + status: json['status'] as int?, + ctime: json['ctime'] as int?, + mtime: json['mtime'] as int?, + rank: json['rank'] as int?, + isApp: json['is_app'] as int?, + url: json['url'] as String?, + dataFocus: json['data_focus'] as String?, + focusUrl: json['focus_url'] as String?, + leidaSid: json['leida_sid'] as int?, + gameType: json['game_type'] as int?, + searchImage: json['search_image'] as String?, + syncPlatform: json['sync_platform'] as int?, + centreLogo: json['centre_logo'] as String?, + centreStatus: json['centre_status'] as int?, + centrePcLogo: json['centre_pc_logo'] as String?, + seasonType: json['season_type'] as int?, + ); +} diff --git a/lib/models_new/match/match_info/success_teaminfo.dart b/lib/models_new/match/match_info/success_teaminfo.dart new file mode 100644 index 000000000..b5f04fc09 --- /dev/null +++ b/lib/models_new/match/match_info/success_teaminfo.dart @@ -0,0 +1,67 @@ +class SuccessTeaminfo { + int? id; + String? title; + String? subTitle; + String? eTitle; + int? createTime; + String? area; + String? logo; + int? uid; + String? members; + String? dic; + int? isDeleted; + String? videoUrl; + String? profile; + int? leidaTid; + int? replyId; + int? teamType; + int? regionId; + String? divisionName; + String? divisionLogo; + + SuccessTeaminfo({ + this.id, + this.title, + this.subTitle, + this.eTitle, + this.createTime, + this.area, + this.logo, + this.uid, + this.members, + this.dic, + this.isDeleted, + this.videoUrl, + this.profile, + this.leidaTid, + this.replyId, + this.teamType, + this.regionId, + this.divisionName, + this.divisionLogo, + }); + + factory SuccessTeaminfo.fromJson(Map json) { + return SuccessTeaminfo( + id: json['id'] as int?, + title: json['title'] as String?, + subTitle: json['sub_title'] as String?, + eTitle: json['e_title'] as String?, + createTime: json['create_time'] as int?, + area: json['area'] as String?, + logo: json['logo'] as String?, + uid: json['uid'] as int?, + members: json['members'] as String?, + dic: json['dic'] as String?, + isDeleted: json['is_deleted'] as int?, + videoUrl: json['video_url'] as String?, + profile: json['profile'] as String?, + leidaTid: json['leida_tid'] as int?, + replyId: json['reply_id'] as int?, + teamType: json['team_type'] as int?, + regionId: json['region_id'] as int?, + divisionName: json['division_name'] as String?, + divisionLogo: json['division_logo'] as String?, + ); + } +} diff --git a/lib/models_new/match/match_info/team.dart b/lib/models_new/match/match_info/team.dart new file mode 100644 index 000000000..ea0b274f9 --- /dev/null +++ b/lib/models_new/match/match_info/team.dart @@ -0,0 +1,87 @@ +class MatchTeam { + int? id; + String? title; + String? subTitle; + String? eTitle; + int? createTime; + String? area; + String? logo; + int? uid; + String? members; + String? dic; + int? isDeleted; + String? videoUrl; + String? profile; + int? leidaTid; + int? replyId; + int? teamType; + int? regionId; + String? divisionName; + String? divisionLogo; + + MatchTeam({ + this.id, + this.title, + this.subTitle, + this.eTitle, + this.createTime, + this.area, + this.logo, + this.uid, + this.members, + this.dic, + this.isDeleted, + this.videoUrl, + this.profile, + this.leidaTid, + this.replyId, + this.teamType, + this.regionId, + this.divisionName, + this.divisionLogo, + }); + + factory MatchTeam.fromJson(Map json) => MatchTeam( + id: json['id'] as int?, + title: json['title'] as String?, + subTitle: json['sub_title'] as String?, + eTitle: json['e_title'] as String?, + createTime: json['create_time'] as int?, + area: json['area'] as String?, + logo: json['logo'] as String?, + uid: json['uid'] as int?, + members: json['members'] as String?, + dic: json['dic'] as String?, + isDeleted: json['is_deleted'] as int?, + videoUrl: json['video_url'] as String?, + profile: json['profile'] as String?, + leidaTid: json['leida_tid'] as int?, + replyId: json['reply_id'] as int?, + teamType: json['team_type'] as int?, + regionId: json['region_id'] as int?, + divisionName: json['division_name'] as String?, + divisionLogo: json['division_logo'] as String?, + ); + + Map toJson() => { + 'id': id, + 'title': title, + 'sub_title': subTitle, + 'e_title': eTitle, + 'create_time': createTime, + 'area': area, + 'logo': logo, + 'uid': uid, + 'members': members, + 'dic': dic, + 'is_deleted': isDeleted, + 'video_url': videoUrl, + 'profile': profile, + 'leida_tid': leidaTid, + 'reply_id': replyId, + 'team_type': teamType, + 'region_id': regionId, + 'division_name': divisionName, + 'division_logo': divisionLogo, + }; +} diff --git a/lib/models_new/member/search_archive/vlist.dart b/lib/models_new/member/search_archive/vlist.dart index 465a5ff5e..58f060a18 100644 --- a/lib/models_new/member/search_archive/vlist.dart +++ b/lib/models_new/member/search_archive/vlist.dart @@ -1,5 +1,5 @@ import 'package:PiliPlus/models/model_video.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; class VListItemModel extends BaseVideoItemModel { int? comment; @@ -20,7 +20,9 @@ class VListItemModel extends BaseVideoItemModel { title = json['title']; review = json['review']; pubdate = json['created']; - if (json['length'] != null) duration = Utils.duration(json['length']); + if (json['length'] != null) { + duration = DurationUtil.parseDuration(json['length']); + } aid = json['aid']; bvid = json['bvid']; hideClick = json['hide_click']; diff --git a/lib/pages/about/view.dart b/lib/pages/about/view.dart index aeee0f9f3..14d0d24db 100644 --- a/lib/pages/about/view.dart +++ b/lib/pages/about/view.dart @@ -11,6 +11,7 @@ import 'package:PiliPlus/utils/cache_manage.dart'; import 'package:PiliPlus/utils/login_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/update.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart' show Clipboard, ClipboardData; @@ -120,7 +121,7 @@ class _AboutPageState extends State { ), Obx( () => ListTile( - onTap: () => Utils.checkUpdate(false), + onTap: () => Update.checkUpdate(false), onLongPress: () => Utils.copyText(currentVersion.value), title: const Text('当前版本'), leading: const Icon(Icons.commit_outlined), diff --git a/lib/pages/article/view.dart b/lib/pages/article/view.dart index d02b0e7d5..c09e1bbbe 100644 --- a/lib/pages/article/view.dart +++ b/lib/pages/article/view.dart @@ -22,9 +22,12 @@ import 'package:PiliPlus/pages/article/widgets/opus_content.dart'; import 'package:PiliPlus/pages/dynamics_repost/view.dart'; import 'package:PiliPlus/pages/video/reply/widgets/reply_item_grpc.dart'; import 'package:PiliPlus/pages/video/reply_reply/view.dart'; +import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/grid.dart'; +import 'package:PiliPlus/utils/image_util.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/utils.dart'; @@ -473,8 +476,9 @@ class _ArticlePageState extends State fit: pic.isLongPic == true ? BoxFit.cover : null, - imageUrl: Utils.thumbnailImgUrl( - pic.url, 60), + imageUrl: + ImageUtil.thumbnailUrl( + pic.url, 60), fadeInDuration: const Duration( milliseconds: 120), fadeOutDuration: const Duration( @@ -545,7 +549,7 @@ class _ArticlePageState extends State ), if (pubTime != null) Text( - Utils.dateFormat(pubTime), + DateUtil.format(pubTime), style: TextStyle( color: theme.colorScheme.outline, fontSize: @@ -611,9 +615,9 @@ class _ArticlePageState extends State replyLevel: 1, replyReply: (replyItem, id) => replyReply(context, replyItem, id), - onReply: () => _articleCtr.onReply( + onReply: (replyItem) => _articleCtr.onReply( context, - replyItem: response[index], + replyItem: replyItem, index: index, ), onDelete: (subIndex) => @@ -654,7 +658,7 @@ class _ArticlePageState extends State mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Obx(() => Text( - '${_articleCtr.count.value == -1 ? 0 : Utils.numFormat(_articleCtr.count.value)}条回复')), + '${_articleCtr.count.value == -1 ? 0 : NumUtil.numFormat(_articleCtr.count.value)}条回复')), SizedBox( height: 35, child: TextButton.icon( @@ -857,7 +861,7 @@ class _ArticlePageState extends State foregroundColor: theme.colorScheme.outline, ), label: Text(stat?.count != null - ? Utils.numFormat(stat!.count) + ? NumUtil.numFormat(stat!.count) : text), ); } @@ -1016,7 +1020,7 @@ class _ArticlePageState extends State _articleCtr.stats.value?.like ?.count != null - ? Utils.numFormat( + ? NumUtil.numFormat( _articleCtr.stats.value! .like!.count) : '点赞', diff --git a/lib/pages/article/widgets/article_ops.dart b/lib/pages/article/widgets/article_ops.dart index 3d7fa0762..2ce624840 100644 --- a/lib/pages/article/widgets/article_ops.dart +++ b/lib/pages/article/widgets/article_ops.dart @@ -3,7 +3,7 @@ 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.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/image_util.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -57,7 +57,7 @@ class ArticleOpus extends StatelessWidget { child: ClipRRect( borderRadius: StyleString.mdRadius, child: CachedNetworkImage( - imageUrl: Utils.thumbnailImgUrl(card.url, 60), + imageUrl: ImageUtil.thumbnailUrl(card.url, 60), ), ), ); diff --git a/lib/pages/article/widgets/html_render.dart b/lib/pages/article/widgets/html_render.dart index 1731758d6..8933fbdf6 100644 --- a/lib/pages/article/widgets/html_render.dart +++ b/lib/pages/article/widgets/html_render.dart @@ -1,6 +1,6 @@ import 'package:PiliPlus/models/common/image_preview_type.dart'; import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/image_util.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; @@ -42,7 +42,7 @@ Widget htmlRender({ return CachedNetworkImage( width: maxWidth, height: height != null ? double.parse(height) : null, - imageUrl: Utils.thumbnailImgUrl(imgUrl), + imageUrl: ImageUtil.thumbnailUrl(imgUrl), fit: BoxFit.contain, ); } @@ -63,7 +63,7 @@ Widget htmlRender({ child: CachedNetworkImage( width: size, height: size, - imageUrl: Utils.thumbnailImgUrl(imgUrl, 60), + imageUrl: ImageUtil.thumbnailUrl(imgUrl, 60), fadeInDuration: const Duration(milliseconds: 120), fadeOutDuration: const Duration(milliseconds: 120), placeholder: (context, url) => diff --git a/lib/pages/article/widgets/opus_content.dart b/lib/pages/article/widgets/opus_content.dart index a75779926..b76134c85 100644 --- a/lib/pages/article/widgets/opus_content.dart +++ b/lib/pages/article/widgets/opus_content.dart @@ -11,7 +11,7 @@ import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/pages/dynamics/widgets/vote.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/image_util.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_svg_image/cached_network_svg_image.dart'; import 'package:flutter/foundation.dart' show kDebugMode; @@ -185,7 +185,7 @@ class OpusContent extends StatelessWidget { child: CachedNetworkImage( width: width, height: height, - imageUrl: Utils.thumbnailImgUrl(pic.url!, 60), + imageUrl: ImageUtil.thumbnailUrl(pic.url!, 60), fadeInDuration: const Duration(milliseconds: 120), fadeOutDuration: const Duration(milliseconds: 120), placeholder: (context, url) => @@ -207,7 +207,7 @@ class OpusContent extends StatelessWidget { width: maxWidth, fit: BoxFit.contain, height: element.line!.pic!.height?.toDouble(), - imageUrl: Utils.thumbnailImgUrl(element.line!.pic!.url!), + imageUrl: ImageUtil.thumbnailUrl(element.line!.pic!.url!), ); case 5 when (element.list != null): return SelectionArea( @@ -602,7 +602,7 @@ Widget moduleBlockedItem( image: DecorationImage( fit: BoxFit.fill, image: CachedNetworkImageProvider( - Utils.thumbnailImgUrl( + ImageUtil.thumbnailUrl( Get.isDarkMode ? moduleBlocked.bgImg!.imgDark : moduleBlocked.bgImg!.imgDay, @@ -616,7 +616,7 @@ Widget moduleBlockedItem( return CachedNetworkImage( width: width, fit: BoxFit.contain, - imageUrl: Utils.thumbnailImgUrl( + imageUrl: ImageUtil.thumbnailUrl( Get.isDarkMode ? moduleBlocked.icon!.imgDark : moduleBlocked.icon!.imgDay, diff --git a/lib/pages/article_list/view.dart b/lib/pages/article_list/view.dart index 249cb7e61..e149bac5b 100644 --- a/lib/pages/article_list/view.dart +++ b/lib/pages/article_list/view.dart @@ -9,7 +9,9 @@ import 'package:PiliPlus/models_new/article/article_list/article.dart'; import 'package:PiliPlus/models_new/article/article_list/list.dart'; import 'package:PiliPlus/pages/article_list/controller.dart'; import 'package:PiliPlus/pages/article_list/widgets/item.dart'; +import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/grid.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; @@ -158,11 +160,12 @@ class _ArticleListPageState extends State { TextSpan( children: [ TextSpan( - text: '${Utils.numFormat(item.articlesCount)}篇专栏'), + text: + '${NumUtil.numFormat(item.articlesCount)}篇专栏'), divider, - TextSpan(text: '${Utils.numFormat(item.words)}个字'), + TextSpan(text: '${NumUtil.numFormat(item.words)}个字'), divider, - TextSpan(text: '${Utils.numFormat(item.read)}次阅读'), + TextSpan(text: '${NumUtil.numFormat(item.read)}次阅读'), ], style: style, ), @@ -171,8 +174,7 @@ class _ArticleListPageState extends State { TextSpan( children: [ TextSpan( - text: - '${Utils.dateFormat(item.updateTime, formatType: 'day')}更新'), + text: '${DateUtil.dateFormat(item.updateTime)}更新'), divider, TextSpan(text: '文集号: ${item.id}'), ], diff --git a/lib/pages/blacklist/view.dart b/lib/pages/blacklist/view.dart index 5c431b310..e4b17ebbf 100644 --- a/lib/pages/blacklist/view.dart +++ b/lib/pages/blacklist/view.dart @@ -6,8 +6,8 @@ import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/common/image_type.dart'; import 'package:PiliPlus/models_new/blacklist/list.dart'; import 'package:PiliPlus/pages/blacklist/controller.dart'; +import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/storage.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -89,7 +89,7 @@ class _BlackListPageState extends State { style: const TextStyle(fontSize: 14), ), subtitle: Text( - Utils.dateFormat(item.mtime), + DateUtil.dateFormat(item.mtime), maxLines: 1, style: TextStyle(color: Theme.of(context).colorScheme.outline), diff --git a/lib/pages/dynamics/widgets/action_panel.dart b/lib/pages/dynamics/widgets/action_panel.dart index 1707e9851..c23501ba5 100644 --- a/lib/pages/dynamics/widgets/action_panel.dart +++ b/lib/pages/dynamics/widgets/action_panel.dart @@ -2,8 +2,8 @@ import 'package:PiliPlus/http/dynamics.dart'; import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/pages/dynamics_repost/view.dart'; import 'package:PiliPlus/utils/feed_back.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -97,7 +97,7 @@ class _ActionPanelState extends State { ), label: Text( widget.item.modules.moduleStat!.forward!.count != null - ? Utils.numFormat( + ? NumUtil.numFormat( widget.item.modules.moduleStat!.forward!.count) : '转发', ), @@ -120,7 +120,7 @@ class _ActionPanelState extends State { ), label: Text( widget.item.modules.moduleStat!.comment!.count != null - ? Utils.numFormat( + ? NumUtil.numFormat( widget.item.modules.moduleStat!.comment!.count) : '评论', ), @@ -152,7 +152,7 @@ class _ActionPanelState extends State { }, child: Text( widget.item.modules.moduleStat!.like!.count != null - ? Utils.numFormat( + ? NumUtil.numFormat( widget.item.modules.moduleStat!.like!.count) : '点赞', key: ValueKey( diff --git a/lib/pages/dynamics/widgets/additional_panel.dart b/lib/pages/dynamics/widgets/additional_panel.dart index 1085e0779..2c67c864f 100644 --- a/lib/pages/dynamics/widgets/additional_panel.dart +++ b/lib/pages/dynamics/widgets/additional_panel.dart @@ -5,7 +5,7 @@ import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/models_new/dynamic/dyn_reserve/data.dart'; import 'package:PiliPlus/pages/dynamics/widgets/vote.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -472,7 +472,7 @@ Widget addWidget(ThemeData theme, DynamicItemModel item, BuildContext context, overflow: TextOverflow.ellipsis, ), Text( - '${Utils.numFormat(vote.joinNum)}人参与', + '${NumUtil.numFormat(vote.joinNum)}人参与', maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart index ac6689c8d..bb373ae56 100644 --- a/lib/pages/dynamics/widgets/author_panel.dart +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -8,6 +8,7 @@ import 'package:PiliPlus/http/video.dart'; import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/pages/dynamics/controller.dart'; import 'package:PiliPlus/pages/save_panel/view.dart'; +import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/page_utils.dart'; @@ -59,11 +60,9 @@ class AuthorPanel extends StatelessWidget { final theme = Theme.of(context); final pubTime = item.modules.moduleAuthor?.pubTs != null ? isSave - ? DateTime.fromMillisecondsSinceEpoch( - item.modules.moduleAuthor!.pubTs! * 1000) - .toString() - .substring(0, 19) - : Utils.dateFormat(item.modules.moduleAuthor!.pubTs) + ? DateUtil.format(item.modules.moduleAuthor!.pubTs, + format: DateUtil.longFormatDs) + : DateUtil.dateFormat(item.modules.moduleAuthor!.pubTs) : item.modules.moduleAuthor?.pubTime; return Stack( clipBehavior: Clip.none, diff --git a/lib/pages/dynamics/widgets/module_panel.dart b/lib/pages/dynamics/widgets/module_panel.dart index 8345465f0..7d8d494fc 100644 --- a/lib/pages/dynamics/widgets/module_panel.dart +++ b/lib/pages/dynamics/widgets/module_panel.dart @@ -11,9 +11,9 @@ import 'package:PiliPlus/pages/dynamics/widgets/live_panel.dart'; import 'package:PiliPlus/pages/dynamics/widgets/live_panel_sub.dart'; import 'package:PiliPlus/pages/dynamics/widgets/live_rcmd_panel.dart'; import 'package:PiliPlus/pages/dynamics/widgets/video_panel.dart'; +import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/page_utils.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -130,7 +130,7 @@ Widget module( ), const SizedBox(width: 6), Text( - Utils.dateFormat( + DateUtil.dateFormat( orig.modules.moduleAuthor!.pubTs), style: TextStyle( color: theme.colorScheme.outline, diff --git a/lib/pages/dynamics/widgets/video_panel.dart b/lib/pages/dynamics/widgets/video_panel.dart index a72255083..907eceb3e 100644 --- a/lib/pages/dynamics/widgets/video_panel.dart +++ b/lib/pages/dynamics/widgets/video_panel.dart @@ -4,7 +4,7 @@ import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/models/common/badge_type.dart'; import 'package:PiliPlus/models/dynamics/result.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:flutter/material.dart'; Widget videoSeasonWidget( @@ -120,9 +120,9 @@ Widget videoSeasonWidget( ), const SizedBox(width: 6), ], - Text('${Utils.numFormat(itemContent.stat?.play)}次围观'), + Text('${NumUtil.numFormat(itemContent.stat?.play)}次围观'), const SizedBox(width: 6), - Text('${Utils.numFormat(itemContent.stat?.danmu)}条弹幕'), + Text('${NumUtil.numFormat(itemContent.stat?.danmu)}条弹幕'), const Spacer(), Image.asset( 'assets/images/play.png', diff --git a/lib/pages/dynamics/widgets/vote.dart b/lib/pages/dynamics/widgets/vote.dart index dbe094037..bca888593 100644 --- a/lib/pages/dynamics/widgets/vote.dart +++ b/lib/pages/dynamics/widgets/vote.dart @@ -4,7 +4,8 @@ import 'package:PiliPlus/common/widgets/dialog/report.dart'; import 'package:PiliPlus/http/dynamics.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/dynamics/vote_model.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/date_util.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:flutter/material.dart'; class VotePanel extends StatefulWidget { @@ -56,13 +57,13 @@ class _VotePanelState extends State { runSpacing: 5, children: [ Text( - '至 ${DateTime.fromMillisecondsSinceEpoch(_voteInfo.endTime! * 1000).toString().substring(0, 19)}', + '至 ${DateUtil.format(_voteInfo.endTime, format: DateUtil.longFormatDs)}', ), Text.rich( TextSpan( children: [ TextSpan( - text: Utils.numFormat(_voteInfo.joinNum), + text: NumUtil.numFormat(_voteInfo.joinNum), style: TextStyle(color: theme.colorScheme.primary), ), const TextSpan(text: '人参与'), diff --git a/lib/pages/dynamics_create/view.dart b/lib/pages/dynamics_create/view.dart index a6f2f4b96..1e9eca00a 100644 --- a/lib/pages/dynamics_create/view.dart +++ b/lib/pages/dynamics_create/view.dart @@ -18,6 +18,7 @@ import 'package:PiliPlus/pages/dynamics_select_topic/controller.dart'; import 'package:PiliPlus/pages/dynamics_select_topic/view.dart'; import 'package:PiliPlus/pages/emote/controller.dart'; import 'package:PiliPlus/pages/emote/view.dart'; +import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/request_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:flutter/foundation.dart' show kDebugMode; @@ -25,7 +26,6 @@ import 'package:flutter/material.dart' hide DraggableScrollableSheet; import 'package:flutter/services.dart' show LengthLimitingTextInputFormatter; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:intl/intl.dart'; class CreateDynPanel extends CommonPublishPage { const CreateDynPanel({ @@ -524,8 +524,7 @@ class _CreateDynPanelState extends CommonPublishPageState { visualDensity: VisualDensity.compact, ), onPressed: () => _publishTime.value = null, - label: - Text(DateFormat('yyyy-MM-dd HH:mm').format(_publishTime.value!)), + label: Text(DateUtil.longFormatD.format(_publishTime.value!)), icon: const Icon(Icons.clear, size: 20), iconAlignment: IconAlignment.end, ); diff --git a/lib/pages/dynamics_detail/view.dart b/lib/pages/dynamics_detail/view.dart index 5141c0379..567a12c20 100644 --- a/lib/pages/dynamics_detail/view.dart +++ b/lib/pages/dynamics_detail/view.dart @@ -19,6 +19,7 @@ import 'package:PiliPlus/pages/video/reply/widgets/reply_item_grpc.dart'; import 'package:PiliPlus/pages/video/reply_reply/view.dart'; import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/grid.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/request_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; @@ -517,7 +518,7 @@ class _DynamicDetailPageState extends State _controller.dynItem.modules.moduleStat ?.forward?.count != null - ? Utils.numFormat(_controller + ? NumUtil.numFormat(_controller .dynItem .modules .moduleStat! @@ -608,7 +609,7 @@ class _DynamicDetailPageState extends State ?.like ?.count != null - ? Utils.numFormat(_controller + ? NumUtil.numFormat(_controller .dynItem .modules .moduleStat! @@ -660,7 +661,7 @@ class _DynamicDetailPageState extends State return ScaleTransition(scale: animation, child: child); }, child: Text( - '${_controller.count.value == -1 ? 0 : Utils.numFormat(_controller.count.value)}条回复', + '${_controller.count.value == -1 ? 0 : NumUtil.numFormat(_controller.count.value)}条回复', key: ValueKey(_controller.count.value), ), ), @@ -725,9 +726,9 @@ class _DynamicDetailPageState extends State replyLevel: 1, replyReply: (replyItem, id) => replyReply(context, replyItem, id), - onReply: () => _controller.onReply( + onReply: (replyItem) => _controller.onReply( context, - replyItem: response[index], + replyItem: replyItem, index: index, ), onDelete: (subIndex) => diff --git a/lib/pages/dynamics_select_topic/widgets/item.dart b/lib/pages/dynamics_select_topic/widgets/item.dart index e5dfd845c..2545b091c 100644 --- a/lib/pages/dynamics_select_topic/widgets/item.dart +++ b/lib/pages/dynamics_select_topic/widgets/item.dart @@ -1,6 +1,6 @@ import 'package:PiliPlus/common/widgets/custom_icon.dart'; import 'package:PiliPlus/models_new/dynamic/dyn_topic_top/topic_item.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:flutter/material.dart'; class DynTopicItem extends StatelessWidget { @@ -43,7 +43,7 @@ class DynTopicItem extends StatelessWidget { subtitle: Padding( padding: const EdgeInsets.only(left: 23), child: Text( - '${Utils.numFormat(item.view)}浏览 · ${Utils.numFormat(item.discuss)}讨论', + '${NumUtil.numFormat(item.view)}浏览 · ${NumUtil.numFormat(item.discuss)}讨论', style: TextStyle(color: Theme.of(context).colorScheme.outline), ), ), diff --git a/lib/pages/dynamics_topic/view.dart b/lib/pages/dynamics_topic/view.dart index f23fe30c0..cac15290a 100644 --- a/lib/pages/dynamics_topic/view.dart +++ b/lib/pages/dynamics_topic/view.dart @@ -15,6 +15,7 @@ import 'package:PiliPlus/pages/dynamics_create/view.dart'; import 'package:PiliPlus/pages/dynamics_tab/view.dart'; import 'package:PiliPlus/pages/dynamics_topic/controller.dart'; import 'package:PiliPlus/utils/grid.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/utils.dart'; @@ -221,7 +222,7 @@ class _DynTopicPageState extends State { Row( children: [ Text( - '${Utils.numFormat(response.topicItem!.view)}浏览 · ${Utils.numFormat(response.topicItem!.discuss)}讨论', + '${NumUtil.numFormat(response.topicItem!.view)}浏览 · ${NumUtil.numFormat(response.topicItem!.discuss)}讨论', style: TextStyle( fontSize: 13, color: theme.colorScheme.outline, @@ -248,7 +249,7 @@ class _DynTopicPageState extends State { ? const Icon(FontAwesomeIcons.solidThumbsUp, size: 13) : const Icon(FontAwesomeIcons.thumbsUp, size: 13), label: Text( - Utils.numFormat(response.topicItem!.like), + NumUtil.numFormat(response.topicItem!.like), style: const TextStyle(fontSize: 13), textScaler: TextScaler.noScaling, ), @@ -274,7 +275,7 @@ class _DynTopicPageState extends State { ? const Icon(FontAwesomeIcons.solidStar, size: 13) : const Icon(FontAwesomeIcons.star, size: 13), label: Text( - Utils.numFormat(response.topicItem!.fav), + NumUtil.numFormat(response.topicItem!.fav), style: const TextStyle(fontSize: 13), textScaler: TextScaler.noScaling, ), diff --git a/lib/pages/episode_panel/view.dart b/lib/pages/episode_panel/view.dart index 081935251..f8e633fcc 100644 --- a/lib/pages/episode_panel/view.dart +++ b/lib/pages/episode_panel/view.dart @@ -24,6 +24,8 @@ import 'package:PiliPlus/pages/common/common_slide_page.dart'; import 'package:PiliPlus/pages/video/controller.dart'; import 'package:PiliPlus/pages/video/introduction/ugc/controller.dart'; import 'package:PiliPlus/pages/video/introduction/ugc/widgets/page.dart'; +import 'package:PiliPlus/utils/date_util.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/utils.dart'; @@ -438,7 +440,7 @@ class _EpisodePanelState extends CommonSlidePageState { ), if (duration != null && duration > 0) PBadge( - text: Utils.timeFormat(duration), + text: DurationUtil.formatDuration(duration), right: 6.0, bottom: 6.0, type: PBadgeType.gray, @@ -488,7 +490,7 @@ class _EpisodePanelState extends CommonSlidePageState { ), if (pubdate != null) Text( - Utils.dateFormat(pubdate), + DateUtil.format(pubdate), maxLines: 1, style: TextStyle( fontSize: 12, diff --git a/lib/pages/fav/video/widgets/item.dart b/lib/pages/fav/video/widgets/item.dart index ca9d4d48e..f45aed827 100644 --- a/lib/pages/fav/video/widgets/item.dart +++ b/lib/pages/fav/video/widgets/item.dart @@ -2,7 +2,7 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/image/image_save.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/models_new/fav/fav_folder/list.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/fav_util.dart'; import 'package:flutter/material.dart'; class FavVideoItem extends StatelessWidget { @@ -94,7 +94,7 @@ class FavVideoItem extends StatelessWidget { ), const Spacer(), Text( - Utils.isPublicFavText(item.attr), + FavUtil.isPublicFavText(item.attr), style: TextStyle( fontSize: fontSize, color: color, diff --git a/lib/pages/fav_create/view.dart b/lib/pages/fav_create/view.dart index b1490089a..ada9cfc1a 100644 --- a/lib/pages/fav_create/view.dart +++ b/lib/pages/fav_create/view.dart @@ -2,7 +2,8 @@ import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/http/fav.dart'; import 'package:PiliPlus/http/msg.dart'; import 'package:PiliPlus/models_new/fav/fav_folder/list.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/fav_util.dart'; +import 'package:PiliPlus/utils/image_util.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; @@ -45,7 +46,7 @@ class _CreateFavPageState extends State { FavFolderInfo data = res['data']; _titleController.text = data.title; _introController.text = data.intro ?? ''; - _isPublic = Utils.isPublicFav(data.attr); + _isPublic = FavUtil.isPublicFav(data.attr); _cover = data.cover; _attr = data.attr; } else { @@ -173,7 +174,7 @@ class _CreateFavPageState extends State { Widget _buildBody(ThemeData theme) => SingleChildScrollView( child: Column( children: [ - if (_attr == null || !Utils.isDefaultFav(_attr!)) ...[ + if (_attr == null || !FavUtil.isDefaultFav(_attr!)) ...[ ListTile( tileColor: theme.colorScheme.onInverseSurface, onTap: () { @@ -239,7 +240,7 @@ class _CreateFavPageState extends State { borderRadius: const BorderRadius.all(Radius.circular(6)), child: CachedNetworkImage( - imageUrl: Utils.thumbnailImgUrl(_cover!), + imageUrl: ImageUtil.thumbnailUrl(_cover!), height: constraints.maxHeight, width: constraints.maxHeight * 16 / 9, fit: BoxFit.cover, @@ -287,11 +288,11 @@ class _CreateFavPageState extends State { ), title: TextField( autofocus: true, - readOnly: _attr != null && Utils.isDefaultFav(_attr!), + readOnly: _attr != null && FavUtil.isDefaultFav(_attr!), controller: _titleController, style: TextStyle( fontSize: 14, - color: _attr != null && Utils.isDefaultFav(_attr!) + color: _attr != null && FavUtil.isDefaultFav(_attr!) ? theme.colorScheme.outline : null, ), @@ -314,7 +315,7 @@ class _CreateFavPageState extends State { ), ), const SizedBox(height: 16), - if (_attr == null || !Utils.isDefaultFav(_attr!)) ...[ + if (_attr == null || !FavUtil.isDefaultFav(_attr!)) ...[ ListTile( tileColor: theme.colorScheme.onInverseSurface, title: Row( diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index bf53f8c86..fe24e152a 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -13,6 +13,7 @@ import 'package:PiliPlus/models_new/fav/fav_folder/list.dart'; import 'package:PiliPlus/pages/dynamics_repost/view.dart'; import 'package:PiliPlus/pages/fav_detail/controller.dart'; import 'package:PiliPlus/pages/fav_detail/widget/fav_video_card.dart'; +import 'package:PiliPlus/utils/fav_util.dart'; import 'package:PiliPlus/utils/grid.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/request_utils.dart'; @@ -149,7 +150,7 @@ class _FavDetailPageState extends State { ), Obx(() { final attr = _favDetailController.folderInfo.value.attr; - return attr == -1 || !Utils.isPublicFav(attr) + return attr == -1 || !FavUtil.isPublicFav(attr) ? const SizedBox.shrink() : IconButton( iconSize: 22, @@ -186,7 +187,7 @@ class _FavDetailPageState extends State { _favDetailController.onFav(folderInfo.favState == 1), child: Text('${folderInfo.favState == 1 ? '取消' : ''}收藏'), ), - if (Utils.isPublicFav(folderInfo.attr)) + if (FavUtil.isPublicFav(folderInfo.attr)) PopupMenuItem( onTap: () => showModalBottomSheet( context: context, @@ -207,7 +208,7 @@ class _FavDetailPageState extends State { onTap: _favDetailController.cleanFav, child: const Text('清除失效内容'), ), - if (!Utils.isDefaultFav(folderInfo.attr)) ...[ + if (!FavUtil.isDefaultFav(folderInfo.attr)) ...[ const PopupMenuDivider(height: 12), PopupMenuItem( onTap: () => showConfirmDialog( @@ -392,7 +393,7 @@ class _FavDetailPageState extends State { child: Align( alignment: Alignment.bottomLeft, child: Text( - '共${folderInfo.mediaCount}条视频 · ${Utils.isPublicFavText(folderInfo.attr)}', + '共${folderInfo.mediaCount}条视频 · ${FavUtil.isPublicFavText(folderInfo.attr)}', textAlign: TextAlign.end, style: style, ), diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index 8d575d453..261173cf6 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -7,8 +7,9 @@ import 'package:PiliPlus/common/widgets/stat/stat.dart'; import 'package:PiliPlus/models/common/badge_type.dart'; import 'package:PiliPlus/models/common/stat_type.dart'; import 'package:PiliPlus/models_new/fav/fav_detail/media.dart'; +import 'package:PiliPlus/utils/date_util.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -88,7 +89,7 @@ class FavVideoCardH extends StatelessWidget { height: maxHeight, ), PBadge( - text: Utils.timeFormat(item.duration), + text: DurationUtil.formatDuration(item.duration), right: 6.0, bottom: 6.0, type: PBadgeType.gray, @@ -145,7 +146,7 @@ class FavVideoCardH extends StatelessWidget { ), const Spacer(), Text( - '${Utils.dateFormat(item.favTime)} ${item.upper?.name}', + '${DateUtil.dateFormat(item.favTime)} ${item.upper?.name}', maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( diff --git a/lib/pages/fav_panel/view.dart b/lib/pages/fav_panel/view.dart index 2b8f39c13..5fbb8cb56 100644 --- a/lib/pages/fav_panel/view.dart +++ b/lib/pages/fav_panel/view.dart @@ -1,7 +1,7 @@ import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/models_new/fav/fav_folder/data.dart'; +import 'package:PiliPlus/utils/fav_util.dart'; import 'package:PiliPlus/utils/feed_back.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -94,7 +94,7 @@ class _FavPanelState extends State { 1, index), dense: true, - leading: Utils.isPublicFav(widget + leading: FavUtil.isPublicFav(widget .ctr.favFolderData.value.list[index].attr) ? const Icon(Icons.folder_outlined) : const Icon(Icons.lock_outline), @@ -102,7 +102,7 @@ class _FavPanelState extends State { title: Text(widget .ctr.favFolderData.value.list[index].title!), subtitle: Text( - '${widget.ctr.favFolderData.value.list[index].mediaCount}个内容 . ${Utils.isPublicFavText(widget.ctr.favFolderData.value.list[index].attr)}', + '${widget.ctr.favFolderData.value.list[index].mediaCount}个内容 . ${FavUtil.isPublicFavText(widget.ctr.favFolderData.value.list[index].attr)}', ), trailing: Transform.scale( scale: 0.9, diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index bde71ffa6..a6c36658e 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -10,6 +10,8 @@ import 'package:PiliPlus/models/common/history_business_type.dart'; import 'package:PiliPlus/models_new/history/list.dart'; import 'package:PiliPlus/pages/common/multi_select_controller.dart'; import 'package:PiliPlus/pages/history/base_controller.dart'; +import 'package:PiliPlus/utils/date_util.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart'; @@ -131,7 +133,7 @@ class HistoryItem extends StatelessWidget { PBadge( text: item.progress == -1 ? '已看完' - : '${Utils.timeFormat(item.progress)}/${Utils.timeFormat(item.duration!)}', + : '${DurationUtil.formatDuration(item.progress)}/${DurationUtil.formatDuration(item.duration)}', right: 6.0, bottom: 8.0, type: PBadgeType.gray, @@ -332,7 +334,7 @@ class HistoryItem extends StatelessWidget { ), ), Text( - Utils.dateFormat(item.viewAt!), + DateUtil.chatFormat(item.viewAt!, isHistory: true), style: TextStyle( fontSize: theme.textTheme.labelMedium!.fontSize, color: theme.colorScheme.outline, diff --git a/lib/pages/hot/view.dart b/lib/pages/hot/view.dart index 6f484b155..3fbb17b8f 100644 --- a/lib/pages/hot/view.dart +++ b/lib/pages/hot/view.dart @@ -11,7 +11,7 @@ import 'package:PiliPlus/pages/home/controller.dart'; import 'package:PiliPlus/pages/hot/controller.dart'; import 'package:PiliPlus/pages/rank/view.dart'; import 'package:PiliPlus/utils/grid.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/image_util.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -43,7 +43,7 @@ class _HotPageState extends CommonPageState mainAxisSize: MainAxisSize.min, children: [ CachedNetworkImage( - width: 35, height: 35, imageUrl: Utils.thumbnailImgUrl(iconUrl)), + width: 35, height: 35, imageUrl: ImageUtil.thumbnailUrl(iconUrl)), const SizedBox(height: 4), Text( title, diff --git a/lib/pages/later/widgets/video_card_h_later.dart b/lib/pages/later/widgets/video_card_h_later.dart index e3c479aae..7881a428d 100644 --- a/lib/pages/later/widgets/video_card_h_later.dart +++ b/lib/pages/later/widgets/video_card_h_later.dart @@ -9,8 +9,8 @@ import 'package:PiliPlus/models/common/badge_type.dart'; import 'package:PiliPlus/models/common/stat_type.dart'; import 'package:PiliPlus/models/search/result.dart'; import 'package:PiliPlus/models_new/later/list.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -104,7 +104,7 @@ class VideoCardHLater extends StatelessWidget { PBadge( text: progress == -1 ? '已看完' - : '${Utils.timeFormat(progress)}/${Utils.timeFormat(videoItem.duration)}', + : '${DurationUtil.formatDuration(progress)}/${DurationUtil.formatDuration(videoItem.duration)}', right: 6, bottom: 8, type: PBadgeType.gray, @@ -121,7 +121,8 @@ class VideoCardHLater extends StatelessWidget { ) ] else if (videoItem.duration! > 0) PBadge( - text: Utils.timeFormat(videoItem.duration), + text: + DurationUtil.formatDuration(videoItem.duration), right: 6.0, bottom: 6.0, type: PBadgeType.gray, diff --git a/lib/pages/live/widgets/live_item_app.dart b/lib/pages/live/widgets/live_item_app.dart index 9e114bee9..ef2e9b0e6 100644 --- a/lib/pages/live/widgets/live_item_app.dart +++ b/lib/pages/live/widgets/live_item_app.dart @@ -2,7 +2,7 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/image/image_save.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/models_new/live/live_feed_index/card_data_list_item.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -127,7 +127,7 @@ class LiveCardVApp extends StatelessWidget { ), if (item.watchedShow?.textSmall != null) Text( - '${Utils.numFormat(item.watchedShow!.textSmall)}围观', + '${NumUtil.numFormat(item.watchedShow!.textSmall)}围观', style: const TextStyle(fontSize: 11, color: Colors.white), ), ], diff --git a/lib/pages/live_search/widgets/live_search_room.dart b/lib/pages/live_search/widgets/live_search_room.dart index c41363eaf..79a5e8af8 100644 --- a/lib/pages/live_search/widgets/live_search_room.dart +++ b/lib/pages/live_search/widgets/live_search_room.dart @@ -2,7 +2,7 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/image/image_save.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/models_new/live/live_search/room_item.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -98,7 +98,7 @@ class LiveCardVSearch extends StatelessWidget { ), if (item.watchedShow?.textSmall != null) Text( - '${Utils.numFormat(item.watchedShow!.textSmall)}围观', + '${NumUtil.numFormat(item.watchedShow!.textSmall)}围观', style: const TextStyle(fontSize: 11, color: Colors.white), ), ], diff --git a/lib/pages/live_search/widgets/live_search_user.dart b/lib/pages/live_search/widgets/live_search_user.dart index 425311178..3ad67e258 100644 --- a/lib/pages/live_search/widgets/live_search_user.dart +++ b/lib/pages/live_search/widgets/live_search_user.dart @@ -1,7 +1,7 @@ import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/models/common/image_type.dart'; import 'package:PiliPlus/models_new/live/live_search/user_item.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -57,7 +57,7 @@ class LiveSearchUserItem extends StatelessWidget { ), const SizedBox(height: 2), Text( - '分区: ${item.areaName ?? ''} 关注数: ${Utils.numFormat(item.fansNum ?? 0)}', + '分区: ${item.areaName ?? ''} 关注数: ${NumUtil.numFormat(item.fansNum ?? 0)}', style: style, ), ], diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index 43c26b774..5706b7e60 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -3,6 +3,7 @@ import 'dart:ui'; import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/scroll_physics.dart'; import 'package:PiliPlus/pages/login/controller.dart'; +import 'package:PiliPlus/utils/image_util.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; @@ -55,8 +56,7 @@ class _LoginPageState extends State { Uint8List pngBytes = byteData!.buffer.asUint8List(); SmartDialog.dismiss(); SmartDialog.showLoading(msg: '正在保存至图库'); - String picName = - "PiliPlus_loginQRCode_${DateTime.now().toString().replaceAll(' ', '_').replaceAll(':', '-').split('.').first}"; + String picName = "PiliPlus_loginQRCode_${ImageUtil.time}"; final SaveResult result = await SaverGallery.saveImage( Uint8List.fromList(pngBytes), fileName: picName, diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index a1c9aeab8..07c843161 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -10,7 +10,7 @@ import 'package:PiliPlus/models_new/msgfeed_unread/data.dart'; import 'package:PiliPlus/models_new/single_unread/data.dart'; import 'package:PiliPlus/services/account_service.dart'; import 'package:PiliPlus/utils/storage.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/update.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -45,7 +45,7 @@ class MainController extends GetxController void onInit() { super.onInit(); if (GStorage.autoUpdate) { - Utils.checkUpdate(); + Update.checkUpdate(); } setNavBarConfig(); diff --git a/lib/pages/match_info/controller.dart b/lib/pages/match_info/controller.dart new file mode 100644 index 000000000..2b4848a5a --- /dev/null +++ b/lib/pages/match_info/controller.dart @@ -0,0 +1,48 @@ +import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'; +import 'package:PiliPlus/grpc/reply.dart'; +import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/http/match.dart'; +import 'package:PiliPlus/models_new/match/match_info/contest.dart'; +import 'package:PiliPlus/pages/common/reply_controller.dart'; +import 'package:get/get.dart'; + +class MatchInfoController extends ReplyController { + final int cid = int.parse(Get.parameters['cid']!); + + int get replyType => 27; + + @override + dynamic get sourceId => cid.toString(); + + final Rx> infoState = + LoadingState.loading().obs; + + @override + void onInit() { + super.onInit(); + getMatchInfo(); + } + + Future getMatchInfo() async { + var res = await MatchHttp.matchInfo(cid); + if (res.isSuccess) { + queryData(); + } + infoState.value = res; + } + + @override + List? getDataList(MainListReply response) { + return response.replies; + } + + @override + Future> customGetData() => ReplyGrpc.mainList( + type: replyType, + oid: cid, + mode: mode.value, + cursorNext: cursorNext, + offset: paginationReply?.nextOffset, + antiGoodsReply: antiGoodsReply, + ); +} diff --git a/lib/pages/match_info/view.dart b/lib/pages/match_info/view.dart new file mode 100644 index 000000000..103bbb6bf --- /dev/null +++ b/lib/pages/match_info/view.dart @@ -0,0 +1,317 @@ +import 'package:PiliPlus/common/skeleton/video_reply.dart'; +import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.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/refresh_indicator.dart'; +import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart' + show ReplyInfo; +import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/models/common/image_type.dart'; +import 'package:PiliPlus/models/common/reply/reply_sort_type.dart'; +import 'package:PiliPlus/models_new/match/match_info/contest.dart'; +import 'package:PiliPlus/models_new/match/match_info/team.dart'; +import 'package:PiliPlus/pages/match_info/controller.dart'; +import 'package:PiliPlus/pages/video/reply/widgets/reply_item_grpc.dart'; +import 'package:PiliPlus/pages/video/reply_reply/view.dart'; +import 'package:PiliPlus/utils/date_util.dart'; +import 'package:PiliPlus/utils/feed_back.dart'; +import 'package:PiliPlus/utils/num_util.dart'; +import 'package:PiliPlus/utils/utils.dart'; +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; + +class MatchInfoPage extends StatefulWidget { + const MatchInfoPage({super.key}); + + @override + State createState() => _MatchInfoPageState(); +} + +class _MatchInfoPageState extends State { + final _controller = + Get.put(MatchInfoController(), tag: Utils.generateRandomString(8)); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + return Scaffold( + appBar: AppBar(title: const Text('比赛详情')), + body: SafeArea( + bottom: false, + child: refreshIndicator( + onRefresh: _controller.onRefresh, + child: CustomScrollView( + controller: _controller.scrollController, + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + Obx(() => _buildInfo(theme, _controller.infoState.value)), + SliverPadding( + padding: EdgeInsets.only( + bottom: MediaQuery.paddingOf(context).bottom + 80), + sliver: Obx( + () => _buildReply(theme, _controller.loadingState.value)), + ) + ], + ), + ), + ), + floatingActionButton: FloatingActionButton( + heroTag: null, + onPressed: () { + feedBack(); + _controller.onReply( + context, + oid: _controller.cid, + replyType: _controller.replyType, + ); + }, + tooltip: '评论动态', + child: const Icon(Icons.reply), + ), + ); + } + + Widget _buildInfo(ThemeData theme, LoadingState infoState) { + if (infoState.isSuccess) { + MatchContest? data = infoState.dataOrNull; + if (data != null) { + try { + Widget teamInfo(MatchTeam team) { + return Column( + spacing: 5, + mainAxisSize: MainAxisSize.min, + children: [ + NetworkImgLayer( + width: 50, + height: 50, + src: 'https://i1.hdslb.com${team.logo}', + type: ImageType.emote, + ), + Text(team.title!) + ], + ); + } + + return SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Column( + spacing: 12, + mainAxisSize: MainAxisSize.min, + children: [ + Center( + child: Text( + '${data.season?.title ?? ''} ${data.gameStage ?? ''}'), + ), + Row( + spacing: 20, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (data.homeId != 0) + Expanded( + child: Align( + alignment: const Alignment(0.8, 1), + child: teamInfo(data.homeTeam!), + ), + ), + Column( + spacing: 10, + mainAxisSize: MainAxisSize.min, + children: [ + if (data.homeId != 0) + Text( + data.contestStatus == 1 + ? 'VS' + : '${data.homeScore} : ${data.awayScore}', + style: const TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + ), + ) + else if (data.season?.logo != null) + NetworkImgLayer( + width: 50, + height: 50, + src: 'https://i1.hdslb.com${data.season!.logo}', + type: ImageType.emote, + ), + if (data.contestStatus == 2) + FilledButton.tonal( + style: FilledButton.styleFrom( + padding: + const EdgeInsets.symmetric(horizontal: 12), + shape: const RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(6))), + visualDensity: VisualDensity.compact, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + onPressed: () => Get.toNamed( + '/liveRoom?roomid=${data.liveRoom}'), + child: const Text('看直播'), + ) + else if (data.contestStatus == 3) + Text( + '${DateUtil.dateFormat(data.stime)}${data.contestStatus == 3 ? ' 已结束' : ''}', + style: + TextStyle(color: theme.colorScheme.outline), + ) + else if (data.contestStatus == 1) + Text( + DateUtil.format(data.stime, + format: DateFormat('yy-MM-dd HH:mm')), + style: + TextStyle(color: theme.colorScheme.outline), + ), + ], + ), + if (data.awayId != 0) + Expanded( + child: Align( + alignment: const Alignment(-0.8, -1), + child: teamInfo(data.awayTeam!), + ), + ), + ], + ) + ], + ), + ), + ); + } catch (_) { + return const SliverToBoxAdapter(); + } + } + } + return const SliverToBoxAdapter(); + } + + Widget _buildReply( + ThemeData theme, LoadingState?> loadingState) { + return switch (loadingState) { + Loading() => SliverList.builder( + itemBuilder: (context, index) => const VideoReplySkeleton(), + itemCount: 8, + ), + Success(:var response) => response?.isNotEmpty == true + ? SliverMainAxisGroup( + slivers: [ + _buildHeader(theme), + SliverList.builder( + itemCount: response!.length, + itemBuilder: (context, index) { + if (index == response.length - 1) { + _controller.onLoadMore(); + } + return ReplyItemGrpc( + replyItem: response[index], + replyLevel: 1, + replyReply: (replyItem, id) => + replyReply(context, replyItem, id), + onReply: (replyItem) => _controller.onReply( + context, + replyItem: replyItem, + index: index, + ), + onDelete: (subIndex) => + _controller.onRemove(index, subIndex), + upMid: _controller.upMid, + onCheckReply: (item) => _controller + .onCheckReply(context, item, isManual: true), + onToggleTop: (isUpTop, rpid) => _controller.onToggleTop( + index, + _controller.cid, + _controller.replyType, + isUpTop, + rpid, + ), + ); + }, + ) + ], + ) + : HttpError(onReload: _controller.onReload), + Error(:var errMsg) => HttpError( + errMsg: errMsg, + onReload: _controller.onReload, + ), + }; + } + + void replyReply(BuildContext context, ReplyInfo replyItem, int? id) { + EasyThrottle.throttle('replyReply', const Duration(milliseconds: 500), () { + int oid = replyItem.oid.toInt(); + int rpid = replyItem.id.toInt(); + Get.to( + Scaffold( + appBar: AppBar(title: const Text('评论详情')), + body: SafeArea( + top: false, + bottom: false, + child: VideoReplyReplyPanel( + enableSlide: false, + id: id, + oid: oid, + rpid: rpid, + isVideoDetail: false, + replyType: _controller.replyType, + firstFloor: replyItem, + ), + ), + ), + ); + }); + } + + Widget _buildHeader(ThemeData theme) { + return SliverPersistentHeader( + delegate: CustomSliverPersistentHeaderDelegate( + bgColor: theme.colorScheme.surface, + child: Container( + height: 45, + padding: const EdgeInsets.only(left: 12, right: 6), + child: Row( + children: [ + Obx( + () => AnimatedSwitcher( + duration: const Duration(milliseconds: 400), + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition(scale: animation, child: child); + }, + child: Text( + '${_controller.count.value == -1 ? 0 : NumUtil.numFormat(_controller.count.value)}条回复', + key: ValueKey(_controller.count.value), + ), + ), + ), + const Spacer(), + SizedBox( + height: 35, + child: TextButton.icon( + onPressed: () => _controller.queryBySort(), + icon: Icon( + Icons.sort, + size: 16, + color: theme.colorScheme.secondary, + ), + label: Obx(() => Text( + _controller.sortType.value.label, + style: TextStyle( + fontSize: 13, + color: theme.colorScheme.secondary, + ), + )), + ), + ) + ], + ), + ), + ), + pinned: true, + ); + } +} diff --git a/lib/pages/media/widgets/item.dart b/lib/pages/media/widgets/item.dart index d5f0debeb..f145bf2d7 100644 --- a/lib/pages/media/widgets/item.dart +++ b/lib/pages/media/widgets/item.dart @@ -1,6 +1,6 @@ import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/models_new/fav/fav_folder/list.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/fav_util.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -73,7 +73,7 @@ class FavFolderItem extends StatelessWidget { maxLines: 1, ), Text( - ' 共${item.mediaCount}条视频 · ${Utils.isPublicFavText(item.attr)}', + ' 共${item.mediaCount}条视频 · ${FavUtil.isPublicFavText(item.attr)}', style: theme.textTheme.labelSmall! .copyWith(color: theme.colorScheme.outline), ) diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index 72b9dfd63..3e4e8f755 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -11,6 +11,7 @@ import 'package:PiliPlus/models_new/space/space/setting.dart'; import 'package:PiliPlus/models_new/space/space/tab2.dart'; import 'package:PiliPlus/pages/common/common_data_controller.dart'; import 'package:PiliPlus/services/account_service.dart'; +import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/request_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/utils.dart'; @@ -19,7 +20,6 @@ import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart' import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:intl/intl.dart'; class MemberController extends CommonDataController with GetTickerProviderStateMixin { @@ -91,7 +91,7 @@ class MemberController extends CommonDataController } else if (data.card!.endTime! > DateTime.now().millisecondsSinceEpoch ~/ 1000) { endTime = - ':至 ${DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.fromMillisecondsSinceEpoch(data.card!.endTime! * 1000))}'; + ':至 ${DateUtil.longFormatDs.format(DateTime.fromMillisecondsSinceEpoch(data.card!.endTime! * 1000))}'; } } tab2?.retainWhere((item) => implTabs.contains(item.param)); diff --git a/lib/pages/member/widget/user_info_card.dart b/lib/pages/member/widget/user_info_card.dart index dcd8153fc..0b9b34657 100644 --- a/lib/pages/member/widget/user_info_card.dart +++ b/lib/pages/member/widget/user_info_card.dart @@ -5,6 +5,8 @@ import 'package:PiliPlus/models_new/space/space/card.dart'; import 'package:PiliPlus/models_new/space/space/images.dart'; import 'package:PiliPlus/models_new/space/space/live.dart'; import 'package:PiliPlus/utils/extension.dart'; +import 'package:PiliPlus/utils/image_util.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart' show Accounts; import 'package:PiliPlus/utils/utils.dart'; @@ -55,7 +57,7 @@ class UserInfoCard extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Text( - Utils.numFormat(count), + NumUtil.numFormat(count), style: const TextStyle(fontSize: 14), ), Text( @@ -86,7 +88,7 @@ class UserInfoCard extends StatelessWidget { imgList: [SourceModel(url: imgUrl)], ), child: CachedNetworkImage( - imageUrl: Utils.thumbnailImgUrl(imgUrl), + imageUrl: ImageUtil.thumbnailUrl(imgUrl), width: double.infinity, height: 135, imageBuilder: (context, imageProvider) => DecoratedBox( @@ -164,7 +166,7 @@ class UserInfoCard extends StatelessWidget { ), if (card.nameplate?.imageSmall?.isNotEmpty == true) CachedNetworkImage( - imageUrl: Utils.thumbnailImgUrl(card.nameplate!.imageSmall!), + imageUrl: ImageUtil.thumbnailUrl(card.nameplate!.imageSmall!), height: 20, placeholder: (context, url) { return const SizedBox.shrink(); @@ -488,13 +490,13 @@ class UserInfoCard extends StatelessWidget { children: [ if (isDark && card.prInfo?.iconNight?.isNotEmpty == true) ...[ CachedNetworkImage( - imageUrl: Utils.thumbnailImgUrl(card.prInfo!.iconNight!), + imageUrl: ImageUtil.thumbnailUrl(card.prInfo!.iconNight!), height: 20, ), const SizedBox(width: 16), ] else if (card.prInfo?.icon?.isNotEmpty == true) ...[ CachedNetworkImage( - imageUrl: Utils.thumbnailImgUrl(card.prInfo!.icon!), + imageUrl: ImageUtil.thumbnailUrl(card.prInfo!.icon!), height: 20, ), const SizedBox(width: 16), diff --git a/lib/pages/member_coin_arc/widgets/item.dart b/lib/pages/member_coin_arc/widgets/item.dart index dfae19208..dafca4fdb 100644 --- a/lib/pages/member_coin_arc/widgets/item.dart +++ b/lib/pages/member_coin_arc/widgets/item.dart @@ -3,10 +3,13 @@ import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/image/image_save.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/stat/stat.dart'; +import 'package:PiliPlus/common/widgets/video_card/video_card_v.dart'; import 'package:PiliPlus/http/search.dart'; import 'package:PiliPlus/models/common/badge_type.dart'; import 'package:PiliPlus/models/common/stat_type.dart'; import 'package:PiliPlus/models_new/member/coin_like_arc/item.dart'; +import 'package:PiliPlus/utils/date_util.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; @@ -73,7 +76,7 @@ class MemberCoinLikeItem extends StatelessWidget { bottom: 6, right: 6, type: PBadgeType.gray, - text: Utils.timeFormat(item.duration), + text: DurationUtil.formatDuration(item.duration), ) ], ); @@ -104,8 +107,11 @@ class MemberCoinLikeItem extends StatelessWidget { ), const Spacer(), Text( - Utils.customStampStr( - timestamp: item.ctime, date: 'MM-DD'), + DateUtil.dateFormat( + item.ctime, + shortFormat: VideoCardV.shortFormat, + longFormat: VideoCardV.longFormat, + ), style: TextStyle( fontSize: 11, color: Theme.of(context).colorScheme.outline, diff --git a/lib/pages/member_favorite/widget/item.dart b/lib/pages/member_favorite/widget/item.dart index 1798b2fc6..18b38cde0 100644 --- a/lib/pages/member_favorite/widget/item.dart +++ b/lib/pages/member_favorite/widget/item.dart @@ -4,6 +4,8 @@ import 'package:PiliPlus/common/widgets/image/image_save.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/models_new/space/space_fav/list.dart'; import 'package:PiliPlus/models_new/sub/sub/list.dart'; +import 'package:PiliPlus/utils/fav_util.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -26,7 +28,7 @@ class MemberFavItem extends StatelessWidget { return; } - if (item.type == 0) { + if (item.type == 0 || item.type == 11) { var res = await Get.toNamed( '/favDetail', parameters: { @@ -109,11 +111,11 @@ class MemberFavItem extends StatelessWidget { const Spacer(), Text( item.type == 0 - ? '${item.mediaCount}个内容 · ${Utils.isPublicFavText(item.attr)}' + ? '${item.mediaCount}个内容 · ${FavUtil.isPublicFavText(item.attr)}' : item.type == 11 ? '${item.mediaCount}个内容 · ${item.upper?.name}' : item.type == 21 - ? '创建者: ${item.upper?.name}\n${item.mediaCount}个视频 · ${Utils.numFormat(item.viewCount)}播放' + ? '创建者: ${item.upper?.name}\n${item.mediaCount}个视频 · ${NumUtil.numFormat(item.viewCount)}播放' : '${item.mediaCount}个内容', style: TextStyle( fontSize: 12, diff --git a/lib/pages/member_home/widgets/video_card_v_member_home.dart b/lib/pages/member_home/widgets/video_card_v_member_home.dart index cad798508..607cd603e 100644 --- a/lib/pages/member_home/widgets/video_card_v_member_home.dart +++ b/lib/pages/member_home/widgets/video_card_v_member_home.dart @@ -6,6 +6,7 @@ import 'package:PiliPlus/http/search.dart'; import 'package:PiliPlus/models/common/badge_type.dart'; import 'package:PiliPlus/models_new/space/space_archive/item.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; @@ -97,7 +98,7 @@ class VideoCardVMemberHome extends StatelessWidget { right: 7, size: PBadgeSize.small, type: PBadgeType.gray, - text: Utils.timeFormat(videoItem.duration), + text: DurationUtil.formatDuration(videoItem.duration), ) ], ); diff --git a/lib/pages/member_profile/view.dart b/lib/pages/member_profile/view.dart index 96a3e52df..bf2e625a1 100644 --- a/lib/pages/member_profile/view.dart +++ b/lib/pages/member_profile/view.dart @@ -8,7 +8,10 @@ import 'package:PiliPlus/models/user/info.dart'; import 'package:PiliPlus/models_new/account_myinfo/data.dart'; import 'package:PiliPlus/pages/mine/controller.dart'; import 'package:PiliPlus/services/account_service.dart'; +import 'package:PiliPlus/utils/app_sign.dart'; +import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/extension.dart'; +import 'package:PiliPlus/utils/image_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/utils.dart'; @@ -21,7 +24,6 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart' hide FormData, MultipartFile; import 'package:image_cropper/image_cropper.dart'; import 'package:image_picker/image_picker.dart'; -import 'package:intl/intl.dart'; import 'package:mime/mime.dart'; class EditProfilePage extends StatefulWidget { @@ -128,7 +130,7 @@ class _EditProfilePageState extends State { padding: const EdgeInsets.symmetric(vertical: 5), child: ClipOval( child: CachedNetworkImage( - imageUrl: Utils.thumbnailImgUrl(response.face), + imageUrl: ImageUtil.thumbnailUrl(response.face), ), ), ), @@ -178,7 +180,7 @@ class _EditProfilePageState extends State { if (res != null) { _update( type: ProfileType.birthday, - datum: DateFormat('yyyy-MM-dd').format(res), + datum: DateUtil.longFormat.format(res), ); } }), @@ -350,7 +352,7 @@ class _EditProfilePageState extends State { else if (type == ProfileType.sex) 'sex': datum.toString(), }; - Utils.appSign(data); + AppSign.appSign(data); Request() .post( '/x/member/app/${type.name}/update', diff --git a/lib/pages/member_season_series/widget/season_series_card.dart b/lib/pages/member_season_series/widget/season_series_card.dart index 233e1b2e4..1df70f608 100644 --- a/lib/pages/member_season_series/widget/season_series_card.dart +++ b/lib/pages/member_season_series/widget/season_series_card.dart @@ -3,7 +3,7 @@ import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/image/image_save.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/models_new/space/space_season_series/season.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/date_util.dart'; import 'package:flutter/material.dart'; class SeasonSeriesCard extends StatelessWidget { @@ -88,7 +88,7 @@ class SeasonSeriesCard extends StatelessWidget { ), const Spacer(), Text( - Utils.dateFormat(item.meta!.ptime), + DateUtil.dateFormat(item.meta!.ptime), maxLines: 1, style: TextStyle( fontSize: 12, diff --git a/lib/pages/member_video/widgets/video_card_h_member_video.dart b/lib/pages/member_video/widgets/video_card_h_member_video.dart index 65f46b5df..af4b973fc 100644 --- a/lib/pages/member_video/widgets/video_card_h_member_video.dart +++ b/lib/pages/member_video/widgets/video_card_h_member_video.dart @@ -8,6 +8,8 @@ import 'package:PiliPlus/common/widgets/video_popup_menu.dart'; import 'package:PiliPlus/models/common/badge_type.dart'; import 'package:PiliPlus/models/common/stat_type.dart'; import 'package:PiliPlus/models_new/space/space_archive/item.dart'; +import 'package:PiliPlus/utils/date_util.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; @@ -141,14 +143,14 @@ class VideoCardHMemberVideo extends StatelessWidget { text: videoItem.history!.progress == videoItem.history!.duration ? '已看完' - : '${Utils.timeFormat(videoItem.history!.progress)}/${Utils.timeFormat(videoItem.history!.duration)}', + : '${DurationUtil.formatDuration(videoItem.history!.progress)}/${DurationUtil.formatDuration(videoItem.history!.duration)}', right: 6.0, bottom: 6.0, type: PBadgeType.gray, ); } catch (_) { return PBadge( - text: Utils.timeFormat( + text: DurationUtil.formatDuration( videoItem.duration), right: 6.0, bottom: 6.0, @@ -158,7 +160,8 @@ class VideoCardHMemberVideo extends StatelessWidget { }), ] else if (videoItem.duration > 0) PBadge( - text: Utils.timeFormat(videoItem.duration), + text: DurationUtil.formatDuration( + videoItem.duration), right: 6.0, bottom: 6.0, type: PBadgeType.gray, @@ -214,7 +217,7 @@ class VideoCardHMemberVideo extends StatelessWidget { ), Text( videoItem.season != null - ? Utils.dateFormat(videoItem.season!.mtime) + ? DateUtil.dateFormat(videoItem.season!.mtime) : videoItem.publishTimeText ?? '', maxLines: 1, style: TextStyle( diff --git a/lib/pages/msg_feed_top/at_me/view.dart b/lib/pages/msg_feed_top/at_me/view.dart index 1de2e53b4..b03e6790e 100644 --- a/lib/pages/msg_feed_top/at_me/view.dart +++ b/lib/pages/msg_feed_top/at_me/view.dart @@ -11,7 +11,7 @@ import 'package:PiliPlus/models_new/msg/msg_at/item.dart'; import 'package:PiliPlus/pages/msg_feed_top/at_me/controller.dart'; import 'package:PiliPlus/pages/whisper_settings/view.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/date_util.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -141,7 +141,7 @@ class _AtMePageState extends State { ], const SizedBox(height: 4), Text( - Utils.dateFormat(item.atTime), + DateUtil.dateFormat(item.atTime), style: theme.textTheme.bodyMedium!.copyWith( fontSize: 13, color: theme.colorScheme.outline, diff --git a/lib/pages/msg_feed_top/like_me/view.dart b/lib/pages/msg_feed_top/like_me/view.dart index 6fc58a71e..ea1838fb6 100644 --- a/lib/pages/msg_feed_top/like_me/view.dart +++ b/lib/pages/msg_feed_top/like_me/view.dart @@ -12,7 +12,7 @@ import 'package:PiliPlus/models_new/msg/msg_like/item.dart'; import 'package:PiliPlus/pages/msg_feed_top/like_me/controller.dart'; import 'package:PiliPlus/pages/whisper_settings/view.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/date_util.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -295,7 +295,7 @@ class _LikeMePageState extends State { ], const SizedBox(height: 4), Text( - Utils.dateFormat(item.likeTime), + DateUtil.dateFormat(item.likeTime), style: theme.textTheme.bodyMedium!.copyWith( fontSize: 13, color: theme.colorScheme.outline, diff --git a/lib/pages/msg_feed_top/reply_me/view.dart b/lib/pages/msg_feed_top/reply_me/view.dart index e784b270a..040ed9bf4 100644 --- a/lib/pages/msg_feed_top/reply_me/view.dart +++ b/lib/pages/msg_feed_top/reply_me/view.dart @@ -11,7 +11,7 @@ import 'package:PiliPlus/models_new/msg/msg_reply/item.dart'; import 'package:PiliPlus/pages/msg_feed_top/reply_me/controller.dart'; import 'package:PiliPlus/pages/whisper_settings/view.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/date_util.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -162,7 +162,7 @@ class _ReplyMePageState extends State { style: theme.textTheme.labelMedium!.copyWith( color: theme.colorScheme.outline, height: 1.5)), Text( - Utils.dateFormat(item.replyTime), + DateUtil.dateFormat(item.replyTime), style: theme.textTheme.bodyMedium!.copyWith( fontSize: 13, color: theme.colorScheme.outline, diff --git a/lib/pages/pgc_review/child/view.dart b/lib/pages/pgc_review/child/view.dart index c129a2af9..a7fe2b190 100644 --- a/lib/pages/pgc_review/child/view.dart +++ b/lib/pages/pgc_review/child/view.dart @@ -12,8 +12,8 @@ import 'package:PiliPlus/models_new/pgc/pgc_review/list.dart'; import 'package:PiliPlus/pages/pgc_review/child/controller.dart'; import 'package:PiliPlus/pages/pgc_review/post/view.dart'; import 'package:PiliPlus/utils/extension.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/storage.dart' show Accounts; -import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; @@ -341,7 +341,7 @@ class _PgcReviewChildPageState extends State color: isLike ? primary : color, ), Text( - Utils.numFormat(item.stat?.likes ?? 0), + NumUtil.numFormat(item.stat?.likes ?? 0), style: TextStyle( color: isLike ? primary : color, fontSize: 12, @@ -378,7 +378,7 @@ class _PgcReviewChildPageState extends State () => _controller.count.value == null ? const SizedBox.shrink() : Text( - '${Utils.numFormat(_controller.count.value)}条点评', + '${NumUtil.numFormat(_controller.count.value)}条点评', style: const TextStyle(fontSize: 13), ), ), diff --git a/lib/pages/save_panel/view.dart b/lib/pages/save_panel/view.dart index 1fd2999f6..bffd30289 100644 --- a/lib/pages/save_panel/view.dart +++ b/lib/pages/save_panel/view.dart @@ -10,13 +10,15 @@ import 'package:PiliPlus/pages/dynamics/widgets/dynamic_panel.dart'; import 'package:PiliPlus/pages/video/introduction/pgc/controller.dart'; import 'package:PiliPlus/pages/video/introduction/ugc/controller.dart'; import 'package:PiliPlus/pages/video/reply/widgets/reply_item_grpc.dart'; -import 'package:PiliPlus/utils/download.dart'; +import 'package:PiliPlus/utils/date_util.dart'; +import 'package:PiliPlus/utils/image_util.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:intl/intl.dart' show DateFormat; import 'package:pretty_qr_code/pretty_qr_code.dart'; import 'package:saver_gallery/saver_gallery.dart'; import 'package:share_plus/share_plus.dart'; @@ -225,8 +227,7 @@ class _SavePanelState extends State { Future _onSaveOrSharePic([bool isShare = false]) async { if (!isShare) { - if (mounted && - !await DownloadUtils.checkPermissionDependOnSdkInt(context)) { + if (mounted && !await ImageUtil.checkPermissionDependOnSdkInt(context)) { return; } } @@ -238,7 +239,7 @@ class _SavePanelState extends State { ByteData? byteData = await image.toByteData(format: ImageByteFormat.png); Uint8List pngBytes = byteData!.buffer.asUint8List(); String picName = - "plpl_reply_${DateTime.now().toString().substring(0, 19).replaceAll(RegExp(r'[- :]'), '')}"; + "plpl_reply_${DateFormat('yyyyMMddHHmmss').format(DateTime.now())}"; if (isShare) { Get.back(); SmartDialog.dismiss(); @@ -424,10 +425,9 @@ class _SavePanelState extends State { ), const SizedBox(height: 4), Text( - DateTime.now() - .toString() - .split('.') - .first, + DateUtil.longFormatDs + .format( + DateTime.now()), textAlign: TextAlign.end, style: TextStyle( fontSize: 13, diff --git a/lib/pages/search/widgets/hot_keyword.dart b/lib/pages/search/widgets/hot_keyword.dart index b3112e58c..d2b088005 100644 --- a/lib/pages/search/widgets/hot_keyword.dart +++ b/lib/pages/search/widgets/hot_keyword.dart @@ -1,6 +1,6 @@ import 'package:PiliPlus/models_new/search/search_trending/list.dart'; import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/image_util.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; @@ -53,7 +53,7 @@ class HotKeyword extends StatelessWidget { Padding( padding: const EdgeInsets.only(left: 4), child: CachedNetworkImage( - imageUrl: Utils.thumbnailImgUrl(i.icon!), + imageUrl: ImageUtil.thumbnailUrl(i.icon!), height: 15, ), ) diff --git a/lib/pages/search_panel/article/widgets/item.dart b/lib/pages/search_panel/article/widgets/item.dart index 4c4ddfe4b..bd18886a8 100644 --- a/lib/pages/search_panel/article/widgets/item.dart +++ b/lib/pages/search_panel/article/widgets/item.dart @@ -2,7 +2,7 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/image/image_save.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/models/search/result.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/date_util.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -75,7 +75,7 @@ class SearchArticleItem extends StatelessWidget { ), const Spacer(), Text( - Utils.dateFormat(item.pubTime, formatType: 'detail'), + DateUtil.dateFormat(item.pubTime), style: textStyle, ), Row( diff --git a/lib/pages/search_panel/pgc/widgets/item.dart b/lib/pages/search_panel/pgc/widgets/item.dart index 142dfee7e..78f95d21c 100644 --- a/lib/pages/search_panel/pgc/widgets/item.dart +++ b/lib/pages/search_panel/pgc/widgets/item.dart @@ -3,8 +3,8 @@ import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/image/image_save.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/models/search/result.dart'; +import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; class SearchPgcItem extends StatelessWidget { @@ -81,7 +81,7 @@ class SearchPgcItem extends StatelessWidget { const Text('·'), const SizedBox(width: 3), Text( - Utils.dateFormat(item.pubtime).toString(), + DateUtil.dateFormat(item.pubtime), style: style, ), ], diff --git a/lib/pages/search_panel/user/widgets/item.dart b/lib/pages/search_panel/user/widgets/item.dart index bd1d6eb43..fc512900f 100644 --- a/lib/pages/search_panel/user/widgets/item.dart +++ b/lib/pages/search_panel/user/widgets/item.dart @@ -1,6 +1,6 @@ import 'package:PiliPlus/common/widgets/pendant_avatar.dart'; import 'package:PiliPlus/models/search/result.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -56,7 +56,7 @@ class SearchUserItem extends StatelessWidget { ], ), Text( - '粉丝:${Utils.numFormat(item.fans)} 视频:${Utils.numFormat(item.videos)}', + '粉丝:${NumUtil.numFormat(item.fans)} 视频:${NumUtil.numFormat(item.videos)}', style: style, ), if (item.officialVerify?['desc'] != null && diff --git a/lib/pages/search_panel/video/controller.dart b/lib/pages/search_panel/video/controller.dart index 5077ef2df..1d1ba77b1 100644 --- a/lib/pages/search_panel/video/controller.dart +++ b/lib/pages/search_panel/video/controller.dart @@ -6,10 +6,10 @@ import 'package:PiliPlus/models/search/result.dart'; import 'package:PiliPlus/pages/search/widgets/search_text.dart'; import 'package:PiliPlus/pages/search_panel/controller.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; +import 'package:PiliPlus/utils/date_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:intl/intl.dart'; class SearchVideoController extends SearchPanelController { @@ -152,7 +152,7 @@ class SearchVideoController final theme = Theme.of(context); Widget dateWidget([bool isFirst = true]) { return SearchText( - text: DateFormat('yyyy-MM-dd') + text: DateUtil.longFormat .format(isFirst ? pubBeginDate : pubEndDate), textAlign: TextAlign.center, onTap: (text) { diff --git a/lib/pages/search_trending/view.dart b/lib/pages/search_trending/view.dart index 2f4f49f2f..239b79d7d 100644 --- a/lib/pages/search_trending/view.dart +++ b/lib/pages/search_trending/view.dart @@ -6,7 +6,7 @@ import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models_new/search/search_trending/list.dart'; import 'package:PiliPlus/pages/search_trending/controller.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/image_util.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -197,7 +197,7 @@ class _SearchTrendingPageState extends State { if (item.icon?.isNotEmpty == true) ...[ const SizedBox(width: 4), CachedNetworkImage( - imageUrl: Utils.thumbnailImgUrl(item.icon!), + imageUrl: ImageUtil.thumbnailUrl(item.icon!), height: 16, ), ] else if (item.showLiveIcon == true) ...[ diff --git a/lib/pages/setting/widgets/model.dart b/lib/pages/setting/widgets/model.dart index 966c8bc58..324d212e3 100644 --- a/lib/pages/setting/widgets/model.dart +++ b/lib/pages/setting/widgets/model.dart @@ -49,7 +49,7 @@ import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/global_data.dart'; import 'package:PiliPlus/utils/recommend_filter.dart'; import 'package:PiliPlus/utils/storage.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/update.dart'; import 'package:auto_orientation/auto_orientation.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; @@ -2572,7 +2572,7 @@ List get extraSettings => [ defaultVal: true, onChanged: (val) { if (val) { - Utils.checkUpdate(false); + Update.checkUpdate(false); } }, ), diff --git a/lib/pages/subscription_detail/view.dart b/lib/pages/subscription_detail/view.dart index 52554b5e5..f4d5f4550 100644 --- a/lib/pages/subscription_detail/view.dart +++ b/lib/pages/subscription_detail/view.dart @@ -7,6 +7,7 @@ import 'package:PiliPlus/models_new/sub/sub_detail/media.dart'; import 'package:PiliPlus/pages/subscription_detail/controller.dart'; import 'package:PiliPlus/pages/subscription_detail/widget/sub_video_card.dart'; import 'package:PiliPlus/utils/grid.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -172,7 +173,7 @@ class _SubDetailPageState extends State { ), Obx( () => Text( - '${Utils.numFormat(_subDetailController.playCount.value)}次播放', + '${NumUtil.numFormat(_subDetailController.playCount.value)}次播放', style: style, ), ), diff --git a/lib/pages/subscription_detail/widget/sub_video_card.dart b/lib/pages/subscription_detail/widget/sub_video_card.dart index 6048c015d..dcd89ff0b 100644 --- a/lib/pages/subscription_detail/widget/sub_video_card.dart +++ b/lib/pages/subscription_detail/widget/sub_video_card.dart @@ -8,6 +8,8 @@ import 'package:PiliPlus/models/common/badge_type.dart'; import 'package:PiliPlus/models/common/search_type.dart'; import 'package:PiliPlus/models/common/stat_type.dart'; import 'package:PiliPlus/models_new/sub/sub_detail/media.dart'; +import 'package:PiliPlus/utils/date_util.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; @@ -70,7 +72,7 @@ class SubVideoCardH extends StatelessWidget { height: maxHeight, ), PBadge( - text: Utils.timeFormat(videoItem.duration!), + text: DurationUtil.formatDuration(videoItem.duration), right: 6.0, bottom: 6.0, type: PBadgeType.gray, @@ -106,7 +108,7 @@ class SubVideoCardH extends StatelessWidget { ), ), Text( - Utils.dateFormat(videoItem.pubtime), + DateUtil.dateFormat(videoItem.pubtime), style: TextStyle( fontSize: 12, color: Theme.of(context).colorScheme.outline, diff --git a/lib/pages/video/ai_conclusion/view.dart b/lib/pages/video/ai_conclusion/view.dart index f3b26ee62..7950f6150 100644 --- a/lib/pages/video/ai_conclusion/view.dart +++ b/lib/pages/video/ai_conclusion/view.dart @@ -1,7 +1,7 @@ import 'package:PiliPlus/models_new/video/video_ai_conclusion/model_result.dart'; import 'package:PiliPlus/pages/common/common_collapse_slide_page.dart'; import 'package:PiliPlus/pages/video/controller.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -115,7 +115,8 @@ class _AiDetailState extends CommonCollapseSlidePageState { ), children: [ TextSpan( - text: Utils.formatDuration(item.timestamp!), + text: DurationUtil.formatDuration( + item.timestamp), style: TextStyle( color: theme.colorScheme.primary, ), diff --git a/lib/pages/video/controller.dart b/lib/pages/video/controller.dart index ee66150ef..f1097d954 100644 --- a/lib/pages/video/controller.dart +++ b/lib/pages/video/controller.dart @@ -39,6 +39,7 @@ import 'package:PiliPlus/pages/video/widgets/header_control.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/plugin/pl_player/models/data_source.dart'; import 'package:PiliPlus/plugin/pl_player/models/play_status.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; @@ -611,7 +612,7 @@ class VideoDetailController extends GetxController ), contentPadding: const EdgeInsets.only(left: 16, right: 8), subtitle: Text( - '${Utils.formatDuration(item.segment.first / 1000)} 至 ${Utils.formatDuration(item.segment.second / 1000)}', + '${DurationUtil.formatDuration(item.segment.first / 1000)} 至 ${DurationUtil.formatDuration(item.segment.second / 1000)}', style: const TextStyle(fontSize: 13), ), trailing: Row( diff --git a/lib/pages/video/introduction/pgc/view.dart b/lib/pages/video/introduction/pgc/view.dart index 8a61f05c0..e66e87755 100644 --- a/lib/pages/video/introduction/pgc/view.dart +++ b/lib/pages/video/introduction/pgc/view.dart @@ -13,7 +13,7 @@ import 'package:PiliPlus/pages/video/introduction/pgc/controller.dart'; import 'package:PiliPlus/pages/video/introduction/pgc/widgets/pgc_panel.dart'; import 'package:PiliPlus/pages/video/introduction/ugc/widgets/action_item.dart'; import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart' show HapticFeedback; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -273,7 +273,7 @@ class _PgcIntroPageState extends State selectStatus: pgcIntroController.hasLike.value, isLoading: false, semanticsLabel: '点赞', - text: Utils.numFormat(item.stat!.likes), + text: NumUtil.numFormat(item.stat!.likes), needAnim: true, hasTriple: pgcIntroController.hasLike.value && pgcIntroController.hasCoin && @@ -299,7 +299,7 @@ class _PgcIntroPageState extends State selectStatus: pgcIntroController.hasCoin, isLoading: false, semanticsLabel: '投币', - text: Utils.numFormat(item.stat!.coins), + text: NumUtil.numFormat(item.stat!.coins), needAnim: true, ), ), @@ -314,7 +314,7 @@ class _PgcIntroPageState extends State selectStatus: pgcIntroController.hasFav.value, isLoading: false, semanticsLabel: '收藏', - text: Utils.numFormat(item.stat!.favorite), + text: NumUtil.numFormat(item.stat!.favorite), needAnim: true, ), ), @@ -325,7 +325,7 @@ class _PgcIntroPageState extends State selectStatus: false, isLoading: false, semanticsLabel: '评论', - text: Utils.numFormat(item.stat!.reply), + text: NumUtil.numFormat(item.stat!.reply), ), ActionItem( icon: const Icon(FontAwesomeIcons.shareFromSquare), @@ -333,7 +333,7 @@ class _PgcIntroPageState extends State selectStatus: false, isLoading: false, semanticsLabel: '转发', - text: Utils.numFormat(item.stat!.share), + text: NumUtil.numFormat(item.stat!.share), ), ], ), diff --git a/lib/pages/video/introduction/ugc/view.dart b/lib/pages/video/introduction/ugc/view.dart index de785127d..6f80971d4 100644 --- a/lib/pages/video/introduction/ugc/view.dart +++ b/lib/pages/video/introduction/ugc/view.dart @@ -16,9 +16,11 @@ import 'package:PiliPlus/pages/video/introduction/ugc/widgets/action_item.dart'; import 'package:PiliPlus/pages/video/introduction/ugc/widgets/page.dart'; import 'package:PiliPlus/pages/video/introduction/ugc/widgets/season.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; +import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/id_utils.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/request_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; @@ -345,7 +347,7 @@ class _VideoInfoState extends State { const SizedBox(height: 0), Obx( () => Text( - '${Utils.numFormat(videoIntroController.userStat['follower'])}粉丝 ${videoIntroController.userStat['archive_count'] != null ? '${Utils.numFormat(videoIntroController.userStat['archive_count'])}视频' : ''}', + '${NumUtil.numFormat(videoIntroController.userStat['follower'])}粉丝 ${videoIntroController.userStat['archive_count'] != null ? '${NumUtil.numFormat(videoIntroController.userStat['archive_count'])}视频' : ''}', style: TextStyle( fontSize: 12, color: theme.colorScheme.outline, @@ -570,11 +572,11 @@ class _VideoInfoState extends State { color: theme.colorScheme.outline, ), Text( - Utils.dateFormat( - !widget.isLoading - ? videoDetail.pubdate - : videoItem['pubdate'], - formatType: 'detail'), + DateUtil.format( + !widget.isLoading + ? videoDetail.pubdate + : videoItem['pubdate'], + ), style: TextStyle( fontSize: 12, color: theme.colorScheme.outline, @@ -853,7 +855,7 @@ class _VideoInfoState extends State { isLoading: widget.isLoading, semanticsLabel: '点赞', text: !widget.isLoading - ? Utils.numFormat(videoDetail.stat!.like!) + ? NumUtil.numFormat(videoDetail.stat!.like!) : '-', needAnim: true, hasTriple: videoIntroController.hasLike.value && @@ -892,7 +894,7 @@ class _VideoInfoState extends State { isLoading: widget.isLoading, semanticsLabel: '投币', text: !widget.isLoading - ? Utils.numFormat(videoDetail.stat!.coin!) + ? NumUtil.numFormat(videoDetail.stat!.coin!) : '-', needAnim: true, ), @@ -909,7 +911,7 @@ class _VideoInfoState extends State { isLoading: widget.isLoading, semanticsLabel: '收藏', text: !widget.isLoading - ? Utils.numFormat(videoDetail.stat!.favorite!) + ? NumUtil.numFormat(videoDetail.stat!.favorite!) : '-', needAnim: true, ), @@ -932,7 +934,7 @@ class _VideoInfoState extends State { isLoading: widget.isLoading, semanticsLabel: '分享', text: !widget.isLoading - ? Utils.numFormat(videoDetail.stat!.share!) + ? NumUtil.numFormat(videoDetail.stat!.share!) : '分享', ), ], diff --git a/lib/pages/video/medialist/view.dart b/lib/pages/video/medialist/view.dart index df92f904a..dd54315e3 100644 --- a/lib/pages/video/medialist/view.dart +++ b/lib/pages/video/medialist/view.dart @@ -11,7 +11,7 @@ import 'package:PiliPlus/models/common/badge_type.dart'; import 'package:PiliPlus/models/common/stat_type.dart'; import 'package:PiliPlus/models_new/media_list/media_list.dart'; import 'package:PiliPlus/pages/common/common_collapse_slide_page.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:flutter/material.dart' hide RefreshCallback; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -216,8 +216,8 @@ class _MediaListPanelState }, ), PBadge( - text: - Utils.timeFormat(item.duration!), + text: DurationUtil.formatDuration( + item.duration), right: 6.0, bottom: 6.0, type: PBadgeType.gray, diff --git a/lib/pages/video/member/view.dart b/lib/pages/video/member/view.dart index 42ae5d59c..a7529688e 100644 --- a/lib/pages/video/member/view.dart +++ b/lib/pages/video/member/view.dart @@ -18,6 +18,7 @@ import 'package:PiliPlus/services/account_service.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/grid.dart'; import 'package:PiliPlus/utils/id_utils.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/request_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; @@ -270,12 +271,14 @@ class _HorizontalMemberPageState extends State { title: const ['粉丝', '关注', '获赞'][index ~/ 2], num: index == 0 ? _controller.userStat['follower'] != null - ? Utils.numFormat(_controller.userStat['follower']) + ? NumUtil.numFormat( + _controller.userStat['follower']) : '' : index == 2 ? _controller.userStat['following'] ?? '' : _controller.userStat['likes'] != null - ? Utils.numFormat(_controller.userStat['likes']) + ? NumUtil.numFormat( + _controller.userStat['likes']) : '', onTap: () { if (index == 0) { diff --git a/lib/pages/video/post_panel/view.dart b/lib/pages/video/post_panel/view.dart index a83932b35..587562bd1 100644 --- a/lib/pages/video/post_panel/view.dart +++ b/lib/pages/video/post_panel/view.dart @@ -13,8 +13,8 @@ import 'package:PiliPlus/models_new/sponsor_block/segment_item.dart'; import 'package:PiliPlus/pages/common/common_collapse_slide_page.dart'; import 'package:PiliPlus/pages/video/controller.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/storage.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; @@ -423,7 +423,7 @@ class _PostPanelState extends CommonCollapseSlidePageState { required int index, required bool isFirst, }) { - String value = Utils.timeFormat( + String value = DurationUtil.formatDuration( isFirst ? list![index].segment.first : list![index].segment.second); return [ Text( diff --git a/lib/pages/video/reply/view.dart b/lib/pages/video/reply/view.dart index 9421ce4c0..989358819 100644 --- a/lib/pages/video/reply/view.dart +++ b/lib/pages/video/reply/view.dart @@ -218,9 +218,9 @@ class _VideoReplyPanelState extends State replyItem: response[index], replyLevel: widget.replyLevel, replyReply: widget.replyReply, - onReply: () => _videoReplyController.onReply( + onReply: (replyItem) => _videoReplyController.onReply( context, - replyItem: response[index], + replyItem: replyItem, index: index, ), onDelete: (subIndex) => diff --git a/lib/pages/video/reply/widgets/reply_item_grpc.dart b/lib/pages/video/reply/widgets/reply_item_grpc.dart index b2d41673a..fae414374 100644 --- a/lib/pages/video/reply/widgets/reply_item_grpc.dart +++ b/lib/pages/video/reply/widgets/reply_item_grpc.dart @@ -16,9 +16,12 @@ import 'package:PiliPlus/pages/dynamics/widgets/vote.dart'; import 'package:PiliPlus/pages/save_panel/view.dart'; import 'package:PiliPlus/pages/video/controller.dart'; import 'package:PiliPlus/pages/video/reply/widgets/zan_grpc.dart'; +import 'package:PiliPlus/utils/date_util.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/global_data.dart'; +import 'package:PiliPlus/utils/image_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/url_utils.dart'; @@ -54,7 +57,7 @@ class ReplyItemGrpc extends StatelessWidget { final int replyLevel; final Function(ReplyInfo replyItem, int? rpid)? replyReply; final bool needDivider; - final VoidCallback? onReply; + final ValueChanged? onReply; final ValueChanged? onDelete; final Int64? upMid; final VoidCallback? showDialogue; @@ -231,11 +234,8 @@ class ReplyItemGrpc extends StatelessWidget { children: [ Text( replyLevel == 0 - ? DateTime.fromMillisecondsSinceEpoch( - replyItem.ctime.toInt() * 1000) - .toString() - .substring(0, 19) - : Utils.dateFormat(replyItem.ctime.toInt()), + ? DateUtil.longFormatDs.format(DateTime.now()) + : DateUtil.dateFormat(replyItem.ctime.toInt()), style: TextStyle( fontSize: theme.textTheme.labelSmall!.fontSize, color: theme.colorScheme.outline, @@ -347,7 +347,7 @@ class ReplyItemGrpc extends StatelessWidget { style: _style, onPressed: () { feedBack(); - onReply?.call(); + onReply?.call(replyItem); }, child: Row(children: [ Icon( @@ -712,7 +712,9 @@ class ReplyItemGrpc extends StatelessWidget { tag: Get.arguments['heroTag']) .plPlayerController .seekTo( - Duration(seconds: Utils.duration(matchStr)), + Duration( + seconds: + DurationUtil.parseDuration(matchStr)), type: 'slider'); } catch (e) { SmartDialog.showToast('跳转失败: $e'); @@ -736,7 +738,7 @@ class ReplyItemGrpc extends StatelessWidget { if (content.urls[matchStr]?.hasPrefixIcon() == true) ...[ WidgetSpan( child: CachedNetworkImage( - imageUrl: Utils.thumbnailImgUrl( + imageUrl: ImageUtil.thumbnailUrl( content.urls[matchStr]!.prefixIcon), height: 19, color: theme.colorScheme.primary, @@ -854,7 +856,7 @@ class ReplyItemGrpc extends StatelessWidget { if (content.urls[patternStr]?.hasPrefixIcon() == true) ...[ WidgetSpan( child: CachedNetworkImage( - imageUrl: Utils.thumbnailImgUrl( + imageUrl: ImageUtil.thumbnailUrl( content.urls[patternStr]!.prefixIcon), height: 19, color: theme.colorScheme.primary, diff --git a/lib/pages/video/reply/widgets/zan_grpc.dart b/lib/pages/video/reply/widgets/zan_grpc.dart index 60cd704bf..d48dfd05c 100644 --- a/lib/pages/video/reply/widgets/zan_grpc.dart +++ b/lib/pages/video/reply/widgets/zan_grpc.dart @@ -2,7 +2,7 @@ import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart' show ReplyInfo; import 'package:PiliPlus/http/reply.dart'; import 'package:PiliPlus/utils/feed_back.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:fixnum/fixnum.dart' as $fixnum; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -146,7 +146,7 @@ class _ZanButtonGrpcState extends State { return ScaleTransition(scale: animation, child: child); }, child: Text( - Utils.numFormat(widget.replyItem.like.toInt()), + NumUtil.numFormat(widget.replyItem.like.toInt()), style: TextStyle( color: isLike ? primary : color, fontSize: theme.textTheme.labelSmall!.fontSize, diff --git a/lib/pages/video/reply_reply/view.dart b/lib/pages/video/reply_reply/view.dart index bdf48756a..d7c9bbd87 100644 --- a/lib/pages/video/reply_reply/view.dart +++ b/lib/pages/video/reply_reply/view.dart @@ -8,6 +8,7 @@ import 'package:PiliPlus/pages/common/common_slide_page.dart'; import 'package:PiliPlus/pages/video/reply/widgets/reply_item_grpc.dart'; import 'package:PiliPlus/pages/video/reply_new/view.dart'; import 'package:PiliPlus/pages/video/reply_reply/controller.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/request_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; @@ -180,7 +181,7 @@ class _VideoReplyReplyPanelState replyItem: firstFloor!, replyLevel: 2, needDivider: false, - onReply: () => _onReply(firstFloor!, -1), + onReply: (replyItem) => _onReply(replyItem, -1), upMid: _videoReplyReplyController.upMid, onViewImage: widget.onViewImage, onDismissed: widget.onDismissed, @@ -252,7 +253,7 @@ class _VideoReplyReplyPanelState Obx( () => _videoReplyReplyController.count.value != -1 ? Text( - '相关回复共${Utils.numFormat(_videoReplyReplyController.count.value)}条', + '相关回复共${NumUtil.numFormat(_videoReplyReplyController.count.value)}条', style: const TextStyle(fontSize: 13), ) : const SizedBox.shrink(), @@ -427,7 +428,7 @@ class _VideoReplyReplyPanelState return ReplyItemGrpc( replyItem: replyItem, replyLevel: widget.isDialogue ? 3 : 2, - onReply: () => _onReply(replyItem, index), + onReply: (replyItem) => _onReply(replyItem, index), onDelete: (subIndex) { _videoReplyReplyController.onRemove(index, null); }, diff --git a/lib/pages/video/view.dart b/lib/pages/video/view.dart index d1dec15f5..fab7c4679 100644 --- a/lib/pages/video/view.dart +++ b/lib/pages/video/view.dart @@ -43,12 +43,12 @@ import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart'; import 'package:PiliPlus/plugin/pl_player/view.dart'; import 'package:PiliPlus/services/service_locator.dart'; import 'package:PiliPlus/services/shutdown_timer_service.dart'; -import 'package:PiliPlus/utils/download.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/id_utils.dart'; +import 'package:PiliPlus/utils/image_util.dart'; +import 'package:PiliPlus/utils/num_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'package:auto_orientation/auto_orientation.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:easy_debounce/easy_throttle.dart'; @@ -751,7 +751,7 @@ class _VideoDetailPageVState extends State context); break; case 'savePic': - DownloadUtils.downloadImg( + ImageUtil.downloadImg( context, [ videoDetailController @@ -1292,7 +1292,7 @@ class _VideoDetailPageVState extends State videoDetailController.showNoteList(context); break; case 'savePic': - DownloadUtils.downloadImg( + ImageUtil.downloadImg( context, [videoDetailController.videoItem['pic']], ); @@ -1475,7 +1475,7 @@ class _VideoDetailPageVState extends State if (text == '评论') { return Tab( text: - '评论${_videoReplyController.count.value == -1 ? '' : ' ${Utils.numFormat(_videoReplyController.count.value)}'}', + '评论${_videoReplyController.count.value == -1 ? '' : ' ${NumUtil.numFormat(_videoReplyController.count.value)}'}', ); } else { return Tab(text: text); diff --git a/lib/pages/video/view_point/view.dart b/lib/pages/video/view_point/view.dart index f66b695aa..db15e97bf 100644 --- a/lib/pages/video/view_point/view.dart +++ b/lib/pages/video/view_point/view.dart @@ -5,7 +5,7 @@ import 'package:PiliPlus/common/widgets/progress_bar/segment_progress_bar.dart'; import 'package:PiliPlus/pages/common/common_collapse_slide_page.dart'; import 'package:PiliPlus/pages/video/controller.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; -import 'package:PiliPlus/utils/utils.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -151,7 +151,7 @@ class _ViewPointsPageState ), ), subtitle: Text( - '${segment.from != null ? Utils.timeFormat(segment.from) : ''} - ${segment.to != null ? Utils.timeFormat(segment.to) : ''}', + '${segment.from != null ? DurationUtil.formatDuration(segment.from) : ''} - ${segment.to != null ? DurationUtil.formatDuration(segment.to) : ''}', style: TextStyle( fontSize: 13, color: currentIndex == index diff --git a/lib/pages/video/widgets/header_control.dart b/lib/pages/video/widgets/header_control.dart index 40a91a9f7..09229a761 100644 --- a/lib/pages/video/widgets/header_control.dart +++ b/lib/pages/video/widgets/header_control.dart @@ -21,7 +21,7 @@ import 'package:PiliPlus/pages/video/introduction/ugc/widgets/menu_row.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/plugin/pl_player/models/play_repeat.dart'; import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart'; -import 'package:PiliPlus/utils/download.dart'; +import 'package:PiliPlus/utils/image_util.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; @@ -37,6 +37,7 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; +import 'package:intl/intl.dart' show DateFormat; import 'package:marquee/marquee.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:media_kit/media_kit.dart'; @@ -148,7 +149,7 @@ class HeaderControlState extends State { dense: true, onTap: () { Get.back(); - DownloadUtils.downloadImg( + ImageUtil.downloadImg( context, [widget.videoDetailCtr.videoItem['pic']], ); @@ -1786,12 +1787,15 @@ class HeaderControlState extends State { ); } + static final _format = DateFormat('HH:mm'); + void startClock() { clock ??= Timer.periodic(const Duration(seconds: 1), (Timer t) { if (!mounted) { + clock?.cancel(); return; } - now.value = DateTime.now().toString().split(' ')[1].substring(0, 5); + now.value = _format.format(DateTime.now()); }); } diff --git a/lib/pages/whisper/widgets/item.dart b/lib/pages/whisper/widgets/item.dart index 16b9218b9..83e07eddf 100644 --- a/lib/pages/whisper/widgets/item.dart +++ b/lib/pages/whisper/widgets/item.dart @@ -7,8 +7,8 @@ import 'package:PiliPlus/grpc/bilibili/app/im/v1.pb.dart' show Session, SessionId, SessionPageType, SessionType, UnreadStyle; import 'package:PiliPlus/models/common/badge_type.dart'; import 'package:PiliPlus/pages/whisper_secondary/view.dart'; +import 'package:PiliPlus/utils/date_util.dart'; import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'package:fixnum/fixnum.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -232,8 +232,7 @@ class WhisperSessionItem extends StatelessWidget { children: [ if (item.hasTimestamp()) Text( - Utils.dateFormat((item.timestamp ~/ 1000000).toInt(), - formatType: "day"), + DateUtil.dateFormat((item.timestamp ~/ 1000000).toInt()), style: TextStyle( fontSize: 12, color: theme.colorScheme.outline, diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index e2de43d2a..889ca097c 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -11,8 +11,11 @@ import 'package:PiliPlus/models/common/badge_type.dart'; import 'package:PiliPlus/models/common/image_preview_type.dart'; import 'package:PiliPlus/models/common/image_type.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; +import 'package:PiliPlus/utils/date_util.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/id_utils.dart'; +import 'package:PiliPlus/utils/image_util.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; @@ -60,7 +63,7 @@ class ChatItem extends StatelessWidget { Padding( padding: const EdgeInsets.only(top: 6, bottom: 18), child: Text( - Utils.dateFormat(item.timestamp.toInt()), + DateUtil.chatFormat(item.timestamp.toInt()), textAlign: TextAlign.center, style: TextStyle(color: theme.colorScheme.outline), ), @@ -417,7 +420,7 @@ class ChatItem extends StatelessWidget { type: PBadgeType.gray, text: content['times'] == 0 ? '--:--' - : Utils.timeFormat(content['times']), + : DurationUtil.formatDuration(content['times']), ) ], ), @@ -728,7 +731,7 @@ class ChatItem extends StatelessWidget { child: GestureDetector( onTap: url == null ? null : () => PiliScheme.routePushFromUrl(url), child: CachedNetworkImage( - imageUrl: Utils.thumbnailImgUrl(content['pic_url']), + imageUrl: ImageUtil.thumbnailUrl(content['pic_url']), ), ), ), diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index d487506a6..efdc53ba3 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -25,10 +25,10 @@ import 'package:PiliPlus/plugin/pl_player/widgets/bottom_control.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/common_btn.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/forward_seek.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/play_pause_btn.dart'; +import 'package:PiliPlus/utils/duration_util.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:fl_chart/fl_chart.dart'; @@ -340,20 +340,19 @@ class _PLVideoPlayerState extends State // 播放时间 Obx(() { return Text( - Utils.timeFormat(plPlayerController.positionSeconds.value), + DurationUtil.formatDuration( + plPlayerController.positionSeconds.value), style: const TextStyle( color: Colors.white, fontSize: 10, height: 1.4, fontFeatures: [FontFeature.tabularFigures()], ), - semanticsLabel: - '已播放${Utils.durationReadFormat(Utils.timeFormat(plPlayerController.positionSeconds.value))}', ); }), Obx( () => Text( - Utils.timeFormat( + DurationUtil.formatDuration( plPlayerController.durationSeconds.value.inSeconds), style: const TextStyle( color: Color(0xFFD0D0D0), @@ -361,8 +360,6 @@ class _PLVideoPlayerState extends State height: 1.4, fontFeatures: [FontFeature.tabularFigures()], ), - semanticsLabel: - '共${Utils.durationReadFormat(Utils.timeFormat(plPlayerController.durationSeconds.value.inSeconds))}', ), ), ], @@ -1139,7 +1136,7 @@ class _PLVideoPlayerState extends State children: [ Obx(() { return Text( - Utils.timeFormat(plPlayerController + DurationUtil.formatDuration(plPlayerController .sliderTempPosition.value.inSeconds), style: textStyle, ); diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index d49bb536e..06baf2583 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -23,6 +23,7 @@ import 'package:PiliPlus/pages/later_search/view.dart'; import 'package:PiliPlus/pages/live_room/view.dart'; import 'package:PiliPlus/pages/login/view.dart'; import 'package:PiliPlus/pages/main/view.dart'; +import 'package:PiliPlus/pages/match_info/view.dart'; import 'package:PiliPlus/pages/media/view.dart'; import 'package:PiliPlus/pages/member/view.dart'; import 'package:PiliPlus/pages/member_dynamics/view.dart'; @@ -179,6 +180,7 @@ class Routes { CustomGetPage(name: '/upowerRank', page: () => const UpowerRankPage()), CustomGetPage(name: '/spaceSetting', page: () => const SpaceSettingPage()), CustomGetPage(name: '/dynTopicRcmd', page: () => const DynTopicRcmdPage()), + CustomGetPage(name: '/matchInfo', page: () => const MatchInfoPage()), ]; } diff --git a/lib/utils/accounts/account.dart b/lib/utils/accounts/account.dart index 348e1d8cb..04e01ccb0 100644 --- a/lib/utils/accounts/account.dart +++ b/lib/utils/accounts/account.dart @@ -1,6 +1,6 @@ import 'package:PiliPlus/models/common/account_type.dart'; +import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'package:cookie_jar/cookie_jar.dart'; import 'package:hive/hive.dart'; @@ -46,7 +46,7 @@ class LoginAccount implements Account { @override late final Map headers = { 'x-bili-mid': _midStr, - 'x-bili-aurora-eid': Utils.genAuroraEid(mid), + 'x-bili-aurora-eid': IdUtils.genAuroraEid(mid), }; @override late String csrf = @@ -165,7 +165,7 @@ extension BiliCookieJar on DefaultCookieJar { void setBuvid3() { domainCookies['bilibili.com'] ??= {'/': {}}; domainCookies['bilibili.com']!['/']!['buvid3'] ??= SerializableCookie( - Cookie('buvid3', Utils.genBuvid3())..setBiliDomain()); + Cookie('buvid3', IdUtils.genBuvid3())..setBiliDomain()); } static DefaultCookieJar fromJson(Map json) => diff --git a/lib/utils/accounts/account_manager/account_mgr.dart b/lib/utils/accounts/account_manager/account_mgr.dart index 77a294c46..8e1f6a500 100644 --- a/lib/utils/accounts/account_manager/account_mgr.dart +++ b/lib/utils/accounts/account_manager/account_mgr.dart @@ -6,9 +6,9 @@ import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/constants.dart'; import 'package:PiliPlus/models/common/account_type.dart'; import 'package:PiliPlus/utils/accounts/account.dart'; +import 'package:PiliPlus/utils/app_sign.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/storage.dart'; -import 'package:PiliPlus/utils/utils.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart' show kDebugMode; @@ -121,7 +121,7 @@ class AccountManager extends Interceptor { } dataPtr['ts'] ??= (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(); - Utils.appSign(dataPtr); + AppSign.appSign(dataPtr); // if (kDebugMode) debugPrint(dataPtr.toString()); } } diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index c5dfcc11f..7d8818c30 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -802,6 +802,21 @@ class PiliScheme { } launchURL(); return false; + case 'match' || 'v': + if (path.contains('match/data/detail') || + path.contains('match/singledata')) { + String? cid = uriDigitRegExp.firstMatch(path)?.group(1); + if (cid != null) { + PageUtils.toDupNamed( + '/matchInfo', + parameters: {'cid': cid}, + off: off, + ); + return true; + } + } + launchURL(); + return false; default: Map map = IdUtils.matchAvorBv(input: area?.split('?').first); if (map.isNotEmpty) { diff --git a/lib/utils/app_sign.dart b/lib/utils/app_sign.dart new file mode 100644 index 000000000..7be544c03 --- /dev/null +++ b/lib/utils/app_sign.dart @@ -0,0 +1,19 @@ +import 'dart:convert'; + +import 'package:PiliPlus/common/constants.dart'; +import 'package:crypto/crypto.dart'; + +class AppSign { + static void appSign(Map params, + [String appkey = Constants.appKey, String appsec = Constants.appSec]) { + params['appkey'] = appkey; + var searchParams = Uri( + queryParameters: + params.map((key, value) => MapEntry(key, value.toString()))).query; + var sortedQueryString = (searchParams.split('&')..sort()).join('&'); + + params['sign'] = md5 + .convert(utf8.encode(sortedQueryString + appsec)) + .toString(); // 获取MD5哈希值 + } +} diff --git a/lib/utils/date_util.dart b/lib/utils/date_util.dart new file mode 100644 index 000000000..c5480675e --- /dev/null +++ b/lib/utils/date_util.dart @@ -0,0 +1,79 @@ +import 'package:intl/intl.dart' show DateFormat; + +class DateUtil { + static final _shortFormat = DateFormat('MM-dd'); + static final longFormat = DateFormat('yyyy-MM-dd'); + static final _shortFormatD = DateFormat('MM-dd HH:mm'); + static final longFormatD = DateFormat('yyyy-MM-dd HH:mm'); + static final longFormatDs = DateFormat('yyyy-MM-dd HH:mm:ss'); + + static String dateFormat( + int? time, { + DateFormat? shortFormat, + DateFormat? longFormat, + }) { + if (time == null || time == 0) { + return ''; + } + + final now = DateTime.now(); + final date = DateTime.fromMillisecondsSinceEpoch(time * 1000); + final diff = now.difference(date); + + final diffInMins = diff.inMinutes; + if (diffInMins < 1) return '刚刚'; + if (diffInMins < 60) return '$diffInMins分钟前'; + + final diffInHours = diff.inHours; + if (diffInHours < 24) return '$diffInHours小时前'; + + final today = DateTime(now.year, now.month, now.day); + final dateDay = DateTime(date.year, date.month, date.day); + final dayDiff = today.difference(dateDay).inDays; + if (dayDiff == 1) { + return '昨天 ${_twoDigits(date.hour)}:${_twoDigits(date.minute)}'; + } + if (dayDiff < 4) { + return '$dayDiff天前'; + } + final DateFormat sdf = now.year == date.year + ? shortFormat ?? _shortFormat + : longFormat ?? DateUtil.longFormat; + return sdf.format(date); + } + + static String _twoDigits(int n) => n.toString().padLeft(2, '0'); + + static String chatFormat(int? time, {bool isHistory = false}) { + if (time == null || time == 0) { + return ''; + } + + final now = DateTime.now(); + final date = DateTime.fromMillisecondsSinceEpoch(time * 1000); + + final today = DateTime(now.year, now.month, now.day); + final dateDay = DateTime(date.year, date.month, date.day); + if (today == dateDay) { + return '${isHistory ? '今天 ' : ''}${_twoDigits(date.hour)}:${_twoDigits(date.minute)}'; + } + final isYesterday = today.subtract(const Duration(days: 1)) == dateDay; + if (isYesterday) { + return '昨天 ${_twoDigits(date.hour)}:${_twoDigits(date.minute)}'; + } + if (isHistory) { + final DateFormat sdf = + now.year == date.year ? _shortFormatD : longFormatD; + return sdf.format(date); + } + return longFormatD.format(date); + } + + static String format(int? time, {DateFormat? format}) { + if (time == null || time == 0) { + return ''; + } + final date = DateTime.fromMillisecondsSinceEpoch(time * 1000); + return (format ?? longFormatD).format(date); + } +} diff --git a/lib/utils/duration_util.dart b/lib/utils/duration_util.dart new file mode 100644 index 000000000..46bec3ac4 --- /dev/null +++ b/lib/utils/duration_util.dart @@ -0,0 +1,29 @@ +import 'dart:math' show pow; + +class DurationUtil { + static String formatDuration(num? seconds) { + if (seconds == null || seconds == 0) { + return '00:00'; + } + int h = seconds ~/ 3600; + seconds %= 3600; + int m = seconds ~/ 60; + seconds %= 60; + String sms = seconds is double + ? seconds.toStringAsFixed(3).padLeft(6, '0') + : seconds.toString().padLeft(2, '0'); + return h == 0 + ? "${m.toString().padLeft(2, '0')}:$sms" + : "${h.toString().padLeft(2, '0')}:${m.toString().padLeft(2, '0')}:$sms"; + } + + static int parseDuration(String data) { + List split = + data.split(':').reversed.map((e) => int.parse(e)).toList(); + int duration = 0; + for (int i = 0; i < split.length; i++) { + duration += split[i] * pow(60, i).toInt(); + } + return duration; + } +} diff --git a/lib/utils/fav_util.dart b/lib/utils/fav_util.dart new file mode 100644 index 000000000..8e43467e3 --- /dev/null +++ b/lib/utils/fav_util.dart @@ -0,0 +1,19 @@ +class FavUtil { + static bool isDefaultFav(int? attr) { + if (attr == null) { + return false; + } + return (attr & 2) == 0; + } + + static String isPublicFavText(int? attr) { + if (attr == null) { + return ''; + } + return isPublicFav(attr) ? '公开' : '私密'; + } + + static bool isPublicFav(int attr) { + return (attr & 1) == 0; + } +} diff --git a/lib/utils/id_utils.dart b/lib/utils/id_utils.dart index 715f299b2..f06e2d543 100644 --- a/lib/utils/id_utils.dart +++ b/lib/utils/id_utils.dart @@ -2,6 +2,9 @@ import 'dart:convert'; +import 'package:PiliPlus/utils/utils.dart'; +import 'package:uuid/v4.dart'; + class IdUtils { static const XOR_CODE = 23442827791579; static const MASK_CODE = 2251799813685247; @@ -67,18 +70,41 @@ class IdUtils { return result; } - // eid生成 - static String? genAuroraEid(int uid) { + static String genBuvid3() { + return '${const UuidV4().generate().toUpperCase()}${Utils.random.nextInt(100000).toString().padLeft(5, "0")}infoc'; + } + + static String genAuroraEid(int uid) { if (uid == 0) { - return null; + return ''; } - String uidString = uid.toString(); - List resultBytes = List.generate( - uidString.length, - (i) => uidString.codeUnitAt(i) ^ "ad1va46a7lza".codeUnitAt(i % 12), - ); - String auroraEid = base64Url.encode(resultBytes); - auroraEid = auroraEid.replaceAll(RegExp(r'=*$', multiLine: true), ''); - return auroraEid; + + var midByte = utf8.encode(uid.toString()); + + const key = 'ad1va46a7lza'; + for (int i = 0; i < midByte.length; i++) { + midByte[i] ^= key.codeUnitAt(i % key.length); + } + + String base64Encoded = base64.encode(midByte).replaceAll('=', ''); + + return base64Encoded; + } + + static String genTraceId() { + String randomId = Utils.generateRandomString(32); + + StringBuffer randomTraceId = StringBuffer(randomId.substring(0, 24)); + + int ts = DateTime.now().millisecondsSinceEpoch ~/ 1000; + + for (int i = 2; i >= 0; i--) { + ts >>= 8; + randomTraceId.write((ts & 0xFF).toRadixString(16).padLeft(2, '0')); + } + + randomTraceId.write(randomId.substring(30, 32)); + + return '${randomTraceId.toString()}:${randomTraceId.toString().substring(16, 32)}:0:0'; } } diff --git a/lib/utils/download.dart b/lib/utils/image_util.dart similarity index 83% rename from lib/utils/download.dart rename to lib/utils/image_util.dart index 5a6758167..88c779f06 100644 --- a/lib/utils/download.dart +++ b/lib/utils/image_util.dart @@ -2,30 +2,34 @@ import 'dart:io'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/utils/extension.dart'; +import 'package:PiliPlus/utils/global_data.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:intl/intl.dart' show DateFormat; import 'package:live_photo_maker/live_photo_maker.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:saver_gallery/saver_gallery.dart'; import 'package:share_plus/share_plus.dart'; -class DownloadUtils { +class ImageUtil { + static String get time => + DateFormat('yyyy-MM-dd_HH-mm-ss').format(DateTime.now()); + // 图片分享 - static Future onShareImg(String imgUrl) async { + static Future onShareImg(String url) async { try { SmartDialog.showLoading(); var response = await Request() - .get(imgUrl, options: Options(responseType: ResponseType.bytes)); + .get(url, options: Options(responseType: ResponseType.bytes)); final temp = await getTemporaryDirectory(); SmartDialog.dismiss(); - String imgName = - "plpl_pic_${DateTime.now().toString().split('-').join()}.jpg"; - var path = '${temp.path}/$imgName'; + var name = Utils.getFileName(url); + var path = '${temp.path}/$name'; File(path).writeAsBytesSync(response.data); Rect? sharePositionOrigin; @@ -35,7 +39,7 @@ class DownloadUtils { Share.shareXFiles( [XFile(path)], - subject: imgUrl, + subject: url, sharePositionOrigin: sharePositionOrigin, ); } catch (e) { @@ -111,17 +115,9 @@ class DownloadUtils { SmartDialog.showLoading(msg: '正在下载'); String tmpPath = (await getTemporaryDirectory()).path; - String time = DateTime.now() - .toString() - .replaceAll(' ', '_') - .replaceAll(':', '-') - .split('.') - .first; - late String imageName = - "cover_$time.${url.split('.').lastOrNull ?? 'jpg'}"; + late String imageName = "cover_${Utils.getFileName(url)}"; late String imagePath = '$tmpPath/$imageName'; - String videoName = - "video_$time.${liveUrl.split('.').lastOrNull ?? 'mp4'}"; + String videoName = "video_${Utils.getFileName(liveUrl)}"; String videoPath = '$tmpPath/$videoName'; await Request.dio.download(liveUrl, videoPath); @@ -167,10 +163,7 @@ class DownloadUtils { } static Future downloadImg( - BuildContext context, - List imgList, { - String imgType = 'cover', - }) async { + BuildContext context, List imgList) async { if (!await checkPermissionDependOnSdkInt(context)) return false; final cancelToken = CancelToken(); SmartDialog.showLoading( @@ -238,4 +231,28 @@ class DownloadUtils { SmartDialog.dismiss(status: SmartStatus.loading); } } + + static final regExp = + RegExp(r'(@(\d+[a-z]_?)*)(\..*)?$', caseSensitive: false); + + static String thumbnailUrl(String? src, [int? quality]) { + if (src != null && quality != 100) { + bool hasMatch = false; + src = src.splitMapJoin( + regExp, + onMatch: (Match match) { + hasMatch = true; + String suffix = match.group(3) ?? '.webp'; + return '${match.group(1)}_${quality ?? GlobalData().imgQuality}q$suffix'; + }, + onNonMatch: (String str) { + return str; + }, + ); + if (!hasMatch) { + src += '@${quality ?? GlobalData().imgQuality}q.webp'; + } + } + return src.http2https; + } } diff --git a/lib/utils/num_util.dart b/lib/utils/num_util.dart new file mode 100644 index 000000000..4811f139f --- /dev/null +++ b/lib/utils/num_util.dart @@ -0,0 +1,62 @@ +import 'package:flutter/foundation.dart' show kDebugMode, debugPrint; +import 'package:get/get_utils/get_utils.dart'; + +class NumUtil { + static final _numRegExp = RegExp(r'([\d\.]+)([千万亿])?'); + + static int _getUnit(String? unit) { + switch (unit) { + case '千': + return 1000; + case '万': + return 10000; + case '亿': + return 100000000; + default: + return 1; + } + } + + static int parseNum(String numberStr) { + if (numberStr == '-') return 0; + try { + final match = _numRegExp.firstMatch(numberStr)!; + var number = double.parse(match.group(1)!); + number *= _getUnit(match.group(2)); + return number.toInt(); + } catch (e) { + if (kDebugMode) debugPrint('parse failed: "$numberStr" : $e'); + return 0; + } + } + + static String numFormat(dynamic number) { + if (number == null) { + return '0'; + } + if (number is String) { + number = int.tryParse(number) ?? number; + if (number is String) { + return number; + } + } + + String format(first, second) { + double result = ((number / first) as double).toPrecision(1); + int intRes = result.toInt(); + if (result == intRes) { + return '$intRes$second'; + } else { + return '$result$second'; + } + } + + if (number >= 100000000) { + return format(100000000, '亿'); + } else if (number >= 10000) { + return format(10000, '万'); + } else { + return number.toString(); + } + } +} diff --git a/lib/utils/update.dart b/lib/utils/update.dart new file mode 100644 index 000000000..73ef2377e --- /dev/null +++ b/lib/utils/update.dart @@ -0,0 +1,132 @@ +import 'dart:io' show Platform; + +import 'package:PiliPlus/build_config.dart'; +import 'package:PiliPlus/http/api.dart'; +import 'package:PiliPlus/http/init.dart'; +import 'package:PiliPlus/utils/page_utils.dart'; +import 'package:PiliPlus/utils/storage.dart'; +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/foundation.dart' show kDebugMode; +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; + +class Update { + // 检查更新 + static Future checkUpdate([bool isAuto = true]) async { + if (kDebugMode) return; + SmartDialog.dismiss(); + try { + final res = await Request().get(Api.latestApp, uaType: 'mob'); + if (res.data is Map || res.data.isEmpty) { + if (!isAuto) { + SmartDialog.showToast('检查更新失败,GitHub接口未返回数据,请检查网络'); + } + return; + } + DateTime latest = DateTime.parse(res.data[0]['created_at']); + DateTime current = DateTime.parse('${BuildConfig.buildTime}Z'); + current = current.copyWith(hour: current.hour - 8); + if (current.compareTo(latest) >= 0) { + if (!isAuto) { + SmartDialog.showToast('已是最新版本'); + } + } else { + SmartDialog.show( + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (context) { + final ThemeData theme = Theme.of(context); + return AlertDialog( + title: const Text('🎉 发现新版本 '), + content: SizedBox( + height: 280, + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${res.data[0]['tag_name']}', + style: const TextStyle(fontSize: 20), + ), + const SizedBox(height: 8), + Text('${res.data[0]['body']}'), + TextButton( + onPressed: () => PageUtils.launchURL( + 'https://github.com/bggRGjQaUbCoE/PiliPlus/commits/main'), + child: Text( + "点此查看完整更新(即commit)内容", + style: TextStyle( + color: theme.colorScheme.primary, + ), + ), + ), + ], + ), + ), + ), + actions: [ + TextButton( + onPressed: () { + SmartDialog.dismiss(); + GStorage.setting.put(SettingBoxKey.autoUpdate, false); + }, + child: Text( + '不再提醒', + style: TextStyle( + color: theme.colorScheme.outline, + ), + ), + ), + TextButton( + onPressed: SmartDialog.dismiss, + child: Text( + '取消', + style: TextStyle( + color: theme.colorScheme.outline, + ), + ), + ), + TextButton( + onPressed: () => onDownload(res.data[0]), + child: const Text('Github'), + ), + ], + ); + }, + ); + } + } catch (e) { + if (kDebugMode) debugPrint('failed to check update: $e'); + } + } + + // 下载适用于当前系统的安装包 + static Future onDownload(data) async { + SmartDialog.dismiss(); + try { + void download(plat) { + if (data['assets'].isNotEmpty) { + for (dynamic i in data['assets']) { + if (i['name'].contains(plat)) { + PageUtils.launchURL(i['browser_download_url']); + break; + } + } + } + } + + if (Platform.isAndroid) { + // 获取设备信息 + AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo; + // [arm64-v8a] + download(androidInfo.supportedAbis.first); + } else { + download('ios'); + } + } catch (_) { + PageUtils.launchURL( + 'https://github.com/bggRGjQaUbCoE/PiliPlus/releases/latest'); + } + } +} diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 0ada69cbb..1af1e6fb4 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -1,70 +1,31 @@ +// ignore_for_file: constant_identifier_names + import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:math'; -import 'package:PiliPlus/build_config.dart'; -import 'package:PiliPlus/common/constants.dart'; -import 'package:PiliPlus/http/api.dart'; -import 'package:PiliPlus/http/init.dart'; -import 'package:PiliPlus/models/home/rcmd/result.dart'; -import 'package:PiliPlus/models/model_rec_video_item.dart'; -import 'package:PiliPlus/models/model_video.dart'; -import 'package:PiliPlus/utils/extension.dart'; -import 'package:PiliPlus/utils/global_data.dart'; -import 'package:PiliPlus/utils/page_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; -import 'package:crypto/crypto.dart'; import 'package:device_info_plus/device_info_plus.dart'; -import 'package:flutter/foundation.dart' show kDebugMode; -import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:path/path.dart' as path; import 'package:share_plus/share_plus.dart'; -import 'package:uuid/v4.dart'; class Utils { static final Random random = Random(); static const channel = MethodChannel("PiliPlus"); - static final _numRegExp = RegExp(r'([\d\.]+)([千万亿])?'); - - static final regExp = - RegExp(r'(@(\d+[a-z]_?)*)(\..*)?$', caseSensitive: false); - - static String thumbnailImgUrl(String? src, [int? quality]) { - if (src != null && quality != 100) { - bool hasMatch = false; - src = src.splitMapJoin( - regExp, - onMatch: (Match match) { - hasMatch = true; - String suffix = match.group(3) ?? '.webp'; - return '${match.group(1)}_${quality ?? GlobalData().imgQuality}q$suffix'; - }, - onNonMatch: (String str) { - return str; - }, - ); - if (!hasMatch) { - src += '@${quality ?? GlobalData().imgQuality}q.webp'; - } - } - return src.http2https; - } - static bool? _isIpad; static Future isIpad() async { - if (!Platform.isIOS) { - return false; - } if (_isIpad != null) { return _isIpad!; } + if (!Platform.isIOS) { + return false; + } DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); IosDeviceInfo info = await deviceInfo.iosInfo; _isIpad = info.model.toLowerCase().contains("ipad"); @@ -99,24 +60,6 @@ class Utils { return numericRegex.hasMatch(str); } - static bool isDefaultFav(int? attr) { - if (attr == null) { - return false; - } - return (attr & 2) == 0; - } - - static String isPublicFavText(int? attr) { - if (attr == null) { - return ''; - } - return isPublicFav(attr) ? '公开' : '私密'; - } - - static bool isPublicFav(int attr) { - return (attr & 1) == 0; - } - static String generateRandomString(int length) { const characters = '0123456789abcdefghijklmnopqrstuvwxyz'; @@ -124,45 +67,6 @@ class Utils { (_) => characters.codeUnitAt(random.nextInt(characters.length)))); } - static String genAuroraEid(int uid) { - if (uid == 0) { - return ''; // Return null for a UID of 0 - } - - // 1. Convert UID to a byte array. - var midByte = utf8.encode(uid.toString()); - - // 2. XOR each byte with the corresponding byte from the key. - const key = 'ad1va46a7lza'; - for (int i = 0; i < midByte.length; i++) { - midByte[i] ^= key.codeUnitAt(i % key.length); - } - - // 3. Perform Base64 encoding without padding. - String base64Encoded = - base64.encode(midByte).replaceAll('=', ''); // Remove padding - - // Return the resulting x-bili-aurora-eid. - return base64Encoded; - } - - static String genTraceId() { - String randomId = generateRandomString(32); - - StringBuffer randomTraceId = StringBuffer(randomId.substring(0, 24)); - - int ts = DateTime.now().millisecondsSinceEpoch ~/ 1000; - - for (int i = 2; i >= 0; i--) { - ts >>= 8; - randomTraceId.write((ts & 0xFF).toRadixString(16).padLeft(2, '0')); - } - - randomTraceId.write(randomId.substring(30, 32)); - - return '${randomTraceId.toString()}:${randomTraceId.toString().substring(16, 32)}:0:0'; - } - static void copyText( String text, { bool needToast = true, @@ -174,313 +78,10 @@ class Utils { } } - // static Future getCookiePath() async { - // final Directory tempDir = await getApplicationSupportDirectory(); - // final String tempPath = "${tempDir.path}/.plpl/"; - // final Directory dir = Directory(tempPath); - // final bool b = await dir.exists(); - // if (!b) { - // dir.createSync(recursive: true); - // } - // return tempPath; - // } - - static int getUnit(String? unit) { - switch (unit) { - case '千': - return 1000; - case '万': - return 10000; - case '亿': - return 100000000; - default: - return 1; - } - } - - static int parseNum(String numberStr) { - if (numberStr == '-') return 0; - try { - final match = _numRegExp.firstMatch(numberStr)!; - var number = double.parse(match.group(1)!); - number *= getUnit(match.group(2)); - return number.toInt(); - } catch (e) { - if (kDebugMode) debugPrint('parse failed: "$numberStr" : $e'); - return 0; - } - } - - static String numFormat(dynamic number) { - if (number == null) { - return '0'; - } - if (number is String) { - number = int.tryParse(number) ?? number; - if (number is String) { - return number; - } - } - - String format(first, second) { - double result = ((number / first) as double).toPrecision(1); - int intRes = result.toInt(); - if (result == intRes) { - return '$intRes$second'; - } else { - return '$result$second'; - } - } - - if (number >= 100000000) { - return format(100000000, '亿'); - } else if (number >= 10000) { - return format(10000, '万'); - } else { - return number.toString(); - } - } - - static String durationReadFormat(String duration) { - List durationParts = duration.split(':'); - - if (durationParts.length == 3) { - if (durationParts[0] != '00') { - return '${int.parse(durationParts[0])}小时${durationParts[1]}分钟${durationParts[2]}秒'; - } - durationParts.removeAt(0); - } - if (durationParts.length == 2) { - if (durationParts[0] != '00') { - return '${int.parse(durationParts[0])}分钟${durationParts[1]}秒'; - } - durationParts.removeAt(0); - } - return '${int.parse(durationParts[0])}秒'; - } - - static String videoItemSemantics(BaseVideoItemModel videoItem) { - StringBuffer semanticsLabel = StringBuffer(); - bool emptyStatCheck(int? stat) { - return stat == null || stat <= 0; - } - - if (videoItem is RecVideoItemAppModel) { - if (videoItem.goto == 'picture') { - semanticsLabel.write('动态,'); - } else if (videoItem.goto == 'bangumi') { - semanticsLabel.write('番剧,'); - } - } - semanticsLabel.write(videoItem.title); - - if (!emptyStatCheck(videoItem.stat.view)) { - semanticsLabel - ..write(',${Utils.numFormat(videoItem.stat.view)}') - ..write( - (videoItem is RecVideoItemAppModel && videoItem.goto == 'picture') - ? '浏览' - : '播放'); - } - if (!emptyStatCheck(videoItem.stat.danmu)) { - semanticsLabel.write(',${Utils.numFormat(videoItem.stat.danmu)}弹幕'); - } - if ((videoItem is BaseRecVideoItemModel) && videoItem.rcmdReason != null) { - semanticsLabel.write(',${videoItem.rcmdReason}'); - } - if (!emptyStatCheck(videoItem.duration) && videoItem.duration > 0) { - semanticsLabel.write( - ',时长${Utils.durationReadFormat(Utils.timeFormat(videoItem.duration))}'); - } - if (videoItem.pubdate != null) { - semanticsLabel - .write(',${Utils.dateFormat(videoItem.pubdate!, formatType: 'day')}'); - } - if (videoItem.owner.name != '') { - semanticsLabel.write(',Up主:${videoItem.owner.name}'); - } - if (videoItem is BaseRecVideoItemModel && videoItem.isFollowed) { - semanticsLabel.write(',已关注'); - } - return semanticsLabel.toString(); - } - - static String timeFormat(dynamic time) { - if (time is String && time.contains(':')) { - return time; - } - if (time == null || time == 0) { - return '00:00'; - } - return formatDuration(time); - } - - static String shortenChineseDateString(String date) { - return date.contains("年") - ? RegExp(r'\d+') - .allMatches(date) - .map((match) => match.group(0)?.length == 4 - ? match.group(0)!.substring(2) - : match.group(0)) - .join('-') - : date; - // if (date.contains("年")) return '${date.split("年").first}年'; - // return date; - } - - // 完全相对时间显示 - static String formatTimestampToRelativeTime(timeStamp) { - var difference = DateTime.now() - .difference(DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000)); - - if (difference.inDays > 365) { - return '${difference.inDays ~/ 365}年前'; - } else if (difference.inDays > 30) { - return '${difference.inDays ~/ 30}个月前'; - } else if (difference.inDays > 0) { - return '${difference.inDays}天前'; - } else if (difference.inHours > 0) { - return '${difference.inHours}小时前'; - } else if (difference.inMinutes > 0) { - return '${difference.inMinutes}分钟前'; - } else { - return '刚刚'; - } - } - - // 时间显示,刚刚,x分钟前 - static String dateFormat(timeStamp, {formatType = 'list'}) { - if (timeStamp == null || timeStamp == 0 || timeStamp == '') { - return ''; - } - // 当前时间 - int time = (DateTime.now().millisecondsSinceEpoch / 1000).round(); - // 对比 - int distance = (time - timeStamp).toInt(); - // 当前年日期 - String currentYearStr = 'MM月DD日 hh:mm'; - String lastYearStr = 'YY年MM月DD日 hh:mm'; - if (formatType == 'detail') { - currentYearStr = 'MM-DD hh:mm'; - lastYearStr = 'YY-MM-DD hh:mm'; - return customStampStr( - timestamp: timeStamp, date: lastYearStr, toInt: false); - } else if (formatType == 'day') { - if (distance <= 43200) { - return customStampStr( - timestamp: timeStamp, - date: 'hh:mm', - toInt: true, - ); - } - return customStampStr( - timestamp: timeStamp, - date: 'YY-MM-DD', - toInt: true, - ); - } - if (distance <= 60) { - return '刚刚'; - } else if (distance <= 3600) { - return '${(distance / 60).floor()}分钟前'; - } else if (distance <= 43200) { - return '${(distance / 60 / 60).floor()}小时前'; - } else if (DateTime.fromMillisecondsSinceEpoch(time * 1000).year == - DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000).year) { - return customStampStr( - timestamp: timeStamp, date: currentYearStr, toInt: false); - } else { - return customStampStr( - timestamp: timeStamp, date: lastYearStr, toInt: false); - } - } - - // 时间戳转时间 - static String customStampStr({ - int? timestamp, // 为空则显示当前时间 - String? date, // 显示格式,比如:'YY年MM月DD日 hh:mm:ss' - bool toInt = true, // 去除0开头 - }) { - timestamp ??= (DateTime.now().millisecondsSinceEpoch / 1000).round(); - String timeStr = - (DateTime.fromMillisecondsSinceEpoch(timestamp * 1000)).toString(); - - dynamic dateArr = timeStr.split(' ')[0]; - dynamic timeArr = timeStr.split(' ')[1]; - - // ignore: non_constant_identifier_names - String YY = dateArr.split('-')[0]; - // ignore: non_constant_identifier_names - String MM = dateArr.split('-')[1]; - // ignore: non_constant_identifier_names - String DD = dateArr.split('-')[2]; - - String hh = timeArr.split(':')[0]; - String mm = timeArr.split(':')[1]; - String ss = timeArr.split(':')[2]; - - ss = ss.split('.')[0]; - - // 去除0开头 - if (toInt) { - MM = (int.parse(MM)).toString(); - DD = (int.parse(DD)).toString(); - hh = (int.parse(hh)).toString(); - // mm = (int.parse(mm)).toString(); - } - - if (date == null) { - return timeStr; - } - - date = date - .replaceAll('YY', YY) - .replaceAll('MM', MM) - .replaceAll('DD', DD) - .replaceAll('hh', hh) - .replaceAll('mm', mm) - .replaceAll('ss', ss); - // if (int.parse(YY) == DateTime.now().year && - // int.parse(MM) == DateTime.now().month) { - // // 当天 - // if (int.parse(DD) == DateTime.now().day) { - // return '今天'; - // } - // } - return date; - } - static String makeHeroTag(v) { return v.toString() + random.nextInt(9999).toString(); } - static String formatDuration(num seconds) { - int h = seconds ~/ 3600; - seconds %= 3600; - int m = seconds ~/ 60; - seconds %= 60; - String sms = seconds is double - ? seconds.toStringAsFixed(3).padLeft(6, '0') - : seconds.toString().padLeft(2, '0'); - return h == 0 - ? "${m.toString().padLeft(2, '0')}:$sms" - : "${h.toString().padLeft(2, '0')}:${m.toString().padLeft(2, '0')}:$sms"; - } - - static int duration(String duration) { - List timeList = duration.split(':'); - int len = timeList.length; - if (len == 2) { - return int.parse(timeList[0]) * 60 + int.parse(timeList[1]); - } - if (len == 3) { - return int.parse(timeList[0]) * 3600 + - int.parse(timeList[1]) * 60 + - int.parse(timeList[2]); - } - return 0; - } - static int findClosestNumber(int target, List numbers) { List filterNums = numbers.where((number) => number <= target).toList(); return filterNums.isNotEmpty @@ -488,138 +89,6 @@ class Utils { : numbers.reduce((a, b) => a > b ? b : a); } - // 检查更新 - static Future checkUpdate([bool isAuto = true]) async { - if (kDebugMode) return; - SmartDialog.dismiss(); - try { - final res = await Request().get(Api.latestApp, uaType: 'mob'); - if (res.data is Map || res.data.isEmpty) { - if (!isAuto) { - SmartDialog.showToast('检查更新失败,GitHub接口未返回数据,请检查网络'); - } - return; - } - DateTime latest = DateTime.parse(res.data[0]['created_at']); - DateTime current = DateTime.parse('${BuildConfig.buildTime}Z'); - current = current.copyWith(hour: current.hour - 8); - if (current.compareTo(latest) >= 0) { - if (!isAuto) { - SmartDialog.showToast('已是最新版本'); - } - } else { - SmartDialog.show( - animationType: SmartAnimationType.centerFade_otherSlide, - builder: (context) { - final ThemeData theme = Theme.of(context); - return AlertDialog( - title: const Text('🎉 发现新版本 '), - content: SizedBox( - height: 280, - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '${res.data[0]['tag_name']}', - style: const TextStyle(fontSize: 20), - ), - const SizedBox(height: 8), - Text('${res.data[0]['body']}'), - TextButton( - onPressed: () => PageUtils.launchURL( - 'https://github.com/bggRGjQaUbCoE/PiliPlus/commits/main'), - child: Text( - "点此查看完整更新(即commit)内容", - style: TextStyle( - color: theme.colorScheme.primary, - ), - ), - ), - ], - ), - ), - ), - actions: [ - TextButton( - onPressed: () { - SmartDialog.dismiss(); - GStorage.setting.put(SettingBoxKey.autoUpdate, false); - }, - child: Text( - '不再提醒', - style: TextStyle( - color: theme.colorScheme.outline, - ), - ), - ), - TextButton( - onPressed: SmartDialog.dismiss, - child: Text( - '取消', - style: TextStyle( - color: theme.colorScheme.outline, - ), - ), - ), - TextButton( - onPressed: () => onDownload(res.data[0]), - child: const Text('Github'), - ), - ], - ); - }, - ); - } - } catch (e) { - if (kDebugMode) debugPrint('failed to check update: $e'); - } - } - - // 下载适用于当前系统的安装包 - static Future onDownload(data) async { - await SmartDialog.dismiss(); - try { - void download(plat) { - if (data['assets'].isNotEmpty) { - for (dynamic i in data['assets']) { - if (i['name'].contains(plat)) { - PageUtils.launchURL(i['browser_download_url']); - break; - } - } - } - } - - if (Platform.isAndroid) { - // 获取设备信息 - AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo; - // [arm64-v8a] - download(androidInfo.supportedAbis.first); - } else { - download('ios'); - } - } catch (_) { - PageUtils.launchURL( - 'https://github.com/bggRGjQaUbCoE/PiliPlus/releases/latest'); - } - } - - static void appSign(Map params, - [String appkey = Constants.appKey, String appsec = Constants.appSec]) { - params['appkey'] = appkey; - var searchParams = Uri( - queryParameters: - params.map((key, value) => MapEntry(key, value.toString()))).query; - var sortedQueryString = (searchParams.split('&')..sort()).join('&'); - - params['sign'] = md5 - .convert(utf8.encode(sortedQueryString + appsec)) - .toString(); // 获取MD5哈希值 - } - static List generateRandomBytes(int minLength, int maxLength) { return List.generate( minLength + random.nextInt(maxLength - minLength + 1), @@ -638,8 +107,4 @@ class Utils { final i1 = fileExt ? uri.length : uri.lastIndexOf('.'); return uri.substring(i0, i1); } - - static String genBuvid3() { - return '${const UuidV4().generate().toUpperCase()}${random.nextInt(100000).toString().padLeft(5, "0")}infoc'; - } }