diff --git a/.github/workflows/beta_ci.yml b/.github/workflows/beta_ci.yml
new file mode 100644
index 00000000..e839aca1
--- /dev/null
+++ b/.github/workflows/beta_ci.yml
@@ -0,0 +1,208 @@
+name: Pilipala Beta
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - "main"
+ paths-ignore:
+ - "**.md"
+ - "**.txt"
+ - ".github/**"
+ - ".idea/**"
+ - "!.github/workflows/**"
+
+jobs:
+ update_version:
+ name: Read and update version
+ runs-on: ubuntu-latest
+
+ outputs:
+ # 定义输出变量 version,以便在其他job中引用
+ new_version: ${{ steps.version.outputs.new_version }}
+ last_commit: ${{ steps.get-last-commit.outputs.last_commit }}
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.ref_name }}
+ fetch-depth: 0
+
+ - name: 获取first parent commit次数
+ id: get-first-parent-commit-count
+ run: |
+ version=$(yq e .version pubspec.yaml | cut -d "+" -f 1)
+ recent_release_tag=$(git tag -l | grep $version | egrep -v "[-|+]" || true)
+ if [[ "x$recent_release_tag" == "x" ]]; then
+ echo "当前版本tag不存在,请手动生成tag."
+ exit 1
+ fi
+ git log --oneline --first-parent $recent_release_tag..HEAD
+ first_parent_commit_count=$(git rev-list --first-parent --count $recent_release_tag..HEAD)
+ echo "count=$first_parent_commit_count" >> $GITHUB_OUTPUT
+
+ - name: 获取最后一次提交
+ id: get-last-commit
+ run: |
+ last_commit=$(git log -1 --pretty="%h %s" --first-parent)
+ echo "last_commit=$last_commit" >> $GITHUB_OUTPUT
+
+ - name: 更新版本号
+ id: version
+ run: |
+ # 读取版本号
+ VERSION=$(yq e .version pubspec.yaml | cut -d "+" -f 1)
+
+ # 获取GitHub Actions的run_number
+ #RUN_NUMBER=${{ github.run_number }}
+
+ # 构建新版本号
+ NEW_VERSION=$VERSION-beta.${{ steps.get-first-parent-commit-count.outputs.count }}
+
+ # 输出新版本号
+ echo "New version: $NEW_VERSION"
+
+ # 设置新版本号为输出变量
+ echo "new_version=$NEW_VERSION" >>$GITHUB_OUTPUT
+
+ android:
+ name: Build CI (Android)
+ needs: update_version
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.ref_name }}
+
+ - 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: 更新版本号
+ id: version
+ run: |
+ # 更新pubspec.yaml文件中的版本号
+ sed -i "s/version: .*+/version: ${{ needs.update_version.outputs.new_version }}+/g" pubspec.yaml
+
+ - 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: 重命名应用
+ 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]}v${{ needs.update_version.outputs.new_version }}.apk"
+ mv "$file" "$new_file_name"
+ fi
+ done
+
+ - name: 上传
+ uses: actions/upload-artifact@v3
+ with:
+ name: Pilipala-Beta
+ path: |
+ build/app/outputs/flutter-apk/Pili-*.apk
+
+ iOS:
+ name: Build CI (iOS)
+ needs: update_version
+ runs-on: macos-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.ref_name }}
+
+ - 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: 更新版本号
+ id: version
+ run: |
+ # 更新pubspec.yaml文件中的版本号
+ sed -i "" "s/version: .*+/version: ${{ needs.update_version.outputs.new_version }}+/g" pubspec.yaml
+
+ - 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: 重命名应用
+ run: |
+ DATE=${{ steps.date.outputs.date }}
+ for file in app.ipa; do
+ new_file_name="build/Pili-v${{ needs.update_version.outputs.new_version }}.ipa"
+ mv "$file" "$new_file_name"
+ done
+
+ - name: 上传
+ uses: actions/upload-artifact@v3
+ with:
+ if-no-files-found: error
+ name: Pilipala-Beta
+ path: |
+ build/Pili-*.ipa
+
+ upload:
+ runs-on: ubuntu-latest
+
+ needs:
+ - update_version
+ - android
+ - iOS
+ steps:
+ - uses: actions/download-artifact@v3
+ with:
+ name: Pilipala-Beta
+ path: ./Pilipala-Beta
+
+ - name: 发送到Telegram频道
+ uses: xireiki/channel-post@v1.0.7
+ with:
+ bot_token: ${{ secrets.BOT_TOKEN }}
+ chat_id: ${{ secrets.CHAT_ID }}
+ large_file: true
+ api_id: ${{ secrets.TELEGRAM_API_ID }}
+ api_hash: ${{ secrets.TELEGRAM_API_HASH }}
+ method: sendFile
+ path: Pilipala-Beta/*
+ parse_mode: Markdown
+ context: "*Beta版本: v${{ needs.update_version.outputs.new_version }}*\n更新内容: [${{ needs.update_version.outputs.last_commit }}](${{ github.event.head_commit.url }})"
diff --git a/.github/workflows/main.yml b/.github/workflows/release_ci.yml
similarity index 100%
rename from .github/workflows/main.yml
rename to .github/workflows/release_ci.yml
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index d82845fe..c52d8447 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -223,6 +223,10 @@
android:pathPattern="/mobile/video/.*" />
+
+
diff --git a/assets/images/live/default_bg.webp b/assets/images/live/default_bg.webp
new file mode 100644
index 00000000..a58259de
Binary files /dev/null and b/assets/images/live/default_bg.webp differ
diff --git a/change_log/1.0.20.0303.md b/change_log/1.0.20.0303.md
new file mode 100644
index 00000000..1d8c4e00
--- /dev/null
+++ b/change_log/1.0.20.0303.md
@@ -0,0 +1,31 @@
+## 1.0.20
+
+
+### 功能
++ 评论区增加表情
++ 首页渐变背景开关
++ 媒体库显示「我的订阅」
++ 评论区链接解析
++ 默认启动页设置
+
+### 修复
++ 评论区内容重复
++ pip相关问题
++ 播放多p视频评论不刷新
++ 视频评论翻页重复
+
+### 优化
++ url scheme优化
++ 图片预览放大
++ 图片加载速度
++ 视频评论区复制
++ 全屏显示视频标题
++ 网络异常处理
+
+
+
+
+
+
+更多更新日志可在Github上查看
+问题反馈、功能建议请查看「关于」页面。
diff --git a/change_log/1.0.21.0306.md b/change_log/1.0.21.0306.md
new file mode 100644
index 00000000..3a582dbb
--- /dev/null
+++ b/change_log/1.0.21.0306.md
@@ -0,0 +1,9 @@
+## 1.0.21
+
+### 修复
++ 推荐视频全屏问题
++ 番剧全屏播放时灰屏问题
++ 评论回调导致页面卡死问题
+
+更多更新日志可在Github上查看
+问题反馈、功能建议请查看「关于」页面。
diff --git a/lib/common/widgets/http_error.dart b/lib/common/widgets/http_error.dart
index b02182c6..cbc6659b 100644
--- a/lib/common/widgets/http_error.dart
+++ b/lib/common/widgets/http_error.dart
@@ -22,20 +22,27 @@ class HttpError extends StatelessWidget {
"assets/images/error.svg",
height: 200,
),
- const SizedBox(height: 20),
+ const SizedBox(height: 30),
Text(
errMsg ?? '请求异常',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleSmall,
),
- const SizedBox(height: 30),
- OutlinedButton.icon(
+ const SizedBox(height: 20),
+ FilledButton.tonal(
onPressed: () {
fn!();
},
- icon: const Icon(Icons.arrow_forward_outlined, size: 20),
- label: Text(btnText ?? '点击重试'),
- )
+ style: ButtonStyle(
+ backgroundColor: MaterialStateProperty.resolveWith((states) {
+ return Theme.of(context).colorScheme.primary.withAlpha(20);
+ }),
+ ),
+ child: Text(
+ btnText ?? '点击重试',
+ style: TextStyle(color: Theme.of(context).colorScheme.primary),
+ ),
+ ),
],
),
),
diff --git a/lib/common/widgets/network_img_layer.dart b/lib/common/widgets/network_img_layer.dart
index 03adc166..06c35974 100644
--- a/lib/common/widgets/network_img_layer.dart
+++ b/lib/common/widgets/network_img_layer.dart
@@ -2,6 +2,7 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/utils/extension.dart';
+import 'package:pilipala/utils/global_data.dart';
import '../../utils/storage.dart';
import '../constants.dart';
@@ -32,8 +33,10 @@ class NetworkImgLayer extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final int defaultImgQuality = GlobalData().imgQuality;
final String imageUrl =
- '${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? 100}q.webp';
+ '${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? defaultImgQuality}q.webp';
+ print(imageUrl);
int? memCacheWidth, memCacheHeight;
double aspectRatio = (width / height).toDouble();
@@ -81,7 +84,7 @@ class NetworkImgLayer extends StatelessWidget {
fadeOutDuration ?? const Duration(milliseconds: 120),
fadeInDuration:
fadeInDuration ?? const Duration(milliseconds: 120),
- filterQuality: FilterQuality.high,
+ filterQuality: FilterQuality.low,
errorWidget: (BuildContext context, String url, Object error) =>
placeholder(context),
placeholder: (BuildContext context, String url) =>
@@ -104,17 +107,19 @@ class NetworkImgLayer extends StatelessWidget {
? 0
: StyleString.imgRadius.x),
),
- child: Center(
- child: Image.asset(
- type == 'avatar'
- ? 'assets/images/noface.jpeg'
- : 'assets/images/loading.png',
- width: width,
- height: height,
- cacheWidth: width.cacheSize(context),
- cacheHeight: height.cacheSize(context),
- ),
- ),
+ child: type == 'bg'
+ ? const SizedBox()
+ : Center(
+ child: Image.asset(
+ type == 'avatar'
+ ? 'assets/images/noface.jpeg'
+ : 'assets/images/loading.png',
+ width: width,
+ height: height,
+ cacheWidth: width.cacheSize(context),
+ cacheHeight: height.cacheSize(context),
+ ),
+ ),
);
}
}
diff --git a/lib/common/widgets/stat/danmu.dart b/lib/common/widgets/stat/danmu.dart
index 44f662a9..c1c439db 100644
--- a/lib/common/widgets/stat/danmu.dart
+++ b/lib/common/widgets/stat/danmu.dart
@@ -3,7 +3,7 @@ import 'package:pilipala/utils/utils.dart';
class StatDanMu extends StatelessWidget {
final String? theme;
- final int? danmu;
+ final dynamic danmu;
final String? size;
const StatDanMu({Key? key, this.theme, this.danmu, this.size})
diff --git a/lib/common/widgets/stat/view.dart b/lib/common/widgets/stat/view.dart
index 8b97b605..2665e2d4 100644
--- a/lib/common/widgets/stat/view.dart
+++ b/lib/common/widgets/stat/view.dart
@@ -3,7 +3,7 @@ import 'package:pilipala/utils/utils.dart';
class StatView extends StatelessWidget {
final String? theme;
- final int? view;
+ final dynamic view;
final String? size;
const StatView({Key? key, this.theme, this.view, this.size})
diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart
index 9fc7daad..99059a9e 100644
--- a/lib/common/widgets/video_card_h.dart
+++ b/lib/common/widgets/video_card_h.dart
@@ -38,6 +38,10 @@ class VideoCardH extends StatelessWidget {
Widget build(BuildContext context) {
final int aid = videoItem.aid;
final String bvid = videoItem.bvid;
+ String type = 'video';
+ try {
+ type = videoItem.type;
+ } catch (_) {}
final String heroTag = Utils.makeHeroTag(aid);
return GestureDetector(
onLongPress: () {
@@ -53,6 +57,10 @@ class VideoCardH extends StatelessWidget {
child: InkWell(
onTap: () async {
try {
+ if (type == 'ketang') {
+ SmartDialog.showToast('课堂视频暂不支持播放');
+ return;
+ }
final int cid =
videoItem.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid);
Get.toNamed('/video?bvid=$bvid&cid=$cid',
@@ -95,12 +103,20 @@ class VideoCardH extends StatelessWidget {
height: maxHeight,
),
),
- PBadge(
- text: Utils.timeFormat(videoItem.duration!),
- right: 6.0,
- bottom: 6.0,
- type: 'gray',
- ),
+ if (videoItem.duration != 0)
+ PBadge(
+ text: Utils.timeFormat(videoItem.duration!),
+ right: 6.0,
+ bottom: 6.0,
+ type: 'gray',
+ ),
+ if (type != 'video')
+ PBadge(
+ text: type,
+ left: 6.0,
+ bottom: 6.0,
+ type: 'primary',
+ ),
// if (videoItem.rcmdReason != null &&
// videoItem.rcmdReason.content != '')
// pBadge(videoItem.rcmdReason.content, context,
@@ -324,8 +340,9 @@ class VideoContent extends StatelessWidget {
reSrc: 11,
);
SmartDialog.dismiss();
- SmartDialog.showToast(
- res['msg'] ?? '成功');
+ SmartDialog.showToast(res['code'] == 0
+ ? '成功'
+ : res['msg']);
},
child: const Text('确认'),
)
diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart
index 0bbd5377..0d96f7b7 100644
--- a/lib/common/widgets/video_card_v.dart
+++ b/lib/common/widgets/video_card_v.dart
@@ -1,6 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
+import '../../models/model_rec_video_item.dart';
+import 'stat/danmu.dart';
+import 'stat/view.dart';
import '../../http/dynamics.dart';
import '../../http/search.dart';
import '../../http/user.dart';
@@ -158,12 +161,12 @@ class VideoCardV extends StatelessWidget {
height: maxHeight,
),
),
- if (videoItem.duration != null)
+ if (videoItem.duration > 0)
if (crossAxisCount == 1) ...[
PBadge(
bottom: 10,
right: 10,
- text: videoItem.duration,
+ text: Utils.timeFormat(videoItem.duration),
)
] else ...[
PBadge(
@@ -171,7 +174,7 @@ class VideoCardV extends StatelessWidget {
right: 7,
size: 'small',
type: 'gray',
- text: videoItem.duration,
+ text: Utils.timeFormat(videoItem.duration),
)
],
],
@@ -228,6 +231,7 @@ class VideoContent extends StatelessWidget {
const SizedBox(height: 2),
VideoStat(
videoItem: videoItem,
+ crossAxisCount: crossAxisCount,
),
],
if (crossAxisCount == 1) const SizedBox(height: 4),
@@ -291,6 +295,7 @@ class VideoContent extends StatelessWidget {
),
VideoStat(
videoItem: videoItem,
+ crossAxisCount: crossAxisCount,
),
const Spacer(),
],
@@ -314,29 +319,41 @@ class VideoContent extends StatelessWidget {
class VideoStat extends StatelessWidget {
final dynamic videoItem;
+ final int crossAxisCount;
const VideoStat({
Key? key,
required this.videoItem,
+ required this.crossAxisCount,
}) : super(key: key);
@override
Widget build(BuildContext context) {
- return RichText(
- maxLines: 1,
- text: TextSpan(
- style: TextStyle(
- fontSize: MediaQuery.textScalerOf(context)
- .scale(Theme.of(context).textTheme.labelSmall!.fontSize!),
- color: Theme.of(context).colorScheme.outline,
+ return Row(
+ children: [
+ StatView(
+ theme: 'gray',
+ view: videoItem.stat.view,
),
- children: [
- if (videoItem.stat.view != '-')
- TextSpan(text: '${videoItem.stat.view}观看'),
- if (videoItem.stat.danmu != '-')
- TextSpan(text: ' • ${videoItem.stat.danmu}弹幕'),
- ],
- ),
+ const SizedBox(width: 8),
+ StatDanMu(
+ theme: 'gray',
+ danmu: videoItem.stat.danmu,
+ ),
+ if (videoItem is RecVideoItemModel) ...[
+ crossAxisCount > 1 ? const Spacer() : const SizedBox(width: 8),
+ RichText(
+ maxLines: 1,
+ text: TextSpan(
+ style: TextStyle(
+ fontSize: 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/http/api.dart b/lib/http/api.dart
index 532ca341..8aa62233 100644
--- a/lib/http/api.dart
+++ b/lib/http/api.dart
@@ -214,6 +214,9 @@ class Api {
// https://api.bilibili.com/x/relation/tags
static const String followingsClass = '/x/relation/tags';
+ // 搜索follow
+ static const followSearch = '/x/relation/followings/search';
+
// 粉丝
// vmid 用户id pn 页码 ps 每页个数,最大50 order: desc
// order_type 排序规则 最近访问传空,最常访问传 attention
@@ -230,6 +233,10 @@ 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';
@@ -470,4 +477,30 @@ class Api {
/// 获取未读动态数
static const getUnreadDynamic = '/x/web-interface/dynamic/entrance';
+
+ /// 用户动态主页
+ static const dynamicSpmPrefix = 'https://space.bilibili.com/1/dynamic';
+
+ /// 激活buvid3
+ static const activateBuvidApi = '/x/internal/gaia-gateway/ExClimbWuzhi';
+
+ /// 我的订阅
+ static const userSubFolder = '/x/v3/fav/folder/collected/list';
+
+ /// 我的订阅详情
+ static const userSubFolderDetail = '/x/space/fav/season/list';
+
+ /// 表情
+ static const emojiList = '/x/emote/user/panel/web';
+
+ /// 已读标记
+ static const String ackSessionMsg =
+ '${HttpString.tUrl}/session_svr/v1/session_svr/update_ack';
+
+ /// 发送私信
+ static const String sendMsg = '${HttpString.tUrl}/web_im/v1/web_im/send_msg';
+
+ /// 排行榜
+ static const String getRankApi = "/x/web-interface/ranking/v2";
+
}
diff --git a/lib/http/init.dart b/lib/http/init.dart
index dcc9f402..a0b36369 100644
--- a/lib/http/init.dart
+++ b/lib/http/init.dart
@@ -1,7 +1,9 @@
// ignore_for_file: avoid_print
import 'dart:async';
+import 'dart:convert';
import 'dart:developer';
import 'dart:io';
+import 'dart:math' show Random;
import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
@@ -11,6 +13,7 @@ import 'package:hive/hive.dart';
import 'package:pilipala/utils/id_utils.dart';
import '../utils/storage.dart';
import '../utils/utils.dart';
+import 'api.dart';
import 'constants.dart';
import 'interceptor.dart';
@@ -24,6 +27,7 @@ class Request {
late bool enableSystemProxy;
late String systemProxyHost;
late String systemProxyPort;
+ static final RegExp spmPrefixExp = RegExp(r'');
/// 设置cookie
static setCookie() async {
@@ -51,13 +55,12 @@ class Request {
}
setOptionsHeaders(userInfo, userInfo != null && userInfo.mid != null);
- if (cookie.isEmpty) {
- try {
- await Request().get(HttpString.baseUrl);
- } catch (e) {
- log("setCookie, ${e.toString()}");
- }
+ try {
+ await buvidActivate();
+ } catch (e) {
+ log("setCookie, ${e.toString()}");
}
+
final String cookieString = cookie
.map((Cookie cookie) => '${cookie.name}=${cookie.value}')
.join('; ');
@@ -87,6 +90,33 @@ class Request {
dio.options.headers['referer'] = 'https://www.bilibili.com/';
}
+ static Future buvidActivate() async {
+ var html = await Request().get(Api.dynamicSpmPrefix);
+ String spmPrefix = spmPrefixExp.firstMatch(html.data)!.group(1)!;
+ Random rand = Random();
+ String rand_png_end = base64.encode(
+ List.generate(32, (_) => rand.nextInt(256)) +
+ List.filled(4, 0) +
+ [73, 69, 78, 68] +
+ List.generate(4, (_) => rand.nextInt(256))
+ );
+
+ String jsonData = json.encode({
+ '3064': 1,
+ '39c8': '${spmPrefix}.fp.risk',
+ '3c43': {
+ 'adca': 'Linux',
+ 'bfe9': rand_png_end.substring(rand_png_end.length - 50),
+ },
+ });
+
+ await Request().post(
+ Api.activateBuvidApi,
+ data: {'payload': jsonData},
+ options: Options(contentType: 'application/json')
+ );
+ }
+
/*
* config it and create
*/
diff --git a/lib/http/live.dart b/lib/http/live.dart
index c62fb6bd..e624120e 100644
--- a/lib/http/live.dart
+++ b/lib/http/live.dart
@@ -1,5 +1,6 @@
import '../models/live/item.dart';
import '../models/live/room_info.dart';
+import '../models/live/room_info_h5.dart';
import 'api.dart';
import 'init.dart';
@@ -46,4 +47,22 @@ 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 bf84b6eb..1af0f9a4 100644
--- a/lib/http/member.dart
+++ b/lib/http/member.dart
@@ -79,6 +79,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,7 +90,11 @@ 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),
+ 'dm_img_inter': '{"ds":[],"wh":[0,0,0],"of":[0,0,0]}',
});
var res = await Request().get(
Api.memberArchive,
@@ -101,10 +107,13 @@ class MemberHttp {
'data': MemberArchiveDataModel.fromJson(res.data['data'])
};
} else {
+ Map errMap = {
+ -352: '风控校验失败,请检查登录状态',
+ };
return {
'status': false,
'data': [],
- 'msg': res.data['message'],
+ 'msg': errMap[res.data['code']] ?? res.data['message'],
};
}
}
@@ -123,10 +132,13 @@ class MemberHttp {
'data': DynamicsDataModel.fromJson(res.data['data']),
};
} else {
+ Map errMap = {
+ -352: '风控校验失败,请检查登录状态',
+ };
return {
'status': false,
'data': [],
- 'msg': res.data['message'],
+ 'msg': errMap[res.data['code']] ?? res.data['message'],
};
}
}
@@ -461,4 +473,41 @@ class MemberHttp {
};
}
}
+
+ // 搜索follow
+ static Future getfollowSearch({
+ required int mid,
+ required int ps,
+ required int pn,
+ required String name,
+ }) async {
+ Map data = {
+ 'vmid': mid,
+ 'pn': pn,
+ 'ps': ps,
+ 'order': 'desc',
+ 'order_type': 'attention',
+ 'gaia_source': 'main_web',
+ 'name': name,
+ 'web_location': 333.999,
+ };
+ Map params = await WbiSign().makSign(data);
+ var res = await Request().get(Api.followSearch, data: {
+ ...data,
+ 'w_rid': params['w_rid'],
+ 'wts': params['wts'],
+ });
+ if (res.data['code'] == 0) {
+ return {
+ 'status': true,
+ 'data': FollowDataModel.fromJson(res.data['data'])
+ };
+ } else {
+ return {
+ 'status': false,
+ 'data': [],
+ 'msg': res.data['message'],
+ };
+ }
+ }
}
diff --git a/lib/http/msg.dart b/lib/http/msg.dart
index 70af5b55..d1d31958 100644
--- a/lib/http/msg.dart
+++ b/lib/http/msg.dart
@@ -1,3 +1,4 @@
+import 'dart:math';
import '../models/msg/account.dart';
import '../models/msg/session.dart';
import '../utils/wbi_sign.dart';
@@ -22,14 +23,22 @@ class MsgHttp {
Map signParams = await WbiSign().makSign(params);
var res = await Request().get(Api.sessionList, data: signParams);
if (res.data['code'] == 0) {
- return {
- 'status': true,
- 'data': SessionDataModel.fromJson(res.data['data']),
- };
+ try {
+ return {
+ 'status': true,
+ 'data': SessionDataModel.fromJson(res.data['data']),
+ };
+ } catch (err) {
+ return {
+ 'status': false,
+ 'data': [],
+ 'msg': err.toString(),
+ };
+ }
} else {
return {
'status': false,
- 'date': [],
+ 'data': [],
'msg': res.data['message'],
};
}
@@ -42,12 +51,16 @@ class MsgHttp {
'mobi_app': 'web',
});
if (res.data['code'] == 0) {
- return {
- 'status': true,
- 'data': res.data['data']
- .map((e) => AccountListModel.fromJson(e))
- .toList(),
- };
+ try {
+ return {
+ 'status': true,
+ 'data': res.data['data']
+ .map((e) => AccountListModel.fromJson(e))
+ .toList(),
+ };
+ } catch (err) {
+ print('err🔟: $err');
+ }
} else {
return {
'status': false,
@@ -86,4 +99,125 @@ 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']}",
+ };
+ }
+ }
+
+ // 发送私信
+ static Future sendMsg({
+ int? senderUid,
+ int? receiverId,
+ int? receiverType,
+ int? msgType,
+ dynamic content,
+ }) async {
+ String csrf = await Request.getCsrf();
+ Map params = await WbiSign().makSign({
+ 'msg[sender_uid]': senderUid,
+ 'msg[receiver_id]': receiverId,
+ 'msg[receiver_type]': receiverType ?? 1,
+ 'msg[msg_type]': msgType ?? 1,
+ 'msg[msg_status]': 0,
+ 'msg[dev_id]': getDevId(),
+ 'msg[timestamp]': DateTime.now().millisecondsSinceEpoch ~/ 1000,
+ 'msg[new_face_version]': 0,
+ 'msg[content]': content,
+ 'from_firework': 0,
+ 'build': 0,
+ 'mobi_app': 'web',
+ 'csrf_token': csrf,
+ 'csrf': csrf,
+ });
+ var res =
+ await Request().post(Api.sendMsg, queryParameters: {
+ ...params,
+ 'csrf_token': csrf,
+ 'csrf': csrf,
+ }, data: {
+ 'w_sender_uid': params['msg[sender_uid]'],
+ 'w_receiver_id': params['msg[receiver_id]'],
+ 'w_dev_id': params['msg[dev_id]'],
+ 'w_rid': params['w_rid'],
+ 'wts': params['wts'],
+ 'csrf_token': csrf,
+ 'csrf': csrf,
+ });
+ 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']}",
+ };
+ }
+ }
+
+ static String getDevId() {
+ final List b = [
+ '0',
+ '1',
+ '2',
+ '3',
+ '4',
+ '5',
+ '6',
+ '7',
+ '8',
+ '9',
+ 'A',
+ 'B',
+ 'C',
+ 'D',
+ 'E',
+ 'F'
+ ];
+ final List s = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".split('');
+ for (int i = 0; i < s.length; i++) {
+ if ('-' == s[i] || '4' == s[i]) {
+ continue;
+ }
+ final int randomInt = Random().nextInt(16);
+ if ('x' == s[i]) {
+ s[i] = b[randomInt];
+ } else {
+ s[i] = b[3 & randomInt | 8];
+ }
+ }
+ return s.join();
+ }
}
diff --git a/lib/http/reply.dart b/lib/http/reply.dart
index fab433fc..f080ed51 100644
--- a/lib/http/reply.dart
+++ b/lib/http/reply.dart
@@ -1,4 +1,5 @@
import '../models/video/reply/data.dart';
+import '../models/video/reply/emote.dart';
import 'api.dart';
import 'init.dart';
@@ -100,4 +101,23 @@ class ReplyHttp {
};
}
}
+
+ static Future getEmoteList({String? business}) async {
+ var res = await Request().get(Api.emojiList, data: {
+ 'business': business ?? 'reply',
+ 'web_location': '333.1245',
+ });
+ if (res.data['code'] == 0) {
+ return {
+ 'status': true,
+ 'data': EmoteModelData.fromJson(res.data['data']),
+ };
+ } else {
+ return {
+ 'status': false,
+ 'date': [],
+ 'msg': res.data['message'],
+ };
+ }
+ }
}
diff --git a/lib/http/user.dart b/lib/http/user.dart
index c1f86285..7d3def4e 100644
--- a/lib/http/user.dart
+++ b/lib/http/user.dart
@@ -6,6 +6,8 @@ import '../models/user/fav_folder.dart';
import '../models/user/history.dart';
import '../models/user/info.dart';
import '../models/user/stat.dart';
+import '../models/user/sub_detail.dart';
+import '../models/user/sub_folder.dart';
import 'api.dart';
import 'init.dart';
@@ -305,4 +307,46 @@ class UserHttp {
return {'status': false, 'msg': res.data['message']};
}
}
+
+ // 我的订阅
+ static Future userSubFolder({
+ required int mid,
+ required int pn,
+ required int ps,
+ }) async {
+ var res = await Request().get(Api.userSubFolder, data: {
+ 'up_mid': mid,
+ 'ps': ps,
+ 'pn': pn,
+ 'platform': 'web',
+ });
+ if (res.data['code'] == 0) {
+ return {
+ 'status': true,
+ 'data': SubFolderModelData.fromJson(res.data['data'])
+ };
+ } else {
+ return {'status': false, 'msg': res.data['message']};
+ }
+ }
+
+ static Future userSubFolderDetail({
+ required int seasonId,
+ required int pn,
+ required int ps,
+ }) async {
+ var res = await Request().get(Api.userSubFolderDetail, data: {
+ 'season_id': seasonId,
+ 'ps': ps,
+ 'pn': pn,
+ });
+ if (res.data['code'] == 0) {
+ return {
+ 'status': true,
+ 'data': SubDetailModelData.fromJson(res.data['data'])
+ };
+ } else {
+ return {'status': false, 'msg': res.data['message']};
+ }
+ }
}
diff --git a/lib/http/video.dart b/lib/http/video.dart
index e7b09801..73e8b698 100644
--- a/lib/http/video.dart
+++ b/lib/http/video.dart
@@ -9,6 +9,7 @@ import '../models/user/fav_folder.dart';
import '../models/video/ai.dart';
import '../models/video/play/url.dart';
import '../models/video_detail_res.dart';
+import '../utils/recommend_filter.dart';
import '../utils/storage.dart';
import '../utils/wbi_sign.dart';
import 'api.dart';
@@ -46,8 +47,13 @@ class VideoHttp {
setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]);
for (var i in res.data['data']['item']) {
//过滤掉live与ad,以及拉黑用户
- if (i['goto'] == 'av' && !blackMidsList.contains(i['owner']['mid'])) {
- list.add(RecVideoItemModel.fromJson(i));
+ if (i['goto'] == 'av' &&
+ (i['owner'] != null &&
+ !blackMidsList.contains(i['owner']['mid']))) {
+ RecVideoItemModel videoItem = RecVideoItemModel.fromJson(i);
+ if (!RecommendFilter.filter(videoItem)) {
+ list.add(videoItem);
+ }
}
}
return {'status': true, 'data': list};
@@ -59,7 +65,9 @@ class VideoHttp {
}
}
- static Future rcmdVideoListApp({int? ps, required int freshIdx}) async {
+ // 添加额外的loginState变量模拟未登录状态
+ static Future rcmdVideoListApp(
+ {bool loginStatus = true, required int freshIdx}) async {
try {
var res = await Request().get(
Api.recommendListApp,
@@ -72,9 +80,11 @@ class VideoHttp {
'device_name': 'vivo',
'pull': freshIdx == 0 ? 'true' : 'false',
'appkey': Constants.appKey,
- 'access_key': localCache
- .get(LocalCacheKey.accessKey, defaultValue: {})['value'] ??
- ''
+ 'access_key': loginStatus
+ ? (localCache.get(LocalCacheKey.accessKey,
+ defaultValue: {})['value'] ??
+ '')
+ : ''
},
);
if (res.data['code'] == 0) {
@@ -87,12 +97,15 @@ class VideoHttp {
(!enableRcmdDynamic ? i['card_goto'] != 'picture' : true) &&
(i['args'] != null &&
!blackMidsList.contains(i['args']['up_mid']))) {
- list.add(RecVideoItemAppModel.fromJson(i));
+ RecVideoItemAppModel videoItem = RecVideoItemAppModel.fromJson(i);
+ if (!RecommendFilter.filter(videoItem)) {
+ list.add(videoItem);
+ }
}
}
return {'status': true, 'data': list};
} else {
- return {'status': false, 'data': [], 'msg': ''};
+ return {'status': false, 'data': [], 'msg': res.data['message']};
}
} catch (err) {
return {'status': false, 'data': [], 'msg': err.toString()};
@@ -117,7 +130,7 @@ class VideoHttp {
}
return {'status': true, 'data': list};
} else {
- return {'status': false, 'data': []};
+ return {'status': false, 'data': [], 'msg': res.data['message']};
}
} catch (err) {
return {'status': false, 'data': [], 'msg': err};
@@ -203,7 +216,10 @@ class VideoHttp {
if (res.data['code'] == 0) {
List list = [];
for (var i in res.data['data']) {
- list.add(HotVideoItemModel.fromJson(i));
+ HotVideoItemModel videoItem = HotVideoItemModel.fromJson(i);
+ if (!RecommendFilter.filter(videoItem, relatedVideos: true)) {
+ list.add(videoItem);
+ }
}
return {'status': true, 'data': list};
} else {
@@ -224,10 +240,11 @@ class VideoHttp {
// 获取投币状态
static Future hasCoinVideo({required String bvid}) async {
var res = await Request().get(Api.hasCoinVideo, data: {'bvid': bvid});
+ print('res: $res');
if (res.data['code'] == 0) {
return {'status': true, 'data': res.data['data']};
} else {
- return {'status': true, 'data': []};
+ return {'status': false, 'data': []};
}
}
@@ -305,7 +322,7 @@ class VideoHttp {
if (res.data['code'] == 0) {
return {'status': true, 'data': res.data['data']};
} else {
- return {'status': false, 'data': []};
+ return {'status': false, 'data': [], 'msg': res.data['message']};
}
}
@@ -361,7 +378,7 @@ class VideoHttp {
if (res.data['code'] == 0) {
return {'status': true, 'data': res.data['data']};
} else {
- return {'status': true, 'data': []};
+ return {'status': false, 'data': []};
}
}
@@ -377,7 +394,7 @@ class VideoHttp {
if (res.data['code'] == 0) {
return {'status': true, 'data': res.data['data']};
} else {
- return {'status': true, 'data': []};
+ return {'status': false, 'data': []};
}
}
@@ -433,6 +450,8 @@ class VideoHttp {
});
if (res.data['code'] == 0) {
return {'status': true, 'data': res.data['data']};
+ } else {
+ return {'status': false, 'data': null, 'msg': res.data['message']};
}
}
@@ -453,10 +472,30 @@ class VideoHttp {
'data': AiConclusionModel.fromJson(res.data['data']),
};
} else {
- return {
- 'status': false,
- 'data': []
- };
+ return {'status': false, 'data': []};
+ }
+ }
+
+ // 视频排行
+ static Future getRankVideoList(int rid) async {
+ try {
+ var rankApi = "${Api.getRankApi}?rid=$rid&type=all";
+ var res = await Request().get(rankApi);
+ if (res.data['code'] == 0) {
+ List list = [];
+ List blackMidsList =
+ setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]);
+ for (var i in res.data['data']['list']) {
+ if (!blackMidsList.contains(i['owner']['mid'])) {
+ list.add(HotVideoItemModel.fromJson(i));
+ }
+ }
+ return {'status': true, 'data': list};
+ } else {
+ return {'status': false, 'data': [], 'msg': res.data['message']};
+ }
+ } catch (err) {
+ return {'status': false, 'data': [], 'msg': err};
}
}
}
diff --git a/lib/main.dart b/lib/main.dart
index bdf3f6b7..44bb1dcd 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -16,11 +16,13 @@ import 'package:pilipala/pages/search/index.dart';
import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/router/app_pages.dart';
import 'package:pilipala/pages/main/view.dart';
+import 'package:pilipala/services/disable_battery_opt.dart';
import 'package:pilipala/services/service_locator.dart';
import 'package:pilipala/utils/app_scheme.dart';
import 'package:pilipala/utils/data.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:media_kit/media_kit.dart'; // Provides [Player], [Media], [Playlist] etc.
+import 'package:pilipala/utils/recommend_filter.dart';
import 'package:catcher_2/catcher_2.dart';
import './services/loggeer.dart';
@@ -34,6 +36,7 @@ void main() async {
await setupServiceLocator();
Request();
await Request.setCookie();
+ RecommendFilter();
// 异常捕获 logo记录
final Catcher2Options debugConfig = Catcher2Options(
@@ -68,8 +71,8 @@ void main() async {
statusBarColor: Colors.transparent,
));
Data.init();
- GStrorage.lazyInit();
PiliSchame.init();
+ DisableBatteryOpt();
});
}
diff --git a/lib/models/common/dynamic_badge_mode.dart b/lib/models/common/dynamic_badge_mode.dart
new file mode 100644
index 00000000..2609c5e2
--- /dev/null
+++ b/lib/models/common/dynamic_badge_mode.dart
@@ -0,0 +1,9 @@
+enum DynamicBadgeMode { hidden, point, number }
+
+extension DynamicBadgeModeDesc on DynamicBadgeMode {
+ String get description => ['隐藏', '红点', '数字'][index];
+}
+
+extension DynamicBadgeModeCode on DynamicBadgeMode {
+ int get code => [0, 1, 2][index];
+}
diff --git a/lib/models/common/dynamics_type.dart b/lib/models/common/dynamics_type.dart
index 337f6aec..f4e20a4b 100644
--- a/lib/models/common/dynamics_type.dart
+++ b/lib/models/common/dynamics_type.dart
@@ -7,5 +7,5 @@ enum DynamicsType {
extension BusinessTypeExtension on DynamicsType {
String get values => ['all', 'video', 'pgc', 'article'][index];
- String get labels => ['全部', '视频', '追番', '专栏'][index];
+ String get labels => ['全部', '投稿', '番剧', '专栏'][index];
}
diff --git a/lib/models/common/gesture_mode.dart b/lib/models/common/gesture_mode.dart
new file mode 100644
index 00000000..1149ae12
--- /dev/null
+++ b/lib/models/common/gesture_mode.dart
@@ -0,0 +1,12 @@
+enum FullScreenGestureMode {
+ /// 从上滑到下
+ fromToptoBottom,
+
+ /// 从下滑到上
+ fromBottomtoTop,
+}
+
+extension FullScreenGestureModeExtension on FullScreenGestureMode {
+ String get values => ['fromToptoBottom', 'fromBottomtoTop'][index];
+ String get labels => ['从上往下滑进入全屏', '从下往上滑进入全屏'][index];
+}
diff --git a/lib/models/common/index.dart b/lib/models/common/index.dart
new file mode 100644
index 00000000..89a05076
--- /dev/null
+++ b/lib/models/common/index.dart
@@ -0,0 +1,4 @@
+library commonn_model;
+
+export './business_type.dart';
+export './gesture_mode.dart';
diff --git a/lib/models/common/nav_bar_config.dart b/lib/models/common/nav_bar_config.dart
new file mode 100644
index 00000000..9ebe8e6f
--- /dev/null
+++ b/lib/models/common/nav_bar_config.dart
@@ -0,0 +1,56 @@
+import 'package:flutter/material.dart';
+
+List defaultNavigationBars = [
+ {
+ 'id': 0,
+ 'icon': const Icon(
+ Icons.home_outlined,
+ size: 21,
+ ),
+ 'selectIcon': const Icon(
+ Icons.home,
+ size: 21,
+ ),
+ 'label': "首页",
+ 'count': 0,
+ },
+ {
+ 'id': 1,
+ 'icon': const Icon(
+ Icons.trending_up,
+ size: 21,
+ ),
+ 'selectIcon': const Icon(
+ Icons.trending_up_outlined,
+ size: 21,
+ ),
+ 'label': "排行榜",
+ 'count': 0,
+ },
+ {
+ 'id': 2,
+ 'icon': const Icon(
+ Icons.motion_photos_on_outlined,
+ size: 21,
+ ),
+ 'selectIcon': const Icon(
+ Icons.motion_photos_on,
+ size: 21,
+ ),
+ 'label': "动态",
+ 'count': 0,
+ },
+ {
+ 'id': 3,
+ 'icon': const Icon(
+ Icons.video_collection_outlined,
+ size: 20,
+ ),
+ 'selectIcon': const Icon(
+ Icons.video_collection,
+ size: 21,
+ ),
+ 'label': "媒体库",
+ 'count': 0,
+ }
+];
diff --git a/lib/models/common/rank_type.dart b/lib/models/common/rank_type.dart
new file mode 100644
index 00000000..2ce6d3b5
--- /dev/null
+++ b/lib/models/common/rank_type.dart
@@ -0,0 +1,240 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:pilipala/pages/rank/zone/index.dart';
+
+enum RandType {
+ all,
+ creation,
+ animation,
+ music,
+ dance,
+ game,
+ knowledge,
+ technology,
+ sport,
+ car,
+ life,
+ food,
+ animal,
+ madness,
+ fashion,
+ entertainment,
+ film,
+ origin,
+ rookie
+}
+
+extension RankTypeDesc on RandType {
+ String get description => [
+ '全站',
+ '国创相关',
+ '动画',
+ '音乐',
+ '舞蹈',
+ '游戏',
+ '知识',
+ '科技',
+ '运动',
+ '汽车',
+ '生活',
+ '美食',
+ '动物圈',
+ '鬼畜',
+ '时尚',
+ '娱乐',
+ '影视'
+ ][index];
+
+ String get id => [
+ 'all',
+ 'creation',
+ 'animation',
+ 'music',
+ 'dance',
+ 'game',
+ 'knowledge',
+ 'technology',
+ 'sport',
+ 'car',
+ 'life',
+ 'food',
+ 'animal',
+ 'madness',
+ 'fashion',
+ 'entertainment',
+ 'film'
+ ][index];
+}
+
+List tabsConfig = [
+ {
+ 'icon': const Icon(
+ Icons.live_tv_outlined,
+ size: 15,
+ ),
+ 'label': '全站',
+ 'type': RandType.all,
+ 'ctr': Get.put,
+ 'page': const ZonePage(rid: 0),
+ },
+ {
+ 'icon': const Icon(
+ Icons.live_tv_outlined,
+ size: 15,
+ ),
+ 'label': '国创相关',
+ 'type': RandType.creation,
+ 'ctr': Get.put,
+ 'page': const ZonePage(rid: 168),
+ },
+ {
+ 'icon': const Icon(
+ Icons.live_tv_outlined,
+ size: 15,
+ ),
+ 'label': '动画',
+ 'type': RandType.animation,
+ 'ctr': Get.put,
+ 'page': const ZonePage(rid: 1),
+ },
+ {
+ 'icon': const Icon(
+ Icons.live_tv_outlined,
+ size: 15,
+ ),
+ 'label': '音乐',
+ 'type': RandType.music,
+ 'ctr': Get.put,
+ 'page': const ZonePage(rid: 3),
+ },
+ {
+ 'icon': const Icon(
+ Icons.live_tv_outlined,
+ size: 15,
+ ),
+ 'label': '舞蹈',
+ 'type': RandType.dance,
+ 'ctr': Get.put,
+ 'page': const ZonePage(rid: 129),
+ },
+ {
+ 'icon': const Icon(
+ Icons.live_tv_outlined,
+ size: 15,
+ ),
+ 'label': '游戏',
+ 'type': RandType.game,
+ 'ctr': Get.put,
+ 'page': const ZonePage(rid: 4),
+ },
+ {
+ 'icon': const Icon(
+ Icons.live_tv_outlined,
+ size: 15,
+ ),
+ 'label': '知识',
+ 'type': RandType.knowledge,
+ 'ctr': Get.put,
+ 'page': const ZonePage(rid: 36),
+ },
+ {
+ 'icon': const Icon(
+ Icons.live_tv_outlined,
+ size: 15,
+ ),
+ 'label': '科技',
+ 'type': RandType.technology,
+ 'ctr': Get.put,
+ 'page': const ZonePage(rid: 188),
+ },
+ {
+ 'icon': const Icon(
+ Icons.live_tv_outlined,
+ size: 15,
+ ),
+ 'label': '运动',
+ 'type': RandType.sport,
+ 'ctr': Get.put,
+ 'page': const ZonePage(rid: 234),
+ },
+ {
+ 'icon': const Icon(
+ Icons.live_tv_outlined,
+ size: 15,
+ ),
+ 'label': '汽车',
+ 'type': RandType.car,
+ 'ctr': Get.put,
+ 'page': const ZonePage(rid: 223),
+ },
+ {
+ 'icon': const Icon(
+ Icons.live_tv_outlined,
+ size: 15,
+ ),
+ 'label': '生活',
+ 'type': RandType.life,
+ 'ctr': Get.put,
+ 'page': const ZonePage(rid: 160),
+ },
+ {
+ 'icon': const Icon(
+ Icons.live_tv_outlined,
+ size: 15,
+ ),
+ 'label': '美食',
+ 'type': RandType.food,
+ 'ctr': Get.put,
+ 'page': const ZonePage(rid: 211),
+ },
+ {
+ 'icon': const Icon(
+ Icons.live_tv_outlined,
+ size: 15,
+ ),
+ 'label': '动物圈',
+ 'type': RandType.animal,
+ 'ctr': Get.put,
+ 'page': const ZonePage(rid: 217),
+ },
+ {
+ 'icon': const Icon(
+ Icons.live_tv_outlined,
+ size: 15,
+ ),
+ 'label': '鬼畜',
+ 'type': RandType.madness,
+ 'ctr': Get.put,
+ 'page': const ZonePage(rid: 119),
+ },
+ {
+ 'icon': const Icon(
+ Icons.live_tv_outlined,
+ size: 15,
+ ),
+ 'label': '时尚',
+ 'type': RandType.fashion,
+ 'ctr': Get.put,
+ 'page': const ZonePage(rid: 155),
+ },
+ {
+ 'icon': const Icon(
+ Icons.live_tv_outlined,
+ size: 15,
+ ),
+ 'label': '娱乐',
+ 'type': RandType.entertainment,
+ 'ctr': Get.put,
+ 'page': const ZonePage(rid: 5),
+ },
+ {
+ 'icon': const Icon(
+ Icons.live_tv_outlined,
+ size: 15,
+ ),
+ 'label': '影视',
+ 'type': RandType.film,
+ 'ctr': Get.put,
+ 'page': const ZonePage(rid: 181),
+ }
+];
diff --git a/lib/models/common/rcmd_type.dart b/lib/models/common/rcmd_type.dart
index dbb64b15..2dfdad1c 100644
--- a/lib/models/common/rcmd_type.dart
+++ b/lib/models/common/rcmd_type.dart
@@ -1,7 +1,7 @@
// 首页推荐类型
-enum RcmdType { web, app }
+enum RcmdType { web, app, notLogin }
extension RcmdTypeExtension on RcmdType {
- String get values => ['web', 'app'][index];
- String get labels => ['web端', 'app端'][index];
+ String get values => ['web', 'app', 'notLogin'][index];
+ String get labels => ['web端', 'app端', '游客模式'][index];
}
diff --git a/lib/models/common/reply_sort_type.dart b/lib/models/common/reply_sort_type.dart
index 7c203c13..89da82b3 100644
--- a/lib/models/common/reply_sort_type.dart
+++ b/lib/models/common/reply_sort_type.dart
@@ -1,6 +1,6 @@
-enum ReplySortType { time, like, reply }
+enum ReplySortType { time, like }
extension ReplySortTypeExtension on ReplySortType {
- String get titles => ['最新评论', '最热评论', '回复最多'][index];
- String get labels => ['最新', '最热', '最多回复'][index];
+ String get titles => ['最新评论', '最热评论'][index];
+ String get labels => ['最新', '最热'][index];
}
diff --git a/lib/models/dynamics/up.dart b/lib/models/dynamics/up.dart
index cfd1fa7d..9bb82f70 100644
--- a/lib/models/dynamics/up.dart
+++ b/lib/models/dynamics/up.dart
@@ -2,18 +2,28 @@ class FollowUpModel {
FollowUpModel({
this.liveUsers,
this.upList,
+ this.liveList,
+ this.myInfo,
});
LiveUsers? liveUsers;
List? upList;
+ List? liveList;
+ MyInfo? myInfo;
FollowUpModel.fromJson(Map json) {
liveUsers = json['live_users'] != null
? LiveUsers.fromJson(json['live_users'])
: null;
+ liveList = json['live_users'] != null
+ ? json['live_users']['items']
+ .map((e) => LiveUserItem.fromJson(e))
+ .toList()
+ : [];
upList = json['up_list'] != null
? json['up_list'].map((e) => UpItem.fromJson(e)).toList()
: [];
+ myInfo = json['my_info'] != null ? MyInfo.fromJson(json['my_info']) : null;
}
}
@@ -93,3 +103,21 @@ class UpItem {
uname = json['uname'];
}
}
+
+class MyInfo {
+ MyInfo({
+ this.face,
+ this.mid,
+ this.name,
+ });
+
+ String? face;
+ int? mid;
+ String? name;
+
+ MyInfo.fromJson(Map json) {
+ face = json['face'];
+ mid = json['mid'];
+ name = json['name'];
+ }
+}
diff --git a/lib/models/github/latest.dart b/lib/models/github/latest.dart
index 8730a4ba..c4b88b63 100644
--- a/lib/models/github/latest.dart
+++ b/lib/models/github/latest.dart
@@ -17,8 +17,9 @@ class LatestDataModel {
url = json['url'];
tagName = json['tag_name'];
createdAt = json['created_at'];
- assets =
- json['assets'].map((e) => AssetItem.fromJson(e)).toList();
+ assets = json['assets'] != null
+ ? json['assets'].map((e) => AssetItem.fromJson(e)).toList()
+ : [];
body = json['body'];
}
}
diff --git a/lib/models/home/rcmd/result.dart b/lib/models/home/rcmd/result.dart
index d5bc7d89..78747d1a 100644
--- a/lib/models/home/rcmd/result.dart
+++ b/lib/models/home/rcmd/result.dart
@@ -1,3 +1,5 @@
+import 'package:pilipala/utils/id_utils.dart';
+
class RecVideoItemAppModel {
RecVideoItemAppModel({
this.id,
@@ -28,7 +30,7 @@ class RecVideoItemAppModel {
int? cid;
String? pic;
RcmdStat? stat;
- String? duration;
+ int? duration;
String? title;
int? isFollowed;
RcmdOwner? owner;
@@ -50,17 +52,32 @@ class RecVideoItemAppModel {
? json['player_args']['aid']
: int.parse(json['param'] ?? '-1');
aid = json['player_args'] != null ? json['player_args']['aid'] : -1;
- bvid = null;
+ bvid = json['player_args'] != null
+ ? IdUtils.av2bv(json['player_args']['aid'])
+ : '';
cid = json['player_args'] != null ? json['player_args']['cid'] : -1;
pic = json['cover'];
stat = RcmdStat.fromJson(json);
- duration = json['cover_right_text'];
+ // 改用player_args中的duration作为原始数据(秒数)
+ duration =
+ json['player_args'] != null ? json['player_args']['duration'] : -1;
+ //duration = json['cover_right_text'];
title = json['title'];
- isFollowed = 0;
owner = RcmdOwner.fromJson(json);
rcmdReason = json['rcmd_reason_style'] != null
? RcmdReason.fromJson(json['rcmd_reason_style'])
: null;
+ // 由于app端api并不会直接返回与owner的关注状态
+ // 所以借用推荐原因是否为“已关注”、“新关注”等判别关注状态,从而与web端接口等效
+ isFollowed = rcmdReason != null &&
+ rcmdReason!.content != null &&
+ rcmdReason!.content!.contains('关注')
+ ? 1
+ : 0;
+ // 如果是,就无需再显示推荐原因,交由view统一处理即可
+ if (isFollowed == 1) {
+ rcmdReason = null;
+ }
goto = json['goto'];
param = int.parse(json['param']);
uri = json['uri'];
diff --git a/lib/models/live/quality.dart b/lib/models/live/quality.dart
new file mode 100644
index 00000000..677d615b
--- /dev/null
+++ b/lib/models/live/quality.dart
@@ -0,0 +1,43 @@
+enum LiveQuality {
+ dolby,
+ super4K,
+ origin,
+ bluRay,
+ superHD,
+ smooth,
+ flunt,
+}
+
+extension LiveQualityCode on LiveQuality {
+ static final List _codeList = [
+ 30000,
+ 20000,
+ 10000,
+ 400,
+ 250,
+ 150,
+ 80,
+ ];
+ int get code => _codeList[index];
+
+ static LiveQuality? fromCode(int code) {
+ final index = _codeList.indexOf(code);
+ if (index != -1) {
+ return LiveQuality.values[index];
+ }
+ return null;
+ }
+}
+
+extension VideoQualityDesc on LiveQuality {
+ static final List _descList = [
+ '杜比',
+ '4K',
+ '原画',
+ '蓝光',
+ '超清',
+ '高清',
+ '流畅',
+ ];
+ get description => _descList[index];
+}
diff --git a/lib/models/live/room_info_h5.dart b/lib/models/live/room_info_h5.dart
new file mode 100644
index 00000000..a0c19621
--- /dev/null
+++ b/lib/models/live/room_info_h5.dart
@@ -0,0 +1,130 @@
+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/member/archive.dart b/lib/models/member/archive.dart
index 5d2ea77e..d735ab7c 100644
--- a/lib/models/member/archive.dart
+++ b/lib/models/member/archive.dart
@@ -142,7 +142,7 @@ class Stat {
Stat.fromJson(Map json) {
view = json["play"];
- danmaku = json['comment'];
+ danmaku = json['video_review'];
}
}
diff --git a/lib/models/model_rec_video_item.dart b/lib/models/model_rec_video_item.dart
index bd42fd82..1503f192 100644
--- a/lib/models/model_rec_video_item.dart
+++ b/lib/models/model_rec_video_item.dart
@@ -1,5 +1,3 @@
-import 'package:pilipala/utils/utils.dart';
-
import './model_owner.dart';
import 'package:hive/hive.dart';
@@ -38,7 +36,7 @@ class RecVideoItemModel {
@HiveField(6)
String? title = '';
@HiveField(7)
- String? duration = '';
+ int? duration = -1;
@HiveField(8)
int? pubdate = -1;
@HiveField(9)
@@ -58,7 +56,7 @@ class RecVideoItemModel {
uri = json["uri"];
pic = json["pic"];
title = json["title"];
- duration = Utils.tampToSeektime(json["duration"]);
+ duration = json["duration"];
pubdate = json["pubdate"];
owner = Owner.fromJson(json["owner"]);
stat = Stat.fromJson(json["stat"]);
@@ -77,14 +75,15 @@ class Stat {
this.danmu,
});
@HiveField(0)
- String? view;
+ int? view;
@HiveField(1)
int? like;
@HiveField(2)
int? danmu;
Stat.fromJson(Map json) {
- view = Utils.numFormat(json["view"]);
+ // 无需在model中转换以保留原始数据,在view层处理即可
+ view = json["view"];
like = json["like"];
danmu = json['danmaku'];
}
diff --git a/lib/models/model_rec_video_item.g.dart b/lib/models/model_rec_video_item.g.dart
index 1de6ab03..dc614354 100644
--- a/lib/models/model_rec_video_item.g.dart
+++ b/lib/models/model_rec_video_item.g.dart
@@ -24,7 +24,7 @@ class RecVideoItemModelAdapter extends TypeAdapter {
uri: fields[4] as String?,
pic: fields[5] as String?,
title: fields[6] as String?,
- duration: fields[7] as String?,
+ duration: fields[7] as int?,
pubdate: fields[8] as int?,
owner: fields[9] as Owner?,
stat: fields[10] as Stat?,
@@ -87,7 +87,7 @@ class StatAdapter extends TypeAdapter {
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return Stat(
- view: fields[0] as String?,
+ view: fields[0] as int?,
like: fields[1] as int?,
danmu: fields[2] as int?,
);
diff --git a/lib/models/msg/session.dart b/lib/models/msg/session.dart
index ea241249..b6c1b6a6 100644
--- a/lib/models/msg/session.dart
+++ b/lib/models/msg/session.dart
@@ -8,7 +8,7 @@ class SessionDataModel {
this.hasMore,
});
- List? sessionList;
+ List? sessionList;
int? hasMore;
SessionDataModel.fromJson(Map json) {
@@ -121,35 +121,37 @@ class LastMsg {
this.msgKey,
this.msgStatus,
this.notifyCode,
- this.newFaceVersion,
+ // this.newFaceVersion,
});
int? senderIid;
int? receiverType;
int? receiverId;
int? msgType;
- Map? content;
+ dynamic content;
int? msgSeqno;
int? timestamp;
String? atUids;
int? msgKey;
int? msgStatus;
String? notifyCode;
- int? newFaceVersion;
+ // int? newFaceVersion;
LastMsg.fromJson(Map json) {
senderIid = json['sender_uid'];
receiverType = json['receiver_type'];
receiverId = json['receiver_id'];
msgType = json['msg_type'];
- content = jsonDecode(json['content']);
+ content = json['content'] != null && json['content'] != ''
+ ? jsonDecode(json['content'])
+ : '';
msgSeqno = json['msg_seqno'];
timestamp = json['timestamp'];
atUids = json['at_uids'];
msgKey = json['msg_key'];
msgStatus = json['msg_status'];
notifyCode = json['notify_code'];
- newFaceVersion = json['new_face_version'];
+ // newFaceVersion = json['new_face_version'];
}
}
@@ -214,7 +216,9 @@ class MessageItem {
receiverId = json['receiver_id'];
// 1 文本 2 图片 18 系统提示 10 系统通知 5 撤回的消息
msgType = json['msg_type'];
- content = jsonDecode(json['content']);
+ content = json['content'] != null && json['content'] != ''
+ ? jsonDecode(json['content'])
+ : '';
msgSeqno = json['msg_seqno'];
timestamp = json['timestamp'];
atUids = json['at_uids'];
diff --git a/lib/models/search/result.dart b/lib/models/search/result.dart
index 3d381ed9..0067791c 100644
--- a/lib/models/search/result.dart
+++ b/lib/models/search/result.dart
@@ -85,7 +85,9 @@ class SearchVideoItemModel {
// title = json['title'].replaceAll(RegExp(r'<.*?>'), '');
title = Em.regTitle(json['title']);
description = json['description'];
- pic = 'https:${json['pic']}';
+ pic = json['pic'] != null && json['pic'].startsWith('//')
+ ? 'https:${json['pic']}'
+ : json['pic'] ?? '';
videoReview = json['video_review'];
pubdate = json['pubdate'];
senddate = json['senddate'];
diff --git a/lib/models/user/fav_folder.dart b/lib/models/user/fav_folder.dart
index 6d3f9975..c45e2de9 100644
--- a/lib/models/user/fav_folder.dart
+++ b/lib/models/user/fav_folder.dart
@@ -15,7 +15,7 @@ class FavFolderData {
? json['list']
.map((e) => FavFolderItemData.fromJson(e))
.toList()
- : [FavFolderItemData()];
+ : [];
hasMore = json['has_more'];
}
}
diff --git a/lib/models/user/sub_detail.dart b/lib/models/user/sub_detail.dart
new file mode 100644
index 00000000..a1e52e55
--- /dev/null
+++ b/lib/models/user/sub_detail.dart
@@ -0,0 +1,123 @@
+class SubDetailModelData {
+ DetailInfo? info;
+ List? medias;
+
+ SubDetailModelData({this.info, this.medias});
+
+ SubDetailModelData.fromJson(Map json) {
+ info = DetailInfo.fromJson(json['info']);
+ if (json['medias'] != null) {
+ medias = [];
+ json['medias'].forEach((v) {
+ medias!.add(SubDetailMediaItem.fromJson(v));
+ });
+ }
+ }
+}
+
+class SubDetailMediaItem {
+ int? id;
+ String? title;
+ String? cover;
+ String? pic;
+ int? duration;
+ int? pubtime;
+ String? bvid;
+ Map? upper;
+ Map? cntInfo;
+ int? enableVt;
+ String? vtDisplay;
+
+ SubDetailMediaItem({
+ this.id,
+ this.title,
+ this.cover,
+ this.pic,
+ this.duration,
+ this.pubtime,
+ this.bvid,
+ this.upper,
+ this.cntInfo,
+ this.enableVt,
+ this.vtDisplay,
+ });
+
+ SubDetailMediaItem.fromJson(Map json) {
+ id = json['id'];
+ title = json['title'];
+ cover = json['cover'];
+ pic = json['cover'];
+ duration = json['duration'];
+ pubtime = json['pubtime'];
+ bvid = json['bvid'];
+ upper = json['upper'];
+ cntInfo = json['cnt_info'];
+ enableVt = json['enable_vt'];
+ vtDisplay = json['vt_display'];
+ }
+
+ Map toJson() {
+ final data = {};
+ data['id'] = id;
+ data['title'] = title;
+ data['cover'] = cover;
+ data['duration'] = duration;
+ data['pubtime'] = pubtime;
+ data['bvid'] = bvid;
+ data['upper'] = upper;
+ data['cnt_info'] = cntInfo;
+ data['enable_vt'] = enableVt;
+ data['vt_display'] = vtDisplay;
+ return data;
+ }
+}
+
+class DetailInfo {
+ int? id;
+ int? seasonType;
+ String? title;
+ String? cover;
+ Map? upper;
+ Map? cntInfo;
+ int? mediaCount;
+ String? intro;
+ int? enableVt;
+
+ DetailInfo({
+ this.id,
+ this.seasonType,
+ this.title,
+ this.cover,
+ this.upper,
+ this.cntInfo,
+ this.mediaCount,
+ this.intro,
+ this.enableVt,
+ });
+
+ DetailInfo.fromJson(Map json) {
+ id = json['id'];
+ seasonType = json['season_type'];
+ title = json['title'];
+ cover = json['cover'];
+ upper = json['upper'];
+ cntInfo = json['cnt_info'];
+ mediaCount = json['media_count'];
+ intro = json['intro'];
+ enableVt = json['enable_vt'];
+ }
+
+ Map toJson() {
+ final data = {};
+ data['id'] = id;
+ data['season_type'] = seasonType;
+ data['title'] = title;
+ data['cover'] = cover;
+ data['upper'] = upper;
+ data['cnt_info'] = cntInfo;
+ data['media_count'] = mediaCount;
+ data['intro'] = intro;
+ data['enable_vt'] = enableVt;
+ return data;
+ }
+}
diff --git a/lib/models/user/sub_folder.dart b/lib/models/user/sub_folder.dart
new file mode 100644
index 00000000..d496a1cf
--- /dev/null
+++ b/lib/models/user/sub_folder.dart
@@ -0,0 +1,111 @@
+class SubFolderModelData {
+ final int? count;
+ final List? list;
+
+ SubFolderModelData({
+ this.count,
+ this.list,
+ });
+
+ factory SubFolderModelData.fromJson(Map json) {
+ return SubFolderModelData(
+ count: json['count'],
+ list: json['list'] != null
+ ? (json['list'] as List)
+ .map((i) => SubFolderItemData.fromJson(i))
+ .toList()
+ : null,
+ );
+ }
+}
+
+class SubFolderItemData {
+ final int? id;
+ final int? fid;
+ final int? mid;
+ final int? attr;
+ final String? title;
+ final String? cover;
+ final Upper? upper;
+ final int? coverType;
+ final String? intro;
+ final int? ctime;
+ final int? mtime;
+ final int? state;
+ final int? favState;
+ final int? mediaCount;
+ final int? viewCount;
+ final int? vt;
+ final int? playSwitch;
+ final int? type;
+ final String? link;
+ final String? bvid;
+
+ SubFolderItemData({
+ this.id,
+ this.fid,
+ this.mid,
+ this.attr,
+ this.title,
+ this.cover,
+ this.upper,
+ this.coverType,
+ this.intro,
+ this.ctime,
+ this.mtime,
+ this.state,
+ this.favState,
+ this.mediaCount,
+ this.viewCount,
+ this.vt,
+ this.playSwitch,
+ this.type,
+ this.link,
+ this.bvid,
+ });
+
+ factory SubFolderItemData.fromJson(Map json) {
+ return SubFolderItemData(
+ id: json['id'],
+ fid: json['fid'],
+ mid: json['mid'],
+ attr: json['attr'],
+ title: json['title'],
+ cover: json['cover'],
+ upper: json['upper'] != null ? Upper.fromJson(json['upper']) : null,
+ coverType: json['cover_type'],
+ intro: json['intro'],
+ ctime: json['ctime'],
+ mtime: json['mtime'],
+ state: json['state'],
+ favState: json['fav_state'],
+ mediaCount: json['media_count'],
+ viewCount: json['view_count'],
+ vt: json['vt'],
+ playSwitch: json['play_switch'],
+ type: json['type'],
+ link: json['link'],
+ bvid: json['bvid'],
+ );
+ }
+}
+
+class Upper {
+ final int? mid;
+ final String? name;
+ final String? face;
+
+ Upper({
+ this.mid,
+ this.name,
+ this.face,
+ });
+
+ factory Upper.fromJson(Map json) {
+ return Upper(
+ mid: json['mid'],
+ name: json['name'],
+ face: json['face'],
+ );
+ }
+}
diff --git a/lib/models/video/play/url.dart b/lib/models/video/play/url.dart
index 4c43cb00..792cd50d 100644
--- a/lib/models/video/play/url.dart
+++ b/lib/models/video/play/url.dart
@@ -34,6 +34,7 @@ class PlayUrlModel {
String? seekParam;
String? seekType;
Dash? dash;
+ List? durl;
List? supportFormats;
// String? highFormat;
int? lastPlayTime;
@@ -52,7 +53,8 @@ class PlayUrlModel {
videoCodecid = json['video_codecid'];
seekParam = json['seek_param'];
seekType = json['seek_type'];
- dash = Dash.fromJson(json['dash']);
+ dash = json['dash'] != null ? Dash.fromJson(json['dash']) : null;
+ durl = json['durl']?.map((e) => Durl.fromJson(e)).toList();
supportFormats = json['support_formats'] != null
? json['support_formats']
.map((e) => FormatItem.fromJson(e))
@@ -250,3 +252,30 @@ class Flac {
audio = json['audio'] != null ? AudioItem.fromJson(json['audio']) : null;
}
}
+
+class Durl {
+ Durl({
+ this.order,
+ this.length,
+ this.size,
+ this.ahead,
+ this.vhead,
+ this.url,
+ });
+
+ int? order;
+ int? length;
+ int? size;
+ String? ahead;
+ String? vhead;
+ String? url;
+
+ Durl.fromJson(Map json) {
+ order = json['order'];
+ length = json['length'];
+ size = json['size'];
+ ahead = json['ahead'];
+ vhead = json['vhead'];
+ url = json['url'];
+ }
+}
diff --git a/lib/models/video/reply/content.dart b/lib/models/video/reply/content.dart
index 9180ec97..d62a4bca 100644
--- a/lib/models/video/reply/content.dart
+++ b/lib/models/video/reply/content.dart
@@ -9,6 +9,7 @@ class ReplyContent {
this.vote,
this.richText,
this.isText,
+ this.topicsMeta,
});
String? message;
@@ -20,6 +21,7 @@ class ReplyContent {
Map? vote;
Map? richText;
bool? isText;
+ Map? topicsMeta;
ReplyContent.fromJson(Map json) {
message = json['message']
@@ -39,6 +41,7 @@ class ReplyContent {
richText = json['rich_text'] ?? {};
// 不包含@ 笔记 图片的时候,文字可折叠
isText = atNameToMid!.isEmpty && vote!.isEmpty && pictures!.isEmpty;
+ topicsMeta = json['topics_meta'] ?? {};
}
}
diff --git a/lib/models/video/reply/emote.dart b/lib/models/video/reply/emote.dart
new file mode 100644
index 00000000..b4071826
--- /dev/null
+++ b/lib/models/video/reply/emote.dart
@@ -0,0 +1,120 @@
+class EmoteModelData {
+ final List? packages;
+
+ EmoteModelData({
+ required this.packages,
+ });
+
+ factory EmoteModelData.fromJson(Map jsonRes) {
+ final List? packages =
+ jsonRes['packages'] is List ? [] : null;
+ if (packages != null) {
+ for (final dynamic item in jsonRes['packages']!) {
+ if (item != null) {
+ try {
+ packages.add(PackageItem.fromJson(item));
+ } catch (_) {}
+ }
+ }
+ }
+ return EmoteModelData(
+ packages: packages,
+ );
+ }
+}
+
+class PackageItem {
+ final int? id;
+ final String? text;
+ final String? url;
+ final int? mtime;
+ final int? type;
+ final int? attr;
+ final Meta? meta;
+ final List? emote;
+
+ PackageItem({
+ required this.id,
+ required this.text,
+ required this.url,
+ required this.mtime,
+ required this.type,
+ required this.attr,
+ required this.meta,
+ required this.emote,
+ });
+
+ factory PackageItem.fromJson(Map jsonRes) {
+ final List? emote = jsonRes['emote'] is List ? [] : null;
+ if (emote != null) {
+ for (final dynamic item in jsonRes['emote']!) {
+ if (item != null) {
+ try {
+ emote.add(Emote.fromJson(item));
+ } catch (_) {}
+ }
+ }
+ }
+ return PackageItem(
+ id: jsonRes['id'],
+ text: jsonRes['text'],
+ url: jsonRes['url'],
+ mtime: jsonRes['mtime'],
+ type: jsonRes['type'],
+ attr: jsonRes['attr'],
+ meta: Meta.fromJson(jsonRes['meta']),
+ emote: emote,
+ );
+ }
+}
+
+class Meta {
+ final int? size;
+ final List? suggest;
+
+ Meta({
+ required this.size,
+ required this.suggest,
+ });
+
+ factory Meta.fromJson(Map jsonRes) => Meta(
+ size: jsonRes['size'],
+ suggest: jsonRes['suggest'] is List ? [] : null,
+ );
+}
+
+class Emote {
+ final int? id;
+ final int? packageId;
+ final String? text;
+ final String? url;
+ final int? mtime;
+ final int? type;
+ final int? attr;
+ final Meta? meta;
+ final dynamic activity;
+
+ Emote({
+ required this.id,
+ required this.packageId,
+ required this.text,
+ required this.url,
+ required this.mtime,
+ required this.type,
+ required this.attr,
+ required this.meta,
+ required this.activity,
+ });
+
+ factory Emote.fromJson(Map jsonRes) => Emote(
+ id: jsonRes['id'],
+ packageId: jsonRes['package_id'],
+ text: jsonRes['text'],
+ url: jsonRes['url'],
+ mtime: jsonRes['mtime'],
+ type: jsonRes['type'],
+ attr: jsonRes['attr'],
+ meta: Meta.fromJson(jsonRes['meta']),
+ activity: jsonRes['activity'],
+ );
+}
diff --git a/lib/pages/about/index.dart b/lib/pages/about/index.dart
index 5df05d31..b381691a 100644
--- a/lib/pages/about/index.dart
+++ b/lib/pages/about/index.dart
@@ -7,6 +7,7 @@ import 'package:pilipala/http/index.dart';
import 'package:pilipala/models/github/latest.dart';
import 'package:pilipala/utils/utils.dart';
import 'package:url_launcher/url_launcher.dart';
+import '../../utils/cache_manage.dart';
class AboutPage extends StatefulWidget {
const AboutPage({super.key});
@@ -17,6 +18,19 @@ class AboutPage extends StatefulWidget {
class _AboutPageState extends State {
final AboutController _aboutController = Get.put(AboutController());
+ String cacheSize = '';
+
+ @override
+ void initState() {
+ super.initState();
+ // 读取缓存占用
+ getCacheSize();
+ }
+
+ Future getCacheSize() async {
+ final res = await CacheManage().loadApplicationCache();
+ setState(() => cacheSize = res);
+ }
@override
Widget build(BuildContext context) {
@@ -39,29 +53,54 @@ class _AboutPageState extends State {
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 6),
- Text(
- '使用Flutter开发的哔哩哔哩第三方客户端',
- style: TextStyle(color: Theme.of(context).colorScheme.outline),
- ),
- const SizedBox(height: 20),
Obx(
- () => ListTile(
- title: const Text('当前版本'),
- trailing: Text(_aboutController.currentVersion.value,
- style: subTitleStyle),
- ),
- ),
- Obx(
- () => ListTile(
- onTap: () => _aboutController.onUpdate(),
- title: const Text('最新版本'),
- trailing: Text(
- _aboutController.isLoading.value
- ? '正在获取'
- : _aboutController.isUpdate.value
- ? '有新版本 ❤️${_aboutController.remoteVersion.value}'
- : '当前已是最新版',
- style: subTitleStyle,
+ () => Badge(
+ isLabelVisible: _aboutController.isLoading.value
+ ? false
+ : _aboutController.isUpdate.value,
+ label: const Text('New'),
+ child: Padding(
+ padding: const EdgeInsets.fromLTRB(0, 0, 0, 30),
+ child: FilledButton.tonal(
+ onPressed: () {
+ showModalBottomSheet(
+ context: context,
+ builder: (context) {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ ListTile(
+ onTap: () => _aboutController.githubRelease(),
+ title: const Text('Github下载'),
+ ),
+ ListTile(
+ onTap: () => _aboutController.panDownload(),
+ title: const Text('网盘下载'),
+ ),
+ ListTile(
+ onTap: () => _aboutController.webSiteUrl(),
+ title: const Text('官网下载'),
+ ),
+ ListTile(
+ onTap: () => _aboutController.qimiao(),
+ title: const Text('奇妙应用'),
+ ),
+ SizedBox(
+ height:
+ MediaQuery.of(context).padding.bottom +
+ 20)
+ ],
+ );
+ },
+ );
+ },
+ child: Text(
+ 'V${_aboutController.currentVersion.value}',
+ style: subTitleStyle.copyWith(
+ color: Theme.of(context).primaryColor,
+ ),
+ ),
+ ),
),
),
),
@@ -73,14 +112,9 @@ class _AboutPageState extends State {
// size: 16,
// ),
// ),
- Divider(
- thickness: 1,
- height: 30,
- color: Theme.of(context).colorScheme.outlineVariant,
- ),
ListTile(
onTap: () => _aboutController.githubUrl(),
- title: const Text('Github'),
+ title: const Text('开源地址'),
trailing: Text(
'github.com/guozhigq/pilipala',
style: subTitleStyle,
@@ -115,19 +149,43 @@ class _AboutPageState extends State {
),
),
ListTile(
- onTap: () => _aboutController.qqChanel(),
- title: const Text('QQ群'),
+ onTap: () {
+ showModalBottomSheet(
+ context: context,
+ builder: (context) {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ ListTile(
+ onTap: () => _aboutController.qqChanel(),
+ title: const Text('QQ群'),
+ trailing: Text(
+ '616150809',
+ style: subTitleStyle,
+ ),
+ ),
+ ListTile(
+ onTap: () => _aboutController.tgChanel(),
+ title: const Text('TG频道'),
+ trailing: Text(
+ 'https://t.me/+lm_oOVmF0RJiODk1',
+ style: subTitleStyle,
+ ),
+ ),
+ SizedBox(
+ height: MediaQuery.of(context).padding.bottom + 20)
+ ],
+ );
+ },
+ );
+ },
+ title: const Text('交流社区'),
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('赞助'),
@@ -138,6 +196,17 @@ class _AboutPageState extends State {
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),
+ ),
+ SizedBox(height: MediaQuery.of(context).padding.bottom + 20)
],
),
),
@@ -184,12 +253,16 @@ class AboutController extends GetxController {
// 获取远程版本
Future getRemoteApp() async {
var result = await Request().get(Api.latestApp, extra: {'ua': 'pc'});
+ isLoading.value = false;
+ if (result.data == null || result.data.isEmpty) {
+ SmartDialog.showToast('获取远程版本失败,请检查网络');
+ return;
+ }
data = LatestDataModel.fromJson(result.data);
remoteAppInfo = data;
remoteVersion.value = data.tagName!;
isUpdate.value =
Utils.needUpdate(currentVersion.value, remoteVersion.value);
- isLoading.value = false;
}
// 跳转下载/本地更新
@@ -205,11 +278,26 @@ class AboutController extends GetxController {
);
}
+ githubRelease() {
+ launchUrl(
+ Uri.parse('https://github.com/guozhigq/pilipala/releases'),
+ mode: LaunchMode.externalApplication,
+ );
+ }
+
// 从网盘下载
panDownload() {
- launchUrl(
- Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'),
- mode: LaunchMode.externalApplication,
+ Clipboard.setData(
+ const ClipboardData(text: 'pili'),
+ );
+ SmartDialog.showToast(
+ '已复制提取码:pili',
+ displayTime: const Duration(milliseconds: 500),
+ ).then(
+ (value) => launchUrl(
+ Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'),
+ mode: LaunchMode.externalApplication,
+ ),
);
}
@@ -225,7 +313,7 @@ class AboutController extends GetxController {
// qq频道
qqChanel() {
Clipboard.setData(
- const ClipboardData(text: '489981949'),
+ const ClipboardData(text: '616150809'),
);
SmartDialog.showToast('已复制QQ群号');
}
@@ -266,6 +354,13 @@ class AboutController extends GetxController {
);
}
+ qimiao() {
+ launchUrl(
+ Uri.parse('https://www.magicalapk.com/home'),
+ mode: LaunchMode.externalApplication,
+ );
+ }
+
// 日志
logs() {
Get.toNamed('/logs');
diff --git a/lib/pages/bangumi/controller.dart b/lib/pages/bangumi/controller.dart
index 09afc43a..e5748d6c 100644
--- a/lib/pages/bangumi/controller.dart
+++ b/lib/pages/bangumi/controller.dart
@@ -7,8 +7,8 @@ import 'package:pilipala/utils/storage.dart';
class BangumiController extends GetxController {
final ScrollController scrollController = ScrollController();
- RxList bangumiList = [BangumiListItemModel()].obs;
- RxList bangumiFollowList = [BangumiListItemModel()].obs;
+ RxList bangumiList = [].obs;
+ RxList bangumiFollowList = [].obs;
int _currentPage = 1;
bool isLoadingMore = true;
Box userInfoCache = GStrorage.userInfo;
diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart
index 13dd50c0..12f0c053 100644
--- a/lib/pages/bangumi/introduction/controller.dart
+++ b/lib/pages/bangumi/introduction/controller.dart
@@ -25,13 +25,6 @@ class BangumiIntroController extends GetxController {
? int.tryParse(Get.parameters['epId']!)
: null;
- // 是否预渲染 骨架屏
- bool preRender = false;
-
- // 视频详情 上个页面传入
- Map? videoItem = {};
- BangumiInfoModel? bangumiItem;
-
// 请求状态
RxBool isLoading = false.obs;
@@ -63,27 +56,6 @@ class BangumiIntroController extends GetxController {
@override
void onInit() {
super.onInit();
- if (Get.arguments.isNotEmpty as bool) {
- if (Get.arguments.containsKey('bangumiItem') as bool) {
- preRender = true;
- bangumiItem = Get.arguments['bangumiItem'];
- // bangumiItem!['pic'] = args.pic;
- // if (args.title is String) {
- // videoItem!['title'] = args.title;
- // } else {
- // String str = '';
- // for (Map map in args.title) {
- // str += map['text'];
- // }
- // videoItem!['title'] = str;
- // }
- // if (args.stat != null) {
- // videoItem!['stat'] = args.stat;
- // }
- // videoItem!['pubdate'] = args.pubdate;
- // videoItem!['owner'] = args.owner;
- }
- }
userInfo = userInfoCache.get('userInfoCache');
userLogin = userInfo != null;
}
@@ -183,20 +155,21 @@ class BangumiIntroController extends GetxController {
actions: [
TextButton(onPressed: () => Get.back(), child: const Text('取消')),
TextButton(
- onPressed: () async {
- var res = await VideoHttp.coinVideo(
- bvid: bvid, multiply: _tempThemeValue);
- if (res['status']) {
- SmartDialog.showToast('投币成功 👏');
- hasCoin.value = true;
- bangumiDetail.value.stat!['coins'] =
- bangumiDetail.value.stat!['coins'] + _tempThemeValue;
- } else {
- SmartDialog.showToast(res['msg']);
- }
- Get.back();
- },
- child: const Text('确定'))
+ onPressed: () async {
+ var res = await VideoHttp.coinVideo(
+ bvid: bvid, multiply: _tempThemeValue);
+ if (res['status']) {
+ SmartDialog.showToast('投币成功 👏');
+ hasCoin.value = true;
+ bangumiDetail.value.stat!['coins'] =
+ bangumiDetail.value.stat!['coins'] + _tempThemeValue;
+ } else {
+ SmartDialog.showToast(res['msg']);
+ }
+ Get.back();
+ },
+ child: const Text('确定'),
+ )
],
);
});
@@ -218,14 +191,12 @@ class BangumiIntroController extends GetxController {
addIds: addMediaIdsNew.join(','),
delIds: delMediaIdsNew.join(','));
if (result['status']) {
- if (result['data']['prompt']) {
- addMediaIdsNew = [];
- delMediaIdsNew = [];
- Get.back();
- // 重新获取收藏状态
- queryHasFavVideo();
- SmartDialog.showToast('✅ 操作成功');
- }
+ addMediaIdsNew = [];
+ delMediaIdsNew = [];
+ // 重新获取收藏状态
+ queryHasFavVideo();
+ SmartDialog.showToast('✅ 操作成功');
+ Get.back();
}
}
diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart
index f9efc66c..6255ffda 100644
--- a/lib/pages/bangumi/introduction/view.dart
+++ b/lib/pages/bangumi/introduction/view.dart
@@ -12,11 +12,10 @@ import 'package:pilipala/models/bangumi/info.dart';
import 'package:pilipala/pages/bangumi/widgets/bangumi_panel.dart';
import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/pages/video/detail/introduction/widgets/action_item.dart';
-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 '../../../common/widgets/http_error.dart';
import 'controller.dart';
import 'widgets/intro_detail.dart';
@@ -51,9 +50,6 @@ class _BangumiIntroPanelState extends State
cid = widget.cid!;
bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag);
videoDetailCtr = Get.find(tag: heroTag);
- bangumiIntroController.bangumiDetail.listen((BangumiInfoModel value) {
- bangumiDetail = value;
- });
_futureBuilderFuture = bangumiIntroController.queryBangumiIntro();
videoDetailCtr.cid.listen((int p0) {
cid = p0;
@@ -68,27 +64,32 @@ class _BangumiIntroPanelState extends State
future: _futureBuilderFuture,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
+ if (snapshot.data == null) {
+ return const SliverToBoxAdapter(child: SizedBox());
+ }
if (snapshot.data['status']) {
// 请求成功
-
- return BangumiInfo(
- loadingStatus: false,
- bangumiDetail: bangumiDetail,
- cid: cid,
+ return Obx(
+ () => BangumiInfo(
+ bangumiDetail: bangumiIntroController.bangumiDetail.value,
+ cid: cid,
+ ),
);
} else {
// 请求错误
- // return HttpError(
- // errMsg: snapshot.data['msg'],
- // fn: () => Get.back(),
- // );
- return const SizedBox();
+ return HttpError(
+ errMsg: snapshot.data['msg'],
+ fn: () => Get.back(),
+ );
}
} else {
- return BangumiInfo(
- loadingStatus: true,
- bangumiDetail: bangumiDetail,
- cid: cid,
+ return const SliverToBoxAdapter(
+ child: SizedBox(
+ height: 100,
+ child: Center(
+ child: CircularProgressIndicator(),
+ ),
+ ),
);
}
},
@@ -99,12 +100,10 @@ class _BangumiIntroPanelState extends State
class BangumiInfo extends StatefulWidget {
const BangumiInfo({
super.key,
- this.loadingStatus = false,
this.bangumiDetail,
this.cid,
});
- final bool loadingStatus;
final BangumiInfoModel? bangumiDetail;
final int? cid;
@@ -117,7 +116,6 @@ class _BangumiInfoState extends State {
late final BangumiIntroController bangumiIntroController;
late final VideoDetailController videoDetailCtr;
Box localCache = GStrorage.localCache;
- late final BangumiInfoModel? bangumiItem;
late double sheetHeight;
int? cid;
bool isProcessing = false;
@@ -136,13 +134,10 @@ class _BangumiInfoState extends State {
super.initState();
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) {
cid = p0;
- print('cid: $cid');
setState(() {});
});
}
@@ -182,207 +177,155 @@ class _BangumiInfoState extends State {
padding: const EdgeInsets.only(
left: StyleString.safeSpace, right: StyleString.safeSpace, top: 20),
sliver: SliverToBoxAdapter(
- child: !widget.loadingStatus || bangumiItem != null
- ? Column(
- crossAxisAlignment: CrossAxisAlignment.start,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Stack(
children: [
- Row(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Stack(
- children: [
- NetworkImgLayer(
- width: 105,
- height: 160,
- src: !widget.loadingStatus
- ? widget.bangumiDetail!.cover!
- : bangumiItem!.cover!,
- ),
- if (bangumiItem != null &&
- bangumiItem!.rating != null)
- PBadge(
- text:
- '评分 ${!widget.loadingStatus ? widget.bangumiDetail!.rating!['score']! : bangumiItem!.rating!['score']!}',
- top: null,
- right: 6,
- bottom: 6,
- left: null,
+ NetworkImgLayer(
+ width: 105,
+ height: 160,
+ src: widget.bangumiDetail!.cover!,
+ ),
+ PBadge(
+ text: '评分 ${widget.bangumiDetail!.rating!['score']!}',
+ top: null,
+ right: 6,
+ bottom: 6,
+ left: null,
+ ),
+ ],
+ ),
+ const SizedBox(width: 10),
+ Expanded(
+ child: InkWell(
+ onTap: () => showIntroDetail(),
+ child: SizedBox(
+ height: 158,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Row(
+ children: [
+ Expanded(
+ child: Text(
+ widget.bangumiDetail!.title!,
+ style: const TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ ),
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ ),
),
- ],
- ),
- const SizedBox(width: 10),
- Expanded(
- child: InkWell(
- onTap: () => showIntroDetail(),
- child: SizedBox(
- height: 158,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- mainAxisSize: MainAxisSize.min,
- children: [
- Row(
- children: [
- Expanded(
- child: Text(
- !widget.loadingStatus
- ? widget.bangumiDetail!.title!
- : bangumiItem!.title!,
- style: const TextStyle(
- fontSize: 16,
- fontWeight: FontWeight.w500,
- ),
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- ),
- ),
- const SizedBox(width: 20),
- SizedBox(
- width: 34,
- height: 34,
- child: IconButton(
- style: ButtonStyle(
- padding: MaterialStateProperty.all(
- EdgeInsets.zero),
- backgroundColor:
- MaterialStateProperty.resolveWith(
- (Set states) {
- return t
- .colorScheme.primaryContainer
- .withOpacity(0.7);
- }),
- ),
- onPressed: () =>
- bangumiIntroController.bangumiAdd(),
- icon: Icon(
- Icons.favorite_border_rounded,
- color: t.colorScheme.primary,
- size: 22,
- ),
- ),
- ),
- ],
+ const SizedBox(width: 20),
+ SizedBox(
+ width: 34,
+ height: 34,
+ child: IconButton(
+ style: ButtonStyle(
+ padding: MaterialStateProperty.all(
+ EdgeInsets.zero),
+ backgroundColor:
+ MaterialStateProperty.resolveWith(
+ (Set states) {
+ return t.colorScheme.primaryContainer
+ .withOpacity(0.7);
+ }),
),
- Row(
- children: [
- StatView(
- theme: 'gray',
- view: !widget.loadingStatus
- ? widget.bangumiDetail!.stat!['views']
- : bangumiItem!.stat!['views'],
- size: 'medium',
- ),
- const SizedBox(width: 6),
- StatDanMu(
- theme: 'gray',
- danmu: !widget.loadingStatus
- ? widget
- .bangumiDetail!.stat!['danmakus']
- : bangumiItem!.stat!['danmakus'],
- size: 'medium',
- ),
- ],
+ onPressed: () =>
+ bangumiIntroController.bangumiAdd(),
+ icon: Icon(
+ Icons.favorite_border_rounded,
+ color: t.colorScheme.primary,
+ size: 22,
),
- const SizedBox(height: 6),
- Row(
- children: [
- Text(
- !widget.loadingStatus
- ? (widget.bangumiDetail!.areas!
- .isNotEmpty
- ? widget.bangumiDetail!.areas!
- .first['name']
- : '')
- : (bangumiItem!.areas!.isNotEmpty
- ? bangumiItem!
- .areas!.first['name']
- : ''),
- style: TextStyle(
- fontSize: 12,
- color: t.colorScheme.outline,
- ),
- ),
- const SizedBox(width: 6),
- Text(
- !widget.loadingStatus
- ? widget.bangumiDetail!
- .publish!['pub_time_show']
- : bangumiItem!
- .publish!['pub_time_show'],
- style: TextStyle(
- fontSize: 12,
- color: t.colorScheme.outline,
- ),
- ),
- ],
- ),
- // const SizedBox(height: 4),
- Text(
- !widget.loadingStatus
- ? widget.bangumiDetail!.newEp!['desc']
- : bangumiItem!.newEp!['desc'],
- style: TextStyle(
- fontSize: 12,
- color: t.colorScheme.outline,
- ),
- ),
- // const SizedBox(height: 10),
- const Spacer(),
- Text(
- '简介:${!widget.loadingStatus ? widget.bangumiDetail!.evaluate! : bangumiItem!.evaluate!}',
- maxLines: 3,
- overflow: TextOverflow.ellipsis,
- style: TextStyle(
- fontSize: 13,
- color: t.colorScheme.outline,
- ),
- ),
- ],
+ ),
),
+ ],
+ ),
+ Row(
+ children: [
+ StatView(
+ theme: 'gray',
+ view: widget.bangumiDetail!.stat!['views'],
+ size: 'medium',
+ ),
+ const SizedBox(width: 6),
+ StatDanMu(
+ theme: 'gray',
+ danmu: widget.bangumiDetail!.stat!['danmakus'],
+ size: 'medium',
+ ),
+ ],
+ ),
+ const SizedBox(height: 6),
+ Row(
+ children: [
+ Text(
+ (widget.bangumiDetail!.areas!.isNotEmpty
+ ? widget.bangumiDetail!.areas!.first['name']
+ : ''),
+ style: TextStyle(
+ fontSize: 12,
+ color: t.colorScheme.outline,
+ ),
+ ),
+ const SizedBox(width: 6),
+ Text(
+ widget.bangumiDetail!.publish!['pub_time_show'],
+ style: TextStyle(
+ fontSize: 12,
+ color: t.colorScheme.outline,
+ ),
+ ),
+ ],
+ ),
+ Text(
+ widget.bangumiDetail!.newEp!['desc'],
+ style: TextStyle(
+ fontSize: 12,
+ color: t.colorScheme.outline,
),
),
- ),
- ],
+ const Spacer(),
+ Text(
+ '简介:${widget.bangumiDetail!.evaluate!}',
+ maxLines: 3,
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ fontSize: 13,
+ color: t.colorScheme.outline,
+ ),
+ ),
+ ],
+ ),
),
- const SizedBox(height: 6),
- // 点赞收藏转发 布局样式1
- // SingleChildScrollView(
- // padding: const EdgeInsets.only(top: 7, bottom: 7),
- // scrollDirection: Axis.horizontal,
- // child: actionRow(
- // context,
- // bangumiIntroController,
- // videoDetailCtr,
- // ),
- // ),
- // 点赞收藏转发 布局样式2
- actionGrid(context, bangumiIntroController),
- // 番剧分p
- if ((!widget.loadingStatus &&
- widget.bangumiDetail!.episodes!.isNotEmpty) ||
- bangumiItem != null &&
- bangumiItem!.episodes!.isNotEmpty) ...[
- BangumiPanel(
- pages: bangumiItem != null
- ? bangumiItem!.episodes!
- : widget.bangumiDetail!.episodes!,
- cid: cid ??
- (bangumiItem != null
- ? bangumiItem!.episodes!.first.cid
- : widget.bangumiDetail!.episodes!.first.cid),
- sheetHeight: sheetHeight,
- changeFuc: (bvid, cid, aid) => bangumiIntroController
- .changeSeasonOrbangu(bvid, cid, aid),
- )
- ],
- ],
- )
- : const SizedBox(
- height: 100,
- child: Center(
- child: CircularProgressIndicator(),
),
),
- ),
+ ],
+ ),
+ const SizedBox(height: 6),
+
+ /// 点赞收藏转发
+ actionGrid(context, bangumiIntroController),
+ // 番剧分p
+ if (widget.bangumiDetail!.episodes!.isNotEmpty) ...[
+ BangumiPanel(
+ pages: widget.bangumiDetail!.episodes!,
+ cid: cid ?? widget.bangumiDetail!.episodes!.first.cid,
+ sheetHeight: sheetHeight,
+ changeFuc: (bvid, cid, aid) =>
+ bangumiIntroController.changeSeasonOrbangu(bvid, cid, aid),
+ bangumiDetail: bangumiIntroController.bangumiDetail.value,
+ )
+ ],
+ ],
+ )),
);
}
@@ -402,57 +345,44 @@ class _BangumiInfoState extends State {
children: [
Obx(
() => ActionItem(
- icon: const Icon(FontAwesomeIcons.thumbsUp),
- selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
- onTap:
- handleState(bangumiIntroController.actionLikeVideo),
- selectStatus: bangumiIntroController.hasLike.value,
- loadingStatus: false,
- text: !widget.loadingStatus
- ? widget.bangumiDetail!.stat!['likes']!.toString()
- : bangumiItem!.stat!['likes']!.toString()),
+ icon: const Icon(FontAwesomeIcons.thumbsUp),
+ selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
+ onTap: handleState(bangumiIntroController.actionLikeVideo),
+ selectStatus: bangumiIntroController.hasLike.value,
+ text: widget.bangumiDetail!.stat!['likes']!.toString(),
+ ),
),
Obx(
() => ActionItem(
- icon: const Icon(FontAwesomeIcons.b),
- selectIcon: const Icon(FontAwesomeIcons.b),
- onTap:
- handleState(bangumiIntroController.actionCoinVideo),
- selectStatus: bangumiIntroController.hasCoin.value,
- loadingStatus: false,
- text: !widget.loadingStatus
- ? widget.bangumiDetail!.stat!['coins']!.toString()
- : bangumiItem!.stat!['coins']!.toString()),
+ icon: const Icon(FontAwesomeIcons.b),
+ selectIcon: const Icon(FontAwesomeIcons.b),
+ onTap: handleState(bangumiIntroController.actionCoinVideo),
+ selectStatus: bangumiIntroController.hasCoin.value,
+ text: widget.bangumiDetail!.stat!['coins']!.toString(),
+ ),
),
Obx(
() => ActionItem(
- icon: const Icon(FontAwesomeIcons.star),
- selectIcon: const Icon(FontAwesomeIcons.solidStar),
- onTap: () => showFavBottomSheet(),
- selectStatus: bangumiIntroController.hasFav.value,
- loadingStatus: false,
- text: !widget.loadingStatus
- ? widget.bangumiDetail!.stat!['favorite']!.toString()
- : bangumiItem!.stat!['favorite']!.toString()),
+ icon: const Icon(FontAwesomeIcons.star),
+ selectIcon: const Icon(FontAwesomeIcons.solidStar),
+ onTap: () => showFavBottomSheet(),
+ selectStatus: bangumiIntroController.hasFav.value,
+ text: widget.bangumiDetail!.stat!['favorite']!.toString(),
+ ),
),
ActionItem(
icon: const Icon(FontAwesomeIcons.comment),
selectIcon: const Icon(FontAwesomeIcons.reply),
onTap: () => videoDetailCtr.tabCtr.animateTo(1),
selectStatus: false,
- loadingStatus: false,
- text: !widget.loadingStatus
- ? widget.bangumiDetail!.stat!['reply']!.toString()
- : bangumiItem!.stat!['reply']!.toString(),
+ text: widget.bangumiDetail!.stat!['reply']!.toString(),
),
ActionItem(
- icon: const Icon(FontAwesomeIcons.shareFromSquare),
- onTap: () => bangumiIntroController.actionShareVideo(),
- selectStatus: false,
- loadingStatus: false,
- text: !widget.loadingStatus
- ? widget.bangumiDetail!.stat!['share']!.toString()
- : bangumiItem!.stat!['share']!.toString()),
+ icon: const Icon(FontAwesomeIcons.shareFromSquare),
+ onTap: () => bangumiIntroController.actionShareVideo(),
+ selectStatus: false,
+ text: widget.bangumiDetail!.stat!['share']!.toString(),
+ ),
],
),
),
@@ -460,63 +390,4 @@ class _BangumiInfoState extends State {
);
});
}
-
- Widget actionRow(BuildContext context, videoIntroController, videoDetailCtr) {
- return Row(children: [
- Obx(
- () => ActionRowItem(
- icon: const Icon(FontAwesomeIcons.thumbsUp),
- onTap: handleState(videoIntroController.actionLikeVideo),
- selectStatus: videoIntroController.hasLike.value,
- loadingStatus: widget.loadingStatus,
- text: !widget.loadingStatus
- ? widget.bangumiDetail!.stat!['likes']!.toString()
- : '-',
- ),
- ),
- const SizedBox(width: 8),
- Obx(
- () => ActionRowItem(
- icon: const Icon(FontAwesomeIcons.b),
- onTap: handleState(videoIntroController.actionCoinVideo),
- selectStatus: videoIntroController.hasCoin.value,
- loadingStatus: widget.loadingStatus,
- text: !widget.loadingStatus
- ? widget.bangumiDetail!.stat!['coins']!.toString()
- : '-',
- ),
- ),
- const SizedBox(width: 8),
- Obx(
- () => ActionRowItem(
- icon: const Icon(FontAwesomeIcons.heart),
- onTap: () => showFavBottomSheet(),
- selectStatus: videoIntroController.hasFav.value,
- loadingStatus: widget.loadingStatus,
- text: !widget.loadingStatus
- ? widget.bangumiDetail!.stat!['favorite']!.toString()
- : '-',
- ),
- ),
- const SizedBox(width: 8),
- ActionRowItem(
- icon: const Icon(FontAwesomeIcons.comment),
- onTap: () {
- videoDetailCtr.tabCtr.animateTo(1);
- },
- selectStatus: false,
- loadingStatus: widget.loadingStatus,
- text: !widget.loadingStatus
- ? widget.bangumiDetail!.stat!['reply']!.toString()
- : '-',
- ),
- const SizedBox(width: 8),
- ActionRowItem(
- icon: const Icon(FontAwesomeIcons.share),
- onTap: () => videoIntroController.actionShareVideo(),
- selectStatus: false,
- loadingStatus: widget.loadingStatus,
- text: '转发'),
- ]);
- }
}
diff --git a/lib/pages/bangumi/view.dart b/lib/pages/bangumi/view.dart
index 560f09a5..f59f94a2 100644
--- a/lib/pages/bangumi/view.dart
+++ b/lib/pages/bangumi/view.dart
@@ -9,7 +9,6 @@ import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/pages/home/index.dart';
import 'package:pilipala/pages/main/index.dart';
-import 'package:pilipala/pages/rcmd/view.dart';
import 'controller.dart';
import 'widgets/bangumu_card_v.dart';
@@ -199,7 +198,10 @@ class _BangumiPageState extends State
} else {
return HttpError(
errMsg: data['msg'],
- fn: () => {},
+ fn: () {
+ _futureBuilderFuture =
+ _bangumidController.queryBangumiListFeed();
+ },
);
}
} else {
@@ -208,7 +210,6 @@ class _BangumiPageState extends State
},
),
),
- const LoadingMore()
],
),
);
diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart
index b5ec4efe..791cc108 100644
--- a/lib/pages/bangumi/widgets/bangumi_panel.dart
+++ b/lib/pages/bangumi/widgets/bangumi_panel.dart
@@ -14,12 +14,14 @@ class BangumiPanel extends StatefulWidget {
this.cid,
this.sheetHeight,
this.changeFuc,
+ this.bangumiDetail,
});
final List pages;
final int? cid;
final double? sheetHeight;
final Function? changeFuc;
+ final BangumiInfoModel? bangumiDetail;
@override
State createState() => _BangumiPanelState();
@@ -65,6 +67,47 @@ class _BangumiPanelState extends State {
super.dispose();
}
+ Widget buildPageListItem(
+ EpisodeItem page,
+ int index,
+ bool isCurrentIndex,
+ ) {
+ Color primary = Theme.of(context).colorScheme.primary;
+ return ListTile(
+ onTap: () {
+ Get.back();
+ setState(() {
+ changeFucCall(page, index);
+ });
+ },
+ dense: false,
+ leading: isCurrentIndex
+ ? Image.asset(
+ 'assets/images/live.gif',
+ color: primary,
+ height: 12,
+ )
+ : null,
+ title: Text(
+ '第${page.title}话 ${page.longTitle!}',
+ style: TextStyle(
+ fontSize: 14,
+ color: isCurrentIndex
+ ? primary
+ : Theme.of(context).colorScheme.onSurface,
+ ),
+ ),
+ trailing: page.badge != null
+ ? Text(
+ page.badge!,
+ style: TextStyle(
+ color: Theme.of(context).colorScheme.primary,
+ ),
+ )
+ : const SizedBox(),
+ );
+ }
+
void showBangumiPanel() {
showBottomSheet(
context: context,
@@ -106,37 +149,21 @@ class _BangumiPanelState extends State {
child: Material(
child: ScrollablePositionedList.builder(
itemCount: widget.pages.length,
- itemBuilder: (BuildContext context, int index) =>
- ListTile(
- onTap: () {
- setState(() {
- changeFucCall(widget.pages[index], index);
- });
- },
- dense: false,
- leading: index == currentIndex
- ? Image.asset(
- 'assets/images/live.gif',
- color: Theme.of(context).colorScheme.primary,
- height: 12,
+ itemBuilder: (BuildContext context, int index) {
+ bool isLastItem = index == widget.pages.length - 1;
+ bool isCurrentIndex = currentIndex == index;
+ return isLastItem
+ ? SizedBox(
+ height:
+ MediaQuery.of(context).padding.bottom +
+ 20,
)
- : null,
- title: Text(
- '第${index + 1}话 ${widget.pages[index].longTitle!}',
- style: TextStyle(
- fontSize: 14,
- color: index == currentIndex
- ? Theme.of(context).colorScheme.primary
- : Theme.of(context).colorScheme.onSurface,
- ),
- ),
- trailing: widget.pages[index].badge != null
- ? Image.asset(
- 'assets/images/big-vip.png',
- height: 20,
- )
- : const SizedBox(),
- ),
+ : buildPageListItem(
+ widget.pages[index],
+ index,
+ isCurrentIndex,
+ );
+ },
itemScrollController: itemScrollController,
),
),
@@ -151,7 +178,7 @@ class _BangumiPanelState extends State {
}
void changeFucCall(item, i) async {
- if (item.badge != null && vipStatus != 1) {
+ if (item.badge != null && item.badge == '会员' && vipStatus != 1) {
SmartDialog.showToast('需要大会员');
return;
}
@@ -178,11 +205,11 @@ class _BangumiPanelState extends State {
return Column(
children: [
Padding(
- padding: const EdgeInsets.only(top: 10, bottom: 6),
+ padding: const EdgeInsets.only(top: 10, bottom: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- const Text('合集 '),
+ const Text('选集 '),
Expanded(
child: Text(
' 正在播放:${widget.pages[currentIndex].longTitle}',
@@ -202,7 +229,7 @@ class _BangumiPanelState extends State {
),
onPressed: () => showBangumiPanel(),
child: Text(
- '全${widget.pages.length}话',
+ '${widget.bangumiDetail!.newEp!['desc']}',
style: const TextStyle(fontSize: 13),
),
),
@@ -255,11 +282,16 @@ class _BangumiPanelState extends State {
),
const SizedBox(width: 2),
if (widget.pages[i].badge != null) ...[
- Image.asset(
- 'assets/images/big-vip.png',
- height: 16,
+ const Spacer(),
+ Text(
+ widget.pages[i].badge!,
+ style: TextStyle(
+ fontSize: 12,
+ color:
+ Theme.of(context).colorScheme.primary,
+ ),
),
- ],
+ ]
],
),
const SizedBox(height: 3),
diff --git a/lib/pages/blacklist/index.dart b/lib/pages/blacklist/index.dart
index 3bf64146..402790f5 100644
--- a/lib/pages/blacklist/index.dart
+++ b/lib/pages/blacklist/index.dart
@@ -139,7 +139,7 @@ class BlackListController extends GetxController {
int currentPage = 1;
int pageSize = 50;
RxInt total = 0.obs;
- RxList blackList = [BlackListItem()].obs;
+ RxList blackList = [].obs;
Future queryBlacklist({type = 'init'}) async {
if (type == 'init') {
diff --git a/lib/pages/dynamics/controller.dart b/lib/pages/dynamics/controller.dart
index 26ba2b22..b7676663 100644
--- a/lib/pages/dynamics/controller.dart
+++ b/lib/pages/dynamics/controller.dart
@@ -20,7 +20,7 @@ import 'package:pilipala/utils/utils.dart';
class DynamicsController extends GetxController {
int page = 1;
String? offset = '';
- RxList dynamicsList = [DynamicItemModel()].obs;
+ RxList dynamicsList = [].obs;
Rx dynamicsType = DynamicsType.values[0].obs;
RxString dynamicsTypeLabel = '全部'.obs;
final ScrollController scrollController = ScrollController();
@@ -105,7 +105,7 @@ class DynamicsController extends GetxController {
onSelectType(value) async {
dynamicsType.value = filterTypeList[value]['value'];
- dynamicsList.value = [DynamicItemModel()];
+ dynamicsList.value = [];
page = 1;
initialValue.value = value;
await queryFollowDynamic();
@@ -249,8 +249,8 @@ class DynamicsController extends GetxController {
return {'status': false, 'msg': '账号未登录'};
}
if (type == 'init') {
- upData.value.upList = [];
- upData.value.liveUsers = LiveUsers();
+ upData.value.upList = [];
+ upData.value.liveList = [];
}
var res = await DynamicsHttp.followUp();
if (res['status']) {
@@ -258,20 +258,23 @@ class DynamicsController extends GetxController {
if (upData.value.upList!.isEmpty) {
mid.value = -1;
}
+ upData.value.upList!.insertAll(0, [
+ UpItem(face: '', uname: '全部动态', mid: -1),
+ UpItem(face: userInfo.face, uname: '我', mid: userInfo.mid),
+ ]);
}
return res;
}
onSelectUp(mid) async {
dynamicsType.value = DynamicsType.values[0];
- dynamicsList.value = [DynamicItemModel()];
+ dynamicsList.value = [];
page = 1;
queryFollowDynamic();
}
onRefresh() async {
page = 1;
- print('onRefresh');
await queryFollowUp();
await queryFollowDynamic();
}
@@ -293,7 +296,7 @@ class DynamicsController extends GetxController {
dynamicsType.value = DynamicsType.values[0];
initialValue.value = 0;
SmartDialog.showToast('还原默认加载');
- dynamicsList.value = [DynamicItemModel()];
+ dynamicsList.value = [];
queryFollowDynamic();
}
}
diff --git a/lib/pages/dynamics/detail/controller.dart b/lib/pages/dynamics/detail/controller.dart
index 1520377e..8e117383 100644
--- a/lib/pages/dynamics/detail/controller.dart
+++ b/lib/pages/dynamics/detail/controller.dart
@@ -17,7 +17,7 @@ class DynamicDetailController extends GetxController {
int currentPage = 0;
bool isLoadingMore = false;
RxString noMore = ''.obs;
- RxList replyList = [ReplyItemModel()].obs;
+ RxList replyList = [].obs;
RxInt acount = 0.obs;
final ScrollController scrollController = ScrollController();
@@ -37,6 +37,10 @@ class DynamicDetailController extends GetxController {
}
int deaultReplySortIndex =
setting.get(SettingBoxKey.replySortType, defaultValue: 0);
+ if (deaultReplySortIndex == 2) {
+ setting.put(SettingBoxKey.replySortType, 0);
+ deaultReplySortIndex = 0;
+ }
_sortType = ReplySortType.values[deaultReplySortIndex];
sortTypeTitle.value = _sortType.titles;
sortTypeLabel.value = _sortType.labels;
@@ -92,9 +96,6 @@ class DynamicDetailController extends GetxController {
_sortType = ReplySortType.like;
break;
case ReplySortType.like:
- _sortType = ReplySortType.reply;
- break;
- case ReplySortType.reply:
_sortType = ReplySortType.time;
break;
default:
diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart
index 575c8767..fe594a43 100644
--- a/lib/pages/dynamics/view.dart
+++ b/lib/pages/dynamics/view.dart
@@ -14,6 +14,7 @@ import 'package:pilipala/pages/main/index.dart';
import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/storage.dart';
+import '../mine/controller.dart';
import 'controller.dart';
import 'widgets/dynamic_panel.dart';
import 'widgets/up_panel.dart';
@@ -28,6 +29,7 @@ class DynamicsPage extends StatefulWidget {
class _DynamicsPageState extends State
with AutomaticKeepAliveClientMixin {
final DynamicsController _dynamicsController = Get.put(DynamicsController());
+ final MineController mineController = Get.put(MineController());
late Future _futureBuilderFuture;
late Future _futureBuilderFutureUp;
Box userInfoCache = GStrorage.userInfo;
@@ -192,22 +194,6 @@ class _DynamicsPageState extends State
)
],
),
- // Obx(
- // () => Visibility(
- // visible: _dynamicsController.userLogin.value,
- // child: Positioned(
- // right: 4,
- // top: 0,
- // bottom: 0,
- // child: IconButton(
- // padding: EdgeInsets.zero,
- // onPressed: () =>
- // {feedBack(), _dynamicsController.resetSearch()},
- // icon: const Icon(Icons.history, size: 21),
- // ),
- // ),
- // ),
- // ),
],
),
),
@@ -229,7 +215,8 @@ class _DynamicsPageState extends State
return Obx(() => UpPanel(_dynamicsController.upData.value));
} else {
return const SliverToBoxAdapter(
- child: SizedBox(height: 80));
+ child: SizedBox(height: 80),
+ );
}
} else {
return const SliverToBoxAdapter(
@@ -240,15 +227,6 @@ class _DynamicsPageState extends State
}
},
),
- SliverToBoxAdapter(
- child: Container(
- height: 6,
- color: Theme.of(context)
- .colorScheme
- .onInverseSurface
- .withOpacity(0.5),
- ),
- ),
FutureBuilder(
future: _futureBuilderFuture,
builder: (context, snapshot) {
@@ -280,6 +258,14 @@ class _DynamicsPageState extends State
}
},
);
+ } else if (data['msg'] == "账号未登录") {
+ return HttpError(
+ errMsg: data['msg'],
+ btnText: "去登录",
+ fn: () {
+ mineController.onLogin();
+ },
+ );
} else {
return HttpError(
errMsg: data['msg'],
diff --git a/lib/pages/dynamics/widgets/article_panel.dart b/lib/pages/dynamics/widgets/article_panel.dart
index e68d966d..19707435 100644
--- a/lib/pages/dynamics/widgets/article_panel.dart
+++ b/lib/pages/dynamics/widgets/article_panel.dart
@@ -34,25 +34,25 @@ Widget articlePanel(item, context, {floor = 1}) {
),
const SizedBox(height: 8),
],
- Text(
- item.modules.moduleDynamic.major.opus.title,
- style: Theme.of(context)
- .textTheme
- .titleMedium!
- .copyWith(fontWeight: FontWeight.bold),
- ),
- const SizedBox(height: 2),
- if (item.modules.moduleDynamic.major.opus.summary.text !=
- 'undefined') ...[
- Text(
- item.modules.moduleDynamic.major.opus.summary.richTextNodes.first
- .text,
- maxLines: 4,
- style: const TextStyle(height: 1.55),
- overflow: TextOverflow.ellipsis,
- ),
- const SizedBox(height: 2),
- ],
+ // Text(
+ // item.modules.moduleDynamic.major.opus.title,
+ // style: Theme.of(context)
+ // .textTheme
+ // .titleMedium!
+ // .copyWith(fontWeight: FontWeight.bold),
+ // ),
+ // const SizedBox(height: 2),
+ // if (item.modules.moduleDynamic.major.opus.summary.text !=
+ // 'undefined') ...[
+ // Text(
+ // item.modules.moduleDynamic.major.opus.summary.richTextNodes.first
+ // .text,
+ // maxLines: 4,
+ // style: const TextStyle(height: 1.55),
+ // overflow: TextOverflow.ellipsis,
+ // ),
+ // const SizedBox(height: 2),
+ // ],
picWidget(item, context)
],
),
diff --git a/lib/pages/dynamics/widgets/content_panel.dart b/lib/pages/dynamics/widgets/content_panel.dart
index d10804d7..e1beaeb2 100644
--- a/lib/pages/dynamics/widgets/content_panel.dart
+++ b/lib/pages/dynamics/widgets/content_panel.dart
@@ -45,7 +45,9 @@ class _ContentState extends State {
if (len == 1) {
OpusPicsModel pictureItem = pics.first;
picList.add(pictureItem.url!);
- spanChilds.add(const TextSpan(text: '\n'));
+
+ /// 图片上方的空白间隔
+ // spanChilds.add(const TextSpan(text: '\n'));
spanChilds.add(
WidgetSpan(
child: LayoutBuilder(
diff --git a/lib/pages/dynamics/widgets/rich_node_panel.dart b/lib/pages/dynamics/widgets/rich_node_panel.dart
index 8f744dd5..5ffee5f1 100644
--- a/lib/pages/dynamics/widgets/rich_node_panel.dart
+++ b/lib/pages/dynamics/widgets/rich_node_panel.dart
@@ -19,6 +19,17 @@ InlineSpan richNode(item, context) {
// 动态页面 richTextNodes 层级可能与主页动态层级不同
richTextNodes =
item.modules.moduleDynamic.major.opus.summary.richTextNodes;
+ if (item.modules.moduleDynamic.major.opus.title != null) {
+ spanChilds.add(
+ TextSpan(
+ text: item.modules.moduleDynamic.major.opus.title + '\n',
+ style: Theme.of(context)
+ .textTheme
+ .titleMedium!
+ .copyWith(fontWeight: FontWeight.bold),
+ ),
+ );
+ }
}
if (richTextNodes == null || richTextNodes.isEmpty) {
return spacer;
diff --git a/lib/pages/dynamics/widgets/up_panel.dart b/lib/pages/dynamics/widgets/up_panel.dart
index ba4a562d..fd0ae642 100644
--- a/lib/pages/dynamics/widgets/up_panel.dart
+++ b/lib/pages/dynamics/widgets/up_panel.dart
@@ -1,16 +1,14 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
-import 'package:hive/hive.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/dynamics/up.dart';
import 'package:pilipala/models/live/item.dart';
import 'package:pilipala/pages/dynamics/controller.dart';
import 'package:pilipala/utils/feed_back.dart';
-import 'package:pilipala/utils/storage.dart';
import 'package:pilipala/utils/utils.dart';
class UpPanel extends StatefulWidget {
- final FollowUpModel? upData;
+ final FollowUpModel upData;
const UpPanel(this.upData, {Key? key}) : super(key: key);
@override
@@ -24,39 +22,22 @@ class _UpPanelState extends State {
List upList = [];
List liveList = [];
static const itemPadding = EdgeInsets.symmetric(horizontal: 5, vertical: 0);
- Box userInfoCache = GStrorage.userInfo;
- var userInfo;
+ late MyInfo userInfo;
- @override
- void initState() {
- super.initState();
- upList = widget.upData!.upList!;
- if (widget.upData!.liveUsers != null) {
- liveList = widget.upData!.liveUsers!.items!;
- }
- upList.insert(
- 0,
- UpItem(
- face: 'https://files.catbox.moe/8uc48f.png', uname: '全部动态', mid: -1),
- );
- userInfo = userInfoCache.get('userInfoCache');
- upList.insert(
- 1,
- UpItem(
- face: userInfo.face,
- uname: '我',
- mid: userInfo.mid,
- ),
- );
+ void listFormat() {
+ userInfo = widget.upData.myInfo!;
+ upList = widget.upData.upList!;
+ liveList = widget.upData.liveList!;
}
@override
Widget build(BuildContext context) {
+ listFormat();
return SliverPersistentHeader(
floating: true,
pinned: false,
delegate: _SliverHeaderDelegate(
- height: 124,
+ height: liveList.isNotEmpty || upList.isNotEmpty ? 126 : 0,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
@@ -91,7 +72,7 @@ class _UpPanelState extends State {
color: Theme.of(context).colorScheme.background,
child: Row(
children: [
- Expanded(
+ Flexible(
child: ListView(
scrollDirection: Axis.horizontal,
controller: scrollController,
@@ -121,6 +102,13 @@ class _UpPanelState extends State {
],
),
),
+ Container(
+ height: 6,
+ color: Theme.of(context)
+ .colorScheme
+ .onInverseSurface
+ .withOpacity(0.5),
+ ),
],
)),
);
@@ -171,6 +159,9 @@ class _UpPanelState extends State {
},
onLongPress: () {
feedBack();
+ if (data.mid == -1) {
+ return;
+ }
String heroTag = Utils.makeHeroTag(data.mid);
Get.toNamed('/member?mid=${data.mid}',
arguments: {'face': data.face, 'heroTag': heroTag});
@@ -198,12 +189,19 @@ class _UpPanelState extends State {
backgroundColor: data.type == 'live'
? Theme.of(context).colorScheme.secondaryContainer
: Theme.of(context).colorScheme.primary,
- child: NetworkImgLayer(
- width: 49,
- height: 49,
- src: data.face,
- type: 'avatar',
- ),
+ child: data.face != ''
+ ? NetworkImgLayer(
+ width: 50,
+ height: 50,
+ src: data.face,
+ type: 'avatar',
+ )
+ : const CircleAvatar(
+ radius: 25,
+ backgroundImage: AssetImage(
+ 'assets/images/noface.jpeg',
+ ),
+ ),
),
Padding(
padding: const EdgeInsets.only(top: 4),
@@ -271,13 +269,11 @@ class UpPanelSkeleton extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
- width: 49,
- height: 49,
+ width: 50,
+ height: 50,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.onInverseSurface,
- borderRadius: const BorderRadius.all(
- Radius.circular(24),
- ),
+ borderRadius: BorderRadius.circular(50),
),
),
Container(
diff --git a/lib/pages/emote/controller.dart b/lib/pages/emote/controller.dart
new file mode 100644
index 00000000..c1a4c504
--- /dev/null
+++ b/lib/pages/emote/controller.dart
@@ -0,0 +1,20 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+
+import '../../http/reply.dart';
+import '../../models/video/reply/emote.dart';
+
+class EmotePanelController extends GetxController
+ with GetTickerProviderStateMixin {
+ late List emotePackage;
+ late TabController tabController;
+
+ Future getEmote() async {
+ var res = await ReplyHttp.getEmoteList(business: 'reply');
+ if (res['status']) {
+ emotePackage = res['data'].packages;
+ tabController = TabController(length: emotePackage.length, vsync: this);
+ }
+ return res;
+ }
+}
diff --git a/lib/pages/emote/index.dart b/lib/pages/emote/index.dart
new file mode 100644
index 00000000..32ce53e3
--- /dev/null
+++ b/lib/pages/emote/index.dart
@@ -0,0 +1,4 @@
+library emote;
+
+export './controller.dart';
+export './view.dart';
diff --git a/lib/pages/emote/view.dart b/lib/pages/emote/view.dart
new file mode 100644
index 00000000..d30767c3
--- /dev/null
+++ b/lib/pages/emote/view.dart
@@ -0,0 +1,116 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import '../../models/video/reply/emote.dart';
+import 'controller.dart';
+
+class EmotePanel extends StatefulWidget {
+ final Function onChoose;
+ const EmotePanel({super.key, required this.onChoose});
+
+ @override
+ State createState() => _EmotePanelState();
+}
+
+class _EmotePanelState extends State
+ with AutomaticKeepAliveClientMixin {
+ final EmotePanelController _emotePanelController =
+ Get.put(EmotePanelController());
+ late Future _futureBuilderFuture;
+
+ @override
+ bool get wantKeepAlive => true;
+
+ @override
+ void initState() {
+ _futureBuilderFuture = _emotePanelController.getEmote();
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ super.build(context);
+ return FutureBuilder(
+ future: _futureBuilderFuture,
+ builder: (context, snapshot) {
+ if (snapshot.connectionState == ConnectionState.done) {
+ Map data = snapshot.data as Map;
+ if (data['status']) {
+ List emotePackage =
+ _emotePanelController.emotePackage;
+
+ return Column(
+ children: [
+ Expanded(
+ child: TabBarView(
+ controller: _emotePanelController.tabController,
+ children: emotePackage.map(
+ (e) {
+ int size = e.emote!.first.meta!.size!;
+ int type = e.type!;
+ return Padding(
+ padding: const EdgeInsets.fromLTRB(12, 6, 12, 0),
+ child: GridView.builder(
+ gridDelegate:
+ SliverGridDelegateWithMaxCrossAxisExtent(
+ maxCrossAxisExtent: size == 1 ? 40 : 60,
+ crossAxisSpacing: 8,
+ mainAxisSpacing: 8,
+ ),
+ itemCount: e.emote!.length,
+ itemBuilder: (context, index) {
+ return Material(
+ color: Colors.transparent,
+ clipBehavior: Clip.hardEdge,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(4),
+ ),
+ child: InkWell(
+ onTap: () {
+ widget.onChoose(e, e.emote![index]);
+ },
+ child: Padding(
+ padding: const EdgeInsets.all(3),
+ child: type == 4
+ ? Text(
+ e.emote![index].text!,
+ overflow: TextOverflow.clip,
+ maxLines: 1,
+ )
+ : Image.network(
+ e.emote![index].url!,
+ width: size * 38,
+ height: size * 38,
+ ),
+ ),
+ ),
+ );
+ },
+ ),
+ );
+ },
+ ).toList(),
+ )),
+ Divider(
+ height: 1,
+ color: Theme.of(context).dividerColor.withOpacity(0.1),
+ ),
+ TabBar(
+ controller: _emotePanelController.tabController,
+ dividerColor: Colors.transparent,
+ isScrollable: true,
+ tabs: _emotePanelController.emotePackage
+ .map((e) => Tab(text: e.text))
+ .toList(),
+ ),
+ SizedBox(height: MediaQuery.of(context).padding.bottom + 20),
+ ],
+ );
+ } else {
+ return Center(child: Text(data['msg']));
+ }
+ } else {
+ return const Center(child: Text('加载中...'));
+ }
+ });
+ }
+}
diff --git a/lib/pages/fan/controller.dart b/lib/pages/fan/controller.dart
index 8675ada7..c1c2a427 100644
--- a/lib/pages/fan/controller.dart
+++ b/lib/pages/fan/controller.dart
@@ -10,7 +10,7 @@ class FansController extends GetxController {
int pn = 1;
int ps = 20;
int total = 0;
- RxList fansList = [FansItemModel()].obs;
+ RxList fansList = [].obs;
late int mid;
late String name;
var userInfo;
diff --git a/lib/pages/fav/controller.dart b/lib/pages/fav/controller.dart
index c3f76186..a5f94525 100644
--- a/lib/pages/fav/controller.dart
+++ b/lib/pages/fav/controller.dart
@@ -24,7 +24,7 @@ class FavController extends GetxController {
if (!hasMore.value) {
return;
}
- var res = await await UserHttp.userfavFolder(
+ var res = await UserHttp.userfavFolder(
pn: currentPage,
ps: pageSize,
mid: userInfo!.mid!,
diff --git a/lib/pages/fav_detail/controller.dart b/lib/pages/fav_detail/controller.dart
index c2c63dd5..55d5b884 100644
--- a/lib/pages/fav_detail/controller.dart
+++ b/lib/pages/fav_detail/controller.dart
@@ -16,7 +16,7 @@ class FavDetailController extends GetxController {
RxMap favInfo = {}.obs;
RxList favList = [].obs;
RxString loadingText = '加载中...'.obs;
- int mediaCount = 0;
+ RxInt mediaCount = 0.obs;
@override
void onInit() {
@@ -29,12 +29,12 @@ class FavDetailController extends GetxController {
}
Future queryUserFavFolderDetail({type = 'init'}) async {
- if (type == 'onLoad' && favList.length >= mediaCount) {
+ if (type == 'onLoad' && favList.length >= mediaCount.value) {
loadingText.value = '没有更多了';
return;
}
isLoadingMore = true;
- var res = await await UserHttp.userFavFolderDetail(
+ var res = await UserHttp.userFavFolderDetail(
pn: currentPage,
ps: 20,
mediaId: mediaId!,
@@ -43,11 +43,11 @@ class FavDetailController extends GetxController {
favInfo.value = res['data'].info;
if (currentPage == 1 && type == 'init') {
favList.value = res['data'].medias;
- mediaCount = res['data'].info['media_count'];
+ mediaCount.value = res['data'].info['media_count'];
} else if (type == 'onLoad') {
favList.addAll(res['data'].medias);
}
- if (favList.length >= mediaCount) {
+ if (favList.length >= mediaCount.value) {
loadingText.value = '没有更多了';
}
}
@@ -60,16 +60,14 @@ class FavDetailController extends GetxController {
var result = await VideoHttp.favVideo(
aid: id, addIds: '', delIds: mediaId.toString());
if (result['status']) {
- if (result['data']['prompt']) {
- List dataList = favList;
- for (var i in dataList) {
- if (i.id == id) {
- dataList.remove(i);
- break;
- }
+ List dataList = favList;
+ for (var i in dataList) {
+ if (i.id == id) {
+ dataList.remove(i);
+ break;
}
- SmartDialog.showToast('取消收藏');
}
+ SmartDialog.showToast('取消收藏');
}
}
diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart
index f5897550..d94f5149 100644
--- a/lib/pages/fav_detail/view.dart
+++ b/lib/pages/fav_detail/view.dart
@@ -24,10 +24,12 @@ class _FavDetailPageState extends State {
Get.put(FavDetailController());
late StreamController titleStreamC; // a
Future? _futureBuilderFuture;
+ late String mediaId;
@override
void initState() {
super.initState();
+ mediaId = Get.parameters['mediaId']!;
_futureBuilderFuture = _favDetailController.queryUserFavFolderDetail();
titleStreamC = StreamController();
_controller.addListener(
@@ -82,7 +84,7 @@ class _FavDetailPageState extends State {
style: Theme.of(context).textTheme.titleMedium,
),
Text(
- '共${_favDetailController.item!.mediaCount!}条视频',
+ '共${_favDetailController.mediaCount}条视频',
style: Theme.of(context).textTheme.labelMedium,
)
],
@@ -94,8 +96,8 @@ class _FavDetailPageState extends State {
),
actions: [
IconButton(
- onPressed: () => Get.toNamed(
- '/favSearch?searchType=0&mediaId=${Get.parameters['mediaId']!}'),
+ onPressed: () =>
+ Get.toNamed('/favSearch?searchType=0&mediaId=$mediaId'),
icon: const Icon(Icons.search_outlined),
),
// IconButton(
@@ -173,7 +175,7 @@ class _FavDetailPageState extends State {
padding: const EdgeInsets.only(top: 15, bottom: 8, left: 14),
child: Obx(
() => Text(
- '共${_favDetailController.favList.length}条视频',
+ '共${_favDetailController.mediaCount}条视频',
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize,
diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart
index 9394fbe3..1c4008ff 100644
--- a/lib/pages/fav_detail/widget/fav_video_card.dart
+++ b/lib/pages/fav_detail/widget/fav_video_card.dart
@@ -9,14 +9,20 @@ import 'package:pilipala/models/common/search_type.dart';
import 'package:pilipala/utils/id_utils.dart';
import 'package:pilipala/utils/utils.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
+import '../../../common/widgets/badge.dart';
// 收藏视频卡片 - 水平布局
class FavVideoCardH extends StatelessWidget {
final dynamic videoItem;
final Function? callFn;
+ final int? searchType;
- const FavVideoCardH({Key? key, required this.videoItem, this.callFn})
- : super(key: key);
+ const FavVideoCardH({
+ Key? key,
+ required this.videoItem,
+ this.callFn,
+ this.searchType,
+ }) : super(key: key);
@override
Widget build(BuildContext context) {
@@ -27,7 +33,9 @@ class FavVideoCardH extends StatelessWidget {
onTap: () async {
// int? seasonId;
String? epId;
- if (videoItem.ogv != null && videoItem.ogv['type_name'] == '番剧') {
+ if (videoItem.ogv != null &&
+ (videoItem.ogv['type_name'] == '番剧' ||
+ videoItem.ogv['type_name'] == '国创')) {
videoItem.cid = await SearchHttp.ab2c(bvid: bvid);
// seasonId = videoItem.ogv['season_id'];
epId = videoItem.epId;
@@ -84,28 +92,31 @@ class FavVideoCardH extends StatelessWidget {
height: maxHeight,
),
),
- Positioned(
- right: 4,
- bottom: 4,
- child: Container(
- padding: const EdgeInsets.symmetric(
- vertical: 1, horizontal: 6),
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(4),
- color: Colors.black54.withOpacity(0.4)),
- child: Text(
- Utils.timeFormat(videoItem.duration!),
- style: const TextStyle(
- fontSize: 11, color: Colors.white),
- ),
+ PBadge(
+ text: Utils.timeFormat(videoItem.duration!),
+ right: 6.0,
+ bottom: 6.0,
+ type: 'gray',
+ ),
+ if (videoItem.ogv != null) ...[
+ PBadge(
+ text: videoItem.ogv['type_name'],
+ top: 6.0,
+ right: 6.0,
+ bottom: null,
+ left: null,
),
- )
+ ],
],
);
},
),
),
- VideoContent(videoItem: videoItem, callFn: callFn)
+ VideoContent(
+ videoItem: videoItem,
+ callFn: callFn,
+ searchType: searchType,
+ )
],
),
);
@@ -121,93 +132,123 @@ class FavVideoCardH extends StatelessWidget {
class VideoContent extends StatelessWidget {
final dynamic videoItem;
final Function? callFn;
- const VideoContent({super.key, required this.videoItem, this.callFn});
+ final int? searchType;
+ const VideoContent({
+ super.key,
+ required this.videoItem,
+ this.callFn,
+ this.searchType,
+ });
@override
Widget build(BuildContext context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 2, 6, 0),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
+ child: Stack(
children: [
- Text(
- videoItem.title,
- textAlign: TextAlign.start,
- style: const TextStyle(
- fontWeight: FontWeight.w500,
- letterSpacing: 0.3,
- ),
- maxLines: 2,
- overflow: TextOverflow.ellipsis,
- ),
- const Spacer(),
- Text(
- Utils.dateFormat(videoItem.ctime!),
- style: TextStyle(
- fontSize: 11, color: Theme.of(context).colorScheme.outline),
- ),
- Text(
- videoItem.owner.name,
- style: TextStyle(
- fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
- color: Theme.of(context).colorScheme.outline,
- ),
- ),
- Row(
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
children: [
- StatView(
- theme: 'gray',
- view: videoItem.cntInfo['play'],
+ Text(
+ videoItem.title,
+ textAlign: TextAlign.start,
+ style: const TextStyle(
+ fontWeight: FontWeight.w500,
+ letterSpacing: 0.3,
+ ),
+ maxLines: 2,
+ overflow: TextOverflow.ellipsis,
),
- const SizedBox(width: 8),
- StatDanMu(theme: 'gray', danmu: videoItem.cntInfo['danmaku']),
- const Spacer(),
- SizedBox(
- width: 26,
- height: 26,
- child: IconButton(
- style: ButtonStyle(
- padding: MaterialStateProperty.all(EdgeInsets.zero),
- ),
- onPressed: () {
- showDialog(
- context: Get.context!,
- builder: (context) {
- return AlertDialog(
- title: const Text('提示'),
- content: const Text('要取消收藏吗?'),
- actions: [
- TextButton(
- onPressed: () => Get.back(),
- child: Text(
- '取消',
- style: TextStyle(
- color: Theme.of(context)
- .colorScheme
- .outline),
- )),
- TextButton(
- onPressed: () async {
- await callFn!();
- Get.back();
- },
- child: const Text('确定取消'),
- )
- ],
- );
- },
- );
- },
- icon: Icon(
- Icons.clear_outlined,
+ if (videoItem.ogv != null) ...[
+ Text(
+ videoItem.intro,
+ style: TextStyle(
+ fontSize:
+ Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
- size: 18,
),
),
+ ],
+ const Spacer(),
+ Text(
+ Utils.dateFormat(videoItem.favTime),
+ style: TextStyle(
+ fontSize: 11,
+ color: Theme.of(context).colorScheme.outline),
+ ),
+ if (videoItem.owner.name != '') ...[
+ Text(
+ videoItem.owner.name,
+ style: TextStyle(
+ fontSize:
+ Theme.of(context).textTheme.labelMedium!.fontSize,
+ color: Theme.of(context).colorScheme.outline,
+ ),
+ ),
+ ],
+ Padding(
+ padding: const EdgeInsets.only(top: 2),
+ child: Row(
+ children: [
+ StatView(
+ theme: 'gray',
+ view: videoItem.cntInfo['play'],
+ ),
+ const SizedBox(width: 8),
+ StatDanMu(
+ theme: 'gray', danmu: videoItem.cntInfo['danmaku']),
+ const Spacer(),
+ ],
+ ),
),
],
),
+ searchType != 1
+ ? Positioned(
+ right: 0,
+ bottom: -4,
+ child: IconButton(
+ style: ButtonStyle(
+ padding: MaterialStateProperty.all(EdgeInsets.zero),
+ ),
+ onPressed: () {
+ showDialog(
+ context: Get.context!,
+ builder: (context) {
+ return AlertDialog(
+ title: const Text('提示'),
+ content: const Text('要取消收藏吗?'),
+ actions: [
+ TextButton(
+ onPressed: () => Get.back(),
+ child: Text(
+ '取消',
+ style: TextStyle(
+ color: Theme.of(context)
+ .colorScheme
+ .outline),
+ )),
+ TextButton(
+ onPressed: () async {
+ await callFn!();
+ Get.back();
+ },
+ child: const Text('确定取消'),
+ )
+ ],
+ );
+ },
+ );
+ },
+ icon: Icon(
+ Icons.clear_outlined,
+ color: Theme.of(context).colorScheme.outline,
+ size: 18,
+ ),
+ ),
+ )
+ : const SizedBox(),
],
),
),
diff --git a/lib/pages/fav_search/controller.dart b/lib/pages/fav_search/controller.dart
index 642fea6b..371a3a07 100644
--- a/lib/pages/fav_search/controller.dart
+++ b/lib/pages/fav_search/controller.dart
@@ -1,8 +1,11 @@
import 'package:flutter/material.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:pilipala/http/user.dart';
import 'package:pilipala/models/user/fav_detail.dart';
+import '../../http/video.dart';
+
class FavSearchController extends GetxController {
final ScrollController scrollController = ScrollController();
Rx controller = TextEditingController().obs;
@@ -72,4 +75,19 @@ class FavSearchController extends GetxController {
if (!hasMore) return;
searchFav(type: 'onLoad');
}
+
+ onCancelFav(int id) async {
+ var result = await VideoHttp.favVideo(
+ aid: id, addIds: '', delIds: mediaId.toString());
+ if (result['status']) {
+ List dataList = favList;
+ for (var i in dataList) {
+ if (i.id == id) {
+ dataList.remove(i);
+ break;
+ }
+ }
+ SmartDialog.showToast('取消收藏');
+ }
+ }
}
diff --git a/lib/pages/fav_search/view.dart b/lib/pages/fav_search/view.dart
index 37e3046f..9b2ab15d 100644
--- a/lib/pages/fav_search/view.dart
+++ b/lib/pages/fav_search/view.dart
@@ -8,9 +8,7 @@ import 'package:pilipala/pages/fav_detail/widget/fav_video_card.dart';
import 'controller.dart';
class FavSearchPage extends StatefulWidget {
- final int? sourceType;
- final int? mediaId;
- const FavSearchPage({super.key, this.sourceType, this.mediaId});
+ const FavSearchPage({super.key});
@override
State createState() => _FavSearchPageState();
@@ -19,11 +17,12 @@ class FavSearchPage extends StatefulWidget {
class _FavSearchPageState extends State {
final FavSearchController _favSearchCtr = Get.put(FavSearchController());
late ScrollController scrollController;
+ late int searchType;
@override
void initState() {
super.initState();
-
+ searchType = int.parse(Get.parameters['searchType']!);
scrollController = _favSearchCtr.scrollController;
scrollController.addListener(
() {
@@ -100,7 +99,11 @@ class _FavSearchPageState extends State {
} else {
return FavVideoCardH(
videoItem: _favSearchCtr.favList[index],
- callFn: () => null,
+ searchType: searchType,
+ callFn: () => searchType != 1
+ ? _favSearchCtr
+ .onCancelFav(_favSearchCtr.favList[index].id!)
+ : {},
);
}
},
diff --git a/lib/pages/follow/view.dart b/lib/pages/follow/view.dart
index a9fcab4e..9633e7f0 100644
--- a/lib/pages/follow/view.dart
+++ b/lib/pages/follow/view.dart
@@ -37,6 +37,29 @@ class _FollowPageState extends State {
: '${_followController.name}的关注',
style: Theme.of(context).textTheme.titleMedium,
),
+ actions: [
+ IconButton(
+ onPressed: () => Get.toNamed('/followSearch?mid=$mid'),
+ icon: const Icon(Icons.search_outlined),
+ ),
+ PopupMenuButton(
+ icon: const Icon(Icons.more_vert),
+ itemBuilder: (BuildContext context) => [
+ PopupMenuItem(
+ onTap: () => Get.toNamed('/blackListPage'),
+ child: const Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Icon(Icons.block, size: 19),
+ SizedBox(width: 10),
+ Text('黑名单管理'),
+ ],
+ ),
+ )
+ ],
+ ),
+ const SizedBox(width: 6),
+ ],
),
body: Obx(
() => !_followController.isOwner.value
@@ -87,3 +110,22 @@ class _FollowPageState extends State {
);
}
}
+
+class _FakeAPI {
+ static const List _kOptions = [
+ 'aardvark',
+ 'bobcat',
+ 'chameleon',
+ ];
+ // Searches the options, but injects a fake "network" delay.
+ static Future> search(String query) async {
+ await Future.delayed(
+ const Duration(seconds: 1)); // Fake 1 second delay.
+ if (query == '') {
+ return const Iterable.empty();
+ }
+ return _kOptions.where((String option) {
+ return option.contains(query.toLowerCase());
+ });
+ }
+}
diff --git a/lib/pages/follow/widgets/follow_item.dart b/lib/pages/follow/widgets/follow_item.dart
index ac9cc01b..d21a89bc 100644
--- a/lib/pages/follow/widgets/follow_item.dart
+++ b/lib/pages/follow/widgets/follow_item.dart
@@ -42,7 +42,7 @@ class FollowItem extends StatelessWidget {
overflow: TextOverflow.ellipsis,
),
dense: true,
- trailing: ctr!.isOwner.value
+ trailing: ctr != null && ctr!.isOwner.value
? SizedBox(
height: 34,
child: TextButton(
diff --git a/lib/pages/follow_search/controller.dart b/lib/pages/follow_search/controller.dart
new file mode 100644
index 00000000..9fd1590d
--- /dev/null
+++ b/lib/pages/follow_search/controller.dart
@@ -0,0 +1,73 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:pilipala/http/member.dart';
+
+import '../../models/follow/result.dart';
+
+class FollowSearchController extends GetxController {
+ Rx controller = TextEditingController().obs;
+ final FocusNode searchFocusNode = FocusNode();
+ RxString searchKeyWord = ''.obs;
+ String hintText = '搜索';
+ RxString loadingStatus = 'init'.obs;
+ late int mid = 1;
+ RxString uname = ''.obs;
+ int ps = 20;
+ int pn = 1;
+ RxList followList = [].obs;
+ RxInt total = 0.obs;
+
+ @override
+ void onInit() {
+ super.onInit();
+ mid = int.parse(Get.parameters['mid']!);
+ }
+
+ // 清空搜索
+ void onClear() {
+ if (searchKeyWord.value.isNotEmpty && controller.value.text != '') {
+ controller.value.clear();
+ searchKeyWord.value = '';
+ } else {
+ Get.back();
+ }
+ }
+
+ void onChange(value) {
+ searchKeyWord.value = value;
+ }
+
+ // 提交搜索内容
+ void submit() {
+ loadingStatus.value = 'loading';
+ searchFollow();
+ }
+
+ Future searchFollow({type = 'init'}) async {
+ if (controller.value.text == '') {
+ return {'status': true, 'data': [].obs};
+ }
+ if (type == 'init') {
+ ps = 1;
+ }
+ var res = await MemberHttp.getfollowSearch(
+ mid: mid,
+ ps: ps,
+ pn: pn,
+ name: controller.value.text,
+ );
+ if (res['status']) {
+ if (type == 'init') {
+ followList.value = res['data'].list;
+ } else {
+ followList.addAll(res['data'].list);
+ }
+ total.value = res['data'].total;
+ }
+ return res;
+ }
+
+ void onLoad() {
+ searchFollow(type: 'onLoad');
+ }
+}
diff --git a/lib/pages/follow_search/index.dart b/lib/pages/follow_search/index.dart
new file mode 100644
index 00000000..805d8c47
--- /dev/null
+++ b/lib/pages/follow_search/index.dart
@@ -0,0 +1,4 @@
+library follow_search;
+
+export './controller.dart';
+export './view.dart';
diff --git a/lib/pages/follow_search/view.dart b/lib/pages/follow_search/view.dart
new file mode 100644
index 00000000..6be42676
--- /dev/null
+++ b/lib/pages/follow_search/view.dart
@@ -0,0 +1,121 @@
+import 'package:easy_debounce/easy_throttle.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:pilipala/common/widgets/http_error.dart';
+import 'package:pilipala/pages/follow_search/index.dart';
+
+import '../follow/widgets/follow_item.dart';
+
+class FollowSearchPage extends StatefulWidget {
+ const FollowSearchPage({super.key});
+
+ @override
+ State createState() => _FollowSearchPageState();
+}
+
+class _FollowSearchPageState extends State {
+ final FollowSearchController _followSearchController =
+ Get.put(FollowSearchController());
+ late Future? _futureBuilder;
+ final ScrollController scrollController = ScrollController();
+
+ @override
+ void initState() {
+ super.initState();
+ _futureBuilder = _followSearchController.searchFollow();
+ scrollController.addListener(
+ () {
+ if (scrollController.position.pixels >=
+ scrollController.position.maxScrollExtent - 200) {
+ EasyThrottle.throttle(
+ 'my-throttler', const Duration(milliseconds: 500), () {
+ _followSearchController.onLoad();
+ });
+ }
+ },
+ );
+ }
+
+ void reRequest() {
+ setState(() {
+ _futureBuilder = _followSearchController.searchFollow();
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ titleSpacing: 0,
+ actions: [
+ IconButton(
+ onPressed: reRequest,
+ icon: const Icon(CupertinoIcons.search, size: 22),
+ ),
+ const SizedBox(width: 6),
+ ],
+ title: TextField(
+ autofocus: true,
+ focusNode: _followSearchController.searchFocusNode,
+ controller: _followSearchController.controller.value,
+ textInputAction: TextInputAction.search,
+ onChanged: (value) => _followSearchController.onChange(value),
+ decoration: InputDecoration(
+ hintText: _followSearchController.hintText,
+ border: InputBorder.none,
+ suffixIcon: IconButton(
+ icon: Icon(
+ Icons.clear,
+ size: 22,
+ color: Theme.of(context).colorScheme.outline,
+ ),
+ onPressed: () => _followSearchController.onClear(),
+ ),
+ ),
+ onSubmitted: (String value) => reRequest(),
+ ),
+ ),
+ body: FutureBuilder(
+ future: _futureBuilder,
+ builder: (BuildContext context, AsyncSnapshot snapshot) {
+ if (snapshot.connectionState == ConnectionState.done) {
+ var data = snapshot.data;
+ if (data == null) {
+ return CustomScrollView(
+ slivers: [
+ HttpError(errMsg: snapshot.data['msg'], fn: reRequest)
+ ],
+ );
+ }
+ if (data['status']) {
+ RxList followList = _followSearchController.followList;
+ return Obx(
+ () => followList.isNotEmpty
+ ? ListView.builder(
+ controller: scrollController,
+ itemCount: followList.length,
+ itemBuilder: ((context, index) {
+ return FollowItem(
+ item: followList[index],
+ );
+ }),
+ )
+ : CustomScrollView(
+ slivers: [HttpError(errMsg: '未搜索到结果', fn: reRequest)],
+ ),
+ );
+ } else {
+ return CustomScrollView(
+ slivers: [
+ HttpError(errMsg: snapshot.data['msg'], fn: reRequest)
+ ],
+ );
+ }
+ } else {
+ return const SizedBox();
+ }
+ }),
+ );
+ }
+}
diff --git a/lib/pages/history/view.dart b/lib/pages/history/view.dart
index d8fc60f0..92e1eee7 100644
--- a/lib/pages/history/view.dart
+++ b/lib/pages/history/view.dart
@@ -70,10 +70,6 @@ class _HistoryPageState extends State {
child1: AppBar(
titleSpacing: 0,
centerTitle: false,
- leading: IconButton(
- onPressed: () => Get.back(),
- icon: const Icon(Icons.arrow_back_outlined),
- ),
title: Text(
'观看记录',
style: Theme.of(context).textTheme.titleMedium,
diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart
index 685ff5c3..fb85be0b 100644
--- a/lib/pages/home/controller.dart
+++ b/lib/pages/home/controller.dart
@@ -26,6 +26,7 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
late List defaultTabs;
late List tabbarSort;
RxString defaultSearch = ''.obs;
+ late bool enableGradientBg;
@override
void onInit() {
@@ -33,13 +34,15 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
userInfo = userInfoCache.get('userInfoCache');
userLogin.value = userInfo != null;
userFace.value = userInfo != null ? userInfo.face : '';
- // 进行tabs配置
- setTabConfig();
hideSearchBar =
setting.get(SettingBoxKey.hideSearchBar, defaultValue: true);
if (setting.get(SettingBoxKey.enableSearchWord, defaultValue: true)) {
searchDefault();
}
+ enableGradientBg =
+ setting.get(SettingBoxKey.enableGradientBg, defaultValue: true);
+ // 进行tabs配置
+ setTabConfig();
}
void onRefresh() {
@@ -88,19 +91,21 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
vsync: this,
);
// 监听 tabController 切换
- tabController.animation!.addListener(() {
- if (tabController.indexIsChanging) {
- if (initialIndex.value != tabController.index) {
- initialIndex.value = tabController.index;
+ if (enableGradientBg) {
+ tabController.animation!.addListener(() {
+ if (tabController.indexIsChanging) {
+ if (initialIndex.value != tabController.index) {
+ initialIndex.value = tabController.index;
+ }
+ } else {
+ final int temp = tabController.animation!.value.round();
+ if (initialIndex.value != temp) {
+ initialIndex.value = temp;
+ tabController.index = initialIndex.value;
+ }
}
- } else {
- final int temp = tabController.animation!.value.round();
- if (initialIndex.value != temp) {
- initialIndex.value = temp;
- tabController.index = initialIndex.value;
- }
- }
- });
+ });
+ }
}
void searchDefault() async {
diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart
index ce7b46c6..b0cef90b 100644
--- a/lib/pages/home/view.dart
+++ b/lib/pages/home/view.dart
@@ -48,38 +48,51 @@ class _HomePageState extends State
super.build(context);
Brightness currentBrightness = MediaQuery.of(context).platformBrightness;
// 设置状态栏图标的亮度
- SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
- statusBarIconBrightness: currentBrightness == Brightness.light
- ? Brightness.dark
- : Brightness.light,
- ));
+ if (_homeController.enableGradientBg) {
+ SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
+ statusBarIconBrightness: currentBrightness == Brightness.light
+ ? Brightness.dark
+ : Brightness.light,
+ ));
+ }
return Scaffold(
extendBody: true,
extendBodyBehindAppBar: true,
+ appBar: _homeController.enableGradientBg
+ ? null
+ : AppBar(toolbarHeight: 0, elevation: 0),
body: Stack(
children: [
// gradient background
- Align(
- alignment: Alignment.topLeft,
- child: Opacity(
- opacity: 0.6,
- child: Container(
- width: MediaQuery.of(context).size.width,
- height: MediaQuery.of(context).size.height,
- decoration: BoxDecoration(
- gradient: LinearGradient(
- colors: [
- Theme.of(context).colorScheme.primary.withOpacity(0.9),
- Theme.of(context).colorScheme.primary.withOpacity(0.5),
- Theme.of(context).colorScheme.surface
- ],
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- stops: const [0, 0.0034, 0.34]),
+ if (_homeController.enableGradientBg) ...[
+ Align(
+ alignment: Alignment.topLeft,
+ child: Opacity(
+ opacity: 0.6,
+ child: Container(
+ width: MediaQuery.of(context).size.width,
+ height: MediaQuery.of(context).size.height,
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ colors: [
+ Theme.of(context)
+ .colorScheme
+ .primary
+ .withOpacity(0.9),
+ Theme.of(context)
+ .colorScheme
+ .primary
+ .withOpacity(0.5),
+ Theme.of(context).colorScheme.surface
+ ],
+ begin: Alignment.topLeft,
+ end: Alignment.bottomRight,
+ stops: const [0, 0.0034, 0.34]),
+ ),
),
),
),
- ),
+ ],
Column(
children: [
CustomAppBar(
@@ -90,7 +103,37 @@ class _HomePageState extends State
callback: showUserBottomSheet,
),
if (_homeController.tabs.length > 1) ...[
- const CustomTabs(),
+ if (_homeController.enableGradientBg) ...[
+ const CustomTabs(),
+ ] else ...[
+ const SizedBox(height: 4),
+ SizedBox(
+ width: double.infinity,
+ height: 42,
+ child: Align(
+ alignment: Alignment.center,
+ child: TabBar(
+ controller: _homeController.tabController,
+ tabs: [
+ for (var i in _homeController.tabs)
+ Tab(text: i['label'])
+ ],
+ isScrollable: true,
+ dividerColor: Colors.transparent,
+ enableFeedback: true,
+ splashBorderRadius: BorderRadius.circular(10),
+ tabAlignment: TabAlignment.center,
+ onTap: (value) {
+ feedBack();
+ if (_homeController.initialIndex.value == value) {
+ _homeController.tabsCtrList[value]().animateToTop();
+ }
+ _homeController.initialIndex.value = value;
+ },
+ ),
+ ),
+ ),
+ ],
] else ...[
const SizedBox(height: 6),
],
@@ -372,13 +415,16 @@ class SearchBar extends StatelessWidget {
),
const SizedBox(width: 10),
Obx(
- () => Text(
- ctr!.defaultSearch.value,
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- style: TextStyle(color: colorScheme.outline),
+ () => Expanded(
+ child: Text(
+ ctr!.defaultSearch.value,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(color: colorScheme.outline),
+ ),
),
),
+ const SizedBox(width: 15),
],
),
),
diff --git a/lib/pages/hot/controller.dart b/lib/pages/hot/controller.dart
index 65706c32..85072475 100644
--- a/lib/pages/hot/controller.dart
+++ b/lib/pages/hot/controller.dart
@@ -7,7 +7,7 @@ class HotController extends GetxController {
final ScrollController scrollController = ScrollController();
final int _count = 20;
int _currentPage = 1;
- RxList videoList = [HotVideoItemModel()].obs;
+ RxList videoList = [].obs;
bool isLoadingMore = false;
bool flag = false;
OverlayEntry? popupDialog;
diff --git a/lib/pages/hot/view.dart b/lib/pages/hot/view.dart
index 2673e84c..7a0a57ea 100644
--- a/lib/pages/hot/view.dart
+++ b/lib/pages/hot/view.dart
@@ -89,8 +89,7 @@ class _HotPageState extends State with AutomaticKeepAliveClientMixin {
if (data['status']) {
return Obx(
() => SliverList(
- delegate:
- SliverChildBuilderDelegate((context, index) {
+ delegate: SliverChildBuilderDelegate((context, index) {
return VideoCardH(
videoItem: _hotController.videoList[index],
showPubdate: true,
@@ -110,7 +109,12 @@ class _HotPageState extends State with AutomaticKeepAliveClientMixin {
} else {
return HttpError(
errMsg: data['msg'],
- fn: () => setState(() {}),
+ fn: () {
+ setState(() {
+ _futureBuilderFuture =
+ _hotController.queryHotFeed('init');
+ });
+ },
);
}
} else {
diff --git a/lib/pages/html/controller.dart b/lib/pages/html/controller.dart
index f3187828..1175ce29 100644
--- a/lib/pages/html/controller.dart
+++ b/lib/pages/html/controller.dart
@@ -96,9 +96,6 @@ class HtmlRenderController extends GetxController {
_sortType = ReplySortType.like;
break;
case ReplySortType.like:
- _sortType = ReplySortType.reply;
- break;
- case ReplySortType.reply:
_sortType = ReplySortType.time;
break;
default:
diff --git a/lib/pages/live/controller.dart b/lib/pages/live/controller.dart
index 6a26f0d2..74fb6e9a 100644
--- a/lib/pages/live/controller.dart
+++ b/lib/pages/live/controller.dart
@@ -10,8 +10,7 @@ class LiveController extends GetxController {
int count = 12;
int _currentPage = 1;
RxInt crossAxisCount = 2.obs;
- RxList liveList = [LiveItemModel()].obs;
- bool isLoadingMore = false;
+ RxList liveList = [].obs;
bool flag = false;
OverlayEntry? popupDialog;
Box setting = GStrorage.setting;
@@ -39,7 +38,6 @@ class LiveController extends GetxController {
}
_currentPage += 1;
}
- isLoadingMore = false;
return res;
}
diff --git a/lib/pages/live/view.dart b/lib/pages/live/view.dart
index f693acf1..f3f91c9e 100644
--- a/lib/pages/live/view.dart
+++ b/lib/pages/live/view.dart
@@ -11,7 +11,6 @@ import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/common/widgets/overlay_pop.dart';
import 'package:pilipala/pages/home/index.dart';
import 'package:pilipala/pages/main/index.dart';
-import 'package:pilipala/pages/rcmd/index.dart';
import 'controller.dart';
import 'widgets/live_item.dart';
@@ -45,8 +44,8 @@ class _LivePageState extends State
() {
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 200) {
- EasyThrottle.throttle('liveList', const Duration(seconds: 1), () {
- _liveController.isLoadingMore = true;
+ EasyThrottle.throttle('liveList', const Duration(milliseconds: 200),
+ () {
_liveController.onLoad();
});
}
@@ -108,24 +107,20 @@ class _LivePageState extends State
} else {
return HttpError(
errMsg: data['msg'],
- fn: () => {},
+ fn: () {
+ setState(() {
+ _futureBuilderFuture =
+ _liveController.queryLiveList('init');
+ });
+ },
);
}
} else {
- // 缓存数据
- if (_liveController.liveList.length > 1) {
- return contentGrid(
- _liveController, _liveController.liveList);
- }
- // 骨架屏
- else {
- return contentGrid(_liveController, []);
- }
+ return contentGrid(_liveController, []);
}
},
),
),
- LoadingMore(ctr: _liveController)
],
),
),
diff --git a/lib/pages/live/widgets/live_item.dart b/lib/pages/live/widgets/live_item.dart
index 8fa797fb..9218d4fb 100644
--- a/lib/pages/live/widgets/live_item.dart
+++ b/lib/pages/live/widgets/live_item.dart
@@ -184,18 +184,32 @@ class VideoStat extends StatelessWidget {
tileMode: TileMode.mirror,
),
),
- 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: 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']),
+ // ],
+ // ),
+ // ),
);
}
}
diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart
index 2f489fec..5c2a9800 100644
--- a/lib/pages/live_room/controller.dart
+++ b/lib/pages/live_room/controller.dart
@@ -1,8 +1,13 @@
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:pilipala/http/constants.dart';
import 'package:pilipala/http/live.dart';
+import 'package:pilipala/models/live/quality.dart';
import 'package:pilipala/models/live/room_info.dart';
import 'package:pilipala/plugin/pl_player/index.dart';
+import '../../models/live/room_info_h5.dart';
+import '../../utils/storage.dart';
+import '../../utils/video_utils.dart';
class LiveRoomController extends GetxController {
String cover = '';
@@ -14,17 +19,18 @@ class LiveRoomController extends GetxController {
RxBool volumeOff = false.obs;
PlPlayerController plPlayerController =
PlPlayerController.getInstance(videoType: 'live');
-
- // MeeduPlayerController meeduPlayerController = MeeduPlayerController(
- // colorTheme: Theme.of(Get.context!).colorScheme.primary,
- // pipEnabled: true,
- // controlsStyle: ControlsStyle.live,
- // enabledButtons: const EnabledButtons(pip: true),
- // );
+ Rx roomInfoH5 = RoomInfoH5Model().obs;
+ late bool enableCDN;
+ late int currentQn;
+ int? tempCurrentQn;
+ late List