diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 000000000..e9bf447d4 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,143 @@ +name: CI + +on: + workflow_dispatch: + push: + branches: + - '*' + paths-ignore: + - '**.md' + - '**.txt' + - '.github/**' + - '.idea/**' + - '!.github/workflows/**' + +jobs: + android: + name: Build CI (Android) + runs-on: ubuntu-latest + + steps: + - name: 代码迁出 + uses: actions/checkout@v3 + + - name: 构建Java环境 + uses: actions/setup-java@v3 + with: + distribution: "zulu" + java-version: "17" + token: ${{secrets.GIT_TOKEN}} + + - name: 检查缓存 + uses: actions/cache@v2 + id: cache-flutter + with: + path: /root/flutter-sdk + key: ${{ runner.os }}-flutter-${{ hashFiles('**/pubspec.lock') }} + + - name: 安装Flutter + if: steps.cache-flutter.outputs.cache-hit != 'true' + uses: subosito/flutter-action@v2 + with: + flutter-version: 3.16.5 + channel: any + + - name: 下载项目依赖 + run: flutter pub get + + - name: 解码生成 jks + run: echo $KEYSTORE_BASE64 | base64 -di > android/app/vvex.jks + env: + KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} + + - name: flutter build apk + run: flutter build apk --release --split-per-abi + env: + KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.KEY_PASSWORD}} + + - name: 获取版本号 + id: version + run: echo "version=${GITHUB_SHA::7}" >>$GITHUB_OUTPUT + + - name: 重命名应用 + run: | + for file in build/app/outputs/flutter-apk/app-*.apk; do + if [[ $file =~ app-(.?*)release.apk ]]; then + new_file_name="build/app/outputs/flutter-apk/Pili-${BASH_REMATCH[1]}${{ steps.version.outputs.version }}.apk" + mv "$file" "$new_file_name" + fi + done + + - name: 上传 + uses: actions/upload-artifact@v3 + with: + name: Pilipala-CI + path: | + build/app/outputs/flutter-apk/Pili-*.apk + + iOS: + name: Build CI (iOS) + runs-on: macos-latest + + steps: + - name: 代码迁出 + uses: actions/checkout@v4 + + - name: 安装Flutter + if: steps.cache-flutter.outputs.cache-hit != 'true' + uses: subosito/flutter-action@v2.10.0 + with: + cache: true + flutter-version: 3.16.5 + + - name: flutter build ipa + run: | + flutter build ios --release --no-codesign + ln -sf ./build/ios/iphoneos Payload + zip -r9 app.ipa Payload/runner.app + + - name: 获取版本号 + id: version + run: echo "version=${GITHUB_SHA::7}" >>$GITHUB_OUTPUT + + - name: 重命名应用 + run: | + DATE=${{ steps.date.outputs.date }} + for file in app.ipa; do + new_file_name="build/Pili-${{ steps.version.outputs.version }}.ipa" + mv "$file" "$new_file_name" + done + + - name: 上传 + uses: actions/upload-artifact@v3 + with: + if-no-files-found: error + name: Pilipala-CI + path: | + build/Pili-*.ipa + + upload: + runs-on: ubuntu-latest + + needs: + - android + - iOS + steps: + + - uses: actions/download-artifact@v3 + with: + name: Pilipala-CI + path: ./Pilipala-CI + + - name: Upload Pre-release + uses: ncipollo/release-action@v1 + with: + name: v${{ github.run_number }} + token: ${{ secrets.GIT_TOKEN }} + commit: main + tag: v${{ github.run_number }} + prerelease: true + allowUpdates: true + artifacts: Pilipala-CI/* diff --git a/android/build.gradle b/android/build.gradle index 713d7f6e6..7296566c9 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,6 +1,13 @@ buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlin_version = '1.9.22' repositories { + maven { url "https://maven.aliyun.com/repository/google" } + maven { url "https://maven.aliyun.com/repository/central" } + maven { url "https://maven.aliyun.com/repository/jcenter" } + maven { url "https://maven.aliyun.com/repository/public" } + maven { url "http://download.flutter.io" + allowInsecureProtocol = true + } google() mavenCentral() } @@ -13,6 +20,13 @@ buildscript { allprojects { repositories { + maven { url "https://maven.aliyun.com/repository/google" } + maven { url "https://maven.aliyun.com/repository/central" } + maven { url "https://maven.aliyun.com/repository/jcenter" } + maven { url "https://maven.aliyun.com/repository/public" } + maven { url "http://download.flutter.io" + allowInsecureProtocol = true + } google() mavenCentral() } diff --git a/assets/images/live/default_bg.webp b/assets/images/live/default_bg.webp deleted file mode 100644 index a58259dea..000000000 Binary files a/assets/images/live/default_bg.webp and /dev/null differ diff --git a/lib/common/widgets/network_img_layer.dart b/lib/common/widgets/network_img_layer.dart index 03adc166e..7ea20af96 100644 --- a/lib/common/widgets/network_img_layer.dart +++ b/lib/common/widgets/network_img_layer.dart @@ -35,29 +35,16 @@ class NetworkImgLayer extends StatelessWidget { final String imageUrl = '${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? 100}q.webp'; int? memCacheWidth, memCacheHeight; - double aspectRatio = (width / height).toDouble(); - void setMemCacheSizes() { - if (aspectRatio > 1) { - memCacheHeight = height.cacheSize(context); - } else if (aspectRatio < 1) { - memCacheWidth = width.cacheSize(context); - } else { - if (origAspectRatio != null && origAspectRatio! > 1) { - memCacheWidth = width.cacheSize(context); - } else if (origAspectRatio != null && origAspectRatio! < 1) { - memCacheHeight = height.cacheSize(context); - } else { - memCacheWidth = width.cacheSize(context); - memCacheHeight = height.cacheSize(context); - } - } - } - - setMemCacheSizes(); - - if (memCacheWidth == null && memCacheHeight == null) { - memCacheWidth = width.toInt(); + if (width > height || (origAspectRatio != null && origAspectRatio! > 1)) { + memCacheWidth = width.cacheSize(context); + } else if (width < height || + (origAspectRatio != null && origAspectRatio! < 1)) { + memCacheHeight = height.cacheSize(context); + } else { + // 不能同时设置,否则会导致图片变形 + memCacheWidth = width.cacheSize(context); + // memCacheHeight = height.cacheSize(context); } return src != '' && src != null diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index c78643db0..2a1bedf22 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -2,14 +2,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import '../../http/search.dart'; -import '../../http/user.dart'; -import '../../http/video.dart'; import '../../utils/utils.dart'; import '../constants.dart'; import 'badge.dart'; import 'network_img_layer.dart'; import 'stat/danmu.dart'; import 'stat/view.dart'; +import 'video_popup_menu.dart'; // 视频卡片 - 水平布局 class VideoCardH extends StatelessWidget { @@ -240,113 +239,14 @@ class VideoContent extends StatelessWidget { ), const Spacer(), - // SizedBox( - // width: 20, - // height: 20, - // child: IconButton( - // tooltip: '稍后再看', - // style: ButtonStyle( - // padding: MaterialStateProperty.all(EdgeInsets.zero), - // ), - // onPressed: () async { - // var res = - // await UserHttp.toViewLater(bvid: videoItem.bvid); - // SmartDialog.showToast(res['msg']); - // }, - // icon: Icon( - // Icons.more_vert_outlined, - // color: Theme.of(context).colorScheme.outline, - // size: 14, - // ), - // ), - // ), if (source == 'normal') SizedBox( width: 24, height: 24, - child: PopupMenuButton( - padding: EdgeInsets.zero, - icon: Icon( - Icons.more_vert_outlined, - color: Theme.of(context).colorScheme.outline, - size: 14, - ), - position: PopupMenuPosition.under, - // constraints: const BoxConstraints(maxHeight: 35), - onSelected: (String type) {}, - itemBuilder: (BuildContext context) => - >[ - PopupMenuItem( - onTap: () async { - var res = await UserHttp.toViewLater( - bvid: videoItem.bvid as String); - SmartDialog.showToast(res['msg']); - }, - value: 'pause', - height: 40, - child: const Row( - children: [ - Icon(Icons.watch_later_outlined, size: 16), - SizedBox(width: 6), - Text('稍后再看', style: TextStyle(fontSize: 13)) - ], - ), - ), - const PopupMenuDivider(), - PopupMenuItem( - onTap: () async { - SmartDialog.show( - useSystem: true, - animationType: - SmartAnimationType.centerFade_otherSlide, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('提示'), - content: Text( - '确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?' - '\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'), - actions: [ - TextButton( - onPressed: () => SmartDialog.dismiss(), - child: Text( - '点错了', - style: TextStyle( - color: Theme.of(context) - .colorScheme - .outline), - ), - ), - TextButton( - onPressed: () async { - var res = await VideoHttp.relationMod( - mid: videoItem.owner.mid, - act: 5, - reSrc: 11, - ); - SmartDialog.dismiss(); - SmartDialog.showToast(res['code'] == 0 - ? '成功' - : res['msg']); - }, - child: const Text('确认'), - ) - ], - ); - }, - ); - }, - value: 'pause', - height: 40, - child: Row( - children: [ - const Icon(Icons.block, size: 16), - const SizedBox(width: 6), - Text('拉黑:${videoItem.owner.name}', - style: const TextStyle(fontSize: 13)) - ], - ), - ), - ], + child: VideoPopupMenu( + size: 32, + iconSize: 18, + videoItem: videoItem, ), ), ], diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 43dd05caa..5a9aa6565 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -6,26 +6,23 @@ import 'stat/danmu.dart'; import 'stat/view.dart'; import '../../http/dynamics.dart'; import '../../http/search.dart'; -import '../../http/user.dart'; -import '../../http/video.dart'; import '../../models/common/search_type.dart'; import '../../utils/id_utils.dart'; import '../../utils/utils.dart'; import '../constants.dart'; import 'badge.dart'; import 'network_img_layer.dart'; +import 'video_popup_menu.dart'; // 视频卡片 - 垂直布局 class VideoCardV extends StatelessWidget { final dynamic videoItem; - final int crossAxisCount; final Function()? longPress; final Function()? longPressEnd; const VideoCardV({ Key? key, required this.videoItem, - required this.crossAxisCount, this.longPress, this.longPressEnd, }) : super(key: key); @@ -162,13 +159,6 @@ class VideoCardV extends StatelessWidget { ), ), if (videoItem.duration > 0) - if (crossAxisCount == 1) ...[ - PBadge( - bottom: 10, - right: 10, - text: Utils.timeFormat(videoItem.duration), - ) - ] else ...[ PBadge( bottom: 6, right: 7, @@ -176,12 +166,11 @@ class VideoCardV extends StatelessWidget { type: 'gray', text: Utils.timeFormat(videoItem.duration), ) - ], ], ); }), ), - VideoContent(videoItem: videoItem, crossAxisCount: crossAxisCount) + VideoContent(videoItem: videoItem) ], ), ), @@ -192,18 +181,14 @@ class VideoCardV extends StatelessWidget { class VideoContent extends StatelessWidget { final dynamic videoItem; - final int crossAxisCount; const VideoContent( - {Key? key, required this.videoItem, required this.crossAxisCount}) + {Key? key, required this.videoItem}) : super(key: key); @override Widget build(BuildContext context) { return Expanded( - flex: crossAxisCount == 1 ? 0 : 1, child: Padding( - padding: crossAxisCount == 1 - ? const EdgeInsets.fromLTRB(9, 9, 9, 4) - : const EdgeInsets.fromLTRB(5, 8, 5, 4), + padding: const EdgeInsets.fromLTRB(5, 8, 5, 4), child: Column( crossAxisAlignment: CrossAxisAlignment.start, // mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -217,23 +202,12 @@ class VideoContent extends StatelessWidget { overflow: TextOverflow.ellipsis, ), ), - if (videoItem.goto == 'av' && crossAxisCount == 1) ...[ - const SizedBox(width: 10), - VideoPopupMenu( - size: 32, - iconSize: 18, - videoItem: videoItem, - ), - ], ], ), - if (crossAxisCount > 1) ...[ const SizedBox(height: 2), VideoStat( videoItem: videoItem, ), - ], - if (crossAxisCount == 1) const SizedBox(height: 4), Row( children: [ if (videoItem.goto == 'bangumi') ...[ @@ -272,7 +246,7 @@ class VideoContent extends StatelessWidget { ) ], Expanded( - flex: crossAxisCount == 1 ? 0 : 1, + flex: 1, child: Text( videoItem.owner.name, maxLines: 1, @@ -283,21 +257,7 @@ class VideoContent extends StatelessWidget { ), ), ), - if (crossAxisCount == 1) ...[ - Text( - ' • ', - style: TextStyle( - fontSize: - Theme.of(context).textTheme.labelMedium!.fontSize, - color: Theme.of(context).colorScheme.outline, - ), - ), - VideoStat( - videoItem: videoItem, - ), - const Spacer(), - ], - if (videoItem.goto == 'av' && crossAxisCount != 1) ...[ + if (videoItem.goto == 'av') ...[ VideoPopupMenu( size: 24, iconSize: 14, @@ -377,78 +337,23 @@ class VideoPopupMenu extends StatelessWidget { Icons.more_vert_outlined, color: Theme.of(context).colorScheme.outline, size: iconSize, + ), - position: PopupMenuPosition.under, - // constraints: const BoxConstraints(maxHeight: 35), - onSelected: (String type) {}, - itemBuilder: (BuildContext context) => >[ - PopupMenuItem( - onTap: () async { - var res = - await UserHttp.toViewLater(bvid: videoItem.bvid as String); - SmartDialog.showToast(res['msg']); - }, - value: 'pause', - height: 40, - child: const Row( - children: [ - Icon(Icons.watch_later_outlined, size: 16), - SizedBox(width: 6), - Text('稍后再看', style: TextStyle(fontSize: 13)) - ], - ), - ), - const PopupMenuDivider(), - PopupMenuItem( - onTap: () async { - SmartDialog.show( - useSystem: true, - animationType: SmartAnimationType.centerFade_otherSlide, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('提示'), - content: Text( - '确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?' - '\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'), - actions: [ - TextButton( - onPressed: () => SmartDialog.dismiss(), - child: Text( - '点错了', - style: TextStyle( - color: Theme.of(context).colorScheme.outline), - ), - ), - TextButton( - onPressed: () async { - var res = await VideoHttp.relationMod( - mid: videoItem.owner.mid, - act: 5, - reSrc: 11, - ); - SmartDialog.dismiss(); - SmartDialog.showToast(res['msg'] ?? '成功'); - }, - child: const Text('确认'), - ) - ], - ); - }, - ); - }, - value: 'pause', - height: 40, - child: Row( - children: [ - const Icon(Icons.block, size: 16), - const SizedBox(width: 6), - Text('拉黑:${videoItem.owner.name}', - style: const TextStyle(fontSize: 13)) - ], - ), + if (videoItem is RecVideoItemModel) ...[ + const Spacer(), + RichText( + maxLines: 1, + text: TextSpan( + style: TextStyle( + fontSize: MediaQuery.textScalerOf(context) + .scale(Theme.of(context).textTheme.labelSmall!.fontSize!), + color: Theme.of(context).colorScheme.outline, + ), + text: Utils.formatTimestampToRelativeTime(videoItem.pubdate)), ), - ], - ), + const SizedBox(width: 4), + ] + ], ); } } diff --git a/lib/common/widgets/video_popup_menu.dart b/lib/common/widgets/video_popup_menu.dart new file mode 100644 index 000000000..dbd1c713b --- /dev/null +++ b/lib/common/widgets/video_popup_menu.dart @@ -0,0 +1,139 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; + +import '../../http/user.dart'; +import '../../http/video.dart'; +import '../../pages/mine/controller.dart'; + +class VideoPopupMenu extends StatelessWidget { + final double? size; + final double? iconSize; + final dynamic videoItem; + final double menuItemHeight = 50; + + const VideoPopupMenu({ + Key? key, + required this.size, + required this.iconSize, + required this.videoItem, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: size, + height: size, + child: PopupMenuButton( + padding: EdgeInsets.zero, + icon: Icon( + Icons.more_vert_outlined, + color: Theme.of(context).colorScheme.outline, + size: iconSize, + ), + position: PopupMenuPosition.under, + onSelected: (String type) {}, + itemBuilder: (BuildContext context) => >[ + PopupMenuItem( + onTap: () async { + var res = + await UserHttp.toViewLater(bvid: videoItem.bvid as String); + SmartDialog.showToast(res['msg']); + }, + value: 'pause', + height: menuItemHeight, + child: const Row( + children: [ + Icon(Icons.watch_later_outlined, size: 16), + SizedBox(width: 6), + Text('稍后再看', style: TextStyle(fontSize: 13)) + ], + ), + ), + PopupMenuItem( + onTap: () async { + SmartDialog.show( + useSystem: true, + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('提示'), + content: Text( + '确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?' + '\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'), + actions: [ + TextButton( + onPressed: () => SmartDialog.dismiss(), + child: Text( + '点错了', + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + var res = await VideoHttp.relationMod( + mid: videoItem.owner.mid, + act: 5, + reSrc: 11, + ); + SmartDialog.dismiss(); + SmartDialog.showToast(res['msg'] ?? '成功'); + }, + child: const Text('确认'), + ) + ], + ); + }, + ); + }, + value: 'block', + height: menuItemHeight, + child: Row( + children: [ + const Icon(Icons.block, size: 16), + const SizedBox(width: 6), + Text('拉黑:${videoItem.owner.name}', + style: const TextStyle(fontSize: 13)) + ], + ), + ), + // PopupMenuItem( + // onTap: () async { + // SmartDialog.showToast("还没做"); + // }, + // value: 'anonymize', + // height: menuItemHeight, + // child: const Row( + // children: [ + // Icon(Icons.visibility_off_outlined, size: 16), + // SizedBox(width: 6), + // Text('无痕播放', + // style: TextStyle(fontSize: 13)) + // ], + // ), + // ), + PopupMenuItem( + onTap: () { + MineController.onChangeAnonymity(context); + }, + value: 'anonymous', + height: menuItemHeight, + child: Row( + children: [ + Icon( + MineController.anonymity + ? Icons.visibility_outlined + : Icons.visibility_off_outlined, + size: 16, + ), + const SizedBox(width: 6), + Text(MineController.anonymity ? '退出无痕模式' : '进入无痕模式', + style: const TextStyle(fontSize: 13)) + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/http/api.dart b/lib/http/api.dart index 4b6bbeb82..5991a1dea 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -233,10 +233,6 @@ class Api { static const String liveRoomInfo = '${HttpString.liveBaseUrl}/xlive/web-room/v2/index/getRoomPlayInfo'; - // 直播间详情 H5 - static const String liveRoomInfoH5 = - '${HttpString.liveBaseUrl}/xlive/web-room/v1/index/getH5InfoByRoom'; - // 用户信息 需要Wbi签名 // https://api.bilibili.com/x/space/wbi/acc/info?mid=503427686&token=&platform=web&web_location=1550101&w_rid=d709892496ce93e3d94d6d37c95bde91&wts=1689301482 static const String memberInfo = '/x/space/wbi/acc/info'; @@ -335,6 +331,23 @@ class Api { // 获取指定分组下的up static const String followUpGroup = '/x/relation/tag'; + // 获取消息中心未读信息 + static const String msgFeedUnread = '/x/msgfeed/unread'; + //https://api.bilibili.com/x/msgfeed/reply?platform=web&build=0&mobi_app=web + static const String msgFeedReply = '/x/msgfeed/reply'; + //https://api.bilibili.com/x/msgfeed/at?platform=web&build=0&mobi_app=web + static const String msgFeedAt = '/x/msgfeed/at'; + //https://api.bilibili.com/x/msgfeed/like?platform=web&build=0&mobi_app=web + static const String msgFeedLike = '/x/msgfeed/like'; + //https://message.bilibili.com/x/sys-msg/query_user_notify?csrf=31b0caa533cea4d1a1bd2e921f045ec6&csrf=31b0caa533cea4d1a1bd2e921f045ec6&page_size=20&build=0&mobi_app=web + static const String msgSysUserNotify = '${HttpString.messageBaseUrl}/x/sys-msg/query_user_notify'; + //https://message.bilibili.com/x/sys-msg/query_unified_notify?csrf=31b0caa533cea4d1a1bd2e921f045ec6&csrf=31b0caa533cea4d1a1bd2e921f045ec6&page_size=10&build=0&mobi_app=web + static const String msgSysUnifiedNotify = '${HttpString.messageBaseUrl}/x/sys-msg/query_unified_notify'; + + // 系统信息光标更新(已读标记) + //https://message.bilibili.com/x/sys-msg/update_cursor?csrf=31b0caa533cea4d1a1bd2e921f045ec6&csrf=31b0caa533cea4d1a1bd2e921f045ec6&cursor=1705288500000000000&has_up=0&build=0&mobi_app=web + static const String msgSysUpdateCursor = '${HttpString.messageBaseUrl}/x/sys-msg/update_cursor'; + /// 私聊 /// 'https://api.vc.bilibili.com/session_svr/v1/session_svr/get_sessions? /// session_type=1& @@ -377,7 +390,7 @@ class Api { /// mobi_app: web /// csrf_token: /// csrf: - static const String updateAck = + static const String ackSessionMsg = '${HttpString.tUrl}/session_svr/v1/session_svr/update_ack'; // 获取某个动态详情 diff --git a/lib/http/constants.dart b/lib/http/constants.dart index 3d749ee82..cad413efd 100644 --- a/lib/http/constants.dart +++ b/lib/http/constants.dart @@ -5,6 +5,7 @@ class HttpString { static const String appBaseUrl = 'https://app.bilibili.com'; static const String liveBaseUrl = 'https://api.live.bilibili.com'; static const String passBaseUrl = 'https://passport.bilibili.com'; + static const String messageBaseUrl = 'https://message.bilibili.com'; static const List validateStatusCodes = [ 302, 304, diff --git a/lib/http/init.dart b/lib/http/init.dart index dcc9f402e..367da9c1a 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -13,6 +13,7 @@ import '../utils/storage.dart'; import '../utils/utils.dart'; import 'constants.dart'; import 'interceptor.dart'; +import 'interceptor_anonymity.dart'; class Request { static final Request _instance = Request._internal(); @@ -35,6 +36,7 @@ class Request { ); cookieManager = CookieManager(cookieJar); dio.interceptors.add(cookieManager); + dio.interceptors.add(AnonymityInterceptor()); final List cookie = await cookieManager.cookieJar .loadForRequest(Uri.parse(HttpString.baseUrl)); final userInfo = userInfoCache.get('userInfoCache'); diff --git a/lib/http/interceptor_anonymity.dart b/lib/http/interceptor_anonymity.dart new file mode 100644 index 000000000..9ada8ec8a --- /dev/null +++ b/lib/http/interceptor_anonymity.dart @@ -0,0 +1,51 @@ +// ignore_for_file: avoid_print + +import 'dart:io'; +import 'package:dio/dio.dart'; +// import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import '../pages/mine/controller.dart'; +import 'api.dart'; + +class AnonymityInterceptor extends Interceptor { + static const List anonymityList = [ + Api.videoUrl, + Api.videoIntro, + Api.relatedList, + Api.replyList, + Api.replyReplyList, + Api.searchSuggest, + Api.searchByType, + Api.heartBeat, + Api.ab2c, + Api.bangumiInfo, + Api.liveRoomInfo, + Api.onlineTotal, + Api.webDanmaku, + Api.dynamicDetail, + Api.aiConclusion, + Api.getMemberViewApi, + Api.getSeasonDetailApi, + ]; + + + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + if (MineController.anonymity) { + String uri = options.uri.toString(); + for (var i in anonymityList) { + // 如果请求的url包含无痕列表中的url,则清空cookie + // 但需要保证匹配到url的后半部分不再出现/符号,否则会误伤 + int index = uri.indexOf(i); + if (index == -1) continue; + if (uri.lastIndexOf('/') >= index + i.length) continue; + //SmartDialog.showToast('触发无痕模式\n\n$i\n\n${options.uri}'); + options.headers[HttpHeaders.cookieHeader] = ""; + if(options.data != null && options.data.csrf != null) { + options.data.csrf = ""; + } + break; + } + } + handler.next(options); + } +} diff --git a/lib/http/live.dart b/lib/http/live.dart index e624120ee..c62fb6bdc 100644 --- a/lib/http/live.dart +++ b/lib/http/live.dart @@ -1,6 +1,5 @@ import '../models/live/item.dart'; import '../models/live/room_info.dart'; -import '../models/live/room_info_h5.dart'; import 'api.dart'; import 'init.dart'; @@ -47,22 +46,4 @@ class LiveHttp { }; } } - - static Future liveRoomInfoH5({roomId, qn}) async { - var res = await Request().get(Api.liveRoomInfoH5, data: { - 'room_id': roomId, - }); - if (res.data['code'] == 0) { - return { - 'status': true, - 'data': RoomInfoH5Model.fromJson(res.data['data']) - }; - } else { - return { - 'status': false, - 'data': [], - 'msg': res.data['message'], - }; - } - } } diff --git a/lib/http/member.dart b/lib/http/member.dart index 6b6df7fe3..7c6d70a04 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -1,3 +1,6 @@ +import 'dart:convert'; +import 'dart:math'; + import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:hive/hive.dart'; import '../common/constants.dart'; @@ -79,6 +82,8 @@ class MemberHttp { String order = 'pubdate', bool orderAvoided = true, }) async { + String dmImgStr = Utils.base64EncodeRandomString(16, 64); + String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128); Map params = await WbiSign().makSign({ 'mid': mid, 'ps': ps, @@ -88,12 +93,15 @@ class MemberHttp { 'order': order, 'platform': 'web', 'web_location': 1550101, - 'order_avoided': orderAvoided + 'order_avoided': orderAvoided, + 'dm_img_list': '[]', + 'dm_img_str': dmImgStr.substring(0, dmImgStr.length - 2), + 'dm_cover_img_str': dmCoverImgStr.substring(0, dmCoverImgStr.length - 2), }); var res = await Request().get( Api.memberArchive, data: params, - extra: {'ua': 'pc'}, + extra: {'ua': 'Mozilla/5.0'}, ); if (res.data['code'] == 0) { return { diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 70af5b559..010ec8512 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -5,6 +5,116 @@ import 'api.dart'; import 'init.dart'; class MsgHttp { + + static Future msgFeedReplyMe({int cursor = -1, int cursorTime = -1}) async { + var res = await Request().get(Api.msgFeedReply, data: { + 'id': cursor == -1 ? null : cursor, + 'reply_time': cursorTime == -1 ? null : cursorTime, + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'date': [], + 'msg': res.data['message'], + }; + } + } + static Future msgFeedAtMe({int cursor = -1, int cursorTime = -1}) async { + var res = await Request().get(Api.msgFeedAt,data: { + 'id': cursor == -1 ? null : cursor, + 'at_time': cursorTime == -1 ? null : cursorTime, + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'date': [], + 'msg': res.data['message'], + }; + } + } + static Future msgFeedLikeMe({int cursor = -1, int cursorTime = -1}) async { + var res = await Request().get(Api.msgFeedLike,data: { + 'id': cursor == -1 ? null : cursor, + 'like_time': cursorTime == -1 ? null : cursorTime, + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'date': [], + 'msg': res.data['message'], + }; + } + } + static Future msgFeedSysUserNotify() async { + String csrf = await Request.getCsrf(); + var res = await Request().get(Api.msgSysUserNotify, data: { + 'csrf': csrf, + 'csrf': csrf, + 'page_size': 20, + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'date': [], + 'msg': res.data['message'], + }; + } + } + static Future msgFeedSysUnifiedNotify() async { + String csrf = await Request.getCsrf(); + var res = await Request().get(Api.msgSysUnifiedNotify, data: { + 'csrf': csrf, + 'csrf': csrf, + 'page_size': 10, + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'date': [], + 'msg': res.data['message'], + }; + } + } + static Future msgFeedUnread() async { + var res = await Request().get(Api.msgFeedUnread); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'date': [], + 'msg': res.data['message'], + }; + } + } // 会话列表 static Future sessionList({int? endTs}) async { Map params = { @@ -86,4 +196,35 @@ class MsgHttp { }; } } + + static Future ackSessionMsg({ + int? talkerId, + int? ackSeqno, + }) async { + String csrf = await Request.getCsrf(); + Map params = await WbiSign().makSign({ + 'talker_id': talkerId, + 'session_type': 1, + 'ack_seqno': ackSeqno, + 'build': 0, + 'mobi_app': 'web', + 'csrf_token': csrf, + 'csrf': csrf + }); + var res = await Request().get(Api.ackSessionMsg, data: params); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'date': [], + 'msg': "message: ${res.data['message']}," + " msg: ${res.data['msg']}," + " code: ${res.data['code']}", + }; + } + } } diff --git a/lib/http/video.dart b/lib/http/video.dart index 30df62c3e..733c0bdb7 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -12,6 +12,7 @@ import '../models/video_detail_res.dart'; import '../utils/recommend_filter.dart'; import '../utils/storage.dart'; import '../utils/wbi_sign.dart'; +import '../pages/mine/controller.dart'; import 'api.dart'; import 'init.dart'; @@ -154,7 +155,7 @@ class VideoHttp { } // 免登录查看1080p - if (userInfoCache.get('userInfoCache') == null && + if ((userInfoCache.get('userInfoCache') == null || MineController.anonymity) && setting.get(SettingBoxKey.p1080, defaultValue: true)) { data['try_look'] = 1; } diff --git a/lib/main.dart b/lib/main.dart index fc2149ded..924b461d1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -25,42 +25,59 @@ import 'package:pilipala/utils/recommend_filter.dart'; import 'package:catcher_2/catcher_2.dart'; import './services/loggeer.dart'; + void main() async { WidgetsFlutterBinding.ensureInitialized(); MediaKit.ensureInitialized(); - SystemChrome.setPreferredOrientations( - [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]) - .then((_) async { - await GStrorage.init(); - await setupServiceLocator(); - Request(); - await Request.setCookie(); - RecommendFilter(); - - // 异常捕获 logo记录 - final Catcher2Options debugConfig = Catcher2Options( - SilentReportMode(), + await GStrorage.init(); + if (GStrorage.setting.get(SettingBoxKey.horizontalScreen, defaultValue: false)) { + await SystemChrome.setPreferredOrientations( + //支持竖屏与横屏 [ - FileHandler(await getLogsPath()), - ConsoleHandler( - enableDeviceParameters: false, - enableApplicationParameters: false, - ) + DeviceOrientation.portraitUp, + DeviceOrientation.portraitDown, + DeviceOrientation.landscapeLeft, + DeviceOrientation.landscapeRight, ], ); - - final Catcher2Options releaseConfig = Catcher2Options( - SilentReportMode(), - [FileHandler(await getLogsPath())], + } else { + await SystemChrome.setPreferredOrientations( + //支持竖屏 + [ + DeviceOrientation.portraitUp, + ], ); + } + await setupServiceLocator(); + Request(); + await Request.setCookie(); + RecommendFilter(); + + // 异常捕获 logo记录 + final Catcher2Options debugConfig = Catcher2Options( + SilentReportMode(), + [ + FileHandler(await getLogsPath()), + ConsoleHandler( + enableDeviceParameters: false, + enableApplicationParameters: false, + ) + ], + ); + + final Catcher2Options releaseConfig = Catcher2Options( + SilentReportMode(), + [FileHandler(await getLogsPath())], + ); + + Catcher2( + debugConfig: debugConfig, + releaseConfig: releaseConfig, + runAppFunction: () { + runApp(const MyApp()); + }, + ); - Catcher2( - debugConfig: debugConfig, - releaseConfig: releaseConfig, - runAppFunction: () { - runApp(const MyApp()); - }, - ); // 小白条、导航栏沉浸 SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); @@ -70,8 +87,8 @@ void main() async { statusBarColor: Colors.transparent, )); Data.init(); + GStrorage.lazyInit(); PiliSchame.init(); - }); } class MyApp extends StatelessWidget { diff --git a/lib/models/live/room_info_h5.dart b/lib/models/live/room_info_h5.dart deleted file mode 100644 index a0c196214..000000000 --- a/lib/models/live/room_info_h5.dart +++ /dev/null @@ -1,130 +0,0 @@ -class RoomInfoH5Model { - RoomInfoH5Model({ - this.roomInfo, - this.anchorInfo, - this.isRoomFeed, - this.watchedShow, - this.likeInfoV3, - this.blockInfo, - }); - - RoomInfo? roomInfo; - AnchorInfo? anchorInfo; - int? isRoomFeed; - Map? watchedShow; - LikeInfoV3? likeInfoV3; - Map? blockInfo; - - RoomInfoH5Model.fromJson(Map json) { - roomInfo = RoomInfo.fromJson(json['room_info']); - anchorInfo = AnchorInfo.fromJson(json['anchor_info']); - isRoomFeed = json['is_room_feed']; - watchedShow = json['watched_show']; - likeInfoV3 = LikeInfoV3.fromJson(json['like_info_v3']); - blockInfo = json['block_info']; - } -} - -class RoomInfo { - RoomInfo({ - this.uid, - this.roomId, - this.title, - this.cover, - this.description, - this.liveStatus, - this.liveStartTime, - this.areaId, - this.areaName, - this.parentAreaId, - this.parentAreaName, - this.online, - this.background, - this.appBackground, - this.liveId, - }); - - int? uid; - int? roomId; - String? title; - String? cover; - String? description; - int? liveStatus; - int? liveStartTime; - int? areaId; - String? areaName; - int? parentAreaId; - String? parentAreaName; - int? online; - String? background; - String? appBackground; - String? liveId; - - RoomInfo.fromJson(Map json) { - uid = json['uid']; - roomId = json['room_id']; - title = json['title']; - cover = json['cover']; - description = json['description']; - liveStatus = json['liveS_satus']; - liveStartTime = json['live_start_time']; - areaId = json['area_id']; - areaName = json['area_name']; - parentAreaId = json['parent_area_id']; - parentAreaName = json['parent_area_name']; - online = json['online']; - background = json['background']; - appBackground = json['app_background']; - liveId = json['live_id']; - } -} - -class AnchorInfo { - AnchorInfo({ - this.baseInfo, - this.relationInfo, - }); - - BaseInfo? baseInfo; - RelationInfo? relationInfo; - - AnchorInfo.fromJson(Map json) { - baseInfo = BaseInfo.fromJson(json['base_info']); - relationInfo = RelationInfo.fromJson(json['relation_info']); - } -} - -class BaseInfo { - BaseInfo({ - this.uname, - this.face, - }); - - String? uname; - String? face; - - BaseInfo.fromJson(Map json) { - uname = json['uname']; - face = json['face']; - } -} - -class RelationInfo { - RelationInfo({this.attention}); - - int? attention; - - RelationInfo.fromJson(Map json) { - attention = json['attention']; - } -} - -class LikeInfoV3 { - LikeInfoV3({this.totalLikes}); - - int? totalLikes; - - LikeInfoV3.fromJson(Map json) { - totalLikes = json['total_likes']; - } -} diff --git a/lib/models/msg/msgfeed_at_me.dart b/lib/models/msg/msgfeed_at_me.dart new file mode 100644 index 000000000..a01fe9579 --- /dev/null +++ b/lib/models/msg/msgfeed_at_me.dart @@ -0,0 +1,222 @@ +class MsgFeedAtMe { + Cursor? cursor; + List? items; + + MsgFeedAtMe({cursor, items}); + + MsgFeedAtMe.fromJson(Map json) { + cursor = json['cursor'] != null ? Cursor.fromJson(json['cursor']) : null; + if (json['items'] != null) { + items = []; + json['items'].forEach((v) { + items!.add(AtMeItems.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = {}; + data['cursor'] = cursor?.toJson(); + data['items'] = items?.map((v) => v.toJson()).toList(); + return data; + } +} + +class Cursor { + bool? isEnd; + int? id; + int? time; + + Cursor({isEnd, id, time}); + + Cursor.fromJson(Map json) { + isEnd = json['is_end']; + id = json['id']; + time = json['time']; + } + + Map toJson() { + final Map data = {}; + data['is_end'] = isEnd; + data['id'] = id; + data['time'] = time; + return data; + } +} + +class AtMeItems { + int? id; + User? user; + Item? item; + int? atTime; + + AtMeItems({id, user, item, atTime}); + + AtMeItems.fromJson(Map json) { + id = json['id']; + user = json['user'] != null ? User.fromJson(json['user']) : null; + item = json['item'] != null ? Item.fromJson(json['item']) : null; + atTime = json['at_time']; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['user'] = user?.toJson(); + data['item'] = item?.toJson(); + data['at_time'] = atTime; + return data; + } +} + +class User { + int? mid; + int? fans; + String? nickname; + String? avatar; + String? midLink; + bool? follow; + + User( + {this.mid, + this.fans, + this.nickname, + this.avatar, + this.midLink, + this.follow}); + + User.fromJson(Map json) { + mid = json['mid']; + fans = json['fans']; + nickname = json['nickname']; + avatar = json['avatar']; + midLink = json['mid_link']; + follow = json['follow']; + } + + Map toJson() { + final Map data = {}; + data['mid'] = mid; + data['fans'] = fans; + data['nickname'] = nickname; + data['avatar'] = avatar; + data['mid_link'] = midLink; + data['follow'] = follow; + return data; + } +} + +class Item { + String? type; + String? business; + int? businessId; + String? title; + String? image; + String? uri; + int? subjectId; + int? rootId; + int? targetId; + int? sourceId; + String? sourceContent; + String? nativeUri; + List? atDetails; + List? topicDetails; + bool? hideReplyButton; + + Item( + {this.type, + this.business, + this.businessId, + this.title, + this.image, + this.uri, + this.subjectId, + this.rootId, + this.targetId, + this.sourceId, + this.sourceContent, + this.nativeUri, + this.atDetails, + this.topicDetails, + this.hideReplyButton}); + + Item.fromJson(Map json) { + type = json['type']; + business = json['business']; + businessId = json['business_id']; + title = json['title']; + image = json['image']; + uri = json['uri']; + subjectId = json['subject_id']; + rootId = json['root_id']; + targetId = json['target_id']; + sourceId = json['source_id']; + sourceContent = json['source_content']; + nativeUri = json['native_uri']; + if (json['at_details'] != null) { + atDetails = []; + json['at_details'].forEach((v) { + atDetails!.add(AtDetails.fromJson(v)); + }); + } + topicDetails = json['topic_details']; + hideReplyButton = json['hide_reply_button']; + } + + Map toJson() { + final Map data = {}; + data['type'] = type; + data['business'] = business; + data['business_id'] = businessId; + data['title'] = title; + data['image'] = image; + data['uri'] = uri; + data['subject_id'] = subjectId; + data['root_id'] = rootId; + data['target_id'] = targetId; + data['source_id'] = sourceId; + data['source_content'] = sourceContent; + data['native_uri'] = nativeUri; + data['at_details'] = atDetails?.map((v) => v.toJson()).toList(); + data['topic_details'] = topicDetails?.map((v) => v.toJson()).toList(); + data['hide_reply_button'] = hideReplyButton; + return data; + } +} + +class AtDetails { + int? mid; + int? fans; + String? nickname; + String? avatar; + String? midLink; + bool? follow; + + AtDetails( + {this.mid, + this.fans, + this.nickname, + this.avatar, + this.midLink, + this.follow}); + + AtDetails.fromJson(Map json) { + mid = json['mid']; + fans = json['fans']; + nickname = json['nickname']; + avatar = json['avatar']; + midLink = json['mid_link']; + follow = json['follow']; + } + + Map toJson() { + final Map data = {}; + data['mid'] = mid; + data['fans'] = fans; + data['nickname'] = nickname; + data['avatar'] = avatar; + data['mid_link'] = midLink; + data['follow'] = follow; + return data; + } +} diff --git a/lib/models/msg/msgfeed_like_me.dart b/lib/models/msg/msgfeed_like_me.dart new file mode 100644 index 000000000..a3e5d6b17 --- /dev/null +++ b/lib/models/msg/msgfeed_like_me.dart @@ -0,0 +1,238 @@ +class MsgFeedLikeMe { + Latest? latest; + Total? total; + + MsgFeedLikeMe({latest, total}); + + MsgFeedLikeMe.fromJson(Map json) { + latest = + json['latest'] != null ? Latest.fromJson(json['latest']) : null; + total = json['total'] != null ? Total.fromJson(json['total']) : null; + } + + Map toJson() { + final Map data = {}; + data['latest'] = latest?.toJson(); + data['total'] = total?.toJson(); + return data; + } +} + +class Latest { + List? items; + int? lastViewAt; + + Latest({items, lastViewAt}); + + Latest.fromJson(Map json) { + if (json['items'] != null) { + items = []; + json['items'].forEach((v) { + items!.add(LikeMeItems.fromJson(v)); + }); + } + lastViewAt = json['last_view_at']; + } + + Map toJson() { + final Map data = {}; + data['items'] = items?.map((v) => v.toJson()).toList(); + data['last_view_at'] = lastViewAt; + return data; + } +} + +class LikeMeItems { + int? id; + List? users; + Item? item; + int? counts; + int? likeTime; + int? noticeState; + + LikeMeItems( + {id, + users, + item, + counts, + likeTime, + noticeState}); + + LikeMeItems.fromJson(Map json) { + id = json['id']; + if (json['users'] != null) { + users = []; + json['users'].forEach((v) { + users!.add(Users.fromJson(v)); + }); + } + item = json['item'] != null ? Item.fromJson(json['item']) : null; + counts = json['counts']; + likeTime = json['like_time']; + noticeState = json['notice_state']; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['users'] = users?.map((v) => v.toJson()).toList(); + data['item'] = item?.toJson(); + data['counts'] = counts; + data['like_time'] = likeTime; + data['notice_state'] = noticeState; + return data; + } +} + +class Users { + int? mid; + int? fans; + String? nickname; + String? avatar; + String? midLink; + bool? follow; + + Users( + {mid, + fans, + nickname, + avatar, + midLink, + follow}); + + Users.fromJson(Map json) { + mid = json['mid']; + fans = json['fans']; + nickname = json['nickname']; + avatar = json['avatar']; + midLink = json['mid_link']; + follow = json['follow']; + } + + Map toJson() { + final Map data = {}; + data['mid'] = mid; + data['fans'] = fans; + data['nickname'] = nickname; + data['avatar'] = avatar; + data['mid_link'] = midLink; + data['follow'] = follow; + return data; + } +} + +class Item { + int? itemId; + int? pid; + String? type; + String? business; + int? businessId; + int? replyBusinessId; + int? likeBusinessId; + String? title; + String? desc; + String? image; + String? uri; + String? detailName; + String? nativeUri; + int? ctime; + + Item( + {itemId, + pid, + type, + business, + businessId, + replyBusinessId, + likeBusinessId, + title, + desc, + image, + uri, + detailName, + nativeUri, + ctime}); + + Item.fromJson(Map json) { + itemId = json['item_id']; + pid = json['pid']; + type = json['type']; + business = json['business']; + businessId = json['business_id']; + replyBusinessId = json['reply_business_id']; + likeBusinessId = json['like_business_id']; + title = json['title']; + desc = json['desc']; + image = json['image']; + uri = json['uri']; + detailName = json['detail_name']; + nativeUri = json['native_uri']; + ctime = json['ctime']; + } + + Map toJson() { + final Map data = {}; + data['item_id'] = itemId; + data['pid'] = pid; + data['type'] = type; + data['business'] = business; + data['business_id'] = businessId; + data['reply_business_id'] = replyBusinessId; + data['like_business_id'] = likeBusinessId; + data['title'] = title; + data['desc'] = desc; + data['image'] = image; + data['uri'] = uri; + data['detail_name'] = detailName; + data['native_uri'] = nativeUri; + data['ctime'] = ctime; + return data; + } +} + +class Total { + Cursor? cursor; + List? items; + + Total({cursor, items}); + + Total.fromJson(Map json) { + cursor = + json['cursor'] != null ? Cursor.fromJson(json['cursor']) : null; + if (json['items'] != null) { + items = []; + json['items'].forEach((v) { + items!.add(LikeMeItems.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = {}; + data['cursor'] = cursor?.toJson(); + data['items'] = items?.map((v) => v.toJson()).toList(); + return data; + } +} + +class Cursor { + bool? isEnd; + int? id; + int? time; + + Cursor({isEnd, id, time}); + + Cursor.fromJson(Map json) { + isEnd = json['is_end']; + id = json['id']; + time = json['time']; + } + + Map toJson() { + final Map data = {}; + data['is_end'] = isEnd; + data['id'] = id; + data['time'] = time; + return data; + } +} \ No newline at end of file diff --git a/lib/models/msg/msgfeed_reply_me.dart b/lib/models/msg/msgfeed_reply_me.dart new file mode 100644 index 000000000..4d824a612 --- /dev/null +++ b/lib/models/msg/msgfeed_reply_me.dart @@ -0,0 +1,274 @@ +class MsgFeedReplyMe { + Cursor? cursor; + List? items; + int? lastViewAt; + + MsgFeedReplyMe({this.cursor, this.items, this.lastViewAt}); + + MsgFeedReplyMe.fromJson(Map json) { + cursor = + json['cursor'] != null ? Cursor.fromJson(json['cursor']) : null; + if (json['items'] != null) { + items = []; + json['items'].forEach((v) { + items!.add(ReplyMeItems.fromJson(v)); + }); + } + lastViewAt = json['last_view_at']; + } + + Map toJson() { + final Map data = {}; + data['cursor'] = cursor?.toJson(); + data['items'] = items?.map((v) => v.toJson()).toList(); + data['last_view_at'] = lastViewAt; + return data; + } +} + +class Cursor { + bool? isEnd; + int? id; + int? time; + + Cursor({this.isEnd, this.id, this.time}); + + Cursor.fromJson(Map json) { + isEnd = json['is_end']; + id = json['id']; + time = json['time']; + } + + Map toJson() { + final Map data = {}; + data['is_end'] = isEnd; + data['id'] = id; + data['time'] = time; + return data; + } +} + +class ReplyMeItems { + int? id; + User? user; + Item? item; + int? counts; + int? isMulti; + int? replyTime; + + ReplyMeItems( + {this.id, + this.user, + this.item, + this.counts, + this.isMulti, + this.replyTime}); + + ReplyMeItems.fromJson(Map json) { + id = json['id']; + user = json['user'] != null ? User.fromJson(json['user']) : null; + item = json['item'] != null ? Item.fromJson(json['item']) : null; + counts = json['counts']; + isMulti = json['is_multi']; + replyTime = json['reply_time']; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + if (user != null) { + data['user'] = user!.toJson(); + } + if (item != null) { + data['item'] = item!.toJson(); + } + data['counts'] = counts; + data['is_multi'] = isMulti; + data['reply_time'] = replyTime; + return data; + } +} + +class User { + int? mid; + int? fans; + String? nickname; + String? avatar; + String? midLink; + bool? follow; + + User( + {this.mid, + this.fans, + this.nickname, + this.avatar, + this.midLink, + this.follow}); + + User.fromJson(Map json) { + mid = json['mid']; + fans = json['fans']; + nickname = json['nickname']; + avatar = json['avatar']; + midLink = json['mid_link']; + follow = json['follow']; + } + + Map toJson() { + final Map data = {}; + data['mid'] = mid; + data['fans'] = fans; + data['nickname'] = nickname; + data['avatar'] = avatar; + data['mid_link'] = midLink; + data['follow'] = follow; + return data; + } +} + +class Item { + int? subjectId; + int? rootId; + int? sourceId; + int? targetId; + String? type; + int? businessId; + String? business; + String? title; + String? desc; + String? image; + String? uri; + String? nativeUri; + String? detailTitle; + String? rootReplyContent; + String? sourceContent; + String? targetReplyContent; + List? atDetails; + List? topicDetails; + bool? hideReplyButton; + bool? hideLikeButton; + int? likeState; + dynamic danmu; + String? message; + + Item( + {this.subjectId, + this.rootId, + this.sourceId, + this.targetId, + this.type, + this.businessId, + this.business, + this.title, + this.desc, + this.image, + this.uri, + this.nativeUri, + this.detailTitle, + this.rootReplyContent, + this.sourceContent, + this.targetReplyContent, + this.atDetails, + this.topicDetails, + this.hideReplyButton, + this.hideLikeButton, + this.likeState, + this.danmu, + this.message}); + + Item.fromJson(Map json) { + subjectId = json['subject_id']; + rootId = json['root_id']; + sourceId = json['source_id']; + targetId = json['target_id']; + type = json['type']; + businessId = json['business_id']; + business = json['business']; + title = json['title']; + desc = json['desc']; + image = json['image']; + uri = json['uri']; + nativeUri = json['native_uri']; + detailTitle = json['detail_title']; + rootReplyContent = json['root_reply_content']; + sourceContent = json['source_content']; + targetReplyContent = json['target_reply_content']; + if (json['at_details'] != null) { + atDetails = []; + json['at_details'].forEach((v) { + atDetails!.add(AtDetails.fromJson(v)); + }); + } + topicDetails = json['topic_details']; + hideReplyButton = json['hide_reply_button']; + hideLikeButton = json['hide_like_button']; + likeState = json['like_state']; + danmu = json['danmu']; + message = json['message']; + } + + Map toJson() { + final Map data = {}; + data['subject_id'] = subjectId; + data['root_id'] = rootId; + data['source_id'] = sourceId; + data['target_id'] = targetId; + data['type'] = type; + data['business_id'] = businessId; + data['business'] = business; + data['title'] = title; + data['desc'] = desc; + data['image'] = image; + data['uri'] = uri; + data['native_uri'] = nativeUri; + data['detail_title'] = detailTitle; + data['root_reply_content'] = rootReplyContent; + data['source_content'] = sourceContent; + data['target_reply_content'] = targetReplyContent; + data['at_details'] = atDetails?.map((v) => v.toJson()).toList(); + data['topic_details'] = topicDetails?.map((v) => v.toJson()).toList(); + data['hide_reply_button'] = hideReplyButton; + data['hide_like_button'] = hideLikeButton; + data['like_state'] = likeState; + data['danmu'] = danmu; + data['message'] = message; + return data; + } +} + +class AtDetails { + int? mid; + int? fans; + String? nickname; + String? avatar; + String? midLink; + bool? follow; + + AtDetails( + {this.mid, + this.fans, + this.nickname, + this.avatar, + this.midLink, + this.follow}); + + AtDetails.fromJson(Map json) { + mid = json['mid']; + fans = json['fans']; + nickname = json['nickname']; + avatar = json['avatar']; + midLink = json['mid_link']; + follow = json['follow']; + } + + Map toJson() { + final Map data = {}; + data['mid'] = mid; + data['fans'] = fans; + data['nickname'] = nickname; + data['avatar'] = avatar; + data['mid_link'] = midLink; + data['follow'] = follow; + return data; + } +} diff --git a/lib/models/msg/msgfeed_unread.dart b/lib/models/msg/msgfeed_unread.dart new file mode 100644 index 000000000..df3622ee5 --- /dev/null +++ b/lib/models/msg/msgfeed_unread.dart @@ -0,0 +1,26 @@ +class MsgFeedUnread { + MsgFeedUnread({ + this.at = 0, + this.chat = 0, + this.like = 0, + this.reply = 0, + this.sys_msg = 0, + this.up = 0, + }); + + int at = 0; + int chat = 0; + int like = 0; + int reply = 0; + int sys_msg = 0; + int up = 0; + + MsgFeedUnread.fromJson(Map json) { + at = json['at'] ?? 0; + chat = json['chat'] ?? 0; + like = json['like'] ?? 0; + reply = json['reply'] ?? 0; + sys_msg = json['sys_msg'] ?? 0; + up = json['up'] ?? 0; + } +} diff --git a/lib/models/video/play/url.dart b/lib/models/video/play/url.dart index 4c43cb007..965f0c0c7 100644 --- a/lib/models/video/play/url.dart +++ b/lib/models/video/play/url.dart @@ -52,7 +52,15 @@ class PlayUrlModel { videoCodecid = json['video_codecid']; seekParam = json['seek_param']; seekType = json['seek_type']; - dash = Dash.fromJson(json['dash']); + if (json['dash'] != null) { + dash = Dash.fromJson(json['dash']); + } else if (json['durl'] != null) { + //试看的充电包月视频可能出现没有dash只有durl的情况 + var durlList = json['durl'] + .map((e) => Durl.fromJson(e)) + .toList(); + //TODO + } supportFormats = json['support_formats'] != null ? json['support_formats'] .map((e) => FormatItem.fromJson(e)) @@ -92,6 +100,40 @@ class Dash { } } +class Durl { + int? order; + int? length; + int? size; + String? ahead; + String? vhead; + String? url; + List? backupUrl; + + Durl({ + this.order, + this.length, + this.size, + this.ahead, + this.vhead, + this.url, + this.backupUrl, + }); + + factory Durl.fromJson(Map json) { + return Durl( + order: json['order'], + length: json['length'], + size: json['size'], + ahead: json['ahead'], + vhead: json['vhead'], + url: json['url'], + backupUrl: json['backup_url'] != null + ? List.from(json['backup_url']) + : [], + ); + } +} + class VideoItem { VideoItem({ this.id, diff --git a/lib/pages/about/index.dart b/lib/pages/about/index.dart index 7ac76f964..7079461d9 100644 --- a/lib/pages/about/index.dart +++ b/lib/pages/about/index.dart @@ -44,20 +44,24 @@ class _AboutPageState extends State { appBar: AppBar( title: Text('关于', style: Theme.of(context).textTheme.titleMedium), ), - body: SingleChildScrollView( - child: Column( - children: [ - Image.asset( + body: ListView( + children: [ + ConstrainedBox( + constraints: const BoxConstraints(maxHeight: 150), + child: Image.asset( 'assets/images/logo/logo_android_2.png', - width: 150, ), - Text( - 'PiliPala', - style: Theme.of(context).textTheme.titleMedium, - ), - const SizedBox(height: 6), - Text( + ), + ListTile( + title: Text('PiliPala', + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(height: 2)), + subtitle: Text( '使用Flutter开发的哔哩哔哩第三方客户端', + textAlign: TextAlign.center, style: TextStyle(color: Theme.of(context).colorScheme.outline), ), const SizedBox(height: 20), @@ -104,71 +108,106 @@ class _AboutPageState extends State { style: subTitleStyle, ), ), - ListTile( - onTap: () => _aboutController.webSiteUrl(), - title: const Text('访问官网'), + ), + Obx( + () => ListTile( + onTap: () => _aboutController.onUpdate(), + title: const Text('最新版本'), trailing: Text( - 'https://pilipalanet.mysxl.cn', + _aboutController.isLoading.value + ? '正在获取' + : _aboutController.isUpdate.value + ? '有新版本 ❤️${_aboutController.remoteVersion.value}' + : '当前已是最新版', style: subTitleStyle, ), ), - ListTile( - onTap: () => _aboutController.panDownload(), - title: const Text('网盘下载'), - trailing: Text( - '提取码:pili', - style: TextStyle( - fontSize: 13, - color: Theme.of(context).colorScheme.outline, - ), - ), + ), + // ListTile( + // onTap: () {}, + // title: const Text('更新日志'), + // trailing: const Icon( + // Icons.arrow_forward_ios, + // size: 16, + // ), + // ), + Divider( + thickness: 1, + height: 30, + color: Theme.of(context).colorScheme.outlineVariant, + ), + ListTile( + onTap: () => _aboutController.githubUrl(), + title: const Text('Github'), + trailing: Text( + 'github.com/guozhigq/pilipala', + style: subTitleStyle, ), - ListTile( - onTap: () => _aboutController.feedback(), - title: const Text('问题反馈'), - trailing: Icon( - Icons.arrow_forward_ios, - size: 16, - color: outline, - ), + ), + ListTile( + onTap: () => _aboutController.webSiteUrl(), + title: const Text('访问官网'), + trailing: Text( + 'https://pilipalanet.mysxl.cn', + style: subTitleStyle, ), - ListTile( - onTap: () => _aboutController.qqChanel(), - title: const Text('QQ群'), - trailing: Icon( - Icons.arrow_forward_ios, - size: 16, - color: outline, + ), + ListTile( + onTap: () => _aboutController.panDownload(), + title: const Text('网盘下载'), + trailing: Text( + '提取码:pili', + style: TextStyle( + fontSize: 13, + color: Theme.of(context).colorScheme.outline, ), ), - ListTile( - onTap: () => _aboutController.tgChanel(), - title: const Text('TG频道'), - trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline), - ), - ListTile( - onTap: () => _aboutController.aPay(), - title: const Text('赞助'), - trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline), + ), + ListTile( + onTap: () => _aboutController.feedback(), + title: const Text('问题反馈'), + trailing: Icon( + Icons.arrow_forward_ios, + size: 16, + color: outline, ), - ListTile( - onTap: () => _aboutController.logs(), - title: const Text('错误日志'), - trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline), - ), - ListTile( - onTap: () async { - var cleanStatus = await CacheManage().clearCacheAll(); - if (cleanStatus) { - getCacheSize(); - } - }, - title: const Text('清除缓存'), - subtitle: Text('图片及网络缓存 $cacheSize', style: subTitleStyle), - trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline), + ), + ListTile( + onTap: () => _aboutController.qqChanel(), + title: const Text('QQ群'), + trailing: Icon( + Icons.arrow_forward_ios, + size: 16, + color: outline, ), - ], - ), + ), + ListTile( + onTap: () => _aboutController.tgChanel(), + title: const Text('TG频道'), + trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline), + ), + ListTile( + onTap: () => _aboutController.aPay(), + title: const Text('赞助'), + trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline), + ), + ListTile( + onTap: () => _aboutController.logs(), + title: const Text('错误日志'), + trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline), + ), + ListTile( + onTap: () async { + var cleanStatus = await CacheManage().clearCacheAll(); + if (cleanStatus) { + getCacheSize(); + } + }, + title: const Text('清除缓存'), + subtitle: Text('图片及网络缓存 $cacheSize', style: subTitleStyle), + trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline), + ), + ], ), ); } diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index f9efc66c5..4459bf7e0 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; 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:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; @@ -15,7 +14,6 @@ import 'package:pilipala/pages/video/detail/introduction/widgets/action_item.dar import 'package:pilipala/pages/video/detail/introduction/widgets/action_row_item.dart'; import 'package:pilipala/pages/video/detail/introduction/widgets/fav_panel.dart'; import 'package:pilipala/utils/feed_back.dart'; -import 'package:pilipala/utils/storage.dart'; import 'controller.dart'; import 'widgets/intro_detail.dart'; @@ -116,9 +114,7 @@ class _BangumiInfoState extends State { String heroTag = Get.arguments['heroTag']; late final BangumiIntroController bangumiIntroController; late final VideoDetailController videoDetailCtr; - Box localCache = GStrorage.localCache; late final BangumiInfoModel? bangumiItem; - late double sheetHeight; int? cid; bool isProcessing = false; void Function()? handleState(Future Function() action) { @@ -137,7 +133,6 @@ class _BangumiInfoState extends State { bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag); videoDetailCtr = Get.find(tag: heroTag); bangumiItem = bangumiIntroController.bangumiItem; - sheetHeight = localCache.get('sheetHeight'); cid = widget.cid!; print('cid: $cid'); videoDetailCtr.cid.listen((p0) { @@ -369,7 +364,6 @@ class _BangumiInfoState extends State { (bangumiItem != null ? bangumiItem!.episodes!.first.cid : widget.bangumiDetail!.episodes!.first.cid), - sheetHeight: sheetHeight, changeFuc: (bvid, cid, aid) => bangumiIntroController .changeSeasonOrbangu(bvid, cid, aid), ) diff --git a/lib/pages/bangumi/introduction/widgets/intro_detail.dart b/lib/pages/bangumi/introduction/widgets/intro_detail.dart index c5bbd5663..28a5fedfa 100644 --- a/lib/pages/bangumi/introduction/widgets/intro_detail.dart +++ b/lib/pages/bangumi/introduction/widgets/intro_detail.dart @@ -1,11 +1,10 @@ import 'package:flutter/material.dart'; -import 'package:hive/hive.dart'; +import 'package:get/get.dart'; import 'package:pilipala/common/widgets/stat/danmu.dart'; import 'package:pilipala/common/widgets/stat/view.dart'; -import 'package:pilipala/utils/storage.dart'; -Box localCache = GStrorage.localCache; -late double sheetHeight; +import '../../../../utils/utils.dart'; + class IntroDetail extends StatelessWidget { final dynamic bangumiDetail; @@ -17,7 +16,6 @@ class IntroDetail extends StatelessWidget { @override Widget build(BuildContext context) { - sheetHeight = localCache.get('sheetHeight'); TextStyle smallTitle = TextStyle( fontSize: 12, color: Theme.of(context).colorScheme.onBackground, @@ -25,7 +23,7 @@ class IntroDetail extends StatelessWidget { return Container( color: Theme.of(context).colorScheme.background, padding: const EdgeInsets.only(left: 14, right: 14), - height: sheetHeight, + height: Utils.getSheetHeight(context), child: Column( children: [ Container( diff --git a/lib/pages/bangumi/view.dart b/lib/pages/bangumi/view.dart index f59f94a28..adb980c3a 100644 --- a/lib/pages/bangumi/view.dart +++ b/lib/pages/bangumi/view.dart @@ -10,6 +10,7 @@ import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/main/index.dart'; +import '../../utils/grid.dart'; import 'controller.dart'; import 'widgets/bangumu_card_v.dart'; @@ -216,16 +217,17 @@ class _BangumiPageState extends State } Widget contentGrid(ctr, bangumiList) { + return SliverGrid( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( // 行间距 - mainAxisSpacing: StyleString.cardSpace - 2, - // 列间距 - crossAxisSpacing: StyleString.cardSpace, - // 列数 - crossAxisCount: 3, - mainAxisExtent: Get.size.width / 3 / 0.65 + - MediaQuery.textScalerOf(context).scale(32.0), + mainAxisSpacing: StyleString.cardSpace - 2, + // 列间距 + crossAxisSpacing: StyleString.cardSpace, + // 最大宽度 + maxCrossAxisExtent: Grid.maxRowWidth / 3 * 2, + mainAxisExtent: Grid.calculateActualWidth(context, Grid.maxRowWidth / 3 * 2, StyleString.safeSpace) / 0.65+ + MediaQuery.textScalerOf(context).scale(60), ), delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 5996d6c82..afa6ff0d5 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -7,18 +7,18 @@ import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; +import '../../../utils/utils.dart'; + class BangumiPanel extends StatefulWidget { const BangumiPanel({ super.key, required this.pages, this.cid, - this.sheetHeight, this.changeFuc, }); final List pages; final int? cid; - final double? sheetHeight; final Function? changeFuc; @override @@ -80,7 +80,7 @@ class _BangumiPanelState extends State { }); // 在这里使用 setState 更新状态 return Container( - height: widget.sheetHeight, + height: Utils.getSheetHeight(context), color: Theme.of(context).colorScheme.background, child: Column( children: [ diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 4a92cdfb8..71b434a74 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; +import 'package:waterfall_flow/waterfall_flow.dart'; import 'package:pilipala/common/skeleton/dynamic_card.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/no_data.dart'; @@ -14,6 +15,8 @@ import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; +import '../../common/constants.dart'; +import '../../utils/grid.dart'; import 'controller.dart'; import 'widgets/dynamic_panel.dart'; import 'widgets/up_panel.dart'; @@ -245,14 +248,28 @@ class _DynamicsPageState extends State return const NoData(); } } else { - return SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - return DynamicPanel(item: list[index]); - }, - childCount: list.length, - ), - ); + return SliverWaterfallFlow.extent( + maxCrossAxisExtent: Grid.maxRowWidth * 2, + //cacheExtent: 0.0, + crossAxisSpacing: StyleString.safeSpace, + mainAxisSpacing: StyleString.safeSpace, + + /// follow max child trailing layout offset and layout with full cross axis extend + /// last child as loadmore item/no more item in [GridView] and [WaterfallFlow] + /// with full cross axis extend + // LastChildLayoutType.fullCrossAxisExtend, + + /// as foot at trailing and layout with full cross axis extend + /// show no more item at trailing when children are not full of viewport + /// if children is full of viewport, it's the same as fullCrossAxisExtend + // LastChildLayoutType.foot, + lastChildLayoutTypeBuilder: (index) => + index == list.length + ? LastChildLayoutType.foot + : LastChildLayoutType.none, + children: [ + for (var i in list) DynamicPanel(item: i), + ]); } }, ); diff --git a/lib/pages/dynamics/widgets/action_panel.dart b/lib/pages/dynamics/widgets/action_panel.dart index 0ca09b8c6..83abe20eb 100644 --- a/lib/pages/dynamics/widgets/action_panel.dart +++ b/lib/pages/dynamics/widgets/action_panel.dart @@ -77,7 +77,9 @@ class _ActionPanelState extends State { Expanded( flex: 1, child: TextButton.icon( - onPressed: () {}, + onPressed: () { + SmartDialog.showToast('暂不支持'); + }, icon: const Icon( FontAwesomeIcons.shareFromSquare, size: 16, diff --git a/lib/pages/hot/view.dart b/lib/pages/hot/view.dart index 7a0a57eaf..54df7aeab 100644 --- a/lib/pages/hot/view.dart +++ b/lib/pages/hot/view.dart @@ -13,6 +13,8 @@ import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/hot/controller.dart'; import 'package:pilipala/pages/main/index.dart'; +import '../../utils/grid.dart'; + class HotPage extends StatefulWidget { const HotPage({Key? key}) : super(key: key); @@ -88,8 +90,18 @@ class _HotPageState extends State with AutomaticKeepAliveClientMixin { Map data = snapshot.data as Map; if (data['status']) { return Obx( - () => SliverList( - delegate: SliverChildBuilderDelegate((context, index) { + () => SliverGrid( + gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( + // 行间距 + mainAxisSpacing: StyleString.safeSpace, + // 列间距 + crossAxisSpacing: StyleString.safeSpace, + // 最大宽度 + maxCrossAxisExtent: Grid.maxRowWidth * 2, + mainAxisExtent: Grid.calculateActualWidth(context, Grid.maxRowWidth * 2, StyleString.safeSpace) / 1.9 / StyleString.aspectRatio + ), + delegate: + SliverChildBuilderDelegate((context, index) { return VideoCardH( videoItem: _hotController.videoList[index], showPubdate: true, diff --git a/lib/pages/live/controller.dart b/lib/pages/live/controller.dart index 74fb6e9aa..4ee2bdf29 100644 --- a/lib/pages/live/controller.dart +++ b/lib/pages/live/controller.dart @@ -18,8 +18,6 @@ class LiveController extends GetxController { @override void onInit() { super.onInit(); - crossAxisCount.value = - setting.get(SettingBoxKey.customRows, defaultValue: 2); } // 获取推荐 diff --git a/lib/pages/live/view.dart b/lib/pages/live/view.dart index f3f91c9e1..71a6004b5 100644 --- a/lib/pages/live/view.dart +++ b/lib/pages/live/view.dart @@ -12,6 +12,7 @@ import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/main/index.dart'; +import '../../utils/grid.dart'; import 'controller.dart'; import 'widgets/live_item.dart'; @@ -138,35 +139,22 @@ class _LivePageState extends State } Widget contentGrid(ctr, liveList) { - // double maxWidth = Get.size.width; - // int baseWidth = 500; - // int step = 300; - // int crossAxisCount = - // maxWidth > baseWidth ? 2 + ((maxWidth - baseWidth) / step).ceil() : 2; - // if (maxWidth < 300) { - // crossAxisCount = 1; - // } - int crossAxisCount = ctr.crossAxisCount.value; return SliverGrid( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( // 行间距 mainAxisSpacing: StyleString.safeSpace, // 列间距 crossAxisSpacing: StyleString.safeSpace, - // 列数 - crossAxisCount: crossAxisCount, - mainAxisExtent: - Get.size.width / crossAxisCount / StyleString.aspectRatio + - MediaQuery.textScalerOf(context).scale( - (crossAxisCount == 1 ? 48 : 68), - ), + // 最大宽度 + maxCrossAxisExtent: Grid.maxRowWidth, + mainAxisExtent: Grid.calculateActualWidth(context, Grid.maxRowWidth, StyleString.safeSpace) / StyleString.aspectRatio+ + MediaQuery.textScalerOf(context).scale(80), ), delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { return liveList!.isNotEmpty ? LiveCardV( liveItem: liveList[index], - crossAxisCount: crossAxisCount, longPress: () { _liveController.popupDialog = _createPopupDialog(liveList[index]); diff --git a/lib/pages/live/widgets/live_item.dart b/lib/pages/live/widgets/live_item.dart index 9218d4fbe..f313e0f7f 100644 --- a/lib/pages/live/widgets/live_item.dart +++ b/lib/pages/live/widgets/live_item.dart @@ -8,14 +8,12 @@ import 'package:pilipala/common/widgets/network_img_layer.dart'; // 视频卡片 - 垂直布局 class LiveCardV extends StatelessWidget { final LiveItemModel liveItem; - final int crossAxisCount; final Function()? longPress; final Function()? longPressEnd; const LiveCardV({ Key? key, required this.liveItem, - required this.crossAxisCount, this.longPress, this.longPressEnd, }) : super(key: key); @@ -62,7 +60,6 @@ class LiveCardV extends StatelessWidget { height: maxHeight, ), ), - if (crossAxisCount != 1) Positioned( left: 0, right: 0, @@ -80,7 +77,7 @@ class LiveCardV extends StatelessWidget { }), ), ), - LiveContent(liveItem: liveItem, crossAxisCount: crossAxisCount) + LiveContent(liveItem: liveItem) ], ), ), @@ -91,18 +88,15 @@ class LiveCardV extends StatelessWidget { class LiveContent extends StatelessWidget { final dynamic liveItem; - final int crossAxisCount; const LiveContent( - {Key? key, required this.liveItem, required this.crossAxisCount}) + {Key? key, required this.liveItem}) : super(key: key); @override Widget build(BuildContext context) { return Expanded( - flex: crossAxisCount == 1 ? 0 : 1, + flex: 1, child: Padding( - padding: crossAxisCount == 1 - ? const EdgeInsets.fromLTRB(9, 9, 9, 4) - : const EdgeInsets.fromLTRB(5, 8, 5, 4), + padding: const EdgeInsets.fromLTRB(5, 8, 5, 4), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -114,10 +108,9 @@ class LiveContent extends StatelessWidget { fontWeight: FontWeight.w500, letterSpacing: 0.3, ), - maxLines: crossAxisCount == 1 ? 1 : 2, + maxLines: 2, overflow: TextOverflow.ellipsis, ), - if (crossAxisCount == 1) const SizedBox(height: 4), Row( children: [ Expanded( @@ -133,24 +126,6 @@ class LiveContent extends StatelessWidget { overflow: TextOverflow.ellipsis, ), ), - if (crossAxisCount == 1) ...[ - Text( - ' • ${liveItem!.areaName!}', - style: TextStyle( - fontSize: - Theme.of(context).textTheme.labelMedium!.fontSize, - color: Theme.of(context).colorScheme.outline, - ), - ), - Text( - ' • ${liveItem!.watchedShow!['text_small']}人观看', - style: TextStyle( - fontSize: - Theme.of(context).textTheme.labelMedium!.fontSize, - color: Theme.of(context).colorScheme.outline, - ), - ), - ] ], ), ], @@ -184,32 +159,18 @@ class VideoStat extends StatelessWidget { tileMode: TileMode.mirror, ), ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - liveItem!.areaName!, - style: const TextStyle(fontSize: 11, color: Colors.white), - ), - Text( - liveItem!.watchedShow!['text_small'], - style: const TextStyle(fontSize: 11, color: Colors.white), - ), - ], + child: RichText( + maxLines: 1, + textAlign: TextAlign.justify, + softWrap: false, + text: TextSpan( + style: const TextStyle(fontSize: 11, color: Colors.white), + children: [ + TextSpan(text: liveItem!.areaName!), + TextSpan(text: liveItem!.watchedShow!['text_small']), + ], + ), ), - - // child: RichText( - // maxLines: 1, - // textAlign: TextAlign.justify, - // softWrap: false, - // text: TextSpan( - // style: const TextStyle(fontSize: 11, color: Colors.white), - // children: [ - // TextSpan(text: liveItem!.areaName!), - // TextSpan(text: liveItem!.watchedShow!['text_small']), - // ], - // ), - // ), ); } } diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index ee7b82144..bef16dab2 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -17,6 +17,13 @@ class LiveRoomController extends GetxController { PlPlayerController.getInstance(videoType: 'live'); Rx roomInfoH5 = RoomInfoH5Model().obs; + // MeeduPlayerController meeduPlayerController = MeeduPlayerController( + // colorTheme: Theme.of(Get.context!).colorScheme.primary, + // pipEnabled: true, + // controlsStyle: ControlsStyle.live, + // enabledButtons: const EnabledButtons(pip: true), + // ); + @override void onInit() { super.onInit(); @@ -31,10 +38,11 @@ class LiveRoomController extends GetxController { cover = liveItem.cover; } } + queryLiveInfo(); } - playerInit(source) async { - await plPlayerController.setDataSource( + playerInit(source) { + plPlayerController.setDataSource( DataSource( videoSource: source, audioSource: null, @@ -60,8 +68,7 @@ class LiveRoomController extends GetxController { String videoUrl = (item.urlInfo?.first.host)! + item.baseUrl! + item.urlInfo!.first.extra!; - await playerInit(videoUrl); - return res; + playerInit(videoUrl); } } @@ -75,12 +82,4 @@ class LiveRoomController extends GetxController { volumeOff.value = true; } } - - Future queryLiveInfoH5() async { - var res = await LiveHttp.liveRoomInfoH5(roomId: roomId); - if (res['status']) { - roomInfoH5.value = res['data']; - } - return res; - } } diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 39800b906..3c3ff2c03 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -19,8 +19,6 @@ class LiveRoomPage extends StatefulWidget { class _LiveRoomPageState extends State { final LiveRoomController _liveRoomController = Get.put(LiveRoomController()); PlPlayerController? plPlayerController; - late Future? _futureBuilder; - late Future? _futureBuilderFuture; bool isShowCover = true; bool isPlay = true; @@ -52,123 +50,57 @@ class _LiveRoomPageState extends State { @override Widget build(BuildContext context) { - Widget videoPlayerPanel = FutureBuilder( - future: _futureBuilderFuture, - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData && snapshot.data['status']) { - return PLVideoPlayer( - controller: plPlayerController!, - bottomControl: BottomControl( - controller: plPlayerController, - liveRoomCtr: _liveRoomController, - floating: floating, - ), - ); - } else { - return const SizedBox(); - } - }, - ); - Widget childWhenDisabled = Scaffold( primary: true, - backgroundColor: Colors.black, - body: Stack( - children: [ - // Obx( - // () => Positioned.fill( - // child: Opacity( - // opacity: 0.8, - // child: _liveRoomController - // .roomInfoH5.value.roomInfo?.appBackground != - // '' && - // _liveRoomController - // .roomInfoH5.value.roomInfo?.appBackground != - // null - // ? NetworkImgLayer( - // width: Get.width, - // height: Get.height, - // src: _liveRoomController - // .roomInfoH5.value.roomInfo?.appBackground ?? - // '', - // ) - // : Image.asset( - // 'assets/images/live/default_bg.webp', - // width: Get.width, - // height: Get.height, - // ), - // ), + appBar: PreferredSize( + preferredSize: Size.fromHeight( + MediaQuery.of(context).orientation == Orientation.portrait ? 56 : 0, + ), + child: AppBar( + centerTitle: false, + titleSpacing: 0, + title: _liveRoomController.liveItem != null + ? Row( + children: [ + NetworkImgLayer( + width: 34, + height: 34, + type: 'avatar', + src: _liveRoomController.liveItem.face, + ), + const SizedBox(width: 10), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + _liveRoomController.liveItem.uname, + style: const TextStyle(fontSize: 14), + ), + const SizedBox(height: 1), + if (_liveRoomController.liveItem.watchedShow != null) + Text( + _liveRoomController + .liveItem.watchedShow['text_large'] ?? + '', + style: const TextStyle(fontSize: 12)), + ], + ), + ], + ) + : const SizedBox(), + // actions: [ + // SizedBox( + // height: 34, + // child: ElevatedButton(onPressed: () {}, child: const Text('关注')), // ), - // ), - Positioned.fill( - child: Opacity( - opacity: 0.8, - child: Image.asset( - 'assets/images/live/default_bg.webp', - width: Get.width, - height: Get.height, - ), - ), - ), - Column( + // const SizedBox(width: 12), + // ], + ), + ), + body: Column( + children: [ + Stack( children: [ - AppBar( - centerTitle: false, - titleSpacing: 0, - backgroundColor: Colors.transparent, - foregroundColor: Colors.white, - toolbarHeight: - MediaQuery.of(context).orientation == Orientation.portrait - ? 56 - : 0, - title: FutureBuilder( - future: _futureBuilder, - builder: (context, snapshot) { - if (snapshot.data == null) { - return const SizedBox(); - } - Map data = snapshot.data as Map; - if (data['status']) { - return Obx( - () => Row( - children: [ - NetworkImgLayer( - width: 34, - height: 34, - type: 'avatar', - src: _liveRoomController - .roomInfoH5.value.anchorInfo!.baseInfo!.face, - ), - const SizedBox(width: 10), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - _liveRoomController.roomInfoH5.value - .anchorInfo!.baseInfo!.uname!, - style: const TextStyle(fontSize: 14), - ), - const SizedBox(height: 1), - if (_liveRoomController - .roomInfoH5.value.watchedShow != - null) - Text( - _liveRoomController.roomInfoH5.value - .watchedShow!['text_large'] ?? - '', - style: const TextStyle(fontSize: 12), - ), - ], - ), - ], - ), - ); - } else { - return const SizedBox(); - } - }, - ), - ), PopScope( canPop: plPlayerController?.isFullScreen.value != true, onPopInvoked: (bool didPop) { @@ -186,19 +118,55 @@ class _LiveRoomPageState extends State { Orientation.landscape ? Get.size.height : Get.size.width * 9 / 16, - child: videoPlayerPanel, + child: plPlayerController!.videoPlayerController != null + ? PLVideoPlayer( + controller: plPlayerController!, + bottomControl: BottomControl( + controller: plPlayerController, + liveRoomCtr: _liveRoomController, + floating: floating, + ), + ) + : const SizedBox(), ), ), + // if (_liveRoomController.liveItem != null && + // _liveRoomController.liveItem.cover != null) + // Visibility( + // visible: isShowCover, + // child: Positioned( + // top: 0, + // left: 0, + // right: 0, + // child: NetworkImgLayer( + // type: 'emote', + // src: _liveRoomController.liveItem.cover, + // width: Get.size.width, + // height: videoHeight, + // ), + // ), + // ), ], ), ], ), ); + Widget childWhenEnabled = AspectRatio( + aspectRatio: 16 / 9, + child: plPlayerController!.videoPlayerController != null + ? PLVideoPlayer( + controller: plPlayerController!, + bottomControl: BottomControl( + controller: plPlayerController, + liveRoomCtr: _liveRoomController, + ), + ) + : const SizedBox(), + ); if (Platform.isAndroid) { return PiPSwitcher( childWhenDisabled: childWhenDisabled, - childWhenEnabled: videoPlayerPanel, - floating: floating, + childWhenEnabled: childWhenEnabled, ); } else { return childWhenDisabled; diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 04e0f087f..bf8a6cb1c 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -89,10 +89,6 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { Widget build(BuildContext context) { Box localCache = GStrorage.localCache; double statusBarHeight = MediaQuery.of(context).padding.top; - double sheetHeight = MediaQuery.sizeOf(context).height - - MediaQuery.of(context).padding.top - - MediaQuery.sizeOf(context).width * 9 / 16; - localCache.put('sheetHeight', sheetHeight); localCache.put('statusBarHeight', statusBarHeight); return PopScope( canPop: false, diff --git a/lib/pages/member_archive/controller.dart b/lib/pages/member_archive/controller.dart index 4c41de4ca..38a2ee2a4 100644 --- a/lib/pages/member_archive/controller.dart +++ b/lib/pages/member_archive/controller.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/member.dart'; import 'package:pilipala/models/member/archive.dart'; @@ -42,6 +43,8 @@ class MemberArchiveController extends GetxController { } count = res['data'].page['count']; pn += 1; + } else { + SmartDialog.showToast(res['msg']); } return res; } diff --git a/lib/pages/member_search/controller.dart b/lib/pages/member_search/controller.dart index be4f5b1ac..0d366466b 100644 --- a/lib/pages/member_search/controller.dart +++ b/lib/pages/member_search/controller.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/member.dart'; import 'package:pilipala/models/member/archive.dart'; @@ -75,6 +76,8 @@ class MemberSearchController extends GetxController { } archivePn += 1; hasRequest = true; + } else { + SmartDialog.showToast(res['msg']); } // loadingStatus.value = 'finish'; return res; diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index 5ad9e8524..0650362d0 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -17,6 +17,8 @@ class MineController extends GetxController { Box userInfoCache = GStrorage.userInfo; Box setting = GStrorage.setting; Rx themeType = ThemeType.system.obs; + static bool anonymity = + GStrorage.setting.get(SettingBoxKey.anonymity, defaultValue: false); @override onInit() { @@ -29,6 +31,7 @@ class MineController extends GetxController { themeType.value = ThemeType.values[setting.get(SettingBoxKey.themeMode, defaultValue: ThemeType.system.code)]; + // anonymity = setting.get(SettingBoxKey.anonymity, defaultValue: false); } onLogin() async { @@ -85,6 +88,78 @@ class MineController extends GetxController { userStat.value = UserStat(); userInfoCache.delete('userInfoCache'); userLogin.value = false; + anonymity = false; + } + + static onChangeAnonymity(BuildContext context) { + anonymity = !anonymity; + if (anonymity) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Column(children: [ + const Row( + children: [ + Icon( + Icons.check, + color: Colors.green, + ), + SizedBox(width: 10), + Text('已进入无痕模式', style: TextStyle(fontSize: 15, height: 1.5)) + ], + ), + const SizedBox(height: 10), + const Text( + '搜索、观看视频/直播不携带Cookie与CSRF\n' + '不产生查询或播放记录\n' + '点赞等其它操作不受影响\n' + '(前往隐私设置了解详情)', + style: TextStyle(fontSize: 12.5, height: 1.5)), + Row(children: [ + TextButton( + style: ButtonStyle( + foregroundColor: MaterialStateProperty.resolveWith((states) { + return Theme.of(context).snackBarTheme.actionTextColor; + }), + ), + onPressed: () { + GStrorage.setting.put(SettingBoxKey.anonymity, true); + anonymity = true; + SmartDialog.showToast('已设为永久无痕模式'); + }, + child: const Text('保存为永久')), + const SizedBox(width: 10), + TextButton( + style: ButtonStyle( + foregroundColor: MaterialStateProperty.resolveWith((states) { + return Theme.of(context).snackBarTheme.actionTextColor; + }), + ), + onPressed: () { + GStrorage.setting.put(SettingBoxKey.anonymity, false); + anonymity = true; + SmartDialog.showToast('已设为临时无痕模式'); + }, + child: const Text('仅本次(默认)')) + ]) + ]), + duration: const Duration(seconds: 2), + showCloseIcon: true, + ), + ); + } else { + GStrorage.setting.put(SettingBoxKey.anonymity, false); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('已退出无痕模式'), + duration: Duration(seconds: 1), + // action: SnackBarAction( + // label: '确认', + // onPressed: () {}, + // ), + showCloseIcon: true, + ), + ); + } } onChangeTheme() { diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart index 091b2149d..485f94ff2 100644 --- a/lib/pages/mine/view.dart +++ b/lib/pages/mine/view.dart @@ -44,29 +44,46 @@ class _MinePageState extends State { toolbarHeight: kTextTabBarHeight + 20, backgroundColor: Colors.transparent, centerTitle: false, - title: const Text( - 'PLPL', - style: TextStyle( - height: 2.8, - fontSize: 17, - fontWeight: FontWeight.bold, - fontFamily: 'Jura-Bold', - ), + title: //logo + Row( + children: [ + Image.asset( + 'assets/images/logo/logo_android_2.png', + width: 50, + ), + const SizedBox(width: 5), + Text( + 'PiliPala', + style: Theme.of(context).textTheme.titleMedium, + ), + ], ), actions: [ + IconButton( + onPressed: () { + MineController.onChangeAnonymity(context); + setState(() {}); + }, + icon: Icon( + MineController.anonymity + ? Icons.visibility_off + : Icons.visibility, + size: 22, + ), + ), IconButton( onPressed: () => mineController.onChangeTheme(), icon: Icon( mineController.themeType.value == ThemeType.dark - ? CupertinoIcons.sun_max - : CupertinoIcons.moon, + ? Icons.light_mode + : Icons.mode_night, size: 22, ), ), IconButton( onPressed: () => Get.toNamed('/setting', preventDuplicates: false), icon: const Icon( - CupertinoIcons.slider_horizontal_3, + Icons.settings, ), ), const SizedBox(width: 10), diff --git a/lib/pages/msg_feed_top/at_me/controller.dart b/lib/pages/msg_feed_top/at_me/controller.dart new file mode 100644 index 000000000..e105d9835 --- /dev/null +++ b/lib/pages/msg_feed_top/at_me/controller.dart @@ -0,0 +1,44 @@ +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/http/msg.dart'; +import 'package:pilipala/models/msg/msgfeed_at_me.dart'; + +class AtMeController extends GetxController { + RxList msgFeedAtMeList = [].obs; + bool isLoading = false; + int cursor = -1; + int cursorTime = -1; + bool isEnd = false; + + Future queryMsgFeedAtMe() async { + if (isLoading) return; + isLoading = true; + var res = await MsgHttp.msgFeedAtMe(cursor: cursor, cursorTime: cursorTime); + isLoading = false; + if (res['status']) { + MsgFeedAtMe data = MsgFeedAtMe.fromJson(res['data']); + isEnd = data.cursor?.isEnd ?? false; + if (cursor == -1) { + msgFeedAtMeList.assignAll(data.items!); + } else { + msgFeedAtMeList.addAll(data.items!); + } + cursor = data.cursor?.id ?? -1; + cursorTime = data.cursor?.time ?? -1; + } else { + SmartDialog.showToast(res['msg']); + } + } + + Future onLoad() async { + if (isEnd) return; + queryMsgFeedAtMe(); + } + + Future onRefresh() async { + cursor = -1; + cursorTime = -1; + queryMsgFeedAtMe(); + } + +} diff --git a/lib/pages/msg_feed_top/at_me/index.dart b/lib/pages/msg_feed_top/at_me/index.dart new file mode 100644 index 000000000..6d2ce5339 --- /dev/null +++ b/lib/pages/msg_feed_top/at_me/index.dart @@ -0,0 +1,4 @@ +library whisper; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/msg_feed_top/at_me/view.dart b/lib/pages/msg_feed_top/at_me/view.dart new file mode 100644 index 000000000..ab15b2df3 --- /dev/null +++ b/lib/pages/msg_feed_top/at_me/view.dart @@ -0,0 +1,124 @@ +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; + +import 'controller.dart'; + +class AtMePage extends StatefulWidget { + const AtMePage({super.key}); + + @override + State createState() => _AtMePageState(); +} + +class _AtMePageState extends State { + late final AtMeController _atMeController = Get.put(AtMeController()); + final ScrollController _scrollController = ScrollController(); + + @override + void initState() { + _atMeController.queryMsgFeedAtMe(); + super.initState(); + _scrollController.addListener(_scrollListener); + } + + Future _scrollListener() async { + if (_scrollController.position.pixels >= + _scrollController.position.maxScrollExtent - 200) { + EasyThrottle.throttle('my-throttler', const Duration(milliseconds: 800), + () async { + await _atMeController.onLoad(); + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('@我的'), + ), + body: RefreshIndicator( + onRefresh: () async { + await _atMeController.onRefresh(); + }, + child: SingleChildScrollView( + controller: _scrollController, + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Obx( + () { + if (_atMeController.msgFeedAtMeList.isEmpty) { + return const Center( + child: CircularProgressIndicator(), + ); + } + return ListView.separated( + itemCount: _atMeController.msgFeedAtMeList.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (_, int i) { + return ListTile( + onTap: () { + String nativeUri = _atMeController + .msgFeedAtMeList[i].item?.nativeUri ?? + ""; + SmartDialog.showToast("跳转至:$nativeUri(暂未实现)"); + }, + leading: NetworkImgLayer( + width: 45, + height: 45, + type: 'avatar', + src: _atMeController.msgFeedAtMeList[i].user?.avatar, + ), + title: Text( + "${_atMeController.msgFeedAtMeList[i].user?.nickname} " + "在${_atMeController.msgFeedAtMeList[i].item?.business}中@了我", + style: Theme.of(context).textTheme.bodyMedium!, + ), + subtitle: Text( + _atMeController + .msgFeedAtMeList[i].item?.sourceContent ?? + "", + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Theme.of(context) + .textTheme + .labelMedium! + .copyWith( + color: + Theme.of(context).colorScheme.outline)), + trailing: _atMeController + .msgFeedAtMeList[i].item?.image != + null && + _atMeController.msgFeedAtMeList[i].item?.image != + "" + ? NetworkImgLayer( + width: 45, + height: 45, + type: 'cover', + src: _atMeController + .msgFeedAtMeList[i].item?.image, + ) + : null, + ); + }, + separatorBuilder: (BuildContext context, int index) { + return Divider( + indent: 72, + endIndent: 20, + height: 6, + color: Colors.grey.withOpacity(0.1), + ); + }, + ); + }, + ); + }), + ), + ), + ); + } +} diff --git a/lib/pages/msg_feed_top/like_me/controller.dart b/lib/pages/msg_feed_top/like_me/controller.dart new file mode 100644 index 000000000..4430c73fa --- /dev/null +++ b/lib/pages/msg_feed_top/like_me/controller.dart @@ -0,0 +1,47 @@ +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/http/msg.dart'; +import '../../../models/msg/msgfeed_like_me.dart'; + +class LikeMeController extends GetxController { + RxList msgFeedLikeMeLatestList = [].obs; + RxList msgFeedLikeMeTotalList = [].obs; + bool isLoading = false; + int cursor = -1; + int cursorTime = -1; + bool isEnd = false; + + Future queryMsgFeedLikeMe() async { + if (isLoading) return; + isLoading = true; + var res = await MsgHttp.msgFeedLikeMe(cursor: cursor, cursorTime: cursorTime); + isLoading = false; + if (res['status']) { + MsgFeedLikeMe data = MsgFeedLikeMe.fromJson(res['data']); + isEnd = data.total?.cursor?.isEnd ?? false; + if (cursor == -1) { + msgFeedLikeMeLatestList.assignAll(data.latest?.items??[]); + msgFeedLikeMeTotalList.assignAll(data.total?.items??[]); + } else { + msgFeedLikeMeLatestList.addAll(data.latest?.items??[]); + msgFeedLikeMeTotalList.addAll(data.total?.items??[]); + } + cursor = data.total?.cursor?.id ?? -1; + cursorTime = data.total?.cursor?.time ?? -1; + } else { + SmartDialog.showToast(res['msg']); + } + } + + Future onLoad() async { + if (isEnd) return; + queryMsgFeedLikeMe(); + } + + Future onRefresh() async { + cursor = -1; + cursorTime = -1; + queryMsgFeedLikeMe(); + } + +} diff --git a/lib/pages/msg_feed_top/like_me/index.dart b/lib/pages/msg_feed_top/like_me/index.dart new file mode 100644 index 000000000..6d2ce5339 --- /dev/null +++ b/lib/pages/msg_feed_top/like_me/index.dart @@ -0,0 +1,4 @@ +library whisper; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/msg_feed_top/like_me/view.dart b/lib/pages/msg_feed_top/like_me/view.dart new file mode 100644 index 000000000..41b57f958 --- /dev/null +++ b/lib/pages/msg_feed_top/like_me/view.dart @@ -0,0 +1,179 @@ +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; + +import '../../../models/msg/msgfeed_like_me.dart'; +import 'controller.dart'; + +class LikeMePage extends StatefulWidget { + const LikeMePage({super.key}); + + @override + State createState() => _LikeMePageState(); +} + +class _LikeMePageState extends State { + late final LikeMeController _likeMeController = Get.put(LikeMeController()); + final ScrollController _scrollController = ScrollController(); + + @override + void initState() { + _likeMeController.queryMsgFeedLikeMe(); + super.initState(); + _scrollController.addListener(_scrollListener); + } + + Future _scrollListener() async { + if (_scrollController.position.pixels >= + _scrollController.position.maxScrollExtent - 200) { + EasyThrottle.throttle('my-throttler', const Duration(milliseconds: 800), + () async { + await _likeMeController.onLoad(); + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('收到的赞'), + ), + body: RefreshIndicator( + onRefresh: () async { + await _likeMeController.onRefresh(); + }, + child: SingleChildScrollView( + controller: _scrollController, + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Obx( + () { + if (_likeMeController.msgFeedLikeMeLatestList.isEmpty && + _likeMeController.msgFeedLikeMeTotalList.isEmpty) { + return const Center( + child: CircularProgressIndicator(), + ); + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (_likeMeController + .msgFeedLikeMeLatestList.isNotEmpty) ...[ + Text(" 最新", + style: Theme.of(context) + .textTheme + .labelMedium! + .copyWith( + color: + Theme.of(context).colorScheme.outline)), + LikeMeList( + msgFeedLikeMeList: + _likeMeController.msgFeedLikeMeLatestList), + ], + if (_likeMeController + .msgFeedLikeMeTotalList.isNotEmpty) ...[ + Text(" 累计", + style: Theme.of(context) + .textTheme + .labelMedium! + .copyWith( + color: + Theme.of(context).colorScheme.outline)), + LikeMeList( + msgFeedLikeMeList: + _likeMeController.msgFeedLikeMeTotalList), + ] + ]); + }, + ); + }), + ), + ), + ); + } +} + +class LikeMeList extends StatelessWidget { + const LikeMeList({ + super.key, + required this.msgFeedLikeMeList, + }); + final RxList msgFeedLikeMeList; + + @override + Widget build(BuildContext context) { + return ListView.separated( + itemCount: msgFeedLikeMeList.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (_, int i) { + return ListTile( + onTap: () { + String nativeUri = msgFeedLikeMeList[i].item?.nativeUri ?? ""; + SmartDialog.showToast("跳转至:$nativeUri(暂未实现)"); + }, + leading: SizedBox( + width: 50, + height: 50, + child: Stack( + children: [ + for (var j = 0; + j < msgFeedLikeMeList[i].users!.length && j < 4; + j++) ...[ + Positioned( + left: 15 * (j % 2).toDouble(), + top: 15 * (j ~/ 2).toDouble(), + child: NetworkImgLayer( + width: + msgFeedLikeMeList[i].users!.length > 1 ? 30 : 45, + height: + msgFeedLikeMeList[i].users!.length > 1 ? 30 : 45, + type: 'avatar', + src: msgFeedLikeMeList[i].users![j].avatar, + )), + ] + ], + )), + title: Text( + "${msgFeedLikeMeList[i].users!.map((e) => e.nickname).join("/")}" + "等共 ${msgFeedLikeMeList[i].counts} 人" + "赞了我的${msgFeedLikeMeList[i].item?.business}", + style: + Theme.of(context).textTheme.labelMedium!.copyWith(height: 1.5), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + subtitle: msgFeedLikeMeList[i].item?.title != null && + msgFeedLikeMeList[i].item?.title != "" + ? Text(msgFeedLikeMeList[i].item?.title ?? "", + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.labelMedium!.copyWith( + color: Theme.of(context).colorScheme.outline, + height: 1.5)) + : null, + trailing: msgFeedLikeMeList[i].item?.image != null && + msgFeedLikeMeList[i].item?.image != "" + ? NetworkImgLayer( + width: 45, + height: 45, + type: 'cover', + src: msgFeedLikeMeList[i].item?.image, + ) + : null, + ); + }, + separatorBuilder: (BuildContext context, int index) { + return Divider( + indent: 72, + endIndent: 20, + height: 6, + color: Colors.grey.withOpacity(0.1), + ); + }, + ); + } +} diff --git a/lib/pages/msg_feed_top/reply_me/controller.dart b/lib/pages/msg_feed_top/reply_me/controller.dart new file mode 100644 index 000000000..3bb0740d4 --- /dev/null +++ b/lib/pages/msg_feed_top/reply_me/controller.dart @@ -0,0 +1,45 @@ +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/http/msg.dart'; + +import '../../../models/msg/msgfeed_reply_me.dart'; + +class ReplyMeController extends GetxController { + RxList msgFeedReplyMeList = [].obs; + bool isLoading = false; + int cursor = -1; + int cursorTime = -1; + bool isEnd = false; + + Future queryMsgFeedReplyMe() async { + if (isLoading) return; + isLoading = true; + var res = await MsgHttp.msgFeedReplyMe(cursor: cursor, cursorTime: cursorTime); + isLoading = false; + if (res['status']) { + MsgFeedReplyMe data = MsgFeedReplyMe.fromJson(res['data']); + isEnd = data.cursor?.isEnd ?? false; + if (cursor == -1) { + msgFeedReplyMeList.assignAll(data.items!); + } else { + msgFeedReplyMeList.addAll(data.items!); + } + cursor = data.cursor?.id ?? -1; + cursorTime = data.cursor?.time ?? -1; + } else { + SmartDialog.showToast(res['msg']); + } + } + + Future onLoad() async { + if (isEnd) return; + queryMsgFeedReplyMe(); + } + + Future onRefresh() async { + cursor = -1; + cursorTime = -1; + queryMsgFeedReplyMe(); + } + +} diff --git a/lib/pages/msg_feed_top/reply_me/index.dart b/lib/pages/msg_feed_top/reply_me/index.dart new file mode 100644 index 000000000..6d2ce5339 --- /dev/null +++ b/lib/pages/msg_feed_top/reply_me/index.dart @@ -0,0 +1,4 @@ +library whisper; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/msg_feed_top/reply_me/view.dart b/lib/pages/msg_feed_top/reply_me/view.dart new file mode 100644 index 000000000..616029a70 --- /dev/null +++ b/lib/pages/msg_feed_top/reply_me/view.dart @@ -0,0 +1,149 @@ +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; + +import 'controller.dart'; + +class ReplyMePage extends StatefulWidget { + const ReplyMePage({super.key}); + + @override + State createState() => _ReplyMePageState(); +} + +class _ReplyMePageState extends State { + late final ReplyMeController _replyMeController = + Get.put(ReplyMeController()); + final ScrollController _scrollController = ScrollController(); + + @override + void initState() { + _replyMeController.queryMsgFeedReplyMe(); + super.initState(); + _scrollController.addListener(_scrollListener); + } + + Future _scrollListener() async { + if (_scrollController.position.pixels >= + _scrollController.position.maxScrollExtent - 200) { + EasyThrottle.throttle('my-throttler', const Duration(milliseconds: 800), + () async { + await _replyMeController.onLoad(); + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('回复我的'), + ), + body: RefreshIndicator( + onRefresh: () async { + await _replyMeController.onRefresh(); + }, + child: SingleChildScrollView( + controller: _scrollController, + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Obx( + () { + if (_replyMeController.msgFeedReplyMeList.isEmpty) { + return const Center( + child: CircularProgressIndicator(), + ); + } + return ListView.separated( + itemCount: _replyMeController.msgFeedReplyMeList.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (_, int i) { + return ListTile( + onTap: () { + String nativeUri = _replyMeController + .msgFeedReplyMeList[i].item?.nativeUri ?? + ""; + SmartDialog.showToast("跳转至:$nativeUri(暂未实现)"); + }, + leading: NetworkImgLayer( + width: 45, + height: 45, + type: 'avatar', + src: _replyMeController + .msgFeedReplyMeList[i].user?.avatar, + ), + title: Text( + "${_replyMeController.msgFeedReplyMeList[i].user?.nickname} " + "回复了我的${_replyMeController.msgFeedReplyMeList[i].item?.business}", + style: Theme.of(context).textTheme.bodyMedium!, + ), + subtitle: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 4), + Text( + _replyMeController.msgFeedReplyMeList[i].item + ?.sourceContent ?? + "", + style: Theme.of(context).textTheme.bodyMedium), + const SizedBox(height: 4), + if (_replyMeController.msgFeedReplyMeList[i].item + ?.targetReplyContent != + null && + _replyMeController.msgFeedReplyMeList[i].item + ?.targetReplyContent != + "") + Text( + "| ${_replyMeController.msgFeedReplyMeList[i].item?.targetReplyContent}", + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context) + .textTheme + .labelMedium! + .copyWith( + color: Theme.of(context) + .colorScheme + .outline, + height: 1.5)), + if (_replyMeController.msgFeedReplyMeList[i].item + ?.rootReplyContent != + null && + _replyMeController.msgFeedReplyMeList[i].item + ?.rootReplyContent != + "") + Text( + " | ${_replyMeController.msgFeedReplyMeList[i].item?.rootReplyContent}", + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context) + .textTheme + .labelMedium! + .copyWith( + color: Theme.of(context) + .colorScheme + .outline, + height: 1.5)), + ]), + ); + }, + separatorBuilder: (BuildContext context, int index) { + return Divider( + indent: 72, + endIndent: 20, + height: 6, + color: Colors.grey.withOpacity(0.1), + ); + }, + ); + }, + ); + }), + ), + ), + ); + } +} diff --git a/lib/pages/preview/view.dart b/lib/pages/preview/view.dart index 1c37c8332..dcffc9730 100644 --- a/lib/pages/preview/view.dart +++ b/lib/pages/preview/view.dart @@ -102,15 +102,12 @@ class _ImagePreviewState extends State ); } - // 设置状态栏图标透明 + // 隐藏状态栏,避免遮挡图片内容 setStatusBar() async { - if (Platform.isIOS) { + if (Platform.isIOS || Platform.isAndroid) { await StatusBarControl.setHidden(true, animation: StatusBarAnimation.SLIDE); } - if (Platform.isAndroid) { - await StatusBarControl.setColor(Colors.transparent); - } } @override diff --git a/lib/pages/rcmd/controller.dart b/lib/pages/rcmd/controller.dart index 28ff055b1..5faf3395c 100644 --- a/lib/pages/rcmd/controller.dart +++ b/lib/pages/rcmd/controller.dart @@ -22,8 +22,6 @@ class RcmdController extends GetxController { @override void onInit() { super.onInit(); - crossAxisCount.value = - setting.get(SettingBoxKey.customRows, defaultValue: 2); enableSaveLastData = setting.get(SettingBoxKey.enableSaveLastData, defaultValue: false); defaultRcmdType = diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart index d732f3703..f54d76c4e 100644 --- a/lib/pages/rcmd/view.dart +++ b/lib/pages/rcmd/view.dart @@ -13,6 +13,7 @@ import 'package:pilipala/common/widgets/video_card_v.dart'; import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/main/index.dart'; +import '../../utils/grid.dart'; import 'controller.dart'; class RcmdPage extends StatefulWidget { @@ -143,35 +144,23 @@ class _RcmdPageState extends State } Widget contentGrid(ctr, videoList) { - // double maxWidth = Get.size.width; - // int baseWidth = 500; - // int step = 300; - // int crossAxisCount = - // maxWidth > baseWidth ? 2 + ((maxWidth - baseWidth) / step).ceil() : 2; - // if (maxWidth < 300) { - // crossAxisCount = 1; - // } - int crossAxisCount = ctr.crossAxisCount.value; - double mainAxisExtent = (Get.size.width / - crossAxisCount / - StyleString.aspectRatio) + - (crossAxisCount == 1 ? 68 : MediaQuery.textScalerOf(context).scale(86)); + return SliverGrid( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( // 行间距 mainAxisSpacing: StyleString.safeSpace, // 列间距 crossAxisSpacing: StyleString.safeSpace, - // 列数 - crossAxisCount: crossAxisCount, - mainAxisExtent: mainAxisExtent, + // 最大宽度 + maxCrossAxisExtent: Grid.maxRowWidth, + mainAxisExtent: Grid.calculateActualWidth(context, Grid.maxRowWidth, StyleString.safeSpace) / StyleString.aspectRatio+ + MediaQuery.textScalerOf(context).scale(96), ), delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { return videoList!.isNotEmpty ? VideoCardV( videoItem: videoList[index], - crossAxisCount: crossAxisCount, longPress: () { _rcmdController.popupDialog = _createPopupDialog(videoList[index]); diff --git a/lib/pages/setting/pages/logs.dart b/lib/pages/setting/pages/logs.dart index 0958edb80..ec72f7ca9 100644 --- a/lib/pages/setting/pages/logs.dart +++ b/lib/pages/setting/pages/logs.dart @@ -45,14 +45,19 @@ class _LogsPageState extends State { }).toList(); List> result = []; for (String i in contentList) { - DateTime? date; + dynamic date; String body = i .split("\n") .map((l) { if (l.startsWith("Crash occurred on")) { - date = DateTime.parse( - l.split("Crash occurred on")[1].trim().split('.')[0], - ); + try { + date = DateTime.parse( + l.split("Crash occurred on")[1].trim().split('.')[0], + ); + } catch (e) { + print(e.toString()); + date = l.toString(); + } return ""; } return l; diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart index bfd5db5fc..02aba8778 100644 --- a/lib/pages/setting/play_setting.dart +++ b/lib/pages/setting/play_setting.dart @@ -1,6 +1,8 @@ import 'dart:io'; +import 'package:auto_orientation/auto_orientation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/models/video/play/quality.dart'; @@ -116,6 +118,21 @@ class _PlaySettingState extends State { setKey: SettingBoxKey.enableAutoExit, defaultVal: false, ), + SetSwitchItem( + title: '横屏适配(测试)', + subTitle: '开启该项在播放页启用横屏布局与逻辑', + setKey: SettingBoxKey.horizontalScreen, + defaultVal: false, + callFn: (value) { + if (value) { + autoScreen(); + SmartDialog.showToast('已开启横屏适配'); + } else { + AutoOrientation.portraitUpMode(); + SmartDialog.showToast('已关闭横屏适配'); + } + } + ), const SetSwitchItem( title: '开启硬解', subTitle: '以较低功耗播放视频', @@ -140,6 +157,12 @@ class _PlaySettingState extends State { setKey: SettingBoxKey.enableQuickDouble, defaultVal: true, ), + const SetSwitchItem( + title: '全屏手势反向', + subTitle: '默认播放器中部向上滑动进入全屏,向下退出\n开启后向下全屏,向上退出', + setKey: SettingBoxKey.fullScreenGestureReverse, + defaultVal: false, + ), const SetSwitchItem( title: '弹幕开关', subTitle: '展示弹幕', diff --git a/lib/pages/setting/privacy_setting.dart b/lib/pages/setting/privacy_setting.dart index 07a02318c..fca39c30f 100644 --- a/lib/pages/setting/privacy_setting.dart +++ b/lib/pages/setting/privacy_setting.dart @@ -2,9 +2,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; +import 'package:pilipala/http/interceptor_anonymity.dart'; import 'package:pilipala/http/member.dart'; import 'package:pilipala/utils/storage.dart'; +import '../mine/controller.dart'; + class PrivacySetting extends StatefulWidget { const PrivacySetting({super.key}); @@ -64,6 +67,44 @@ class _PrivacySettingState extends State { dense: false, title: Text('刷新access_key', style: titleStyle), ), + ListTile( + onTap: () { + MineController.onChangeAnonymity(context); + setState(() {}); + }, + dense: false, + title: Text(MineController.anonymity ? '退出无痕模式' : '进入无痕模式', + style: titleStyle), + subtitle: Text( + MineController.anonymity + ? '已进入无痕模式,搜索、观看视频/直播不携带Cookie与CSRF,其余操作不受影响' + : '未开启无痕模式,将使用账户信息提供完整服务', + style: subTitleStyle, + )), + ListTile( + onTap: () { + SmartDialog.show( + builder: (context) { + return AlertDialog( + title: const Text('查看详情'), + content: + Text(AnonymityInterceptor.anonymityList.join('\n')), + actions: [ + TextButton( + onPressed: () async { + SmartDialog.dismiss(); + }, + child: const Text('确认'), + ) + ], + ); + }, + ); + }, + dense: false, + title: Text('了解无痕模式', style: titleStyle), + subtitle: Text('查看无痕模式作用的API列表', style: subTitleStyle), + ), ], ), ); diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index fb3c780a2..ba2f2276b 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -30,7 +30,7 @@ class _StyleSettingState extends State { late int picQuality; late double toastOpacity; late ThemeType _tempThemeValue; - late dynamic defaultCustomRows; + late double maxRowWidth; @override void initState() { @@ -38,7 +38,7 @@ class _StyleSettingState extends State { picQuality = setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10); toastOpacity = setting.get(SettingBoxKey.defaultToastOp, defaultValue: 1.0); _tempThemeValue = settingController.themeType.value; - defaultCustomRows = setting.get(SettingBoxKey.customRows, defaultValue: 2); + maxRowWidth = setting.get(SettingBoxKey.maxRowWidth, defaultValue: 240.0) as double; } @override @@ -84,7 +84,7 @@ class _StyleSettingState extends State { ), const SetSwitchItem( title: 'MD3样式底栏', - subTitle: '符合Material You设计规范的底栏', + subTitle: '符合Material You设计规范的底栏,关闭可使底栏变窄', setKey: SettingBoxKey.enableMYBar, defaultVal: true, ), @@ -104,27 +104,30 @@ class _StyleSettingState extends State { ), ListTile( onTap: () async { - int? result = await showDialog( + double? result = await showDialog( context: context, builder: (context) { - return SelectDialog( - title: '自定义列数', - value: defaultCustomRows, - values: [1, 2, 3, 4, 5].map((e) { - return {'title': '$e 列', 'value': e}; - }).toList()); - }, + return SlideDialog( + title: '最大列宽度(默认240dp)', + value: maxRowWidth, + min: 150.0, + max: 500.0, + divisions: 35, + suffix: 'dp', + ); + } ); if (result != null) { - defaultCustomRows = result; - setting.put(SettingBoxKey.customRows, result); + maxRowWidth = result; + setting.put(SettingBoxKey.maxRowWidth, result); + SmartDialog.showToast('重启生效'); setState(() {}); } }, dense: false, - title: Text('自定义列数', style: titleStyle), + title: Text('最大列宽度(dp)', style: titleStyle), subtitle: Text( - '当前列数 $defaultCustomRows 列', + '当前最大列宽度:${maxRowWidth.toInt()}dp,屏幕宽度:${MediaQuery.of(context).size.width.toPrecision(2)}dp,', style: subTitleStyle, ), ), diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 0162654f8..eca5ffb48 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -243,11 +243,25 @@ class VideoDetailController extends GetxController plPlayerController.headerControl = headerControl; } + void setTriggerFullScreenCallback(void Function({bool? status}) callback) { + plPlayerController.setTriggerFullscreenCallback(callback); + } + // 视频链接 Future queryVideoUrl() async { var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid); if (result['status']) { data = result['data']; + if (data.dash == null) { + // isEffective.value = false; + if (data.acceptDesc != null) { + SmartDialog.showToast('当前视频acceptDesc为:${data.acceptDesc},不支持获取视频链接'); + } else { + SmartDialog.showToast('当前视频未能获取视频链接'); + } + result['status'] = false; + return result; + } final List allVideosList = data.dash!.video!; try { // 当前可播放的最高质量视频 @@ -351,7 +365,11 @@ class VideoDetailController extends GetxController if (result['code'] == -404) { isShowCover.value = false; } - SmartDialog.showToast(result['msg'].toString()); + if (result['code'] == 87008) { + SmartDialog.showToast("当前视频可能是专属视频,可能需包月充电观看(${result['msg']})"); + } else { + SmartDialog.showToast("错误(${result['code']}):${result['msg']}"); + } } return result; } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 0a3ac934b..850f8a28f 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/pages/mine/controller.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/common/widgets/stat/danmu.dart'; @@ -122,9 +123,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { late final VideoDetailController videoDetailCtr; late final Map videoItem; - final Box localCache = GStrorage.localCache; final Box setting = GStrorage.setting; - late double sheetHeight; late final bool loadingStatus; // 加载状态 @@ -152,7 +151,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { videoIntroController = Get.put(VideoIntroController(), tag: heroTag); videoDetailCtr = Get.find(tag: heroTag); videoItem = videoIntroController.videoItem!; - sheetHeight = localCache.get('sheetHeight'); loadingStatus = widget.loadingStatus; owner = loadingStatus ? videoItem['owner'] : widget.videoDetail!.owner; @@ -285,7 +283,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { child: Padding( padding: const EdgeInsets.only(top: 7, bottom: 6), child: Row( - children: [ + children: [ StatView( theme: 'gray', view: !loadingStatus @@ -313,6 +311,19 @@ class _VideoInfoState extends State with TickerProviderStateMixin { color: t.colorScheme.outline, ), ), + if (MineController.anonymity) ...[ + const SizedBox(width: 10), + Icon( + Icons.visibility_off_outlined, + size: 15, + color: t.colorScheme.outline, + ), + const SizedBox(width: 2), + Text( + '无痕模式', + style: TextStyle(fontSize: 12,color: t.colorScheme.outline), + ), + ], const SizedBox(width: 10), if (videoIntroController.isShowOnlineTotal) Obx( @@ -367,7 +378,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { cid: videoIntroController.lastPlayCid.value != 0 ? videoIntroController.lastPlayCid.value : widget.videoDetail!.pages!.first.cid, - sheetHeight: sheetHeight, changeFuc: (bvid, cid, aid) => videoIntroController .changeSeasonOrbangu(bvid, cid, aid), ), @@ -379,7 +389,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { Obx(() => PagesPanel( pages: widget.videoDetail!.pages!, cid: videoIntroController.lastPlayCid.value, - sheetHeight: sheetHeight, changeFuc: (cid) => videoIntroController.changeSeasonOrbangu( videoIntroController.bvid, cid, null), diff --git a/lib/pages/video/detail/introduction/widgets/fav_panel.dart b/lib/pages/video/detail/introduction/widgets/fav_panel.dart index 517caeaa0..6a87a0e80 100644 --- a/lib/pages/video/detail/introduction/widgets/fav_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/fav_panel.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:hive/hive.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/utils/feed_back.dart'; -import 'package:pilipala/utils/storage.dart'; + +import '../../../../../utils/utils.dart'; class FavPanel extends StatefulWidget { const FavPanel({super.key, this.ctr}); @@ -14,21 +14,18 @@ class FavPanel extends StatefulWidget { } class _FavPanelState extends State { - final Box localCache = GStrorage.localCache; - late double sheetHeight; late Future _futureBuilderFuture; @override void initState() { super.initState(); - sheetHeight = localCache.get('sheetHeight'); _futureBuilderFuture = widget.ctr!.queryVideoInFolder(); } @override Widget build(BuildContext context) { return Container( - height: sheetHeight, + height: Utils.getSheetHeight(context), color: Theme.of(context).colorScheme.background, child: Column( children: [ diff --git a/lib/pages/video/detail/introduction/widgets/group_panel.dart b/lib/pages/video/detail/introduction/widgets/group_panel.dart index 64ff913d7..8b3f517b1 100644 --- a/lib/pages/video/detail/introduction/widgets/group_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/group_panel.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:hive/hive.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/http/member.dart'; import 'package:pilipala/models/member/tags.dart'; import 'package:pilipala/utils/feed_back.dart'; -import 'package:pilipala/utils/storage.dart'; + +import '../../../../../utils/utils.dart'; class GroupPanel extends StatefulWidget { final int? mid; @@ -17,8 +17,6 @@ class GroupPanel extends StatefulWidget { } class _GroupPanelState extends State { - final Box localCache = GStrorage.localCache; - late double sheetHeight; late Future _futureBuilderFuture; late List tagsList; bool showDefault = true; @@ -26,7 +24,6 @@ class _GroupPanelState extends State { @override void initState() { super.initState(); - sheetHeight = localCache.get('sheetHeight'); _futureBuilderFuture = MemberHttp.followUpTags(); } @@ -56,7 +53,7 @@ class _GroupPanelState extends State { @override Widget build(BuildContext context) { return Container( - height: sheetHeight, + height: Utils.getSheetHeight(context), color: Theme.of(context).colorScheme.background, child: Column( children: [ diff --git a/lib/pages/video/detail/introduction/widgets/intro_detail.dart b/lib/pages/video/detail/introduction/widgets/intro_detail.dart index 59ed10a35..f01d62bd0 100644 --- a/lib/pages/video/detail/introduction/widgets/intro_detail.dart +++ b/lib/pages/video/detail/introduction/widgets/intro_detail.dart @@ -2,15 +2,10 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:hive/hive.dart'; import 'package:pilipala/common/widgets/stat/danmu.dart'; import 'package:pilipala/common/widgets/stat/view.dart'; -import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; -Box localCache = GStrorage.localCache; -late double sheetHeight; - class IntroDetail extends StatelessWidget { const IntroDetail({ super.key, @@ -20,11 +15,10 @@ class IntroDetail extends StatelessWidget { @override Widget build(BuildContext context) { - sheetHeight = localCache.get('sheetHeight'); return Container( color: Theme.of(context).colorScheme.background, padding: const EdgeInsets.only(left: 14, right: 14), - height: sheetHeight, + height: Utils.getSheetHeight(context), child: Column( children: [ InkWell( diff --git a/lib/pages/video/detail/introduction/widgets/page.dart b/lib/pages/video/detail/introduction/widgets/page.dart index 2ba7f507c..9ee8fa1eb 100644 --- a/lib/pages/video/detail/introduction/widgets/page.dart +++ b/lib/pages/video/detail/introduction/widgets/page.dart @@ -3,17 +3,17 @@ import 'package:get/get.dart'; import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/pages/video/detail/index.dart'; +import '../../../../../utils/utils.dart'; + class PagesPanel extends StatefulWidget { const PagesPanel({ super.key, required this.pages, this.cid, - this.sheetHeight, this.changeFuc, }); final List pages; final int? cid; - final double? sheetHeight; final Function? changeFuc; @override @@ -96,7 +96,7 @@ class _PagesPanelState extends State { _scrollController.jumpTo(currentIndex * 56); }); return Container( - height: widget.sheetHeight, + height: Utils.getSheetHeight(context), color: Theme.of(context).colorScheme.background, child: Column( children: [ diff --git a/lib/pages/video/detail/introduction/widgets/season.dart b/lib/pages/video/detail/introduction/widgets/season.dart index 9e5dd34f7..266bcff40 100644 --- a/lib/pages/video/detail/introduction/widgets/season.dart +++ b/lib/pages/video/detail/introduction/widgets/season.dart @@ -5,17 +5,17 @@ import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/utils/id_utils.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; +import '../../../../../utils/utils.dart'; + class SeasonPanel extends StatefulWidget { const SeasonPanel({ super.key, required this.ugcSeason, this.cid, - this.sheetHeight, this.changeFuc, }); final UgcSeason ugcSeason; final int? cid; - final double? sheetHeight; final Function? changeFuc; @override @@ -104,7 +104,7 @@ class _SeasonPanelState extends State { itemScrollController.jumpTo(index: currentIndex); }); return Container( - height: widget.sheetHeight, + height: Utils.getSheetHeight(context), color: Theme.of(context).colorScheme.background, child: Column( children: [ @@ -131,6 +131,9 @@ class _SeasonPanelState extends State { Theme.of(context).dividerColor.withOpacity(0.1), ), Expanded( + child: Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom), child: Material( child: ScrollablePositionedList.builder( itemCount: episodes.length, @@ -163,7 +166,7 @@ class _SeasonPanelState extends State { itemScrollController: itemScrollController, ), ), - ), + )), ], ), ); diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index df8c75b14..309ceddc4 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -125,7 +125,7 @@ class _VideoReplyPanelState extends State void dispose() { scrollController.removeListener(() {}); fabAnimationCtr.dispose(); - scrollController.dispose(); + // scrollController.dispose(); super.dispose(); } diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index c08c81cce..12f4607be 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -1,14 +1,13 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:hive/hive.dart'; import 'package:pilipala/common/skeleton/video_reply.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/models/common/reply_type.dart'; import 'package:pilipala/models/video/reply/item.dart'; import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart'; -import 'package:pilipala/utils/storage.dart'; +import '../../../../utils/utils.dart'; import 'controller.dart'; class VideoReplyReplyPanel extends StatefulWidget { @@ -35,8 +34,6 @@ class VideoReplyReplyPanel extends StatefulWidget { class _VideoReplyReplyPanelState extends State { late VideoReplyReplyController _videoReplyReplyController; late AnimationController replyAnimationCtl; - final Box localCache = GStrorage.localCache; - late double sheetHeight; Future? _futureBuilderFuture; late ScrollController scrollController; @@ -61,7 +58,6 @@ class _VideoReplyReplyPanelState extends State { }, ); - sheetHeight = localCache.get('sheetHeight'); _futureBuilderFuture = _videoReplyReplyController.queryReplyList(); } @@ -76,7 +72,7 @@ class _VideoReplyReplyPanelState extends State { @override Widget build(BuildContext context) { return Container( - height: widget.source == 'videoDetail' ? sheetHeight : null, + height: widget.source == 'videoDetail' ? Utils.getSheetHeight(context) : null, color: Theme.of(context).colorScheme.background, child: Column( children: [ diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 14311cc95..55125c263 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -1,7 +1,9 @@ import 'dart:async'; import 'dart:io'; +import 'dart:math'; import 'dart:ui'; +import 'package:auto_orientation/auto_orientation.dart'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:floating/floating.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -57,11 +59,13 @@ class _VideoDetailPageState extends State // 自动退出全屏 late bool autoExitFullcreen; late bool autoPlayEnable; + late bool horizontalScreen; late bool autoPiP; final Floating floating = Floating(); // 生命周期监听 late final AppLifecycleListener _lifecycleListener; bool isShowing = true; + RxBool isFullScreen = false.obs; @override void initState() { @@ -85,10 +89,13 @@ class _VideoDetailPageState extends State statusBarHeight = localCache.get('statusBarHeight'); autoExitFullcreen = setting.get(SettingBoxKey.enableAutoExit, defaultValue: false); + horizontalScreen = + setting.get(SettingBoxKey.horizontalScreen, defaultValue: false); autoPlayEnable = setting.get(SettingBoxKey.autoPlayEnable, defaultValue: true); autoPiP = setting.get(SettingBoxKey.autoPiP, defaultValue: false); - + videoDetailController + .setTriggerFullScreenCallback(triggerFullScreenCallback); videoSourceInit(); appbarStreamListen(); lifecycleListener(); @@ -160,6 +167,8 @@ class _VideoDetailPageState extends State /// 未开启自动播放时触发播放 Future handlePlay() async { + videoDetailController + .setTriggerFullScreenCallback(triggerFullScreenCallback); await videoDetailController.playerInit(); plPlayerController = videoDetailController.plPlayerController; plPlayerController!.addStatusLister(playerListener); @@ -190,6 +199,9 @@ class _VideoDetailPageState extends State @override void dispose() { + if (!horizontalScreen) { + AutoOrientation.portraitUpMode(); + } shutdownTimerService.handleWaitingFinished(); if (plPlayerController != null) { plPlayerController!.removeStatusLister(playerListener); @@ -201,6 +213,7 @@ class _VideoDetailPageState extends State videoPlayerServiceHandler.onVideoDetailDispose(); floating.dispose(); _lifecycleListener.dispose(); + exitFullScreen(); super.dispose(); } @@ -228,6 +241,8 @@ class _VideoDetailPageState extends State setState(() => isShowing = true); videoDetailController.isFirstTime = false; final bool autoplay = autoPlayEnable; + videoDetailController + .setTriggerFullScreenCallback(triggerFullScreenCallback); videoDetailController.playerInit(autoplay: autoplay); /// 未开启自动播放时,未播放跳转下一页返回/播放后跳转下一页返回 @@ -276,299 +291,505 @@ class _VideoDetailPageState extends State } } + void triggerFullScreenCallback({bool? status}) { + // SmartDialog.showToast('triggerFullScreen $status $isFullScreen.value'); + setState(() { + isFullScreen.value = status ?? !isFullScreen.value; + }); + } + @override Widget build(BuildContext context) { - final double videoHeight = MediaQuery.sizeOf(context).width * 9 / 16; - final double pinnedHeaderHeight = - statusBarHeight + kToolbarHeight + videoHeight; Widget childWhenDisabled = SafeArea( top: MediaQuery.of(context).orientation == Orientation.portrait && - plPlayerController?.isFullScreen.value == true, + isFullScreen.value == true, bottom: MediaQuery.of(context).orientation == Orientation.portrait && - plPlayerController?.isFullScreen.value == true, - left: false, //plPlayerController?.isFullScreen.value != true, - right: false, //plPlayerController?.isFullScreen.value != true, + isFullScreen.value == true, + left: false, //isFullScreen != true, + right: false, //isFullScreen != true, child: Stack( children: [ Scaffold( - resizeToAvoidBottomInset: false, - key: videoDetailController.scaffoldKey, - backgroundColor: Colors.black, - appBar: PreferredSize( - preferredSize: const Size.fromHeight(0), - child: AppBar( - backgroundColor: Colors.transparent, - elevation: 0, + resizeToAvoidBottomInset: false, + key: videoDetailController.scaffoldKey, + backgroundColor: Colors.black, + appBar: PreferredSize( + preferredSize: const Size.fromHeight(0), + child: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + ), ), - ), - body: ExtendedNestedScrollView( - controller: _extendNestCtr, - headerSliverBuilder: - (BuildContext context2, bool innerBoxIsScrolled) { - return [ + body: Column( + children: [ Obx( () { if (MediaQuery.of(context).orientation == Orientation.landscape || - plPlayerController?.isFullScreen.value == true) { + isFullScreen.value == true) { enterFullScreen(); } else { exitFullScreen(); } - return SliverAppBar( - automaticallyImplyLeading: false, - // 假装使用一个非空变量,避免Obx检测不到而罢工 - pinned: videoDetailController.autoPlay.value ^ - false ^ - videoDetailController.autoPlay.value, - elevation: 0, - scrolledUnderElevation: 0, - forceElevated: innerBoxIsScrolled, - expandedHeight: MediaQuery.of(context).orientation == + final double videoheight = Get.width * 9 / 16; + final double videowidth = Get.width; + return SizedBox( + height: MediaQuery.of(context).orientation == Orientation.landscape || - plPlayerController?.isFullScreen.value == true + isFullScreen.value == true ? MediaQuery.sizeOf(context).height - (MediaQuery.of(context).orientation == Orientation.landscape ? 0 : MediaQuery.of(context).padding.top) - : videoHeight, - backgroundColor: Colors.black, - flexibleSpace: FlexibleSpaceBar( - background: PopScope( - canPop: plPlayerController?.isFullScreen.value != - true, - onPopInvoked: (bool didPop) { - if (plPlayerController?.isFullScreen.value == - true) { - plPlayerController! - .triggerFullScreen(status: false); - } - if (MediaQuery.of(context).orientation == - Orientation.landscape) { - verticalScreen(); - } - }, - child: LayoutBuilder( - builder: (BuildContext context, - BoxConstraints boxConstraints) { - final double maxWidth = - boxConstraints.maxWidth; - final double maxHeight = - boxConstraints.maxHeight; - return Stack( - children: [ - if (isShowing) - FutureBuilder( + : videoheight, + width: MediaQuery.of(context).size.width, + child: PopScope( + canPop: isFullScreen.value != true, + onPopInvoked: (bool didPop) { + if (isFullScreen.value == true) { + plPlayerController! + .triggerFullScreen(status: false); + } + if (MediaQuery.of(context).orientation == + Orientation.landscape && + !horizontalScreen) { + verticalScreen(); + } + }, + child: Stack( + children: [ + if (isShowing) + FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, + AsyncSnapshot snapshot) { + if (snapshot.hasData && + snapshot.data['status']) { + return Obx( + () => !videoDetailController + .autoPlay.value || + plPlayerController == + null || + plPlayerController! + .videoController == + null + ? nil + : PLVideoPlayer( + controller: + plPlayerController!, + headerControl: + videoDetailController + .headerControl, + danmuWidget: Obx( + () => PlDanmaku( + key: Key( + videoDetailController + .danmakuCid + .value + .toString()), + cid: + videoDetailController + .danmakuCid + .value, + playerController: + plPlayerController!, + ), + ), + ), + ); + } else { + return const SizedBox(); + } + }), + + /// 关闭自动播放时 手动播放 + if (!videoDetailController + .autoPlay.value) ...[ + Obx( + () => Visibility( + visible: videoDetailController + .isShowCover.value, + child: Positioned( + top: 0, + left: 0, + right: 0, + child: GestureDetector( + onTap: () { + handlePlay(); + }, + child: NetworkImgLayer( + type: 'emote', + src: videoDetailController + .videoItem['pic'], + width: videowidth, + height: videoheight, + ), + ), + ), + ), + ), + Obx( + () => Visibility( + visible: videoDetailController + .isShowCover.value && + videoDetailController + .isEffective.value, + child: Stack( + children: [ + Positioned( + top: 0, + left: 0, + right: 0, + child: AppBar( + primary: false, + foregroundColor: Colors.white, + elevation: 0, + scrolledUnderElevation: 0, + backgroundColor: + Colors.transparent, + actions: [ + IconButton( + tooltip: '稍后再看', + onPressed: () async { + var res = await UserHttp + .toViewLater( + bvid: + videoDetailController + .bvid); + SmartDialog.showToast( + res['msg']); + }, + icon: const Icon( + Icons.history_outlined), + ), + const SizedBox(width: 14) + ], + ), + ), + Positioned( + right: 12, + bottom: 10, + child: IconButton( + tooltip: '播放', + onPressed: () => handlePlay(), + icon: Image.asset( + 'assets/images/play.png', + width: 60, + height: 60, + )), + ), + ], + )), + ), + ] + ], + )), + ); + }, + ), + SizedBox( + height: MediaQuery.of(context).orientation == + Orientation.landscape || + isFullScreen.value == true + ? 0 + : Get.height - + Get.width * 9 / 16 - + MediaQuery.of(context).padding.top, + width: MediaQuery.of(context).size.width, + child: ColoredBox( + key: Key(heroTag), + color: Theme.of(context).colorScheme.background, + child: Column( + children: [ + Opacity( + opacity: 0, + child: SizedBox( + width: double.infinity, + height: 0, + child: Obx( + () => TabBar( + controller: videoDetailController.tabCtr, + dividerColor: Colors.transparent, + indicatorColor: + Theme.of(context).colorScheme.background, + tabs: videoDetailController.tabs + .map((String name) => Tab(text: name)) + .toList(), + ), + ), + ), + ), + Expanded( + child: TabBarView( + controller: videoDetailController.tabCtr, + children: [ + Builder( + builder: (BuildContext context) { + return CustomScrollView( + key: const PageStorageKey('简介'), + slivers: [ + if (videoDetailController.videoType == + SearchType.video) ...[ + const VideoIntroPanel(), + ] else if (videoDetailController + .videoType == + SearchType.media_bangumi) ...[ + Obx(() => BangumiIntroPanel( + cid: videoDetailController + .cid.value)), + ], + SliverToBoxAdapter( + child: Divider( + indent: 12, + endIndent: 12, + color: Theme.of(context) + .dividerColor + .withOpacity(0.06), + ), + ), + RelatedVideoPanel(), + ], + ); + }, + ), + Obx( + () => VideoReplyPanel( + bvid: videoDetailController.bvid, + oid: videoDetailController.oid.value, + ), + ) + ], + ), + ), + ], + ), + ), + ), + ], + )), + ], + ), + ); + + Widget childWhenDisabledLandscape = SafeArea( + left: isFullScreen.value != true, + right: isFullScreen.value != true, + child: Stack(children: [ + Scaffold( + resizeToAvoidBottomInset: false, + key: videoDetailController.scaffoldKey, + backgroundColor: Theme.of(context).colorScheme.background, + appBar: PreferredSize( + preferredSize: const Size.fromHeight(0), + child: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + ), + ), + body: Obx(() { + // 系数是以下三个方程(分别代表特定平板、折叠屏内屏、普通手机横屏尺寸)的近似解 + // 820x+1180y+983.67z=450 + // 1812x+2176y+1985.68z=680 + // 1080x+2340y+1589.72z=540 + final double videoheight = + sqrt(Get.height * Get.width) * 12.972 - + Get.height * 7.928 - + Get.width * 4.923; + final double videowidth = videoheight * 16 / 9; + return Row( + children: [ + Column( + children: [ + SizedBox( + width: isFullScreen.value == true + ? Get.width + : videowidth, + height: isFullScreen.value == true + ? Get.height + : videoheight, + child: PopScope( + canPop: isFullScreen.value != true, + onPopInvoked: (bool didPop) { + if (isFullScreen.value == true) { + plPlayerController! + .triggerFullScreen(status: false); + } + if (MediaQuery.of(context).orientation == + Orientation.landscape && + !horizontalScreen) { + verticalScreen(); + } + }, + child: Stack( + children: [ + if (isShowing) + FutureBuilder( future: _futureBuilderFuture, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData && snapshot.data['status']) { return Obx( - () => - !videoDetailController - .autoPlay.value - ? nil - : PLVideoPlayer( - controller: - plPlayerController!, - headerControl: + () => !videoDetailController + .autoPlay.value || + plPlayerController == + null || + plPlayerController! + .videoController == + null + ? nil + : PLVideoPlayer( + controller: + plPlayerController!, + headerControl: + videoDetailController + .headerControl, + danmuWidget: Obx( + () => PlDanmaku( + key: Key( videoDetailController - .headerControl, - danmuWidget: Obx( - () => PlDanmaku( - key: Key(videoDetailController .danmakuCid .value .toString()), - cid: videoDetailController + cid: + videoDetailController .danmakuCid .value, - playerController: - plPlayerController!, - ), - ), + playerController: + plPlayerController!, ), + ), + ), ); } else { return const SizedBox(); } - }, - ), + }), - /// 关闭自动播放时 手动播放 - if (!videoDetailController - .autoPlay.value) ...[ - Obx( - () => Visibility( - visible: videoDetailController - .isShowCover.value, - child: Positioned( - top: 0, - left: 0, - right: 0, - child: GestureDetector( - onTap: () { - handlePlay(); - }, - child: NetworkImgLayer( - type: 'emote', - src: videoDetailController - .videoItem['pic'], - width: maxWidth, - height: maxHeight, - ), + /// 关闭自动播放时 手动播放 + if (!videoDetailController + .autoPlay.value) ...[ + Obx( + () => Visibility( + visible: videoDetailController + .isShowCover.value, + child: Positioned( + top: 0, + left: 0, + right: 0, + child: GestureDetector( + onTap: () { + handlePlay(); + }, + child: NetworkImgLayer( + type: 'emote', + src: videoDetailController + .videoItem['pic'], + width: videowidth, + height: videoheight, ), ), ), ), - Obx( - () => Visibility( - visible: videoDetailController - .isShowCover.value && - videoDetailController - .isEffective.value, - child: Stack( - children: [ - Positioned( - top: 0, - left: 0, - right: 0, - child: AppBar( - primary: false, - foregroundColor: - Colors.white, - elevation: 0, - scrolledUnderElevation: 0, - backgroundColor: - Colors.transparent, - actions: [ - IconButton( - tooltip: '稍后再看', - onPressed: () async { - var res = await UserHttp - .toViewLater( - bvid: videoDetailController - .bvid); - SmartDialog - .showToast( - res['msg']); - }, - icon: const Icon(Icons - .history_outlined), - ), - const SizedBox( - width: 14) - ], - ), - ), - Positioned( - right: 12, - bottom: 10, - child: IconButton( - tooltip: '播放', - onPressed: () => - handlePlay(), - icon: Image.asset( - 'assets/images/play.png', - width: 60, - height: 60, - )), + ), + Obx( + () => Visibility( + visible: videoDetailController + .isShowCover.value && + videoDetailController + .isEffective.value, + child: Stack( + children: [ + Positioned( + top: 0, + left: 0, + right: 0, + child: AppBar( + primary: false, + foregroundColor: + Colors.white, + elevation: 0, + scrolledUnderElevation: 0, + backgroundColor: + Colors.transparent, + actions: [ + IconButton( + tooltip: '稍后再看', + onPressed: () async { + var res = await UserHttp + .toViewLater( + bvid: + videoDetailController + .bvid); + SmartDialog.showToast( + res['msg']); + }, + icon: const Icon(Icons + .history_outlined), + ), + const SizedBox(width: 14) + ], ), - ], - )), - ), - ] - ], - ); - }, - )), - ), - ); - }, - ), - ]; - }, - // pinnedHeaderSliverHeightBuilder: () { - // return playerStatus != PlayerStatus.playing - // ? statusBarHeight + kToolbarHeight - // : pinnedHeaderHeight; - // }, - /// 不收回 - pinnedHeaderSliverHeightBuilder: () { - return MediaQuery.of(context).orientation == - Orientation.landscape || - plPlayerController?.isFullScreen.value == true - ? MediaQuery.sizeOf(context).height - : pinnedHeaderHeight; - }, - onlyOneScrollInBody: true, - body: ColoredBox( - key: Key(heroTag), - color: Theme.of(context).colorScheme.background, - child: Column( - children: [ - Opacity( - opacity: 0, - child: SizedBox( - width: double.infinity, - height: 0, - child: Obx( - () => TabBar( - controller: videoDetailController.tabCtr, - dividerColor: Colors.transparent, - indicatorColor: - Theme.of(context).colorScheme.background, - tabs: videoDetailController.tabs - .map((String name) => Tab(text: name)) - .toList(), - ), - ), - ), + ), + Positioned( + right: 12, + bottom: 10, + child: IconButton( + tooltip: '播放', + onPressed: () => + handlePlay(), + icon: Image.asset( + 'assets/images/play.png', + width: 60, + height: 60, + )), + ), + ], + )), + ), + ] + ], + ))), + SizedBox( + width: isFullScreen.value == true + ? Get.width + : videowidth, + height: isFullScreen.value == true + ? 0 + : Get.height - + videoheight - + MediaQuery.of(context).padding.top - + MediaQuery.of(context).padding.bottom, + child: (videoDetailController.videoType == + SearchType.video) + ? const CustomScrollView( + slivers: [VideoIntroPanel()]) + : (videoDetailController.videoType == + SearchType.media_bangumi) + ? Obx(() => BangumiIntroPanel( + cid: videoDetailController.cid.value)) + : const SizedBox(), + ) + ], ), - Expanded( + SizedBox( + width: isFullScreen.value == true + ? 0 + : (Get.width - + MediaQuery.of(context).padding.left - + MediaQuery.of(context).padding.right - + videowidth), + height: Get.height - + MediaQuery.of(context).padding.top - + MediaQuery.of(context).padding.bottom, child: TabBarView( controller: videoDetailController.tabCtr, children: [ - Builder( - builder: (BuildContext context) { - return CustomScrollView( - key: const PageStorageKey('简介'), - slivers: [ - if (videoDetailController.videoType == - SearchType.video) ...[ - const VideoIntroPanel(), - ] else if (videoDetailController.videoType == - SearchType.media_bangumi) ...[ - Obx(() => BangumiIntroPanel( - cid: videoDetailController.cid.value)), - ], - // if (videoDetailController.videoType == - // SearchType.video) ...[ - // SliverPersistentHeader( - // floating: true, - // pinned: true, - // delegate: SliverHeaderDelegate( - // height: 50, - // child: - // const MenuRow(loadingStatus: false), - // ), - // ), - // ], - SliverToBoxAdapter( - child: Divider( - indent: 12, - endIndent: 12, - color: Theme.of(context) - .dividerColor - .withOpacity(0.06), - ), - ), - RelatedVideoPanel(), - ], - ); - }, + CustomScrollView( + slivers: [ + RelatedVideoPanel(), + ], ), Obx( () => VideoReplyPanel( @@ -578,30 +799,11 @@ class _VideoDetailPageState extends State ) ], ), - ), + ) ], - ), - ), - ), - ), - - /// 重新进入会刷新 - // 播放完成/暂停播放 - // StreamBuilder( - // stream: appbarStream.stream, - // initialData: 0, - // builder: ((context, snapshot) { - // return ScrollAppBar( - // snapshot.data!.toDouble(), - // () => continuePlay(), - // playerStatus, - // null, - // ); - // }), - // ) - ], - ), - ); + ); + })) + ])); Widget childWhenEnabled = FutureBuilder( key: Key(heroTag), future: _futureBuilderFuture, @@ -616,14 +818,14 @@ class _VideoDetailPageState extends State controller: plPlayerController, videoDetailCtr: videoDetailController, ), - danmuWidget: Obx( - () => PlDanmaku( - key: Key( - videoDetailController.danmakuCid.value.toString()), - cid: videoDetailController.danmakuCid.value, - playerController: plPlayerController!, - ), - ), + // danmuWidget: Obx( + // () => PlDanmaku( + // key: Key( + // videoDetailController.danmakuCid.value.toString()), + // cid: videoDetailController.danmakuCid.value, + // playerController: plPlayerController!, + // ), + // ), ), ); } else { @@ -631,14 +833,29 @@ class _VideoDetailPageState extends State } }, ); - if (Platform.isAndroid) { - return PiPSwitcher( - childWhenDisabled: childWhenDisabled, - childWhenEnabled: childWhenEnabled, - floating: floating, + return OrientationBuilder( + builder: (BuildContext context, Orientation orientation) { + if (orientation == Orientation.landscape) { + enterFullScreen(); + } + if (Platform.isAndroid) { + return PiPSwitcher( + childWhenDisabled: Container( + key: UniqueKey(), + child: !horizontalScreen || orientation == Orientation.portrait + ? childWhenDisabled + : childWhenDisabledLandscape, + ), + childWhenEnabled: childWhenEnabled, + floating: floating, + ); + } + return Container( + key: UniqueKey(), + child: !horizontalScreen || orientation == Orientation.portrait + ? childWhenDisabled + : childWhenDisabledLandscape, ); - } else { - return childWhenDisabled; - } + }); } } diff --git a/lib/pages/video/detail/widgets/ai_detail.dart b/lib/pages/video/detail/widgets/ai_detail.dart index fb280d91b..9fca75d6b 100644 --- a/lib/pages/video/detail/widgets/ai_detail.dart +++ b/lib/pages/video/detail/widgets/ai_detail.dart @@ -2,14 +2,10 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:hive/hive.dart'; import 'package:pilipala/models/video/ai.dart'; import 'package:pilipala/pages/video/detail/index.dart'; -import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; -Box localCache = GStrorage.localCache; -late double sheetHeight; class AiDetail extends StatelessWidget { final ModelResult? modelResult; @@ -21,11 +17,10 @@ class AiDetail extends StatelessWidget { @override Widget build(BuildContext context) { - sheetHeight = localCache.get('sheetHeight'); return Container( color: Theme.of(context).colorScheme.background, padding: const EdgeInsets.only(left: 14, right: 14), - height: sheetHeight, + height: Utils.getSheetHeight(context), child: Column( children: [ InkWell( diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 8bc7d84ec..aa30ec8fb 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -342,8 +342,7 @@ class _HeaderControlState extends State { }, dense: true, contentPadding: const EdgeInsets.only(), - title: - const Text("额外等待视频播放完毕", style: titleStyle), + title: const Text("额外等待视频播放完毕", style: titleStyle), trailing: Switch( // thumb color (round icon) activeColor: Theme.of(context).colorScheme.primary, @@ -891,7 +890,7 @@ class _HeaderControlState extends State { final DanmakuOption currentOption = danmakuController.option; final DanmakuOption updatedOption = - currentOption.copyWith(strokeWidth: val); + currentOption.copyWith(strokeWidth: val); danmakuController.updateOption(updatedOption); } catch (_) {} }, @@ -938,7 +937,7 @@ class _HeaderControlState extends State { ), ), ), - Text('弹幕时长 ${danmakuDurationVal.toString()} 秒'), + Text('弹幕时长 $danmakuDurationVal 秒'), Padding( padding: const EdgeInsets.only( top: 0, @@ -956,21 +955,21 @@ class _HeaderControlState extends State { enabledThumbRadius: 6.0), ), child: Slider( - min: 2, - max: 16, - value: danmakuDurationVal, - divisions: 28, + min: 1, + max: 6, + value: sqrt(danmakuDurationVal), + divisions: 50, label: danmakuDurationVal.toString(), onChanged: (double val) { - danmakuDurationVal = val; + danmakuDurationVal = (val * val).toPrecision(2); widget.controller!.danmakuDurationVal = danmakuDurationVal; setState(() {}); try { final DanmakuOption updatedOption = danmakuController.option.copyWith( - duration: - val / widget.controller!.playbackSpeed); + duration: danmakuDurationVal / + widget.controller!.playbackSpeed); danmakuController.updateOption(updatedOption); } catch (_) {} }, @@ -1004,7 +1003,7 @@ class _HeaderControlState extends State { margin: const EdgeInsets.all(12), child: Column( children: [ - SizedBox( + const SizedBox( height: 45, child: Center(child: Text('选择播放顺序', style: titleStyle))), Expanded( @@ -1070,7 +1069,9 @@ class _HeaderControlState extends State { else { if (MediaQuery.of(context).orientation == - Orientation.landscape) + Orientation.landscape && + !setting.get(SettingBoxKey.horizontalScreen, + defaultValue: false)) { SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, diff --git a/lib/pages/webview/controller.dart b/lib/pages/webview/controller.dart index 37600ea76..c01f7352d 100644 --- a/lib/pages/webview/controller.dart +++ b/lib/pages/webview/controller.dart @@ -103,7 +103,9 @@ class WebviewController extends GetxController { await SetCookie.onSet(); final result = await UserHttp.userInfo(); if (result['status'] && result['data'].isLogin) { - SmartDialog.showToast('登录成功'); + SmartDialog.showToast('登录成功,当前采用「' + '${GStrorage.setting.get(SettingBoxKey.defaultRcmdType, defaultValue: 'web')}' + '端」推荐'); try { Box userInfoCache = GStrorage.userInfo; await userInfoCache.put('userInfoCache', result['data']); diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index c82cab5b7..d04641223 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -1,12 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/msg.dart'; import 'package:pilipala/models/msg/account.dart'; import 'package:pilipala/models/msg/session.dart'; +import '../../models/msg/msgfeed_unread.dart'; + class WhisperController extends GetxController { RxList sessionList = [].obs; RxList accountList = [].obs; bool isLoading = false; + Rx msgFeedUnread = MsgFeedUnread().obs; + RxList msgFeedTop = [ + { + "name":"回复我的", + "icon":Icons.message_outlined, + "route": "/replyMe", + "value": 0 + }, + { + "name":"@我", + "icon":Icons.alternate_email_outlined, + "route": "/atMe", + "value": 0 + }, + { + "name":"收到的赞", + "icon":Icons.favorite_border_outlined, + "route": "/likeMe", + "value": 0 + }, + { + "name":"系统通知", + "icon":Icons.notifications_none_outlined, + "route": "/sysMsg", + "value": 0 + }, + ].obs; + + Future queryMsgFeedUnread() async { + var res = await MsgHttp.msgFeedUnread(); + if (res['status']) { + msgFeedUnread.value = MsgFeedUnread.fromJson(res['data']); + msgFeedTop[0]["value"] = msgFeedUnread.value.reply; + msgFeedTop[1]["value"] = msgFeedUnread.value.at; + msgFeedTop[2]["value"] = msgFeedUnread.value.like; + msgFeedTop[3]["value"] = msgFeedUnread.value.sys_msg; + // 触发更新 + msgFeedTop.refresh(); + } else { + SmartDialog.showToast(res['msg']); + } + } Future querySessionList(String? type) async { if (isLoading) return; diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index f2779a177..d20e4c889 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -22,6 +22,7 @@ class _WhisperPageState extends State { @override void initState() { super.initState(); + _whisperController.queryMsgFeedUnread(); _futureBuilderFuture = _whisperController.querySessionList('init'); _scrollController.addListener(_scrollListener); } @@ -43,187 +44,178 @@ class _WhisperPageState extends State { appBar: AppBar( title: const Text('消息'), ), - body: Column( - children: [ - // LayoutBuilder( - // builder: (BuildContext context, BoxConstraints constraints) { - // // 在这里根据父级容器的约束条件构建小部件树 - // return Padding( - // padding: const EdgeInsets.only(left: 20, right: 20), - // child: SizedBox( - // height: constraints.maxWidth / 5, - // child: GridView.count( - // primary: false, - // crossAxisCount: 4, - // padding: const EdgeInsets.all(0), - // childAspectRatio: 1.25, - // children: [ - // Column( - // crossAxisAlignment: CrossAxisAlignment.center, - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // SizedBox( - // width: 36, - // height: 36, - // child: IconButton( - // style: ButtonStyle( - // padding: - // MaterialStateProperty.all(EdgeInsets.zero), - // backgroundColor: - // MaterialStateProperty.resolveWith((states) { - // return Theme.of(context) - // .colorScheme - // .primary - // .withOpacity(0.1); - // }), - // ), - // onPressed: () {}, - // icon: Icon( - // Icons.message_outlined, - // size: 18, - // color: Theme.of(context).colorScheme.primary, - // ), - // ), - // ), - // const SizedBox(height: 6), - // const Text('回复我的', style: TextStyle(fontSize: 13)) - // ], - // ), - // ], - // ), - // ), - // ); - // }, - // ), - Expanded( - child: RefreshIndicator( - onRefresh: () async { - await _whisperController.onRefresh(); - }, - child: SingleChildScrollView( - controller: _scrollController, - child: Column( - children: [ - FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data as Map; - if (data['status']) { - List sessionList = _whisperController.sessionList; - return Obx( - () => sessionList.isEmpty - ? const SizedBox() - : ListView.separated( - itemCount: sessionList.length, - shrinkWrap: true, - physics: - const NeverScrollableScrollPhysics(), - itemBuilder: (_, int i) { - return ListTile( - onTap: () => Get.toNamed( - '/whisperDetail', - parameters: { - 'talkerId': sessionList[i] - .talkerId - .toString(), - 'name': sessionList[i] - .accountInfo - .name, - 'face': sessionList[i] - .accountInfo - .face, - 'mid': sessionList[i] - .accountInfo - .mid - .toString(), - }, - ), - leading: Badge( - isLabelVisible: false, - backgroundColor: Theme.of(context) - .colorScheme - .primary, - label: Text(sessionList[i] - .unreadCount - .toString()), - alignment: Alignment.bottomRight, - child: NetworkImgLayer( - width: 45, - height: 45, - type: 'avatar', - src: sessionList[i] - .accountInfo - .face, - ), - ), - title: Text( - sessionList[i].accountInfo.name), - subtitle: Text( - sessionList[i] - .lastMsg - .content['text'] ?? - sessionList[i] - .lastMsg - .content['content'] ?? - sessionList[i] - .lastMsg - .content['title'] ?? - sessionList[i] - .lastMsg - .content[ - 'reply_content'] ?? - '', - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: Theme.of(context) - .textTheme - .labelMedium! - .copyWith( - color: Theme.of(context) - .colorScheme - .outline)), - trailing: Text( - Utils.dateFormat(sessionList[i] - .lastMsg - .timestamp), - style: Theme.of(context) - .textTheme - .labelSmall! - .copyWith( - color: Theme.of(context) - .colorScheme - .outline), - ), - ); - }, - separatorBuilder: - (BuildContext context, int index) { - return Divider( - indent: 72, - endIndent: 20, - height: 6, - color: Colors.grey.withOpacity(0.1), - ); + body: RefreshIndicator( + onRefresh: () async { + await _whisperController.onRefresh(); + }, + child: SingleChildScrollView( + controller: _scrollController, + child: Column( + children: [ + LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + // 在这里根据父级容器的约束条件构建小部件树 + return Padding( + padding: const EdgeInsets.only(left: 20, right: 20), + child: SizedBox( + height: constraints.maxWidth / 4, + child: Obx( + () => GridView.count( + primary: false, + crossAxisCount: 4, + padding: const EdgeInsets.all(0), + childAspectRatio: 1.25, + children: _whisperController.msgFeedTop.map((item) { + return GestureDetector( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Badge( + isLabelVisible: item['value'] > 0, + backgroundColor: + Theme.of(context).colorScheme.primary, + textColor: Theme.of(context) + .colorScheme + .onInverseSurface, + label: Text(" ${item['value']} "), + alignment: Alignment.topRight, + child: CircleAvatar( + radius: 22, + backgroundColor: Theme.of(context) + .colorScheme + .onInverseSurface, + child: Icon( + item['icon'], + size: 20, + color: + Theme.of(context).colorScheme.primary, + ), + ), + ), + const SizedBox(height: 6), + Text(item['name'], + style: const TextStyle(fontSize: 13)) + ], + ), + onTap: () => Get.toNamed(item['route']), + ); + }).toList(), + ), + ), + ), + ); + }), + FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done && + snapshot.data != null) { + Map data = snapshot.data as Map; + if (data['status']) { + List sessionList = _whisperController.sessionList; + return Obx( + () => sessionList.isEmpty + ? const SizedBox() + : ListView.separated( + itemCount: sessionList.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (_, int i) { + return ListTile( + onTap: () => Get.toNamed( + '/whisperDetail', + parameters: { + 'talkerId': + sessionList[i].talkerId.toString(), + 'name': sessionList[i].accountInfo.name, + 'face': sessionList[i].accountInfo.face, + 'mid': sessionList[i] + .accountInfo + .mid + .toString(), }, ), - ); - } else { - // 请求错误 - return const SizedBox(); - } - } else { - // 骨架屏 - return const SizedBox(); - } - }, - ) - ], - ), - ), - ), + leading: Badge( + isLabelVisible: + sessionList[i].unreadCount > 0, + backgroundColor: + Theme.of(context).colorScheme.primary, + textColor: Theme.of(context) + .colorScheme + .onInverseSurface, + label: Text( + " ${sessionList[i].unreadCount.toString()} "), + alignment: Alignment.topRight, + child: NetworkImgLayer( + width: 45, + height: 45, + type: 'avatar', + src: sessionList[i].accountInfo.face, + ), + ), + title: + Text(sessionList[i].accountInfo.name), + subtitle: Text( + sessionList[i] + .lastMsg + .content['text'] ?? + sessionList[i] + .lastMsg + .content['content'] ?? + sessionList[i] + .lastMsg + .content['title'] ?? + sessionList[i] + .lastMsg + .content['reply_content'] ?? + '', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context) + .textTheme + .labelMedium! + .copyWith( + color: Theme.of(context) + .colorScheme + .outline)), + trailing: Text( + Utils.dateFormat( + sessionList[i].lastMsg.timestamp), + style: Theme.of(context) + .textTheme + .labelSmall! + .copyWith( + color: Theme.of(context) + .colorScheme + .outline), + ), + ); + }, + separatorBuilder: + (BuildContext context, int index) { + return Divider( + indent: 72, + endIndent: 20, + height: 6, + color: Colors.grey.withOpacity(0.1), + ); + }, + ), + ); + } else { + // 请求错误 + return const SizedBox(); + } + } else { + // 骨架屏 + return const SizedBox(); + } + }, + ) + ], ), - ], + ), ), ); } diff --git a/lib/pages/whisper_detail/controller.dart b/lib/pages/whisper_detail/controller.dart index 71dd4c031..aa45a882b 100644 --- a/lib/pages/whisper_detail/controller.dart +++ b/lib/pages/whisper_detail/controller.dart @@ -31,4 +31,19 @@ class WhisperDetailController extends GetxController { } return res; } + + Future ackSessionMsg() async { + if (messageList.isEmpty){ + return; + } + var res = await MsgHttp.ackSessionMsg( + talkerId: talkerId, + ackSeqno: messageList.last.msgSeqno, + ); + if (res['status']) { + SmartDialog.showToast("已读成功"); + } else { + SmartDialog.showToast(res['msg']); + } + } } diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 8d2297c47..0ca8a85c2 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -99,6 +99,7 @@ class _WhisperDetailPageState extends State { final Map data = snapshot.data as Map; if (data['status']) { List messageList = _whisperDetailController.messageList; + _whisperDetailController.ackSessionMsg(); return Obx( () => messageList.isEmpty ? const SizedBox() diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 0925d5694..0727a2c88 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -133,6 +133,7 @@ class ChatItem extends StatelessWidget { jsonDecode(content['content']) .map((m) => m['text'] as String) .join("\n"), + textAlign: TextAlign.center, style: TextStyle( letterSpacing: 0.6, height: 5, @@ -199,17 +200,22 @@ class ChatItem extends StatelessWidget { children: [ GestureDetector( onTap: () async { - SmartDialog.showLoading(); - var bvid = content["bvid"]; - final int cid = await SearchHttp.ab2c(bvid: bvid); - final String heroTag = Utils.makeHeroTag(bvid); - SmartDialog.dismiss().then( - (e) => Get.toNamed('/video?bvid=$bvid&cid=$cid', - arguments: { - 'pic': content['thumb'], - 'heroTag': heroTag, - }), - ); + try { + SmartDialog.showLoading(); + var bvid = content["bvid"]; + final int cid = await SearchHttp.ab2c(bvid: bvid); + final String heroTag = Utils.makeHeroTag(bvid); + SmartDialog.dismiss().then( + (e) => Get.toNamed('/video?bvid=$bvid&cid=$cid', + arguments: { + 'pic': content['thumb'], + 'heroTag': heroTag, + }), + ); + } catch (err) { + SmartDialog.dismiss(); + SmartDialog.showToast(err.toString()); + } }, child: NetworkImgLayer( width: 220, @@ -335,7 +341,7 @@ class ChatItem extends StatelessWidget { ), ), Text( - Utils.timeFormat(int.parse(i['field3'])), + i['field3'], style: TextStyle( letterSpacing: 0.6, height: 1.5, diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index dfa580ab6..ebf2230c8 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -13,6 +13,7 @@ import 'package:media_kit/media_kit.dart'; import 'package:media_kit_video/media_kit_video.dart'; import 'package:ns_danmaku/ns_danmaku.dart'; import 'package:pilipala/http/video.dart'; +import 'package:pilipala/pages/mine/controller.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/plugin/pl_player/models/play_repeat.dart'; import 'package:pilipala/services/service_locator.dart'; @@ -30,6 +31,7 @@ Box localCache = GStrorage.localCache; class PlPlayerController { Player? _videoPlayerController; VideoController? _videoController; + void Function({bool? status})? triggerFullscreenCallback; // 添加一个私有静态变量来保存实例 static PlPlayerController? _instance; @@ -231,6 +233,11 @@ class PlPlayerController { // 播放顺序相关 PlayRepeat playRepeat = PlayRepeat.pause; + void setTriggerFullscreenCallback( + void Function({bool? status}) triggerFullscreenCallback) { + this.triggerFullscreenCallback = triggerFullscreenCallback; + } + void updateSliderPositionSecond() { int newSecond = _sliderPosition.value.inSeconds; if (sliderPositionSeconds.value != newSecond) { @@ -951,7 +958,10 @@ class PlPlayerController { /// 进入全屏 await enterFullScreen(); if (mode == FullScreenMode.vertical || - (mode == FullScreenMode.auto && direction.value == 'vertical')) { + (mode == FullScreenMode.auto && direction.value == 'vertical') || + (mode == FullScreenMode.ratio && + (Get.height / Get.width < 1.25 || + direction.value == 'vertical'))) { await verticalScreen(); } else { await landScape(); @@ -992,9 +1002,14 @@ class PlPlayerController { StatusBarControl.setHidden(false, animation: StatusBarAnimation.FADE); // Get.back(); exitFullScreen(); - await verticalScreen(); + if (!setting.get(SettingBoxKey.horizontalScreen, defaultValue: false)) { + await verticalScreen(); + } toggleFullScreen(false); } + if (triggerFullscreenCallback != null) { + triggerFullscreenCallback!(status: status); + } } void addPositionListener(Function(Duration position) listener) => @@ -1024,7 +1039,7 @@ class PlPlayerController { // 记录播放记录 Future makeHeartBeat(int progress, {type = 'playing'}) async { - if (!_enableHeart) { + if (!_enableHeart || MineController.anonymity) { return false; } if (videoType.value == 'live') { diff --git a/lib/plugin/pl_player/models/fullscreen_mode.dart b/lib/plugin/pl_player/models/fullscreen_mode.dart index 9b5028e7a..de5503bbe 100644 --- a/lib/plugin/pl_player/models/fullscreen_mode.dart +++ b/lib/plugin/pl_player/models/fullscreen_mode.dart @@ -5,15 +5,17 @@ enum FullScreenMode { // 始终竖屏 vertical, // 始终横屏 - horizontal + horizontal, + // 屏幕长宽比<1.25或为竖屏视频时竖屏,否则横屏 + ratio, } extension FullScreenModeDesc on FullScreenMode { - String get description => ['自适应', '始终竖屏', '始终横屏'][index]; + String get description => ['按视频方向(默认)', '强制竖屏', '强制横屏', '屏幕长宽比<1.25或为竖屏视频时竖屏,否则横屏'][index]; } extension FullScreenModeCode on FullScreenMode { - static final List _codeList = [0, 1, 2]; + static final List _codeList = [0, 1, 2, 3]; int get code => _codeList[index]; static FullScreenMode? fromCode(int code) { diff --git a/lib/plugin/pl_player/utils/fullscreen.dart b/lib/plugin/pl_player/utils/fullscreen.dart index 06ae2d60c..c5036282d 100644 --- a/lib/plugin/pl_player/utils/fullscreen.dart +++ b/lib/plugin/pl_player/utils/fullscreen.dart @@ -42,6 +42,16 @@ Future verticalScreen() async { ]); } +//全向 +Future autoScreen() async { + await SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + DeviceOrientation.portraitDown, + DeviceOrientation.landscapeLeft, + DeviceOrientation.landscapeRight, + ]); +} + Future enterFullScreen() async { await SystemChrome.setEnabledSystemUIMode( SystemUiMode.immersiveSticky, diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index d073945b7..79aae4cc2 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -48,6 +48,7 @@ class _PLVideoPlayerState extends State late VideoController videoController; final PLVideoPlayerController _ctr = Get.put(PLVideoPlayerController()); + final GlobalKey _playerKey = GlobalKey(); // bool _mountSeekBackwardButton = false; // bool _mountSeekForwardButton = false; // bool _hideSeekBackwardButton = false; @@ -71,11 +72,13 @@ class _PLVideoPlayerState extends State late FullScreenMode mode; late int defaultBtmProgressBehavior; late bool enableQuickDouble; + late bool fullScreenGestureReverse; late bool enableBackgroundPlay; - late double screenWidth; // 用于记录上一次全屏切换手势触发时间,避免误触 DateTime? lastFullScreenToggleTime; + // 记录上一次音量调整值作平均,避免音量调整抖动 + double lastVolume = -1.0; void onDoubleTapSeekBackward() { _ctr.onDoubleTapSeekBackward(); @@ -114,7 +117,6 @@ class _PLVideoPlayerState extends State @override void initState() { super.initState(); - screenWidth = Get.size.width; animationController = AnimationController( vsync: this, duration: const Duration(milliseconds: 300)); videoController = widget.controller.videoController!; @@ -125,6 +127,8 @@ class _PLVideoPlayerState extends State defaultValue: BtmProgresBehavior.values.first.code); enableQuickDouble = setting.get(SettingBoxKey.enableQuickDouble, defaultValue: true); + fullScreenGestureReverse = setting.get(SettingBoxKey.fullScreenGestureReverse, + defaultValue: false); enableBackgroundPlay = setting.get(SettingBoxKey.enableBackgroundPlay, defaultValue: false); Future.microtask(() async { @@ -208,6 +212,7 @@ class _PLVideoPlayerState extends State ); return Stack( fit: StackFit.passthrough, + key: _playerKey, children: [ Obx( () => Video( @@ -443,7 +448,8 @@ class _PLVideoPlayerState extends State if (_.videoType.value == 'live' || _.controlsLock.value) { return; } - final double totalWidth = MediaQuery.sizeOf(context).width; + RenderBox renderBox = _playerKey.currentContext!.findRenderObject() as RenderBox; + final double totalWidth = renderBox.size.width; final double tapPosition = details.localPosition.dx; final double sectionWidth = totalWidth / 3; String type = 'left'; @@ -473,7 +479,8 @@ class _PLVideoPlayerState extends State // final double tapPosition = details.localPosition.dx; final int curSliderPosition = _.sliderPosition.value.inMilliseconds; - final double scale = 90000 / MediaQuery.sizeOf(context).width; + RenderBox renderBox = _playerKey.currentContext!.findRenderObject() as RenderBox; + final double scale = 90000 / renderBox.size.width; final Duration pos = Duration( milliseconds: curSliderPosition + (details.delta.dx * scale).round()); @@ -492,7 +499,8 @@ class _PLVideoPlayerState extends State }, // 垂直方向 音量/亮度调节 onVerticalDragUpdate: (DragUpdateDetails details) async { - final double totalWidth = MediaQuery.sizeOf(context).width; + RenderBox renderBox = _playerKey.currentContext!.findRenderObject() as RenderBox; + final double totalWidth = renderBox.size.width; final double tapPosition = details.localPosition.dx; final double sectionWidth = totalWidth / 3; final double delta = details.delta.dy; @@ -508,10 +516,7 @@ class _PLVideoPlayerState extends State } if (tapPosition < sectionWidth) { // 左边区域 👈 - final double level = (_.isFullScreen.value - ? Get.size.height - : screenWidth * 9 / 16) * - 3; + final double level = renderBox.size.height * 3; final double brightness = _ctr.brightnessValue.value - delta / level; final double result = brightness.clamp(0.0, 1.0); @@ -520,30 +525,33 @@ class _PLVideoPlayerState extends State // 全屏 final double dy = details.delta.dy; const double threshold = 7.0; // 滑动阈值 + void fullScreenTrigger(bool status) async { + lastFullScreenToggleTime = DateTime.now(); + await widget.controller.triggerFullScreen(status: status); + } if (dy > _distance && dy > threshold) { - if (_.isFullScreen.value) { - lastFullScreenToggleTime = DateTime.now(); - // 下滑退出全屏 - await widget.controller.triggerFullScreen(status: false); + // 下滑退出全屏/进入全屏 + if (_.isFullScreen.value ^ fullScreenGestureReverse) { + fullScreenTrigger(fullScreenGestureReverse); } _distance = 0.0; } else if (dy < _distance && dy < -threshold) { - if (!_.isFullScreen.value) { - lastFullScreenToggleTime = DateTime.now(); - // 上滑进入全屏 - await widget.controller.triggerFullScreen(); + // 上划进入全屏/退出全屏 + if (!_.isFullScreen.value ^ fullScreenGestureReverse) { + fullScreenTrigger(!fullScreenGestureReverse); } _distance = 0.0; } _distance = dy; } else { // 右边区域 👈 - final double level = (_.isFullScreen.value - ? Get.size.height - : screenWidth * 9 / 16) * - 3; - final double volume = _ctr.volumeValue.value - delta / level; + final double level = renderBox.size.height * 0.5; + if(lastVolume < 0) { + lastVolume = _ctr.volumeValue.value; + } + final double volume = (lastVolume + _ctr.volumeValue.value - delta / level)/2; final double result = volume.clamp(0.0, 1.0); + lastVolume = result; setVolume(result); } }, @@ -586,7 +594,6 @@ class _PLVideoPlayerState extends State ), /// 进度条 live模式下禁用 - Obx( () { final int value = _.sliderPositionSeconds.value; @@ -610,7 +617,7 @@ class _PLVideoPlayerState extends State } if (_.videoType.value == 'live') { - return const SizedBox(); + return nil; } if (value > max || max <= 0) { return nil; @@ -756,11 +763,12 @@ class _PLVideoPlayerState extends State ) : nil, ), - Expanded( - child: SizedBox( - width: MediaQuery.sizeOf(context).width / 4, - ), - ), + const Spacer(), + // Expanded( + // child: SizedBox( + // width: context.width / 4, + // ), + // ), Expanded( child: _ctr.mountSeekForwardButton.value ? TweenAnimationBuilder( diff --git a/lib/plugin/pl_player/widgets/bottom_control.dart b/lib/plugin/pl_player/widgets/bottom_control.dart index 8f21fc175..7105c82f5 100644 --- a/lib/plugin/pl_player/widgets/bottom_control.dart +++ b/lib/plugin/pl_player/widgets/bottom_control.dart @@ -146,7 +146,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget { size: 15, color: Colors.white, ), - fuc: () => triggerFullScreen!(), + fuc: () => triggerFullScreen!(status: !_.isFullScreen.value), ), ), ], diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index bd658b0b6..56edeff51 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -3,6 +3,9 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; +import 'package:pilipala/pages/msg_feed_top/at_me/view.dart'; +import 'package:pilipala/pages/msg_feed_top/reply_me/view.dart'; +import 'package:pilipala/pages/msg_feed_top/like_me/view.dart'; import 'package:pilipala/pages/follow_search/view.dart'; import 'package:pilipala/pages/setting/pages/logs.dart'; @@ -144,6 +147,14 @@ class Routes { // 私信详情 CustomGetPage( name: '/whisperDetail', page: () => const WhisperDetailPage()), + // 回复我的 + CustomGetPage(name: '/replyMe', page: () => const ReplyMePage()), + // @我的 + CustomGetPage(name: '/atMe', page: () => const AtMePage()), + // 收到的赞 + CustomGetPage(name: '/likeMe', page: () => const LikeMePage()), + // 系统消息 + CustomGetPage(name: '/sysMsg', page: () => const WhisperPage()), // 登录页面 CustomGetPage(name: '/loginPage', page: () => const LoginPage()), // 用户动态 diff --git a/lib/services/audio_session.dart b/lib/services/audio_session.dart index 98707652a..57c42e053 100644 --- a/lib/services/audio_session.dart +++ b/lib/services/audio_session.dart @@ -20,11 +20,15 @@ class AudioSessionHandler { session.interruptionEventStream.listen((event) { final player = PlPlayerController.getInstance(); if (event.begin) { + if (player.playerStatus != PlayerStatus.playing) return; switch (event.type) { case AudioInterruptionType.duck: player.setVolume(player.volume.value * 0.5); break; case AudioInterruptionType.pause: + player.pause(isInterrupt: true); + _playInterrupted = true; + break; case AudioInterruptionType.unknown: player.pause(isInterrupt: true); _playInterrupted = true; @@ -36,7 +40,7 @@ class AudioSessionHandler { player.setVolume(player.volume.value * 2); break; case AudioInterruptionType.pause: - if (_playInterrupted) PlPlayerController.getInstance().play(); + if (_playInterrupted) player.play(); break; case AudioInterruptionType.unknown: break; @@ -47,7 +51,10 @@ class AudioSessionHandler { // 耳机拔出暂停 session.becomingNoisyEventStream.listen((_) { - PlPlayerController.getInstance().pause(); + final player = PlPlayerController.getInstance(); + if (player.playerStatus == PlayerStatus.playing) { + player.pause(); + } }); } } diff --git a/lib/services/loggeer.dart b/lib/services/loggeer.dart index 5555432c1..88f75999a 100644 --- a/lib/services/loggeer.dart +++ b/lib/services/loggeer.dart @@ -18,12 +18,10 @@ class PiliLogger extends Logger { @override void log(Level level, dynamic message, {Object? error, StackTrace? stackTrace, DateTime? time}) async { - if (level == Level.error) { - String dir = (await getApplicationDocumentsDirectory()).path; - // 创建logo文件 - final String filename = p.join(dir, ".pili_logs"); + if (level == Level.error || level == Level.fatal) { // 添加至文件末尾 - await File(filename).writeAsString( + File logFile = await getLogsPath(); + logFile.writeAsString( "**${DateTime.now()}** \n $message \n $stackTrace", mode: FileMode.writeOnlyAppend, ); @@ -35,17 +33,15 @@ class PiliLogger extends Logger { Future getLogsPath() async { String dir = (await getApplicationDocumentsDirectory()).path; final String filename = p.join(dir, ".pili_logs"); - final file = File(filename); + final File file = File(filename); if (!await file.exists()) { - await file.create(); + await file.create(recursive: true); } return file; } Future clearLogs() async { - String dir = (await getApplicationDocumentsDirectory()).path; - final String filename = p.join(dir, ".pili_logs"); - final file = File(filename); + final File file = await getLogsPath(); try { await file.writeAsString(''); } catch (e) { diff --git a/lib/utils/grid.dart b/lib/utils/grid.dart new file mode 100644 index 000000000..bbe727725 --- /dev/null +++ b/lib/utils/grid.dart @@ -0,0 +1,12 @@ +import 'package:flutter/cupertino.dart'; +import 'storage.dart'; +class Grid { + static double maxRowWidth = GStrorage.setting.get(SettingBoxKey.maxRowWidth, defaultValue: 240.0) as double; + + static double calculateActualWidth(BuildContext context, double maxCrossAxisExtent, double crossAxisSpacing) { + double screenWidth = MediaQuery.of(context).size.width; + int columnCount = ((screenWidth - crossAxisSpacing) / (maxCrossAxisExtent + crossAxisSpacing)).ceil(); + double columnWidth = (screenWidth - crossAxisSpacing) ~/ columnCount - crossAxisSpacing; + return columnWidth; + } +} diff --git a/lib/utils/recommend_filter.dart b/lib/utils/recommend_filter.dart index 113e2261a..61e37ed38 100644 --- a/lib/utils/recommend_filter.dart +++ b/lib/utils/recommend_filter.dart @@ -1,4 +1,4 @@ -import 'dart:math'; +// import 'dart:math'; import 'storage.dart'; diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index fff625da4..0a0a69b62 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -42,8 +42,6 @@ class GStrorage { return deletedEntries > 10; }, ); - // 视频设置 - video = await Hive.openBox('video'); } static void regAdapter() { @@ -54,6 +52,11 @@ class GStrorage { Hive.registerAdapter(HotSearchItemAdapter()); } + static Future lazyInit() async { + // 视频设置 + video = await Hive.openBox('video'); + } + static Future close() async { // user.compact(); // user.close(); @@ -89,6 +92,7 @@ class SettingBoxKey { enableAutoBrightness = 'enableAutoBrightness', enableAutoEnter = 'enableAutoEnter', enableAutoExit = 'enableAutoExit', + horizontalScreen = 'horizontalScreen', p1080 = 'p1080', enableCDN = 'enableCDN', autoPiP = 'autoPiP', @@ -96,11 +100,13 @@ class SettingBoxKey { // youtube 双击快进快退 enableQuickDouble = 'enableQuickDouble', + fullScreenGestureReverse = 'fullScreenGestureReverse', enableShowDanmaku = 'enableShowDanmaku', enableBackgroundPlay = 'enableBackgroundPlay', /// 隐私 blackMidsList = 'blackMidsList', + anonymity = 'anonymity', /// 推荐 enableRcmdDynamic = 'enableRcmdDynamic', @@ -130,7 +136,7 @@ class SettingBoxKey { customColor = 'customColor', // 自定义主题色 enableSingleRow = 'enableSingleRow', // 首页单列 displayMode = 'displayMode', - customRows = 'customRows', // 自定义列 + maxRowWidth = 'maxRowWidth', // 首页列最大宽度(dp) enableMYBar = 'enableMYBar', hideSearchBar = 'hideSearchBar', // 收起顶栏 hideTabBar = 'hideTabBar', // 收起底栏 diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index d68ac51bd..cfb6972b7 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -9,6 +9,7 @@ import 'package:crypto/crypto.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:path_provider/path_provider.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -327,6 +328,16 @@ class Utils { return '$formattedHours:$formattedMinutes'; } + static double getSheetHeight(BuildContext context) { + double height = context.height.abs(); + double width = context.width.abs(); + if (height > width) { + return height * 0.7; + } + //横屏状态 + return height; + } + static String appSign( Map params, String appkey, String appsec) { params['appkey'] = appkey; @@ -340,4 +351,17 @@ class Utils { return md5String; } + + static String generateRandomString(int minLength, int maxLength) { + const String printable = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#\$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ '; + + var random = Random(); + int length = minLength + random.nextInt(maxLength - minLength + 1); + return List.generate(length, (index) => printable[random.nextInt(printable.length)]).join(); + } + + static String base64EncodeRandomString(int minLength, int maxLength) { + String randomString = generateRandomString(minLength, maxLength); + return base64.encode(utf8.encode(randomString)); + } } diff --git a/lib/utils/wbi_sign.dart b/lib/utils/wbi_sign.dart index 4f831f165..5da75da11 100644 --- a/lib/utils/wbi_sign.dart +++ b/lib/utils/wbi_sign.dart @@ -80,7 +80,7 @@ class WbiSign { String getMixinKey(String orig) { String temp = ''; for (int i = 0; i < mixinKeyEncTab.length; i++) { - temp += orig.split('')[mixinKeyEncTab[i]]; + temp += orig[mixinKeyEncTab[i]]; } return temp.substring(0, 32); } @@ -104,7 +104,7 @@ class WbiSign { final String queryStr = query.join('&'); final String wbiSign = md5.convert(utf8.encode(queryStr + mixinKey)).toString(); // 计算 w_rid - return {'wts': currTime.toString(), 'w_rid': wbiSign}; + return {'w_rid': wbiSign,'wts': currTime.toString()}; } // 获取最新的 img_key 和 sub_key 可以从缓存中获取 diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index c3b56ecdd..000000000 --- a/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,31 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include -#include -#include -#include -#include - -void fl_register_plugins(FlPluginRegistry* registry) { - g_autoptr(FlPluginRegistrar) dynamic_color_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin"); - dynamic_color_plugin_register_with_registrar(dynamic_color_registrar); - g_autoptr(FlPluginRegistrar) flutter_volume_controller_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterVolumeControllerPlugin"); - flutter_volume_controller_plugin_register_with_registrar(flutter_volume_controller_registrar); - g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin"); - media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar); - g_autoptr(FlPluginRegistrar) media_kit_video_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin"); - media_kit_video_plugin_register_with_registrar(media_kit_video_registrar); - g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); - url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); -} diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h deleted file mode 100644 index e0f0a47bc..000000000 --- a/linux/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void fl_register_plugins(FlPluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake deleted file mode 100644 index 70cdeb4b3..000000000 --- a/linux/flutter/generated_plugins.cmake +++ /dev/null @@ -1,29 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - dynamic_color - flutter_volume_controller - media_kit_libs_linux - media_kit_video - url_launcher_linux -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST - media_kit_native_event_loop -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index 3e5f82f76..000000000 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - -import audio_service -import audio_session -import connectivity_plus -import device_info_plus -import dynamic_color -import flutter_volume_controller -import media_kit_libs_macos_video -import media_kit_video -import package_info_plus -import path_provider_foundation -import screen_brightness_macos -import share_plus -import sqflite -import url_launcher_macos -import wakelock_plus - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin")) - AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) - ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) - DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) - DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) - FlutterVolumeControllerPlugin.register(with: registry.registrar(forPlugin: "FlutterVolumeControllerPlugin")) - MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin")) - MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin")) - FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) - PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) - ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin")) - SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) - SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) - UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) - WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) -} diff --git a/pubspec.lock b/pubspec.lock index f5d63ca95..1f081d1f4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -6,7 +6,7 @@ packages: description: name: _fe_analyzer_shared sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "64.0.0" analyzer: @@ -14,55 +14,55 @@ packages: description: name: analyzer sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "6.2.0" animations: dependency: "direct main" description: name: animations - sha256: "708e4b68c23228c264b038fe7003a2f5d01ce85fc64d8cae090e86b27fcea6c5" - url: "https://pub.flutter-io.cn" + sha256: d3d6dcfb218225bbe68e87ccf6378bbb2e32a94900722c5f81611dad089911cb + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "2.0.10" + version: "2.0.11" appscheme: dependency: "direct main" description: name: appscheme sha256: b885b65219f3839ebafc937024a1bc5ce5a75b0e458fd249ef15e80e81235b6f - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.0.8" archive: dependency: transitive description: name: archive - sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b" - url: "https://pub.flutter-io.cn" + sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "3.4.9" + version: "3.4.10" args: dependency: transitive description: name: args sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.4.2" asn1lib: dependency: transitive description: name: asn1lib - sha256: "21afe4333076c02877d14f4a89df111e658a6d466cbfc802eb705eb91bd5adfd" - url: "https://pub.flutter-io.cn" + sha256: c9c85fedbe2188b95133cbe960e16f5f448860f7133330e272edbbca5893ddc6 + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "1.5.0" + version: "1.5.2" async: dependency: transitive description: name: async sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.11.0" audio_service: @@ -70,7 +70,7 @@ packages: description: name: audio_service sha256: a4d989f1225ea9621898d60f23236dcbfc04876fa316086c23c5c4af075dbac4 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.18.12" audio_service_platform_interface: @@ -78,7 +78,7 @@ packages: description: name: audio_service_platform_interface sha256: "8431a455dac9916cc9ee6f7da5620a666436345c906ad2ebb7fa41d18b3c1bf4" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.1.1" audio_service_web: @@ -86,7 +86,7 @@ packages: description: name: audio_service_web sha256: "523e64ddc914c714d53eec2da85bba1074f08cf26c786d4efb322de510815ea7" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.1.1" audio_session: @@ -94,7 +94,7 @@ packages: description: name: audio_session sha256: "6fdf255ed3af86535c96452c33ecff1245990bb25a605bfb1958661ccc3d467f" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.1.18" audio_video_progress_bar: @@ -102,7 +102,7 @@ packages: description: name: audio_video_progress_bar sha256: "3384875247cdbea748bd9ae8330631cd06a6cabfcda4945d45c9b406da92bc66" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.0.1" auto_orientation: @@ -110,7 +110,7 @@ packages: description: name: auto_orientation sha256: cd56bb59b36fa54cc28ee254bc600524f022a4862f31d5ab20abd7bb1c54e678 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.3.1" boolean_selector: @@ -118,7 +118,7 @@ packages: description: name: boolean_selector sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.1" build: @@ -126,7 +126,7 @@ packages: description: name: build sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.4.1" build_config: @@ -134,7 +134,7 @@ packages: description: name: build_config sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.1" build_daemon: @@ -142,7 +142,7 @@ packages: description: name: build_daemon sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "4.0.1" build_resolvers: @@ -150,79 +150,79 @@ packages: description: name: build_resolvers sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.4.2" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b" - url: "https://pub.flutter-io.cn" + sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "2.4.7" + version: "2.4.8" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: c9e32d21dd6626b5c163d48b037ce906bbe428bc23ab77bcd77bb21e593b6185 - url: "https://pub.flutter-io.cn" + sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "7.2.11" + version: "7.3.0" built_collection: dependency: transitive description: name: built_collection sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "5.1.1" built_value: dependency: transitive description: name: built_value - sha256: c9aabae0718ec394e5bc3c7272e6bb0dc0b32201a08fe185ec1d8401d3e39309 - url: "https://pub.flutter-io.cn" + sha256: a3ec2e0f967bc47f69f95009bb93db936288d61d5343b9436e378b28a2f830c6 + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "8.8.1" + version: "8.9.0" cached_network_image: dependency: "direct main" description: name: cached_network_image - sha256: f98972704692ba679db144261172a8e20feb145636c617af0eb4022132a6797f - url: "https://pub.flutter-io.cn" + sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "3.3.0" + version: "3.3.1" cached_network_image_platform_interface: dependency: transitive description: name: cached_network_image_platform_interface - sha256: "56aa42a7a01e3c9db8456d9f3f999931f1e05535b5a424271e9a38cabf066613" - url: "https://pub.flutter-io.cn" + sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "3.0.0" + version: "4.0.0" cached_network_image_web: dependency: transitive description: name: cached_network_image_web - sha256: "759b9a9f8f6ccbb66c185df805fac107f05730b1dab9c64626d1008cca532257" - url: "https://pub.flutter-io.cn" + sha256: "42a835caa27c220d1294311ac409a43361088625a4f23c820b006dd9bffb3316" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "1.1.0" + version: "1.1.1" catcher_2: dependency: "direct main" description: name: catcher_2 - sha256: ca94d45ffb52bf4b16a425cdff6734ae8443d36d5f06c276f1c2a593120b11ed - url: "https://pub.flutter-io.cn" + sha256: "0691d0a5a2b7ccbe434ff67071218bbff86d264d215c6afc10f404cd6d6e7a50" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "1.1.0" + version: "1.2.0" characters: dependency: transitive description: name: characters sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.3.0" checked_yaml: @@ -230,7 +230,7 @@ packages: description: name: checked_yaml sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.0.3" cli_util: @@ -238,7 +238,7 @@ packages: description: name: cli_util sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.4.1" clock: @@ -246,23 +246,23 @@ packages: description: name: clock sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.1" code_builder: dependency: transitive description: name: code_builder - sha256: feee43a5c05e7b3199bb375a86430b8ada1b04104f2923d0e03cc01ca87b6d84 - url: "https://pub.flutter-io.cn" + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "4.9.0" + version: "4.10.0" collection: dependency: transitive description: name: collection sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.18.0" connectivity_plus: @@ -270,7 +270,7 @@ packages: description: name: connectivity_plus sha256: "77a180d6938f78ca7d2382d2240eb626c0f6a735d0bfdce227d8ffb80f95c48b" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "4.0.2" connectivity_plus_platform_interface: @@ -278,7 +278,7 @@ packages: description: name: connectivity_plus_platform_interface sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.2.4" convert: @@ -286,7 +286,7 @@ packages: description: name: convert sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.1.1" cookie_jar: @@ -294,7 +294,7 @@ packages: description: name: cookie_jar sha256: a6ac027d3ed6ed756bfce8f3ff60cb479e266f3b0fdabd6242b804b6765e52de - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "4.0.8" cross_file: @@ -302,7 +302,7 @@ packages: description: name: cross_file sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.3.3+8" crypto: @@ -310,7 +310,7 @@ packages: description: name: crypto sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.0.3" csslib: @@ -318,7 +318,7 @@ packages: description: name: csslib sha256: "831883fb353c8bdc1d71979e5b342c7d88acfbc643113c14ae51e2442ea0f20f" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.17.3" cupertino_icons: @@ -326,7 +326,7 @@ packages: description: name: cupertino_icons sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.0.6" custom_sliding_segmented_control: @@ -334,7 +334,7 @@ packages: description: name: custom_sliding_segmented_control sha256: "05b73fa48d57218bfdf806bad68a859812b216cd81fe81c6cbefde89f39eb257" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.8.1" dart_style: @@ -342,7 +342,7 @@ packages: description: name: dart_style sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.3.4" dbus: @@ -350,23 +350,23 @@ packages: description: name: dbus sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.7.10" device_info_plus: dependency: "direct main" description: name: device_info_plus - sha256: "0042cb3b2a76413ea5f8a2b40cec2a33e01d0c937e91f0f7c211fde4f7739ba6" - url: "https://pub.flutter-io.cn" + sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "9.1.1" + version: "9.1.2" device_info_plus_platform_interface: dependency: transitive description: name: device_info_plus_platform_interface sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "7.0.0" dio: @@ -374,7 +374,7 @@ packages: description: name: dio sha256: "797e1e341c3dd2f69f2dad42564a6feff3bfb87187d05abb93b9609e6f1645c3" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "5.4.0" dio_cookie_manager: @@ -382,7 +382,7 @@ packages: description: name: dio_cookie_manager sha256: e79498b0f632897ff0c28d6e8178b4bc6e9087412401f618c31fa0904ace050d - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.1.1" dio_http2_adapter: @@ -390,7 +390,7 @@ packages: description: name: dio_http2_adapter sha256: "3bb35e81eb8a688eb1cb15beb97f46823698b44037e7b55227aa1060f5593adc" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.4.0" dismissible_page: @@ -398,23 +398,23 @@ packages: description: name: dismissible_page sha256: "5b2316f770fe83583f770df1f6505cb19102081c5971979806e77f2e507a9958" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.0.2" dynamic_color: dependency: "direct main" description: name: dynamic_color - sha256: "8b8bd1d798bd393e11eddeaa8ae95b12ff028bf7d5998fc5d003488cd5f4ce2f" - url: "https://pub.flutter-io.cn" + sha256: a866f1f8947bfdaf674d7928e769eac7230388a2e7a2542824fad4bb5b87be3b + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "1.6.8" + version: "1.6.9" easy_debounce: dependency: "direct main" description: name: easy_debounce sha256: f082609cfb8f37defb9e37fc28bc978c6712dedf08d4c5a26f820fa10165a236 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.0.3" encrypt: @@ -422,7 +422,7 @@ packages: description: name: encrypt sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "5.0.3" extended_image: @@ -430,7 +430,7 @@ packages: description: name: extended_image sha256: d7f091d068fcac7246c4b22a84b8dac59a62e04d29a5c172710c696e67a22f94 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "8.2.0" extended_image_library: @@ -438,7 +438,7 @@ packages: description: name: extended_image_library sha256: "9b55fc5ebc65fad984de66b8f177a1bef2a84d79203c9c213f75ff83c2c29edd" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "4.0.1" extended_list: @@ -446,7 +446,7 @@ packages: description: name: extended_list sha256: b27a2f0f55dadbf5b273bdaaf9307a7e0098a9fc0c4b8eb60ae98c319af596bc - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.0.1" extended_list_library: @@ -454,23 +454,23 @@ packages: description: name: extended_list_library sha256: cb424a04464e89bd6737f9ae025029bd8e913c7bf37101ad10c2defe0238d842 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.0.0" extended_nested_scroll_view: dependency: "direct main" description: name: extended_nested_scroll_view - sha256: "444a6f883e6e07effc7639e69a309e1fb491b6c19b095e9281714a51ace2b384" - url: "https://pub.flutter-io.cn" + sha256: "835580d40c2c62b448bd14adecd316acba469ba61f1510ef559d17668a85e777" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "6.1.2" + version: "6.2.1" fake_async: dependency: transitive description: name: fake_async sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.3.1" ffi: @@ -478,7 +478,7 @@ packages: description: name: ffi sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.0" file: @@ -486,7 +486,7 @@ packages: description: name: file sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "7.0.0" fixnum: @@ -494,7 +494,7 @@ packages: description: name: fixnum sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.0" floating: @@ -516,7 +516,7 @@ packages: description: name: flutter_cache_manager sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.3.1" flutter_displaymode: @@ -524,7 +524,7 @@ packages: description: name: flutter_displaymode sha256: "42c5e9abd13d28ed74f701b60529d7f8416947e58256e6659c5550db719c57ef" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.6.0" flutter_html: @@ -532,7 +532,7 @@ packages: description: name: flutter_html sha256: "02ad69e813ecfc0728a455e4bf892b9379983e050722b1dce00192ee2e41d1ee" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.0.0-beta.2" flutter_launcher_icons: @@ -540,7 +540,7 @@ packages: description: name: flutter_launcher_icons sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.13.1" flutter_lints: @@ -548,7 +548,7 @@ packages: description: name: flutter_lints sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.0.3" flutter_localizations: @@ -561,7 +561,7 @@ packages: description: name: flutter_mailer sha256: "4fffaa35e911ff5ec2e5a4ebbca62c372e99a154eb3bb2c0bf79f09adf6ecf4c" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.2" flutter_plugin_android_lifecycle: @@ -569,23 +569,23 @@ packages: description: name: flutter_plugin_android_lifecycle sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.0.17" flutter_smart_dialog: dependency: "direct main" description: name: flutter_smart_dialog - sha256: a666d56e3348aae0a85cf3d19c1b5b18799f39e0568023f991aacd83d5d7c441 - url: "https://pub.flutter-io.cn" + sha256: e9ee69eeac16165d142f1974b4db05ca9846cffafb7c94674a38ec07d7e6cda1 + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "4.9.5+1" + version: "4.9.6" flutter_svg: dependency: "direct main" description: name: flutter_svg sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.0.9" flutter_test: @@ -598,7 +598,7 @@ packages: description: name: flutter_volume_controller sha256: "0f10cc759499cb6c3e152a8f6ff8e5ce385b99db7e1f586d1a29d8e6c11f4082" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.3.1" flutter_web_plugins: @@ -611,23 +611,23 @@ packages: description: name: fluttertoast sha256: dfdde255317af381bfc1c486ed968d5a43a2ded9c931e87cbecd88767d6a71c1 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "8.2.4" font_awesome_flutter: dependency: "direct main" description: name: font_awesome_flutter - sha256: "52671aea66da73b58d42ec6d0912b727a42248dd9a7c76d6c20f275783c48c08" - url: "https://pub.flutter-io.cn" + sha256: "275ff26905134bcb59417cf60ad979136f1f8257f2f449914b2c3e05bbb4cd6f" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "10.6.0" + version: "10.7.0" frontend_server_client: dependency: transitive description: name: frontend_server_client sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.2.0" get: @@ -635,7 +635,7 @@ packages: description: name: get sha256: e4e7335ede17452b391ed3b2ede016545706c01a02292a6c97619705e7d2a85e - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "4.6.6" glob: @@ -643,7 +643,7 @@ packages: description: name: glob sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.2" graphs: @@ -651,23 +651,23 @@ packages: description: name: graphs sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.3.1" gt3_flutter_plugin: dependency: "direct main" description: name: gt3_flutter_plugin - sha256: f12bff2bfbcf27467833f8d564dcc24ee2f1b3254a7c7cf5eb2c4590baf11cc1 - url: "https://pub.flutter-io.cn" + sha256: "786b3b53e117678845488f8212d1b959cb969a94e7f5e68d7698d98cf6929eef" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "0.0.8" + version: "0.0.9" hive: dependency: "direct main" description: name: hive sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.2.3" hive_flutter: @@ -675,7 +675,7 @@ packages: description: name: hive_flutter sha256: dca1da446b1d808a51689fb5d0c6c9510c0a2ba01e22805d492c73b68e33eecc - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.0" hive_generator: @@ -683,7 +683,7 @@ packages: description: name: hive_generator sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.0.1" html: @@ -691,31 +691,31 @@ packages: description: name: html sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.15.4" http: dependency: transitive description: name: http - sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 - url: "https://pub.flutter-io.cn" + sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "1.1.2" + version: "1.2.0" http2: dependency: transitive description: name: http2 - sha256: "38db0c4aa9f1cd238a5d2e86aa0cc7cc91c77e0c6c94ba64bbe85e4ff732a952" - url: "https://pub.flutter-io.cn" + sha256: "9ced024a160b77aba8fb8674e38f70875e321d319e6f303ec18e87bd5a4b0c1d" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "2.2.0" + version: "2.3.0" http_client_helper: dependency: transitive description: name: http_client_helper sha256: "8a9127650734da86b5c73760de2b404494c968a3fd55602045ffec789dac3cb1" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.0.0" http_multi_server: @@ -723,7 +723,7 @@ packages: description: name: http_multi_server sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.2.1" http_parser: @@ -731,23 +731,23 @@ packages: description: name: http_parser sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "4.0.2" image: dependency: transitive description: name: image - sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271" - url: "https://pub.flutter-io.cn" + sha256: "49a0d4b0c12402853d3f227fe7c315601b238d126aa4caa5dbb2dcf99421aa4a" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "4.1.3" + version: "4.1.6" intl: dependency: transitive description: name: intl sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.18.1" io: @@ -755,23 +755,23 @@ packages: description: name: io sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.0.4" js: dependency: transitive description: name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.flutter-io.cn" + sha256: cf7243a0c29626284ada2add68a33f5b1102affe3509393e75136e0f6616bd68 + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "0.6.7" + version: "0.6.8" json_annotation: dependency: transitive description: name: json_annotation sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "4.8.1" lints: @@ -779,7 +779,7 @@ packages: description: name: lints sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.1" list_counter: @@ -787,7 +787,7 @@ packages: description: name: list_counter sha256: c447ae3dfcd1c55f0152867090e67e219d42fe6d4f2807db4bbe8b8d69912237 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.0.2" loading_more_list: @@ -795,7 +795,7 @@ packages: description: name: loading_more_list sha256: "6b49eb935345d6cf291e0367d3c238ef0a525a08b671ee41e09ee67d41888a7a" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "6.0.0" loading_more_list_library: @@ -803,7 +803,7 @@ packages: description: name: loading_more_list_library sha256: de6b57edbab83022180f053ec3f598dd5e1192cfd6a285882b8155e3cb5dc581 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.0.0" logger: @@ -811,7 +811,7 @@ packages: description: name: logger sha256: "6bbb9d6f7056729537a4309bda2e74e18e5d9f14302489cc1e93f33b3fe32cac" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.0.2+1" logging: @@ -819,7 +819,7 @@ packages: description: name: logging sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.2.0" mailer: @@ -827,7 +827,7 @@ packages: description: name: mailer sha256: "57f6dd1496699999a7bfd0aa6be0645384f477f4823e16d4321c40a434346382" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "6.0.1" matcher: @@ -835,7 +835,7 @@ packages: description: name: matcher sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.12.16" material_color_utilities: @@ -843,7 +843,7 @@ packages: description: name: material_color_utilities sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.5.0" media_kit: @@ -851,7 +851,7 @@ packages: description: name: media_kit sha256: "3289062540e3b8b9746e5c50d95bd78a9289826b7227e253dff806d002b9e67a" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.10+1" media_kit_libs_android_video: @@ -859,7 +859,7 @@ packages: description: name: media_kit_libs_android_video sha256: "9dd8012572e4aff47516e55f2597998f0a378e3d588d0fad0ca1f11a53ae090c" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.3.6" media_kit_libs_ios_video: @@ -867,7 +867,7 @@ packages: description: name: media_kit_libs_ios_video sha256: b5382994eb37a4564c368386c154ad70ba0cc78dacdd3fb0cd9f30db6d837991 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.4" media_kit_libs_linux: @@ -875,7 +875,7 @@ packages: description: name: media_kit_libs_linux sha256: e186891c31daa6bedab4d74dcdb4e8adfccc7d786bfed6ad81fe24a3b3010310 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.3" media_kit_libs_macos_video: @@ -883,7 +883,7 @@ packages: description: name: media_kit_libs_macos_video sha256: f26aa1452b665df288e360393758f84b911f70ffb3878032e1aabba23aa1032d - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.4" media_kit_libs_video: @@ -891,7 +891,7 @@ packages: description: name: media_kit_libs_video sha256: "3688e0c31482074578652bf038ce6301a5d21e1eda6b54fc3117ffeb4bdba067" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.0.4" media_kit_libs_windows_video: @@ -899,7 +899,7 @@ packages: description: name: media_kit_libs_windows_video sha256: "7bace5f35d9afcc7f9b5cdadb7541d2191a66bb3fc71bfa11c1395b3360f6122" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.0.9" media_kit_native_event_loop: @@ -907,7 +907,7 @@ packages: description: name: media_kit_native_event_loop sha256: a605cf185499d14d58935b8784955a92a4bf0ff4e19a23de3d17a9106303930e - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.0.8" media_kit_video: @@ -915,7 +915,7 @@ packages: description: name: media_kit_video sha256: c048d11a19e379aebbe810647636e3fc6d18374637e2ae12def4ff8a4b99a882 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.2.4" meta: @@ -923,23 +923,23 @@ packages: description: name: meta sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.10.0" mime: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e - url: "https://pub.flutter-io.cn" + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "1.0.4" + version: "1.0.5" nil: dependency: "direct main" description: name: nil sha256: ef05770c48942876d843bf6a4822d35e5da0ff893a61f1d5ad96d15c4a659136 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.1" nm: @@ -947,7 +947,7 @@ packages: description: name: nm sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.5.0" ns_danmaku: @@ -964,7 +964,7 @@ packages: description: name: octo_image sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.0.0" package_config: @@ -972,7 +972,7 @@ packages: description: name: package_config sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.0" package_info_plus: @@ -980,7 +980,7 @@ packages: description: name: package_info_plus sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "4.2.0" package_info_plus_platform_interface: @@ -988,7 +988,7 @@ packages: description: name: package_info_plus_platform_interface sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.0.1" path: @@ -996,7 +996,7 @@ packages: description: name: path sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.8.3" path_parsing: @@ -1004,143 +1004,143 @@ packages: description: name: path_parsing sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.0.1" path_provider: dependency: "direct main" description: name: path_provider - sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa - url: "https://pub.flutter-io.cn" + sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 - url: "https://pub.flutter-io.cn" + sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "2.2.1" + version: "2.2.2" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" - url: "https://pub.flutter-io.cn" + sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "2.3.1" + version: "2.3.2" path_provider_linux: dependency: transitive description: name: path_provider_linux sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" - url: "https://pub.flutter-io.cn" + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_windows: dependency: transitive description: name: path_provider_windows sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.2.1" permission_handler: dependency: "direct main" description: name: permission_handler - sha256: "860c6b871c94c78e202dc69546d4d8fd84bd59faeb36f8fb9888668a53ff4f78" - url: "https://pub.flutter-io.cn" + sha256: "45ff3fbcb99040fde55c528d5e3e6ca29171298a85436274d49c6201002087d6" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "11.1.0" + version: "11.2.0" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: "2f1bec180ee2f5665c22faada971a8f024761f632e93ddc23310487df52dcfa6" - url: "https://pub.flutter-io.cn" + sha256: "758284a0976772f9c744d6384fc5dc4834aa61e3f7aa40492927f244767374eb" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "12.0.1" + version: "12.0.3" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: "1a816084338ada8d574b1cb48390e6e8b19305d5120fe3a37c98825bacc78306" - url: "https://pub.flutter-io.cn" + sha256: c6bf440f80acd2a873d3d91a699e4cc770f86e7e6b576dda98759e8b92b39830 + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "9.2.0" + version: "9.3.0" permission_handler_html: dependency: transitive description: name: permission_handler_html - sha256: "11b762a8c123dced6461933a88ea1edbbe036078c3f9f41b08886e678e7864df" - url: "https://pub.flutter-io.cn" + sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "0.1.0+2" + version: "0.1.1" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: d87349312f7eaf6ce0adaf668daf700ac5b06af84338bd8b8574dfbd93ffe1a1 - url: "https://pub.flutter-io.cn" + sha256: "5c43148f2bfb6d14c5a8162c0a712afe891f2d847f35fcff29c406b37da43c3c" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "4.0.2" + version: "4.1.0" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: "1e8640c1e39121128da6b816d236e714d2cf17fac5a105dd6acdd3403a628004" - url: "https://pub.flutter-io.cn" + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "0.2.0" + version: "0.2.1" petitparser: dependency: transitive description: name: petitparser sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "6.0.2" platform: dependency: transitive description: name: platform - sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" - url: "https://pub.flutter-io.cn" + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "3.1.3" + version: "3.1.4" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 - url: "https://pub.flutter-io.cn" + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "2.1.7" + version: "2.1.8" pointycastle: dependency: transitive description: name: pointycastle - sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" - url: "https://pub.flutter-io.cn" + sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "3.7.3" + version: "3.7.4" pool: dependency: transitive description: name: pool sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.5.1" protobuf: @@ -1148,7 +1148,7 @@ packages: description: name: protobuf sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.1.0" pub_semver: @@ -1156,7 +1156,7 @@ packages: description: name: pub_semver sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.4" pubspec_parse: @@ -1164,7 +1164,7 @@ packages: description: name: pubspec_parse sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.2.3" pull_to_refresh_notification: @@ -1172,7 +1172,7 @@ packages: description: name: pull_to_refresh_notification sha256: "5a06c242a6c3264bac3a7facbe2c6d317a5f54fc10c20b556dbd34ceee32c9aa" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.1.0" rxdart: @@ -1180,7 +1180,7 @@ packages: description: name: rxdart sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.27.7" safe_local_storage: @@ -1188,23 +1188,23 @@ packages: description: name: safe_local_storage sha256: ede4eb6cb7d88a116b3d3bf1df70790b9e2038bc37cb19112e381217c74d9440 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.0.2" saver_gallery: dependency: "direct main" description: name: saver_gallery - sha256: "2657953427ebe5a3b2d08157d41587c01923ccce3f1a616d55082be7470f8530" - url: "https://pub.flutter-io.cn" + sha256: cceebad1f792adad4eb5015b415dcd521779a772ea574f0d7fc534b128deac83 + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "3.0.1" + version: "3.0.2" screen_brightness: dependency: "direct main" description: name: screen_brightness sha256: ed8da4a4511e79422fc1aa88138e920e4008cd312b72cdaa15ccb426c0faaedd - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.2.2+1" screen_brightness_android: @@ -1212,7 +1212,7 @@ packages: description: name: screen_brightness_android sha256: "3df10961e3a9e968a5e076fe27e7f4741fa8a1d3950bdeb48cf121ed529d0caf" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.1.0+2" screen_brightness_ios: @@ -1220,7 +1220,7 @@ packages: description: name: screen_brightness_ios sha256: "99adc3ca5490b8294284aad5fcc87f061ad685050e03cf45d3d018fe398fd9a2" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.1.0" screen_brightness_macos: @@ -1228,7 +1228,7 @@ packages: description: name: screen_brightness_macos sha256: "64b34e7e3f4900d7687c8e8fb514246845a73ecec05ab53483ed025bd4a899fd" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.1.0+1" screen_brightness_platform_interface: @@ -1236,7 +1236,7 @@ packages: description: name: screen_brightness_platform_interface sha256: b211d07f0c96637a15fb06f6168617e18030d5d74ad03795dd8547a52717c171 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.1.0" screen_brightness_windows: @@ -1244,7 +1244,7 @@ packages: description: name: screen_brightness_windows sha256: "9261bf33d0fc2707d8cf16339ce25768100a65e70af0fcabaf032fc12408ba86" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.1.3" scrollable_positioned_list: @@ -1252,31 +1252,31 @@ packages: description: name: scrollable_positioned_list sha256: "1b54d5f1329a1e263269abc9e2543d90806131aa14fe7c6062a8054d57249287" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.3.8" sentry: dependency: transitive description: name: sentry - sha256: "5686ed515bb620dc52b4ae99a6586fe720d443591183cf1f620ec5d1f0eec100" - url: "https://pub.flutter-io.cn" + sha256: a7946f4a90b0feb47214981d881b98149e05f6c576da9f2a2f33945bf561de25 + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "7.15.0" + version: "7.16.0" share_plus: dependency: "direct main" description: name: share_plus - sha256: f74fc3f1cbd99f39760182e176802f693fa0ec9625c045561cfad54681ea93dd - url: "https://pub.flutter-io.cn" + sha256: "3ef39599b00059db0990ca2e30fca0a29d8b37aae924d60063f8e0184cf20900" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "7.2.1" + version: "7.2.2" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface sha256: df08bc3a07d01f5ea47b45d03ffcba1fa9cd5370fb44b3f38c70e42cced0f956 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.3.1" shelf: @@ -1284,7 +1284,7 @@ packages: description: name: shelf sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.4.1" shelf_web_socket: @@ -1292,7 +1292,7 @@ packages: description: name: shelf_web_socket sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.0.4" sky_engine: @@ -1305,7 +1305,7 @@ packages: description: name: source_gen sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.5.0" source_helper: @@ -1313,7 +1313,7 @@ packages: description: name: source_helper sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.3.4" source_span: @@ -1321,31 +1321,31 @@ packages: description: name: source_span sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.10.0" sqflite: dependency: transitive description: name: sqflite - sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a" - url: "https://pub.flutter-io.cn" + sha256: a9016f495c927cb90557c909ff26a6d92d9bd54fc42ba92e19d4e79d61e798c6 + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "2.3.0" + version: "2.3.2" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: bb4738f15b23352822f4c42a531677e5c6f522e079461fd240ead29d8d8a54a6 - url: "https://pub.flutter-io.cn" + sha256: "28d8c66baee4968519fb8bd6cdbedad982d6e53359091f0b74544a9f32ec72d5" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "2.5.0+2" + version: "2.5.3" stack_trace: dependency: transitive description: name: stack_trace sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.11.1" status_bar_control: @@ -1353,7 +1353,7 @@ packages: description: name: status_bar_control sha256: "7f2c1f3f7fd13b85ed284eb7ca3f74ceb8dcfdd25636d3a84186d0a687d36693" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.2.1" stream_channel: @@ -1361,7 +1361,7 @@ packages: description: name: stream_channel sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.2" stream_transform: @@ -1369,7 +1369,7 @@ packages: description: name: stream_transform sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.0" string_scanner: @@ -1377,23 +1377,23 @@ packages: description: name: string_scanner sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.2.0" synchronized: dependency: transitive description: name: synchronized - sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" - url: "https://pub.flutter-io.cn" + sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "3.1.0" + version: "3.1.0+1" system_proxy: dependency: "direct main" description: name: system_proxy sha256: bbdfc9736a963409941fb0e7c494606c1f13c2be34de15833ee385da83cf7ab0 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.1.0" term_glyph: @@ -1401,7 +1401,7 @@ packages: description: name: term_glyph sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.2.1" test_api: @@ -1409,7 +1409,7 @@ packages: description: name: test_api sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.6.1" timing: @@ -1417,7 +1417,7 @@ packages: description: name: timing sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.0.1" typed_data: @@ -1425,7 +1425,7 @@ packages: description: name: typed_data sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.3.2" universal_io: @@ -1433,7 +1433,7 @@ packages: description: name: universal_io sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.2.2" universal_platform: @@ -1441,7 +1441,7 @@ packages: description: name: universal_platform sha256: d315be0f6641898b280ffa34e2ddb14f3d12b1a37882557869646e0cc363d0cc - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.0.0+1" uri_parser: @@ -1449,39 +1449,39 @@ packages: description: name: uri_parser sha256: "6543c9fd86d2862fac55d800a43e67c0dcd1a41677cb69c2f8edfe73bbcf1835" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.0.2" url_launcher: dependency: "direct main" description: name: url_launcher - sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86 - url: "https://pub.flutter-io.cn" + sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "6.2.2" + version: "6.2.4" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" - url: "https://pub.flutter-io.cn" + sha256: "507dc655b1d9cb5ebc756032eb785f114e415f91557b73bf60b7e201dfedeb2f" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "6.2.0" + version: "6.2.2" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 - url: "https://pub.flutter-io.cn" + sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "6.2.1" + version: "6.2.4" url_launcher_linux: dependency: transitive description: name: url_launcher_linux sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.1.1" url_launcher_macos: @@ -1489,31 +1489,31 @@ packages: description: name: url_launcher_macos sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.1.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" - url: "https://pub.flutter-io.cn" + sha256: a932c3a8082e118f80a475ce692fde89dc20fddb24c57360b96bc56f7035de1f + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "2.2.0" + version: "2.3.1" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "7286aec002c8feecc338cc33269e96b73955ab227456e9fb2a91f7fab8a358e9" - url: "https://pub.flutter-io.cn" + sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "2.2.2" + version: "2.2.3" url_launcher_windows: dependency: transitive description: name: url_launcher_windows sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.1.1" uuid: @@ -1521,39 +1521,39 @@ packages: description: name: uuid sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.0.7" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: "0f0c746dd2d6254a0057218ff980fc7f5670fd0fcf5e4db38a490d31eed4ad43" - url: "https://pub.flutter-io.cn" + sha256: "18f6690295af52d081f6808f2f7c69f0eed6d7e23a71539d75f4aeb8f0062172" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "1.1.9+1" + version: "1.1.9+2" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: "0edf6d630d1bfd5589114138ed8fada3234deacc37966bec033d3047c29248b7" - url: "https://pub.flutter-io.cn" + sha256: "531d20465c10dfac7f5cd90b60bbe4dd9921f1ec4ca54c83ebb176dbacb7bb2d" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "1.1.9+1" + version: "1.1.9+2" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: d24333727332d9bd20990f1483af4e09abdb9b1fc7c3db940b56ab5c42790c26 - url: "https://pub.flutter-io.cn" + sha256: "03012b0a33775c5530576b70240308080e1d5050f0faf000118c20e6463bc0ad" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "1.1.9+1" + version: "1.1.9+2" vector_math: dependency: transitive description: name: vector_math sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.1.4" visibility_detector: @@ -1561,7 +1561,7 @@ packages: description: name: visibility_detector sha256: dd5cc11e13494f432d15939c3aa8ae76844c42b723398643ce9addb88a5ed420 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.4.0+2" volume_controller: @@ -1569,7 +1569,7 @@ packages: description: name: volume_controller sha256: "189bdc7a554f476b412e4c8b2f474562b09d74bc458c23667356bce3ca1d48c9" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.0.7" wakelock_plus: @@ -1577,7 +1577,7 @@ packages: description: name: wakelock_plus sha256: f268ca2116db22e57577fb99d52515a24bdc1d570f12ac18bb762361d43b043d - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.4" wakelock_plus_platform_interface: @@ -1585,7 +1585,7 @@ packages: description: name: wakelock_plus_platform_interface sha256: "40fabed5da06caff0796dc638e1f07ee395fb18801fbff3255a2372db2d80385" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.0" watcher: @@ -1593,15 +1593,15 @@ packages: description: name: watcher sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.0" waterfall_flow: - dependency: transitive + dependency: "direct main" description: name: waterfall_flow sha256: "11538b0d890458e55e6248b177732495d20893cfc7e85d7e8dbf4fdce61c9f10" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.0.3" web: @@ -1609,87 +1609,87 @@ packages: description: name: web sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "0.3.0" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b - url: "https://pub.flutter-io.cn" + sha256: cc1f6c632a248278a091fd7d9a68f624906830f7c1c5aa66503fae0804633e1c + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "2.4.0" + version: "2.4.2" webview_cookie_manager: dependency: "direct main" description: name: webview_cookie_manager sha256: "425a9feac5cd2cb62a71da3dda5ac2eaf9ece5481ee8d79f3868dc5ba8223ad3" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "2.0.6" webview_flutter: dependency: "direct main" description: name: webview_flutter - sha256: "42393b4492e629aa3a88618530a4a00de8bb46e50e7b3993fedbfdc5352f0dbf" - url: "https://pub.flutter-io.cn" + sha256: d81b68e88cc353e546afb93fb38958e3717282c5ac6e5d3be4a4aef9fc3c1413 + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "4.4.2" + version: "4.5.0" webview_flutter_android: dependency: transitive description: name: webview_flutter_android - sha256: e313dcdf45d4c95bcb8960351ef2389b7f0687b90bc92483f7f7983ae5758456 - url: "https://pub.flutter-io.cn" + sha256: "4ea3c4e1b8ed590162b15b8a61b41b1ef3ff179a314627c16ce40c086d94b8af" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "3.13.0" + version: "3.14.0" webview_flutter_platform_interface: dependency: transitive description: name: webview_flutter_platform_interface - sha256: "68e86162aa8fc646ae859e1585995c096c95fc2476881fa0c4a8d10f56013a5a" - url: "https://pub.flutter-io.cn" + sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "2.8.0" + version: "2.10.0" webview_flutter_wkwebview: dependency: transitive description: name: webview_flutter_wkwebview - sha256: accdaaa49a2aca2dc3c3230907988954cdd23fed0a19525d6c9789d380f4dc76 - url: "https://pub.flutter-io.cn" + sha256: "4d062ad505390ecef1c4bfb6001cd857a51e00912cc9dfb66edb1886a9ebd80c" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "3.9.4" + version: "3.10.2" win32: dependency: transitive description: name: win32 - sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574 - url: "https://pub.flutter-io.cn" + sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "5.1.1" + version: "5.2.0" win32_registry: dependency: transitive description: name: win32_registry sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.1.2" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" - url: "https://pub.flutter-io.cn" + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "1.0.3" + version: "1.0.4" xml: dependency: transitive description: name: xml sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "6.5.0" yaml: @@ -1697,7 +1697,7 @@ packages: description: name: yaml sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" - url: "https://pub.flutter-io.cn" + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "3.1.2" sdks: diff --git a/pubspec.yaml b/pubspec.yaml index 9597eab75..7a70a4af7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -140,6 +140,8 @@ dependencies: catcher_2: ^1.1.0 logger: ^2.0.2+1 path: 1.8.3 + #瀑布流 + waterfall_flow: ^3.0.3 dev_dependencies: flutter_test: @@ -188,7 +190,6 @@ flutter: - assets/images/ - assets/images/lv/ - assets/images/logo/ - - assets/images/live/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc deleted file mode 100644 index d2cff3b2c..000000000 --- a/windows/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,38 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void RegisterPlugins(flutter::PluginRegistry* registry) { - ConnectivityPlusWindowsPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); - DynamicColorPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); - FlutterVolumeControllerPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("FlutterVolumeControllerPluginCApi")); - MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi")); - MediaKitVideoPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("MediaKitVideoPluginCApi")); - PermissionHandlerWindowsPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); - ScreenBrightnessWindowsPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin")); - SharePlusWindowsPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); - UrlLauncherWindowsRegisterWithRegistrar( - registry->GetRegistrarForPlugin("UrlLauncherWindows")); -} diff --git a/windows/flutter/generated_plugin_registrant.h b/windows/flutter/generated_plugin_registrant.h deleted file mode 100644 index dc139d85a..000000000 --- a/windows/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void RegisterPlugins(flutter::PluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake deleted file mode 100644 index 5d25e1343..000000000 --- a/windows/flutter/generated_plugins.cmake +++ /dev/null @@ -1,33 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - connectivity_plus - dynamic_color - flutter_volume_controller - media_kit_libs_windows_video - media_kit_video - permission_handler_windows - screen_brightness_windows - share_plus - url_launcher_windows -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST - media_kit_native_event_loop -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin)