Compare commits
29 Commits
feature-no
...
feature-bu
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ece96df21 | |||
| c11c5695a2 | |||
| 93581c2932 | |||
| fd43a8cb31 | |||
| 6f34bacb64 | |||
| 90314f89ed | |||
| 45c53de2c2 | |||
| 5c07cb4545 | |||
| 844053b138 | |||
| 6af8b91f63 | |||
| 8aa02f7450 | |||
| 38a1f2e1f7 | |||
| b2e1d98f51 | |||
| e19cf92992 | |||
| 80e10aeaad | |||
| b1a9152a49 | |||
| 935b7577b3 | |||
| fd5c4463d2 | |||
| 7fcbe4dd9d | |||
| 9a0c9f4021 | |||
| 6b3773a074 | |||
| 7be8ebaa7e | |||
| 092b1cee3d | |||
| 252f39e8c7 | |||
| e631ca04a0 | |||
| 3665d6a0f6 | |||
| e3c9e8c13b | |||
| 39995bae23 | |||
| f42d0d01ea |
21
README.md
21
README.md
@ -3,23 +3,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<h1>PiliPala</h1>
|
<h1>PiliPala</h1>
|
||||||
<div align="center">
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
</div>
|
|
||||||
<p>使用Flutter开发的BiliBili第三方客户端</p>
|
<p>使用Flutter开发的BiliBili第三方客户端</p>
|
||||||
|
<br/>
|
||||||
<img src="https://github.com/guozhigq/pilipala/blob/main/assets/sreenshot/510shots_so.png" width="32%" alt="home" />
|
<img src="https://github.com/guozhigq/pilipala/blob/main/assets/sreenshot/510shots_so.png" width="32%" alt="home" />
|
||||||
<img src="https://github.com/guozhigq/pilipala/blob/main/assets/sreenshot/174shots_so.png" width="32%" alt="home" />
|
<img src="https://github.com/guozhigq/pilipala/blob/main/assets/sreenshot/174shots_so.png" width="32%" alt="home" />
|
||||||
<img src="https://github.com/guozhigq/pilipala/blob/main/assets/sreenshot/850shots_so.png" width="32%" alt="home" />
|
<img src="https://github.com/guozhigq/pilipala/blob/main/assets/sreenshot/850shots_so.png" width="32%" alt="home" />
|
||||||
<br/>
|
<br/>
|
||||||
<img src="https://github.com/guozhigq/pilipala/blob/main/assets/sreenshot/main_screen.png" width="96%" alt="home" />
|
<img src="https://github.com/guozhigq/pilipala/blob/main/assets/sreenshot/main_screen.png" width="96%" alt="home" />
|
||||||
<br/>
|
<br/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## 开发环境
|
## 开发环境
|
||||||
|
|||||||
@ -39,13 +39,9 @@
|
|||||||
android:label="PiliPala"
|
android:label="PiliPala"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
android:enableOnBackInvokedCallback="true">
|
||||||
android:enableOnBackInvokedCallback="true"
|
|
||||||
android:allowBackup="false"
|
|
||||||
android:fullBackupContent="false"
|
|
||||||
tools:replace="android:allowBackup">
|
|
||||||
<activity
|
<activity
|
||||||
android:name="com.ryanheise.audioservice.AudioServiceActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:theme="@style/LaunchTheme"
|
android:theme="@style/LaunchTheme"
|
||||||
@ -226,24 +222,6 @@
|
|||||||
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<service
|
|
||||||
android:name="com.ryanheise.audioservice.AudioService"
|
|
||||||
android:foregroundServiceType="mediaPlayback"
|
|
||||||
android:exported="true"
|
|
||||||
tools:ignore="Instantiatable">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.media.browse.MediaBrowserService" />
|
|
||||||
</intent-filter>
|
|
||||||
</service>
|
|
||||||
|
|
||||||
<receiver
|
|
||||||
android:name="com.ryanheise.audioservice.MediaButtonReceiver"
|
|
||||||
android:exported="true"
|
|
||||||
tools:ignore="Instantiatable">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
<!-- Don't delete the meta-data below.
|
<!-- Don't delete the meta-data below.
|
||||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
<meta-data
|
<meta-data
|
||||||
@ -256,8 +234,6 @@
|
|||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
|
||||||
<!--
|
<!--
|
||||||
Media access permissions.
|
Media access permissions.
|
||||||
Android 13 or higher.
|
Android 13 or higher.
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 528 B |
Binary file not shown.
|
Before Width: | Height: | Size: 337 B |
Binary file not shown.
|
Before Width: | Height: | Size: 648 B |
Binary file not shown.
|
Before Width: | Height: | Size: 962 B |
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 KiB |
@ -1,7 +0,0 @@
|
|||||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
|
||||||
android:viewportHeight="24" android:viewportWidth="24"
|
|
||||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M18,13c0,3.31 -2.69,6 -6,6s-6,-2.69 -6,-6s2.69,-6 6,-6v4l5,-5l-5,-5v4c-4.42,0 -8,3.58 -8,8c0,4.42 3.58,8 8,8s8,-3.58 8,-8H18z"/>
|
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M10.86,15.94l0,-4.27l-0.09,0l-1.77,0.63l0,0.69l1.01,-0.31l0,3.26z"/>
|
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M12.25,13.44v0.74c0,1.9 1.31,1.82 1.44,1.82c0.14,0 1.44,0.09 1.44,-1.82v-0.74c0,-1.9 -1.31,-1.82 -1.44,-1.82C13.55,11.62 12.25,11.53 12.25,13.44zM14.29,13.32v0.97c0,0.77 -0.21,1.03 -0.59,1.03c-0.38,0 -0.6,-0.26 -0.6,-1.03v-0.97c0,-0.75 0.22,-1.01 0.59,-1.01C14.07,12.3 14.29,12.57 14.29,13.32z"/>
|
|
||||||
</vector>
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
|
||||||
android:viewportHeight="24" android:viewportWidth="24"
|
|
||||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M11.99,5V1l-5,5l5,5V7c3.31,0 6,2.69 6,6s-2.69,6 -6,6s-6,-2.69 -6,-6h-2c0,4.42 3.58,8 8,8s8,-3.58 8,-8S16.41,5 11.99,5z"/>
|
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M10.89,16h-0.85v-3.26l-1.01,0.31v-0.69l1.77,-0.63h0.09V16z"/>
|
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M15.17,14.24c0,0.32 -0.03,0.6 -0.1,0.82s-0.17,0.42 -0.29,0.57s-0.28,0.26 -0.45,0.33s-0.37,0.1 -0.59,0.1s-0.41,-0.03 -0.59,-0.1s-0.33,-0.18 -0.46,-0.33s-0.23,-0.34 -0.3,-0.57s-0.11,-0.5 -0.11,-0.82V13.5c0,-0.32 0.03,-0.6 0.1,-0.82s0.17,-0.42 0.29,-0.57s0.28,-0.26 0.45,-0.33s0.37,-0.1 0.59,-0.1s0.41,0.03 0.59,0.1c0.18,0.07 0.33,0.18 0.46,0.33s0.23,0.34 0.3,0.57s0.11,0.5 0.11,0.82V14.24zM14.32,13.38c0,-0.19 -0.01,-0.35 -0.04,-0.48s-0.07,-0.23 -0.12,-0.31s-0.11,-0.14 -0.19,-0.17s-0.16,-0.05 -0.25,-0.05s-0.18,0.02 -0.25,0.05s-0.14,0.09 -0.19,0.17s-0.09,0.18 -0.12,0.31s-0.04,0.29 -0.04,0.48v0.97c0,0.19 0.01,0.35 0.04,0.48s0.07,0.24 0.12,0.32s0.11,0.14 0.19,0.17s0.16,0.05 0.25,0.05s0.18,-0.02 0.25,-0.05s0.14,-0.09 0.19,-0.17s0.09,-0.19 0.11,-0.32s0.04,-0.29 0.04,-0.48V13.38z"/>
|
|
||||||
</vector>
|
|
||||||
@ -2,5 +2,4 @@
|
|||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
tools:keep="@drawable/*" />
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 7.7 KiB |
@ -1,4 +0,0 @@
|
|||||||
## 1.0.10
|
|
||||||
|
|
||||||
### 修复
|
|
||||||
+ 长按倍速抬起后未恢复默认倍速
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
## 1.0.11
|
|
||||||
|
|
||||||
### 新功能
|
|
||||||
+ 适配了原生媒体通知栏 @Daydreamer-riri
|
|
||||||
+ 视频主题图标 @Daydreamer-riri
|
|
||||||
+ 关闭软件后自动画中画播放
|
|
||||||
+ UP主分组管理
|
|
||||||
+ md2样式底栏
|
|
||||||
+
|
|
||||||
|
|
||||||
|
|
||||||
### 修复
|
|
||||||
+ 历史记录记忆播放
|
|
||||||
+ 部分类型视频连播
|
|
||||||
+ 播放速度选择框不支持返回手势
|
|
||||||
+ 播放速度选择框不支持返回手势
|
|
||||||
+ 视频播放速度总是显示1.0X
|
|
||||||
+ 评论页面计数错误
|
|
||||||
+ 退出视频还有声音
|
|
||||||
|
|
||||||
|
|
||||||
### 优化
|
|
||||||
+ 视频加载速度
|
|
||||||
|
|
||||||
更多更新日志可在Github上查看
|
|
||||||
问题反馈、功能建议请查看「关于」页面。
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
## 1.0.12
|
|
||||||
|
|
||||||
|
|
||||||
### 修复
|
|
||||||
+ iOS端视频播放时没有声音
|
|
||||||
+ 超过6分钟弹幕不显示
|
|
||||||
+ 视频详情页网络异常
|
|
||||||
|
|
||||||
|
|
||||||
更多更新日志可在Github上查看
|
|
||||||
问题反馈、功能建议请查看「关于」页面。
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
## 1.0.8
|
|
||||||
|
|
||||||
直播弹幕、循环播放等功能开发中
|
|
||||||
|
|
||||||
### 新功能
|
|
||||||
+ 用户拉黑功能
|
|
||||||
+ gif图片保存
|
|
||||||
+ 删除已看历史记录
|
|
||||||
|
|
||||||
### 修复
|
|
||||||
+ 弹幕数量较少
|
|
||||||
+ 弹幕屏蔽设置自动记忆
|
|
||||||
+ 动态页面渲染
|
|
||||||
+ 用户主页数据错乱
|
|
||||||
+ 大家都在搜空白
|
|
||||||
+ 默认自动全屏,顶部操作栏丢失
|
|
||||||
|
|
||||||
|
|
||||||
### 优化
|
|
||||||
+ 全屏状态栏区域显示优化
|
|
||||||
+ 图片保存至PiliPala文件夹
|
|
||||||
|
|
||||||
更多更新日志可在Github上查看
|
|
||||||
问题反馈、功能建议请查看「关于」页面。
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
## 1.0.9
|
|
||||||
|
|
||||||
|
|
||||||
### 新功能
|
|
||||||
+ 自定义倍速、默认倍速
|
|
||||||
+ 历史记录搜索
|
|
||||||
+ 收藏夹搜索
|
|
||||||
+ 历史记录多选删除
|
|
||||||
+ 视频循环播放
|
|
||||||
+ 免登录看1080P
|
|
||||||
+ 评论区视频链接跳转
|
|
||||||
+ up主分组
|
|
||||||
+ up主投稿搜索
|
|
||||||
|
|
||||||
### 修复
|
|
||||||
+ 搜索视频标题乱码
|
|
||||||
+ 屏幕帧率
|
|
||||||
+ 动态页面渲染
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 优化
|
|
||||||
+ 快进手势
|
|
||||||
+ 视频简介链接匹配
|
|
||||||
+ 视频全屏时安全区域
|
|
||||||
|
|
||||||
更多更新日志可在Github上查看
|
|
||||||
问题反馈、功能建议请查看「关于」页面。
|
|
||||||
@ -1,12 +1,6 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- appscheme (1.0.4):
|
- appscheme (1.0.4):
|
||||||
- Flutter
|
- Flutter
|
||||||
- audio_service (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- audio_session (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- auto_orientation (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- connectivity_plus (0.0.1):
|
- connectivity_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- ReachabilitySwift
|
- ReachabilitySwift
|
||||||
@ -18,10 +12,8 @@ PODS:
|
|||||||
- FMDB (2.7.5):
|
- FMDB (2.7.5):
|
||||||
- FMDB/standard (= 2.7.5)
|
- FMDB/standard (= 2.7.5)
|
||||||
- FMDB/standard (2.7.5)
|
- FMDB/standard (2.7.5)
|
||||||
- gt3_flutter_plugin (0.0.8):
|
- image_gallery_saver (2.0.2):
|
||||||
- Flutter
|
- Flutter
|
||||||
- GT3Captcha-iOS
|
|
||||||
- GT3Captcha-iOS (0.15.8.3)
|
|
||||||
- media_kit_libs_ios_video (1.0.4):
|
- media_kit_libs_ios_video (1.0.4):
|
||||||
- Flutter
|
- Flutter
|
||||||
- media_kit_native_event_loop (1.0.0):
|
- media_kit_native_event_loop (1.0.0):
|
||||||
@ -36,8 +28,6 @@ PODS:
|
|||||||
- permission_handler_apple (9.1.1):
|
- permission_handler_apple (9.1.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- ReachabilitySwift (5.0.0)
|
- ReachabilitySwift (5.0.0)
|
||||||
- saver_gallery (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- screen_brightness_ios (0.1.0):
|
- screen_brightness_ios (0.1.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- share_plus (0.0.1):
|
- share_plus (0.0.1):
|
||||||
@ -62,21 +52,17 @@ PODS:
|
|||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- appscheme (from `.symlinks/plugins/appscheme/ios`)
|
- appscheme (from `.symlinks/plugins/appscheme/ios`)
|
||||||
- audio_service (from `.symlinks/plugins/audio_service/ios`)
|
|
||||||
- audio_session (from `.symlinks/plugins/audio_session/ios`)
|
|
||||||
- auto_orientation (from `.symlinks/plugins/auto_orientation/ios`)
|
|
||||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
- flutter_volume_controller (from `.symlinks/plugins/flutter_volume_controller/ios`)
|
- flutter_volume_controller (from `.symlinks/plugins/flutter_volume_controller/ios`)
|
||||||
- gt3_flutter_plugin (from `.symlinks/plugins/gt3_flutter_plugin/ios`)
|
- image_gallery_saver (from `.symlinks/plugins/image_gallery_saver/ios`)
|
||||||
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
|
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
|
||||||
- media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`)
|
- media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`)
|
||||||
- media_kit_video (from `.symlinks/plugins/media_kit_video/ios`)
|
- media_kit_video (from `.symlinks/plugins/media_kit_video/ios`)
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||||
- saver_gallery (from `.symlinks/plugins/saver_gallery/ios`)
|
|
||||||
- screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`)
|
- screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`)
|
||||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||||
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||||
@ -91,18 +77,11 @@ DEPENDENCIES:
|
|||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
trunk:
|
trunk:
|
||||||
- FMDB
|
- FMDB
|
||||||
- GT3Captcha-iOS
|
|
||||||
- ReachabilitySwift
|
- ReachabilitySwift
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
appscheme:
|
appscheme:
|
||||||
:path: ".symlinks/plugins/appscheme/ios"
|
:path: ".symlinks/plugins/appscheme/ios"
|
||||||
audio_service:
|
|
||||||
:path: ".symlinks/plugins/audio_service/ios"
|
|
||||||
audio_session:
|
|
||||||
:path: ".symlinks/plugins/audio_session/ios"
|
|
||||||
auto_orientation:
|
|
||||||
:path: ".symlinks/plugins/auto_orientation/ios"
|
|
||||||
connectivity_plus:
|
connectivity_plus:
|
||||||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||||
device_info_plus:
|
device_info_plus:
|
||||||
@ -111,8 +90,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter
|
:path: Flutter
|
||||||
flutter_volume_controller:
|
flutter_volume_controller:
|
||||||
:path: ".symlinks/plugins/flutter_volume_controller/ios"
|
:path: ".symlinks/plugins/flutter_volume_controller/ios"
|
||||||
gt3_flutter_plugin:
|
image_gallery_saver:
|
||||||
:path: ".symlinks/plugins/gt3_flutter_plugin/ios"
|
:path: ".symlinks/plugins/image_gallery_saver/ios"
|
||||||
media_kit_libs_ios_video:
|
media_kit_libs_ios_video:
|
||||||
:path: ".symlinks/plugins/media_kit_libs_ios_video/ios"
|
:path: ".symlinks/plugins/media_kit_libs_ios_video/ios"
|
||||||
media_kit_native_event_loop:
|
media_kit_native_event_loop:
|
||||||
@ -125,8 +104,6 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||||
permission_handler_apple:
|
permission_handler_apple:
|
||||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||||
saver_gallery:
|
|
||||||
:path: ".symlinks/plugins/saver_gallery/ios"
|
|
||||||
screen_brightness_ios:
|
screen_brightness_ios:
|
||||||
:path: ".symlinks/plugins/screen_brightness_ios/ios"
|
:path: ".symlinks/plugins/screen_brightness_ios/ios"
|
||||||
share_plus:
|
share_plus:
|
||||||
@ -150,30 +127,25 @@ EXTERNAL SOURCES:
|
|||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
appscheme: b1c3f8862331cb20430cf9e0e4af85dbc1572ad8
|
appscheme: b1c3f8862331cb20430cf9e0e4af85dbc1572ad8
|
||||||
audio_service: f509d65da41b9521a61f1c404dd58651f265a567
|
|
||||||
audio_session: 4f3e461722055d21515cf3261b64c973c062f345
|
|
||||||
auto_orientation: 102ed811a5938d52c86520ddd7ecd3a126b5d39d
|
|
||||||
connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
|
connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
|
||||||
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
|
device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea
|
||||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||||
flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529
|
flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529
|
||||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||||
gt3_flutter_plugin: bfa1f26e9a09dc00401514be5ed437f964cabf23
|
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
|
||||||
GT3Captcha-iOS: 5e3b1077834d8a9d6f4d64a447a30af3e14affe6
|
|
||||||
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
|
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
|
||||||
media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a
|
media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a
|
||||||
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e
|
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e
|
||||||
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
|
package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
|
||||||
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
||||||
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
|
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
|
||||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||||
saver_gallery: 2b4e584106fde2407ab51560f3851564963e6b78
|
|
||||||
screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625
|
screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625
|
||||||
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
|
share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028
|
||||||
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
|
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
|
||||||
status_bar_control: 7c84146799e6a076315cc1550f78ef53aae3e446
|
status_bar_control: 7c84146799e6a076315cc1550f78ef53aae3e446
|
||||||
system_proxy: bec1a5c5af67dd3e3ebf43979400a8756c04cc44
|
system_proxy: bec1a5c5af67dd3e3ebf43979400a8756c04cc44
|
||||||
url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b
|
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
|
||||||
volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9
|
volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9
|
||||||
wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47
|
wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47
|
||||||
webview_cookie_manager: eaf920722b493bd0f7611b5484771ca53fed03f7
|
webview_cookie_manager: eaf920722b493bd0f7611b5484771ca53fed03f7
|
||||||
@ -181,4 +153,4 @@ SPEC CHECKSUMS:
|
|||||||
|
|
||||||
PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b
|
PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b
|
||||||
|
|
||||||
COCOAPODS: 1.14.3
|
COCOAPODS: 1.12.1
|
||||||
|
|||||||
@ -140,7 +140,6 @@
|
|||||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
5A372F23F3CF0118D6526BAC /* [CP] Embed Pods Frameworks */,
|
5A372F23F3CF0118D6526BAC /* [CP] Embed Pods Frameworks */,
|
||||||
B78851E7B29A4C3961AC483C /* [CP] Copy Pods Resources */,
|
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@ -157,7 +156,7 @@
|
|||||||
97C146E61CF9000F007C117D /* Project object */ = {
|
97C146E61CF9000F007C117D /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastUpgradeCheck = 1430;
|
LastUpgradeCheck = 1300;
|
||||||
ORGANIZATIONNAME = "";
|
ORGANIZATIONNAME = "";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
97C146ED1CF9000F007C117D = {
|
97C146ED1CF9000F007C117D = {
|
||||||
@ -269,23 +268,6 @@
|
|||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||||
};
|
};
|
||||||
B78851E7B29A4C3961AC483C /* [CP] Copy Pods Resources */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputFileListPaths = (
|
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
|
||||||
);
|
|
||||||
name = "[CP] Copy Pods Resources";
|
|
||||||
outputFileListPaths = (
|
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
|
||||||
showEnvVarsInLog = 0;
|
|
||||||
};
|
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1430"
|
LastUpgradeVersion = "1300"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|||||||
@ -103,9 +103,5 @@
|
|||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>UIBackgroundModes</key>
|
|
||||||
<array>
|
|
||||||
<string>audio</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@ -9,11 +9,7 @@ class StyleString {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Constants {
|
class Constants {
|
||||||
// 27eb53fc9058f8c3 移动端 Android
|
static const String appKey = '27eb53fc9058f8c3';
|
||||||
// 4409e2ce8ffd12b8 TV端
|
|
||||||
static const String appKey = '4409e2ce8ffd12b8';
|
|
||||||
// 59b43e04ad6965f34319062b478f83dd TV端
|
|
||||||
static const String appSec = '59b43e04ad6965f34319062b478f83dd';
|
|
||||||
static const String thirdSign = '04224646d1fea004e79606d3b038c84a';
|
static const String thirdSign = '04224646d1fea004e79606d3b038c84a';
|
||||||
static const String thirdApi =
|
static const String thirdApi =
|
||||||
'https://www.mcbbs.net/template/mcbbs/image/special_photo_bg.png';
|
'https://www.mcbbs.net/template/mcbbs/image/special_photo_bg.png';
|
||||||
|
|||||||
@ -1,5 +1,37 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
// Widget pBadge(
|
||||||
|
// text,
|
||||||
|
// context,
|
||||||
|
// double? top,
|
||||||
|
// double? right,
|
||||||
|
// double? bottom,
|
||||||
|
// double? left, {
|
||||||
|
// type = 'primary',
|
||||||
|
// }) {
|
||||||
|
// Color bgColor = Theme.of(context).colorScheme.primary;
|
||||||
|
// Color color = Theme.of(context).colorScheme.onPrimary;
|
||||||
|
// if (type == 'gray') {
|
||||||
|
// bgColor = Colors.black54.withOpacity(0.4);
|
||||||
|
// color = Colors.white;
|
||||||
|
// }
|
||||||
|
// return Positioned(
|
||||||
|
// top: top,
|
||||||
|
// left: left,
|
||||||
|
// right: right,
|
||||||
|
// bottom: bottom,
|
||||||
|
// child: Container(
|
||||||
|
// padding: const EdgeInsets.symmetric(vertical: 1, horizontal: 6),
|
||||||
|
// decoration:
|
||||||
|
// BoxDecoration(borderRadius: BorderRadius.circular(4), color: bgColor),
|
||||||
|
// child: Text(
|
||||||
|
// text,
|
||||||
|
// style: TextStyle(fontSize: 11, color: color),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
class PBadge extends StatelessWidget {
|
class PBadge extends StatelessWidget {
|
||||||
final String? text;
|
final String? text;
|
||||||
final double? top;
|
final double? top;
|
||||||
|
|||||||
@ -1,47 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class ContentContainer extends StatelessWidget {
|
|
||||||
final Widget? contentWidget;
|
|
||||||
final Widget? bottomWidget;
|
|
||||||
final bool isScrollable;
|
|
||||||
final Clip? childClipBehavior;
|
|
||||||
|
|
||||||
const ContentContainer(
|
|
||||||
{Key? key,
|
|
||||||
this.contentWidget,
|
|
||||||
this.bottomWidget,
|
|
||||||
this.isScrollable = true,
|
|
||||||
this.childClipBehavior})
|
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return LayoutBuilder(
|
|
||||||
builder: (BuildContext context, BoxConstraints constraints) {
|
|
||||||
return SingleChildScrollView(
|
|
||||||
clipBehavior: childClipBehavior ?? Clip.hardEdge,
|
|
||||||
physics: isScrollable ? null : NeverScrollableScrollPhysics(),
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: constraints.copyWith(
|
|
||||||
minHeight: constraints.maxHeight,
|
|
||||||
maxHeight: double.infinity,
|
|
||||||
),
|
|
||||||
child: IntrinsicHeight(
|
|
||||||
child: Column(
|
|
||||||
children: <Widget>[
|
|
||||||
if (contentWidget != null)
|
|
||||||
Expanded(
|
|
||||||
child: contentWidget!,
|
|
||||||
)
|
|
||||||
else
|
|
||||||
Spacer(),
|
|
||||||
if (bottomWidget != null) bottomWidget!,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_html/flutter_html.dart';
|
import 'package:flutter_html/flutter_html.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
|
||||||
|
|
||||||
// ignore: must_be_immutable
|
// ignore: must_be_immutable
|
||||||
class HtmlRender extends StatelessWidget {
|
class HtmlRender extends StatelessWidget {
|
||||||
@ -20,47 +20,35 @@ class HtmlRender extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Html(
|
return Html(
|
||||||
data: htmlContent,
|
data: htmlContent,
|
||||||
|
// tagsList: Html.tags..addAll(["form", "label", "input"]),
|
||||||
onLinkTap: (url, buildContext, attributes) => {},
|
onLinkTap: (url, buildContext, attributes) => {},
|
||||||
extensions: [
|
extensions: [
|
||||||
TagExtension(
|
TagExtension(
|
||||||
tagsToExtend: {"img"},
|
tagsToExtend: {"img"},
|
||||||
builder: (extensionContext) {
|
builder: (extensionContext) {
|
||||||
try {
|
String? imgUrl = extensionContext.attributes['src'];
|
||||||
Map attributes = extensionContext.attributes;
|
if (imgUrl!.startsWith('//')) {
|
||||||
List key = attributes.keys.toList();
|
imgUrl = 'https:$imgUrl';
|
||||||
String? imgUrl = key.contains('src')
|
|
||||||
? attributes['src']
|
|
||||||
: attributes['data-src'];
|
|
||||||
if (imgUrl!.startsWith('//')) {
|
|
||||||
imgUrl = 'https:$imgUrl';
|
|
||||||
}
|
|
||||||
if (imgUrl.startsWith('http://')) {
|
|
||||||
imgUrl = imgUrl.replaceAll('http://', 'https://');
|
|
||||||
}
|
|
||||||
imgUrl = imgUrl.contains('@') ? imgUrl.split('@').first : imgUrl;
|
|
||||||
bool isEmote = imgUrl.contains('/emote/');
|
|
||||||
bool isMall = imgUrl.contains('/mall/');
|
|
||||||
if (isMall) {
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
// bool inTable =
|
|
||||||
// extensionContext.element!.previousElementSibling == null ||
|
|
||||||
// extensionContext.element!.nextElementSibling == null;
|
|
||||||
// imgUrl = Utils().imageUrl(imgUrl!);
|
|
||||||
// return Image.network(
|
|
||||||
// imgUrl,
|
|
||||||
// width: isEmote ? 22 : null,
|
|
||||||
// height: isEmote ? 22 : null,
|
|
||||||
// );
|
|
||||||
return NetworkImgLayer(
|
|
||||||
width: isEmote ? 22 : Get.size.width - 24,
|
|
||||||
height: isEmote ? 22 : 200,
|
|
||||||
src: imgUrl,
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
print(err);
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
}
|
||||||
|
if (imgUrl.startsWith('http://')) {
|
||||||
|
imgUrl = imgUrl.replaceAll('http://', 'https://');
|
||||||
|
}
|
||||||
|
|
||||||
|
print(imgUrl);
|
||||||
|
bool isEmote = imgUrl.contains('/emote/');
|
||||||
|
bool isMall = imgUrl.contains('/mall/');
|
||||||
|
if (isMall) {
|
||||||
|
return SizedBox();
|
||||||
|
}
|
||||||
|
// bool inTable =
|
||||||
|
// extensionContext.element!.previousElementSibling == null ||
|
||||||
|
// extensionContext.element!.nextElementSibling == null;
|
||||||
|
// imgUrl = Utils().imageUrl(imgUrl!);
|
||||||
|
return Image.network(
|
||||||
|
imgUrl,
|
||||||
|
width: isEmote ? 22 : null,
|
||||||
|
height: isEmote ? 22 : null,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -75,13 +63,11 @@ class HtmlRender extends StatelessWidget {
|
|||||||
textDecoration: TextDecoration.none,
|
textDecoration: TextDecoration.none,
|
||||||
),
|
),
|
||||||
"p": Style(
|
"p": Style(
|
||||||
margin: Margins.only(bottom: 10),
|
margin: Margins.only(bottom: 0),
|
||||||
),
|
),
|
||||||
"span": Style(
|
"span": Style(
|
||||||
fontSize: FontSize.medium,
|
fontSize: FontSize.medium,
|
||||||
height: Height(1.65),
|
|
||||||
),
|
),
|
||||||
"div": Style(height: Height.auto()),
|
|
||||||
"li > p": Style(
|
"li > p": Style(
|
||||||
display: Display.inline,
|
display: Display.inline,
|
||||||
),
|
),
|
||||||
@ -89,7 +75,61 @@ class HtmlRender extends StatelessWidget {
|
|||||||
padding: HtmlPaddings.only(bottom: 4),
|
padding: HtmlPaddings.only(bottom: 4),
|
||||||
textAlign: TextAlign.justify,
|
textAlign: TextAlign.justify,
|
||||||
),
|
),
|
||||||
"img": Style(margin: Margins.only(top: 4, bottom: 4)),
|
"image": Style(margin: Margins.only(top: 4, bottom: 4)),
|
||||||
|
"p > img": Style(margin: Margins.only(top: 4, bottom: 4)),
|
||||||
|
"code": Style(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.onInverseSurface),
|
||||||
|
"code > span": Style(textAlign: TextAlign.start),
|
||||||
|
"hr": Style(
|
||||||
|
margin: Margins.zero,
|
||||||
|
padding: HtmlPaddings.zero,
|
||||||
|
border: Border(
|
||||||
|
top: BorderSide(
|
||||||
|
width: 1.0,
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.onBackground.withOpacity(0.3),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'table': Style(
|
||||||
|
border: Border(
|
||||||
|
right: BorderSide(
|
||||||
|
width: 0.5,
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.onBackground.withOpacity(0.3),
|
||||||
|
),
|
||||||
|
bottom: BorderSide(
|
||||||
|
width: 0.5,
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.onBackground.withOpacity(0.3),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'tr': Style(
|
||||||
|
border: Border(
|
||||||
|
top: BorderSide(
|
||||||
|
width: 1.0,
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.onBackground.withOpacity(0.3),
|
||||||
|
),
|
||||||
|
left: BorderSide(
|
||||||
|
width: 1.0,
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.onBackground.withOpacity(0.3),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'thead': Style(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.background,
|
||||||
|
),
|
||||||
|
'th': Style(
|
||||||
|
padding: HtmlPaddings.only(left: 3, right: 3),
|
||||||
|
),
|
||||||
|
'td': Style(
|
||||||
|
padding: HtmlPaddings.all(4.0),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import 'package:pilipala/common/widgets/stat/danmu.dart';
|
|||||||
import 'package:pilipala/common/widgets/stat/view.dart';
|
import 'package:pilipala/common/widgets/stat/view.dart';
|
||||||
import 'package:pilipala/http/search.dart';
|
import 'package:pilipala/http/search.dart';
|
||||||
import 'package:pilipala/http/user.dart';
|
import 'package:pilipala/http/user.dart';
|
||||||
|
import 'package:pilipala/pages/member/index.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
|
|
||||||
@ -17,10 +18,6 @@ class VideoCardH extends StatelessWidget {
|
|||||||
final Function()? longPress;
|
final Function()? longPress;
|
||||||
final Function()? longPressEnd;
|
final Function()? longPressEnd;
|
||||||
final String source;
|
final String source;
|
||||||
final bool showOwner;
|
|
||||||
final bool showView;
|
|
||||||
final bool showDanmaku;
|
|
||||||
final bool showPubdate;
|
|
||||||
|
|
||||||
const VideoCardH({
|
const VideoCardH({
|
||||||
Key? key,
|
Key? key,
|
||||||
@ -28,10 +25,6 @@ class VideoCardH extends StatelessWidget {
|
|||||||
this.longPress,
|
this.longPress,
|
||||||
this.longPressEnd,
|
this.longPressEnd,
|
||||||
this.source = 'normal',
|
this.source = 'normal',
|
||||||
this.showOwner = true,
|
|
||||||
this.showView = true,
|
|
||||||
this.showDanmaku = true,
|
|
||||||
this.showPubdate = false,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -41,9 +34,10 @@ class VideoCardH extends StatelessWidget {
|
|||||||
String heroTag = Utils.makeHeroTag(aid);
|
String heroTag = Utils.makeHeroTag(aid);
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
if (longPress != null) {
|
// if (longPress != null) {
|
||||||
longPress!();
|
// longPress!();
|
||||||
}
|
// }
|
||||||
|
MemberController().blockUser(videoItem.mid);
|
||||||
},
|
},
|
||||||
// onLongPressEnd: (details) {
|
// onLongPressEnd: (details) {
|
||||||
// if (longPressEnd != null) {
|
// if (longPressEnd != null) {
|
||||||
@ -111,14 +105,7 @@ class VideoCardH extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
VideoContent(
|
VideoContent(videoItem: videoItem, source: source)
|
||||||
videoItem: videoItem,
|
|
||||||
source: source,
|
|
||||||
showOwner: showOwner,
|
|
||||||
showView: showView,
|
|
||||||
showDanmaku: showDanmaku,
|
|
||||||
showPubdate: showPubdate,
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -134,20 +121,8 @@ class VideoContent extends StatelessWidget {
|
|||||||
// ignore: prefer_typing_uninitialized_variables
|
// ignore: prefer_typing_uninitialized_variables
|
||||||
final videoItem;
|
final videoItem;
|
||||||
final String source;
|
final String source;
|
||||||
final bool showOwner;
|
const VideoContent(
|
||||||
final bool showView;
|
{super.key, required this.videoItem, this.source = 'normal'});
|
||||||
final bool showDanmaku;
|
|
||||||
final bool showPubdate;
|
|
||||||
|
|
||||||
const VideoContent({
|
|
||||||
super.key,
|
|
||||||
required this.videoItem,
|
|
||||||
this.source = 'normal',
|
|
||||||
this.showOwner = true,
|
|
||||||
this.showView = true,
|
|
||||||
this.showDanmaku = true,
|
|
||||||
this.showPubdate = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -206,61 +181,16 @@ class VideoContent extends StatelessWidget {
|
|||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
// const SizedBox(height: 4),
|
// const SizedBox(height: 4),
|
||||||
if (showPubdate)
|
|
||||||
Text(
|
|
||||||
Utils.dateFormat(videoItem.pubdate!),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 11, color: Theme.of(context).colorScheme.outline),
|
|
||||||
),
|
|
||||||
if (showOwner)
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
videoItem.owner.name,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize:
|
|
||||||
Theme.of(context).textTheme.labelMedium!.fontSize,
|
|
||||||
color: Theme.of(context).colorScheme.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
if (showView) ...[
|
Text(
|
||||||
StatView(
|
videoItem.owner.name,
|
||||||
theme: 'gray',
|
style: TextStyle(
|
||||||
view: videoItem.stat.view,
|
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
),
|
||||||
],
|
|
||||||
if (showDanmaku)
|
|
||||||
StatDanMu(
|
|
||||||
theme: 'gray',
|
|
||||||
danmu: videoItem.stat.danmaku,
|
|
||||||
),
|
|
||||||
|
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
// SizedBox(
|
|
||||||
// width: 20,
|
|
||||||
// height: 20,
|
|
||||||
// child: IconButton(
|
|
||||||
// tooltip: '稍后再看',
|
|
||||||
// style: ButtonStyle(
|
|
||||||
// padding: MaterialStateProperty.all(EdgeInsets.zero),
|
|
||||||
// ),
|
|
||||||
// onPressed: () async {
|
|
||||||
// var res =
|
|
||||||
// await UserHttp.toViewLater(bvid: videoItem.bvid);
|
|
||||||
// SmartDialog.showToast(res['msg']);
|
|
||||||
// },
|
|
||||||
// icon: Icon(
|
|
||||||
// Icons.more_vert_outlined,
|
|
||||||
// color: Theme.of(context).colorScheme.outline,
|
|
||||||
// size: 14,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
if (source == 'normal')
|
if (source == 'normal')
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 24,
|
width: 24,
|
||||||
@ -294,6 +224,20 @@ class VideoContent extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
// PopupMenuItem<String>(
|
||||||
|
// onTap: () async {
|
||||||
|
// MemberController().blockUser(videoItem.mid);
|
||||||
|
// },
|
||||||
|
// value: 'block',
|
||||||
|
// height: 35,
|
||||||
|
// child: const Row(
|
||||||
|
// children: [
|
||||||
|
// Icon(Icons.block, size: 16),
|
||||||
|
// SizedBox(width: 6),
|
||||||
|
// Text('拉黑up', style: TextStyle(fontSize: 13))
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import 'package:pilipala/common/constants.dart';
|
|||||||
import 'package:pilipala/common/widgets/badge.dart';
|
import 'package:pilipala/common/widgets/badge.dart';
|
||||||
import 'package:pilipala/common/widgets/stat/danmu.dart';
|
import 'package:pilipala/common/widgets/stat/danmu.dart';
|
||||||
import 'package:pilipala/common/widgets/stat/view.dart';
|
import 'package:pilipala/common/widgets/stat/view.dart';
|
||||||
import 'package:pilipala/http/dynamics.dart';
|
|
||||||
import 'package:pilipala/http/search.dart';
|
import 'package:pilipala/http/search.dart';
|
||||||
import 'package:pilipala/http/user.dart';
|
import 'package:pilipala/http/user.dart';
|
||||||
import 'package:pilipala/models/common/search_type.dart';
|
import 'package:pilipala/models/common/search_type.dart';
|
||||||
@ -28,11 +27,6 @@ class VideoCardV extends StatelessWidget {
|
|||||||
this.longPressEnd,
|
this.longPressEnd,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
bool isStringNumeric(String str) {
|
|
||||||
RegExp numericRegex = RegExp(r'^\d+$');
|
|
||||||
return numericRegex.hasMatch(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onPushDetail(heroTag) async {
|
void onPushDetail(heroTag) async {
|
||||||
String goto = videoItem.goto;
|
String goto = videoItem.goto;
|
||||||
switch (goto) {
|
switch (goto) {
|
||||||
@ -54,7 +48,7 @@ class VideoCardV extends StatelessWidget {
|
|||||||
arguments: {
|
arguments: {
|
||||||
'pic': videoItem.pic,
|
'pic': videoItem.pic,
|
||||||
'heroTag': heroTag,
|
'heroTag': heroTag,
|
||||||
'videoType': SearchType.media_bangumi,
|
// 'videoType': SearchType.media_bangumi,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -68,47 +62,6 @@ class VideoCardV extends StatelessWidget {
|
|||||||
'heroTag': heroTag,
|
'heroTag': heroTag,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
// 动态
|
|
||||||
case 'picture':
|
|
||||||
try {
|
|
||||||
String dynamicType = 'picture';
|
|
||||||
String uri = videoItem.uri;
|
|
||||||
String id = '';
|
|
||||||
if (videoItem.uri.startsWith('bilibili://article/')) {
|
|
||||||
// https://www.bilibili.com/read/cv27063554
|
|
||||||
dynamicType = 'read';
|
|
||||||
RegExp regex = RegExp(r'\d+');
|
|
||||||
Match match = regex.firstMatch(videoItem.uri)!;
|
|
||||||
String matchedNumber = match.group(0)!;
|
|
||||||
videoItem.param = int.parse(matchedNumber);
|
|
||||||
id = 'cv${videoItem.param}';
|
|
||||||
}
|
|
||||||
if (uri.startsWith('http')) {
|
|
||||||
String path = Uri.parse(uri).path;
|
|
||||||
if (isStringNumeric(path.split('/')[1])) {
|
|
||||||
// 请求接口
|
|
||||||
var res =
|
|
||||||
await DynamicsHttp.dynamicDetail(id: path.split('/')[1]);
|
|
||||||
if (res['status']) {
|
|
||||||
Get.toNamed('/dynamicDetail', arguments: {
|
|
||||||
'item': res['data'],
|
|
||||||
'floor': 1,
|
|
||||||
'action': 'detail'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Get.toNamed('/htmlRender', parameters: {
|
|
||||||
'url': uri,
|
|
||||||
'title': videoItem.title,
|
|
||||||
'id': id,
|
|
||||||
'dynamicType': dynamicType
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
SmartDialog.showToast(err.toString());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
SmartDialog.showToast(videoItem.goto);
|
SmartDialog.showToast(videoItem.goto);
|
||||||
Get.toNamed(
|
Get.toNamed(
|
||||||
@ -325,20 +278,23 @@ class VideoStat extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return RichText(
|
return Row(
|
||||||
maxLines: 1,
|
children: [
|
||||||
text: TextSpan(
|
Text(
|
||||||
style: TextStyle(
|
'${videoItem.stat.view}观看',
|
||||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.outline,
|
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
children: [
|
Text(
|
||||||
if (videoItem.stat.view != '-')
|
' • ${videoItem.stat.danmu}弹幕',
|
||||||
TextSpan(text: '${videoItem.stat.view}观看'),
|
style: TextStyle(
|
||||||
if (videoItem.stat.danmu != '-')
|
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||||
TextSpan(text: ' • ${videoItem.stat.danmu}弹幕'),
|
color: Theme.of(context).colorScheme.outline,
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -97,9 +97,6 @@ class Api {
|
|||||||
// 操作用户关系
|
// 操作用户关系
|
||||||
static const String relationMod = '/x/relation/modify';
|
static const String relationMod = '/x/relation/modify';
|
||||||
|
|
||||||
// 相互关系查询
|
|
||||||
static const String relationSearch = '/x/space/wbi/acc/relation';
|
|
||||||
|
|
||||||
// 评论列表
|
// 评论列表
|
||||||
// https://api.bilibili.com/x/v2/reply/main?csrf=6e22efc1a47225ea25f901f922b5cfdd&mode=3&oid=254175381&pagination_str=%7B%22offset%22:%22%22%7D&plat=1&seek_rpid=0&type=11
|
// https://api.bilibili.com/x/v2/reply/main?csrf=6e22efc1a47225ea25f901f922b5cfdd&mode=3&oid=254175381&pagination_str=%7B%22offset%22:%22%22%7D&plat=1&seek_rpid=0&type=11
|
||||||
static const String replyList = '/x/v2/reply';
|
static const String replyList = '/x/v2/reply';
|
||||||
@ -129,14 +126,12 @@ class Api {
|
|||||||
static const String userFavFolder = '/x/v3/fav/folder/created/list';
|
static const String userFavFolder = '/x/v3/fav/folder/created/list';
|
||||||
|
|
||||||
/// 收藏夹 详情
|
/// 收藏夹 详情
|
||||||
/// media_id 当前收藏夹id 搜索全部时为默认收藏夹id
|
/// media_id int 收藏夹id
|
||||||
/// pn int 当前页
|
/// pn int 当前页
|
||||||
/// ps int pageSize
|
/// ps int pageSize
|
||||||
/// keyword String 搜索词
|
/// keyword String 搜索词
|
||||||
/// order String 排序方式 view 最多播放 mtime 最近收藏 pubtime 最近投稿
|
/// order String 排序方式 view 最多播放 mtime 最近收藏 pubtime 最近投稿
|
||||||
/// tid int 分区id
|
/// tid int 分区id
|
||||||
/// platform web
|
|
||||||
/// type 0 当前收藏夹 1 全部收藏夹
|
|
||||||
// https://api.bilibili.com/x/v3/fav/resource/list?media_id=76614671&pn=1&ps=20&keyword=&order=mtime&type=0&tid=0
|
// https://api.bilibili.com/x/v3/fav/resource/list?media_id=76614671&pn=1&ps=20&keyword=&order=mtime&type=0&tid=0
|
||||||
static const String userFavFolderDetail = '/x/v3/fav/resource/list';
|
static const String userFavFolderDetail = '/x/v3/fav/resource/list';
|
||||||
|
|
||||||
@ -172,9 +167,6 @@ class Api {
|
|||||||
// 删除某条历史记录
|
// 删除某条历史记录
|
||||||
static const String delHistory = '/x/v2/history/delete';
|
static const String delHistory = '/x/v2/history/delete';
|
||||||
|
|
||||||
// 搜索历史记录
|
|
||||||
static const String searchHistory = '/x/web-goblin/history/search';
|
|
||||||
|
|
||||||
// 热搜
|
// 热搜
|
||||||
static const String hotSearchList =
|
static const String hotSearchList =
|
||||||
'https://s.search.bilibili.com/main/hotword';
|
'https://s.search.bilibili.com/main/hotword';
|
||||||
@ -215,7 +207,7 @@ class Api {
|
|||||||
// 粉丝
|
// 粉丝
|
||||||
// vmid 用户id pn 页码 ps 每页个数,最大50 order: desc
|
// vmid 用户id pn 页码 ps 每页个数,最大50 order: desc
|
||||||
// order_type 排序规则 最近访问传空,最常访问传 attention
|
// order_type 排序规则 最近访问传空,最常访问传 attention
|
||||||
static const String fans = '/x/relation/fans';
|
static const String fans = 'https://api.bilibili.com/x/relation/fans';
|
||||||
|
|
||||||
// 直播
|
// 直播
|
||||||
// ?page=1&page_size=30&platform=web
|
// ?page=1&page_size=30&platform=web
|
||||||
@ -312,158 +304,6 @@ class Api {
|
|||||||
|
|
||||||
static const String webDanmaku = '/x/v2/dm/web/seg.so';
|
static const String webDanmaku = '/x/v2/dm/web/seg.so';
|
||||||
|
|
||||||
//发送视频弹幕
|
// 搜索历史记录
|
||||||
//https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/danmaku/action.md
|
static const String searchHistory = '/x/web-goblin/history/search';
|
||||||
static const String shootDanmaku = '/x/v2/dm/post';
|
|
||||||
|
|
||||||
// up主分组
|
|
||||||
static const String followUpTag = '/x/relation/tags';
|
|
||||||
|
|
||||||
// 设置Up主分组
|
|
||||||
// 0 添加至默认分组 否则使用,分割tagid
|
|
||||||
static const String addUsers = '/x/relation/tags/addUsers';
|
|
||||||
|
|
||||||
// 获取指定分组下的up
|
|
||||||
static const String followUpGroup = '/x/relation/tag';
|
|
||||||
|
|
||||||
/// 私聊
|
|
||||||
/// 'https://api.vc.bilibili.com/session_svr/v1/session_svr/get_sessions?
|
|
||||||
/// session_type=1&
|
|
||||||
/// group_fold=1&
|
|
||||||
/// unfollow_fold=0&
|
|
||||||
/// sort_rule=2&
|
|
||||||
/// build=0&
|
|
||||||
/// mobi_app=web&
|
|
||||||
/// w_rid=8641d157fb9a9255eb2159f316ee39e2&
|
|
||||||
/// wts=1697305010
|
|
||||||
|
|
||||||
static const String sessionList =
|
|
||||||
'https://api.vc.bilibili.com/session_svr/v1/session_svr/get_sessions';
|
|
||||||
|
|
||||||
/// 私聊用户信息
|
|
||||||
/// uids
|
|
||||||
/// build=0&mobi_app=web
|
|
||||||
static const String sessionAccountList =
|
|
||||||
'https://api.vc.bilibili.com/account/v1/user/cards';
|
|
||||||
|
|
||||||
/// https://api.vc.bilibili.com/svr_sync/v1/svr_sync/fetch_session_msgs?
|
|
||||||
/// talker_id=400787461&
|
|
||||||
/// session_type=1&
|
|
||||||
/// size=20&
|
|
||||||
/// sender_device_id=1&
|
|
||||||
/// build=0&
|
|
||||||
/// mobi_app=web&
|
|
||||||
/// web_location=333.1296&
|
|
||||||
/// w_rid=cfe3bf58c9fe181bbf4dd6c75175e6b0&
|
|
||||||
/// wts=1697350697
|
|
||||||
|
|
||||||
static const String sessionMsg =
|
|
||||||
'https://api.vc.bilibili.com/svr_sync/v1/svr_sync/fetch_session_msgs';
|
|
||||||
|
|
||||||
/// 标记已读 POST
|
|
||||||
/// talker_id:
|
|
||||||
/// session_type: 1
|
|
||||||
/// ack_seqno: 920224140918926
|
|
||||||
/// build: 0
|
|
||||||
/// mobi_app: web
|
|
||||||
/// csrf_token:
|
|
||||||
/// csrf:
|
|
||||||
static const String updateAck =
|
|
||||||
'https://api.vc.bilibili.com/session_svr/v1/session_svr/update_ack';
|
|
||||||
|
|
||||||
// 获取某个动态详情
|
|
||||||
// timezone_offset=-480
|
|
||||||
// id=849312409672744983
|
|
||||||
// features=itemOpusStyle
|
|
||||||
static const String dynamicDetail = '/x/polymer/web-dynamic/v1/detail';
|
|
||||||
|
|
||||||
// AI总结
|
|
||||||
/// https://api.bilibili.com/x/web-interface/view/conclusion/get?
|
|
||||||
/// bvid=BV1ju4y1s7kn&
|
|
||||||
/// cid=1296086601&
|
|
||||||
/// up_mid=4641697&
|
|
||||||
/// w_rid=1607c6c5a4a35a1297e31992220900ae&
|
|
||||||
/// wts=1697033079
|
|
||||||
static const String aiConclusion = '/x/web-interface/view/conclusion/get';
|
|
||||||
|
|
||||||
// captcha验证码
|
|
||||||
static const String getCaptcha =
|
|
||||||
'https://passport.bilibili.com/x/passport-login/captcha?source=main_web';
|
|
||||||
|
|
||||||
// web端短信验证码
|
|
||||||
static const String smsCode =
|
|
||||||
'https://passport.bilibili.com/x/passport-login/web/sms/send';
|
|
||||||
|
|
||||||
// web端验证码登录
|
|
||||||
|
|
||||||
// web端密码登录
|
|
||||||
|
|
||||||
// app端短信验证码
|
|
||||||
static const String appSmsCode =
|
|
||||||
'https://passport.bilibili.com/x/passport-login/sms/send';
|
|
||||||
|
|
||||||
// app端验证码登录
|
|
||||||
|
|
||||||
// 获取短信验证码
|
|
||||||
// static const String appSafeSmsCode =
|
|
||||||
// 'https://passport.bilibili.com/x/safecenter/common/sms/send';
|
|
||||||
|
|
||||||
/// app端密码登录
|
|
||||||
/// username
|
|
||||||
/// password
|
|
||||||
/// key
|
|
||||||
/// rhash
|
|
||||||
static const String loginInByPwdApi =
|
|
||||||
'https://passport.bilibili.com/x/passport-login/oauth2/login';
|
|
||||||
|
|
||||||
/// 密码加密密钥
|
|
||||||
/// disable_rcmd
|
|
||||||
/// local_id
|
|
||||||
static const getWebKey =
|
|
||||||
'https://passport.bilibili.com/x/passport-login/web/key';
|
|
||||||
|
|
||||||
/// cookie转access_key
|
|
||||||
static const cookieToKey =
|
|
||||||
'https://passport.bilibili.com/x/passport-tv-login/h5/qrcode/confirm';
|
|
||||||
|
|
||||||
/// 申请二维码(TV端)
|
|
||||||
static const getTVCode =
|
|
||||||
'https://passport.snm0516.aisee.tv/x/passport-tv-login/qrcode/auth_code';
|
|
||||||
|
|
||||||
///扫码登录(TV端)
|
|
||||||
static const qrcodePoll =
|
|
||||||
'https://passport.bilibili.com/x/passport-tv-login/qrcode/poll';
|
|
||||||
|
|
||||||
/// 置顶视频
|
|
||||||
static const getTopVideoApi = '/x/space/top/arc';
|
|
||||||
|
|
||||||
/// 主页 - 最近投币的视频
|
|
||||||
/// vmid
|
|
||||||
/// gaia_source = main_web
|
|
||||||
/// web_location
|
|
||||||
/// w_rid
|
|
||||||
/// wts
|
|
||||||
static const getRecentCoinVideoApi = '/x/space/coin/video';
|
|
||||||
|
|
||||||
/// 最近点赞的视频
|
|
||||||
static const getRecentLikeVideoApi = '/x/space/like/video';
|
|
||||||
|
|
||||||
/// 最近追番
|
|
||||||
static const getRecentBangumiApi = '/x/space/bangumi/follow/list';
|
|
||||||
|
|
||||||
/// 用户专栏
|
|
||||||
static const getMemberSeasonsApi = '/x/polymer/web-space/home/seasons_series';
|
|
||||||
|
|
||||||
/// 获赞数 播放数
|
|
||||||
/// mid
|
|
||||||
static const getMemberViewApi = '/x/space/upstat';
|
|
||||||
|
|
||||||
/// 查询某个专栏
|
|
||||||
/// mid
|
|
||||||
/// season_id
|
|
||||||
/// sort_reverse
|
|
||||||
/// page_num
|
|
||||||
/// page_size
|
|
||||||
static const getSeasonDetailApi =
|
|
||||||
'/x/polymer/web-space/seasons_archives_list';
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,37 +2,4 @@ class HttpString {
|
|||||||
static const String baseUrl = 'https://www.bilibili.com';
|
static const String baseUrl = 'https://www.bilibili.com';
|
||||||
static const String baseApiUrl = 'https://api.bilibili.com';
|
static const String baseApiUrl = 'https://api.bilibili.com';
|
||||||
static const String tUrl = 'https://api.vc.bilibili.com';
|
static const String tUrl = 'https://api.vc.bilibili.com';
|
||||||
static const List<int> validateStatusCodes = [
|
|
||||||
302,
|
|
||||||
304,
|
|
||||||
307,
|
|
||||||
400,
|
|
||||||
401,
|
|
||||||
403,
|
|
||||||
404,
|
|
||||||
405,
|
|
||||||
409,
|
|
||||||
412,
|
|
||||||
500,
|
|
||||||
503,
|
|
||||||
504,
|
|
||||||
509,
|
|
||||||
616,
|
|
||||||
617,
|
|
||||||
625,
|
|
||||||
626,
|
|
||||||
628,
|
|
||||||
629,
|
|
||||||
632,
|
|
||||||
643,
|
|
||||||
650,
|
|
||||||
652,
|
|
||||||
658,
|
|
||||||
662,
|
|
||||||
688,
|
|
||||||
689,
|
|
||||||
701,
|
|
||||||
799,
|
|
||||||
8888
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,79 +17,17 @@ class DanmakaHttp {
|
|||||||
'oid': cid,
|
'oid': cid,
|
||||||
'segment_index': segmentIndex,
|
'segment_index': segmentIndex,
|
||||||
};
|
};
|
||||||
var response = await Request().get(
|
|
||||||
Api.webDanmaku,
|
|
||||||
data: params,
|
|
||||||
extra: {'resType': ResponseType.bytes},
|
|
||||||
);
|
|
||||||
return DmSegMobileReply.fromBuffer(response.data);
|
|
||||||
}
|
|
||||||
static Future shootDanmaku({
|
|
||||||
int type = 1,//弹幕类选择(1:视频弹幕 2:漫画弹幕)
|
|
||||||
required int oid,// 视频cid
|
|
||||||
required String msg,//弹幕文本(长度小于 100 字符)
|
|
||||||
int mode = 1,// 弹幕类型(1:滚动弹幕 4:底端弹幕 5:顶端弹幕 6:逆向弹幕(不能使用) 7:高级弹幕 8:代码弹幕(不能使用) 9:BAS弹幕(pool必须为2))
|
|
||||||
// String? aid,// 稿件avid
|
|
||||||
// String? bvid,// bvid与aid必须有一个
|
|
||||||
required String bvid,
|
|
||||||
int? progress,// 弹幕出现在视频内的时间(单位为毫秒,默认为0)
|
|
||||||
int? color,// 弹幕颜色(默认白色,16777215)
|
|
||||||
int? fontsize,// 弹幕字号(默认25)
|
|
||||||
int? pool,// 弹幕池选择(0:普通池 1:字幕池 2:特殊池(代码/BAS弹幕)默认普通池,0)
|
|
||||||
//int? rnd,// 当前时间戳*1000000(若无此项,则发送弹幕冷却时间限制为90s;若有此项,则发送弹幕冷却时间限制为5s)
|
|
||||||
int? colorful,//60001:专属渐变彩色(需要会员)
|
|
||||||
int? checkbox_type,//是否带 UP 身份标识(0:普通;4:带有标识)
|
|
||||||
// String? csrf,//CSRF Token(位于 Cookie) Cookie 方式必要
|
|
||||||
// String? access_key,// APP 登录 Token APP 方式必要
|
|
||||||
}) async {
|
|
||||||
// 构建参数对象
|
|
||||||
// assert(aid != null || bvid != null);
|
|
||||||
// assert(csrf != null || access_key != null);
|
|
||||||
assert(msg.length < 100);
|
|
||||||
// 构建参数对象
|
|
||||||
var params = <String, dynamic>{
|
|
||||||
'type': type,
|
|
||||||
'oid': oid,
|
|
||||||
'msg': msg,
|
|
||||||
'mode': mode,
|
|
||||||
//'aid': aid,
|
|
||||||
'bvid': bvid,
|
|
||||||
'progress': progress,
|
|
||||||
'color': color,
|
|
||||||
'fontsize': fontsize,
|
|
||||||
'pool': pool,
|
|
||||||
'rnd': DateTime.now().microsecondsSinceEpoch,
|
|
||||||
'colorful': colorful,
|
|
||||||
'checkbox_type': checkbox_type,
|
|
||||||
'csrf': await Request.getCsrf(),
|
|
||||||
// 'access_key': access_key,
|
|
||||||
}..removeWhere((key, value) => value == null);
|
|
||||||
|
|
||||||
var response = await Request().post(
|
// 计算函数
|
||||||
Api.shootDanmaku,
|
Future<DmSegMobileReply> computeTask(Map<String, int> params) async {
|
||||||
data: params,
|
var response = await Request().get(
|
||||||
options: Options(
|
Api.webDanmaku,
|
||||||
contentType: Headers.formUrlEncodedContentType,
|
data: params,
|
||||||
),
|
extra: {'resType': ResponseType.bytes},
|
||||||
);
|
);
|
||||||
if (response.statusCode != 200) {
|
return DmSegMobileReply.fromBuffer(response.data);
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'data': [],
|
|
||||||
'msg': '弹幕发送失败,状态码:${response.statusCode}',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (response.data['code'] == 0) {
|
|
||||||
return {
|
|
||||||
'status': true,
|
|
||||||
'data': response.data['data'],
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'data': [],
|
|
||||||
'msg': "${response.data['code']}: ${response.data['message']}",
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return await compute(computeTask, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,6 @@ class DynamicsHttp {
|
|||||||
'data': DynamicsDataModel.fromJson(res.data['data']),
|
'data': DynamicsDataModel.fromJson(res.data['data']),
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
print(err);
|
|
||||||
return {
|
return {
|
||||||
'status': false,
|
'status': false,
|
||||||
'data': [],
|
'data': [],
|
||||||
@ -86,35 +85,4 @@ class DynamicsHttp {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
static Future dynamicDetail({
|
|
||||||
String? id,
|
|
||||||
}) async {
|
|
||||||
var res = await Request().get(Api.dynamicDetail, data: {
|
|
||||||
'timezone_offset': -480,
|
|
||||||
'id': id,
|
|
||||||
'features': 'itemOpusStyle',
|
|
||||||
});
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
try {
|
|
||||||
return {
|
|
||||||
'status': true,
|
|
||||||
'data': DynamicItemModel.fromJson(res.data['data']['item']),
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'data': [],
|
|
||||||
'msg': err.toString(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'data': [],
|
|
||||||
'msg': res.data['message'],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,101 +3,38 @@ import 'package:html/parser.dart';
|
|||||||
import 'package:pilipala/http/index.dart';
|
import 'package:pilipala/http/index.dart';
|
||||||
|
|
||||||
class HtmlHttp {
|
class HtmlHttp {
|
||||||
// article
|
static Future reqHtml(id) async {
|
||||||
static Future reqHtml(id, dynamicType) async {
|
var response = await Request().get("https://www.bilibili.com/opus/$id");
|
||||||
var response = await Request().get(
|
|
||||||
"https://www.bilibili.com/opus/$id",
|
|
||||||
extra: {'ua': 'pc'},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.data.contains('Redirecting to')) {
|
|
||||||
RegExp regex = RegExp(r'//([\w\.]+)/(\w+)/(\w+)');
|
|
||||||
Match match = regex.firstMatch(response.data)!;
|
|
||||||
String matchedString = match.group(0)!;
|
|
||||||
response = await Request().get(
|
|
||||||
'https:$matchedString' + '/',
|
|
||||||
extra: {'ua': 'pc'},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
Document rootTree = parse(response.data);
|
|
||||||
// log(response.data.body.toString());
|
|
||||||
Element body = rootTree.body!;
|
|
||||||
Element appDom = body.querySelector('#app')!;
|
|
||||||
Element authorHeader = appDom.querySelector('.fixed-author-header')!;
|
|
||||||
// 头像
|
|
||||||
String avatar = authorHeader.querySelector('img')!.attributes['src']!;
|
|
||||||
avatar = 'https:${avatar.split('@')[0]}';
|
|
||||||
String uname = authorHeader
|
|
||||||
.querySelector('.fixed-author-header__author__name')!
|
|
||||||
.text;
|
|
||||||
|
|
||||||
// 动态详情
|
|
||||||
Element opusDetail = appDom.querySelector('.opus-detail')!;
|
|
||||||
// 发布时间
|
|
||||||
String updateTime =
|
|
||||||
opusDetail.querySelector('.opus-module-author__pub__text')!.text;
|
|
||||||
//
|
|
||||||
String opusContent =
|
|
||||||
opusDetail.querySelector('.opus-module-content')!.innerHtml;
|
|
||||||
String test = opusDetail
|
|
||||||
.querySelector('.horizontal-scroll-album__pic__img')!
|
|
||||||
.innerHtml;
|
|
||||||
String commentId = opusDetail
|
|
||||||
.querySelector('.bili-comment-container')!
|
|
||||||
.className
|
|
||||||
.split(' ')[1]
|
|
||||||
.split('-')[2];
|
|
||||||
// List imgList = opusDetail.querySelectorAll('bili-album__preview__picture__img');
|
|
||||||
return {
|
|
||||||
'status': true,
|
|
||||||
'avatar': avatar,
|
|
||||||
'uname': uname,
|
|
||||||
'updateTime': updateTime,
|
|
||||||
'content': test + opusContent,
|
|
||||||
'commentId': int.parse(commentId)
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
print('err: $err');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// read
|
|
||||||
static Future reqReadHtml(id, dynamicType) async {
|
|
||||||
var response = await Request().get(
|
|
||||||
"https://www.bilibili.com/$dynamicType/$id/",
|
|
||||||
extra: {'ua': 'pc'},
|
|
||||||
);
|
|
||||||
Document rootTree = parse(response.data);
|
Document rootTree = parse(response.data);
|
||||||
Element body = rootTree.body!;
|
Element body = rootTree.body!;
|
||||||
Element appDom = body.querySelector('#app')!;
|
Element appDom = body.querySelector('#app')!;
|
||||||
Element authorHeader = appDom.querySelector('.up-left')!;
|
Element authorHeader = appDom.querySelector('.fixed-author-header')!;
|
||||||
// 头像
|
// 头像
|
||||||
// String avatar =
|
String avatar = authorHeader.querySelector('img')!.attributes['src']!;
|
||||||
// authorHeader.querySelector('.bili-avatar-img')!.attributes['data-src']!;
|
avatar = 'https:${avatar.split('@')[0]}';
|
||||||
// print(avatar);
|
String uname =
|
||||||
// avatar = 'https:${avatar.split('@')[0]}';
|
authorHeader.querySelector('.fixed-author-header__author__name')!.text;
|
||||||
String uname = authorHeader.querySelector('.up-name')!.text.trim();
|
|
||||||
// 动态详情
|
// 动态详情
|
||||||
Element opusDetail = appDom.querySelector('.article-content')!;
|
Element opusDetail = appDom.querySelector('.opus-detail')!;
|
||||||
// 发布时间
|
// 发布时间
|
||||||
// String updateTime =
|
String updateTime =
|
||||||
// opusDetail.querySelector('.opus-module-author__pub__text')!.text;
|
opusDetail.querySelector('.opus-module-author__pub__text')!.text;
|
||||||
// print(updateTime);
|
|
||||||
|
|
||||||
//
|
//
|
||||||
String opusContent =
|
String opusContent =
|
||||||
opusDetail.querySelector('#read-article-holder')!.innerHtml;
|
opusDetail.querySelector('.opus-module-content')!.innerHtml;
|
||||||
RegExp digitRegExp = RegExp(r'\d+');
|
String commentId = opusDetail
|
||||||
Iterable<Match> matches = digitRegExp.allMatches(id);
|
.querySelector('.bili-comment-container')!
|
||||||
String number = matches.first.group(0)!;
|
.className
|
||||||
|
.split(' ')[1]
|
||||||
|
.split('-')[2];
|
||||||
|
// List imgList = opusDetail.querySelectorAll('bili-album__preview__picture__img');
|
||||||
return {
|
return {
|
||||||
'status': true,
|
'status': true,
|
||||||
'avatar': '',
|
'avatar': avatar,
|
||||||
'uname': uname,
|
'uname': uname,
|
||||||
'updateTime': '',
|
'updateTime': updateTime,
|
||||||
'content': opusContent,
|
'content': opusContent,
|
||||||
'commentId': int.parse(number)
|
'commentId': commentId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,8 +4,6 @@ import 'dart:io';
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:cookie_jar/cookie_jar.dart';
|
import 'package:cookie_jar/cookie_jar.dart';
|
||||||
import 'package:dio/io.dart';
|
|
||||||
import 'package:dio_http2_adapter/dio_http2_adapter.dart';
|
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
@ -18,11 +16,6 @@ class Request {
|
|||||||
static late CookieManager cookieManager;
|
static late CookieManager cookieManager;
|
||||||
static late final Dio dio;
|
static late final Dio dio;
|
||||||
factory Request() => _instance;
|
factory Request() => _instance;
|
||||||
Box setting = GStrorage.setting;
|
|
||||||
static Box localCache = GStrorage.localCache;
|
|
||||||
late dynamic enableSystemProxy;
|
|
||||||
late String systemProxyHost;
|
|
||||||
late String systemProxyPort;
|
|
||||||
|
|
||||||
/// 设置cookie
|
/// 设置cookie
|
||||||
static setCookie() async {
|
static setCookie() async {
|
||||||
@ -47,8 +40,8 @@ class Request {
|
|||||||
log("setCookie, ${e.toString()}");
|
log("setCookie, ${e.toString()}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setOptionsHeaders(userInfo);
|
||||||
}
|
}
|
||||||
setOptionsHeaders(userInfo, userInfo != null && userInfo.mid != null);
|
|
||||||
|
|
||||||
if (cookie.isEmpty) {
|
if (cookie.isEmpty) {
|
||||||
try {
|
try {
|
||||||
@ -66,6 +59,9 @@ class Request {
|
|||||||
static Future<String> getCsrf() async {
|
static Future<String> getCsrf() async {
|
||||||
var cookies = await cookieManager.cookieJar
|
var cookies = await cookieManager.cookieJar
|
||||||
.loadForRequest(Uri.parse(HttpString.baseApiUrl));
|
.loadForRequest(Uri.parse(HttpString.baseApiUrl));
|
||||||
|
// for (var i in cookies) {
|
||||||
|
// print(i);
|
||||||
|
// }
|
||||||
String token = '';
|
String token = '';
|
||||||
if (cookies.where((e) => e.name == 'bili_jct').isNotEmpty) {
|
if (cookies.where((e) => e.name == 'bili_jct').isNotEmpty) {
|
||||||
token = cookies.firstWhere((e) => e.name == 'bili_jct').value;
|
token = cookies.firstWhere((e) => e.name == 'bili_jct').value;
|
||||||
@ -73,10 +69,8 @@ class Request {
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
static setOptionsHeaders(userInfo, status) {
|
static setOptionsHeaders(userInfo) {
|
||||||
if (status) {
|
dio.options.headers['x-bili-mid'] = userInfo.mid.toString();
|
||||||
dio.options.headers['x-bili-mid'] = userInfo.mid.toString();
|
|
||||||
}
|
|
||||||
dio.options.headers['env'] = 'prod';
|
dio.options.headers['env'] = 'prod';
|
||||||
dio.options.headers['app-key'] = 'android64';
|
dio.options.headers['app-key'] = 'android64';
|
||||||
dio.options.headers['x-bili-aurora-eid'] = 'UlMFQVcABlAH';
|
dio.options.headers['x-bili-aurora-eid'] = 'UlMFQVcABlAH';
|
||||||
@ -97,42 +91,15 @@ class Request {
|
|||||||
//响应流上前后两次接受到数据的间隔,单位为毫秒。
|
//响应流上前后两次接受到数据的间隔,单位为毫秒。
|
||||||
receiveTimeout: const Duration(milliseconds: 12000),
|
receiveTimeout: const Duration(milliseconds: 12000),
|
||||||
//Http请求头.
|
//Http请求头.
|
||||||
headers: {},
|
headers: {
|
||||||
|
'keep-alive': true,
|
||||||
|
'user-agent': headerUa('pc'),
|
||||||
|
'Accept-Encoding': 'gzip'
|
||||||
|
},
|
||||||
|
persistentConnection: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
enableSystemProxy =
|
dio = Dio(options);
|
||||||
setting.get(SettingBoxKey.enableSystemProxy, defaultValue: false);
|
|
||||||
systemProxyHost =
|
|
||||||
localCache.get(LocalCacheKey.systemProxyHost, defaultValue: '');
|
|
||||||
systemProxyPort =
|
|
||||||
localCache.get(LocalCacheKey.systemProxyPort, defaultValue: '');
|
|
||||||
|
|
||||||
dio = Dio(options)
|
|
||||||
|
|
||||||
/// fix 第三方登录 302重定向 跟iOS代理问题冲突
|
|
||||||
..httpClientAdapter = Http2Adapter(
|
|
||||||
ConnectionManager(
|
|
||||||
idleTimeout: const Duration(milliseconds: 10000),
|
|
||||||
onClientCreate: (_, config) => config.onBadCertificate = (_) => true,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
/// 设置代理
|
|
||||||
if (enableSystemProxy) {
|
|
||||||
dio.httpClientAdapter = IOHttpClientAdapter(
|
|
||||||
createHttpClient: () {
|
|
||||||
final client = HttpClient();
|
|
||||||
// Config the client.
|
|
||||||
client.findProxy = (uri) {
|
|
||||||
// return 'PROXY host:port';
|
|
||||||
return 'PROXY $systemProxyHost:$systemProxyPort';
|
|
||||||
};
|
|
||||||
client.badCertificateCallback =
|
|
||||||
(X509Certificate cert, String host, int port) => true;
|
|
||||||
return client;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
//添加拦截器
|
//添加拦截器
|
||||||
dio.interceptors.add(ApiInterceptor());
|
dio.interceptors.add(ApiInterceptor());
|
||||||
@ -146,26 +113,30 @@ class Request {
|
|||||||
|
|
||||||
dio.transformer = BackgroundTransformer();
|
dio.transformer = BackgroundTransformer();
|
||||||
dio.options.validateStatus = (status) {
|
dio.options.validateStatus = (status) {
|
||||||
return status! >= 200 && status < 300 ||
|
return status! >= 200 && status < 300 || status == 304 || status == 302;
|
||||||
HttpString.validateStatusCodes.contains(status);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get请求
|
* get请求
|
||||||
*/
|
*/
|
||||||
get(url, {data, options, cancelToken, extra}) async {
|
get(url, {data, cacheOptions, options, cancelToken, extra}) async {
|
||||||
Response response;
|
Response response;
|
||||||
Options options = Options();
|
Options options;
|
||||||
|
String ua = 'pc';
|
||||||
ResponseType resType = ResponseType.json;
|
ResponseType resType = ResponseType.json;
|
||||||
if (extra != null) {
|
if (extra != null) {
|
||||||
|
ua = extra!['ua'] ?? 'pc';
|
||||||
resType = extra!['resType'] ?? ResponseType.json;
|
resType = extra!['resType'] ?? ResponseType.json;
|
||||||
if (extra['ua'] != null) {
|
|
||||||
options.headers = {'user-agent': headerUa(type: extra['ua'])};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
options.responseType = resType;
|
if (cacheOptions != null) {
|
||||||
|
cacheOptions.headers = {'user-agent': headerUa(ua)};
|
||||||
|
options = cacheOptions;
|
||||||
|
} else {
|
||||||
|
options = Options();
|
||||||
|
options.headers = {'user-agent': headerUa(ua)};
|
||||||
|
options.responseType = resType;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
response = await dio.get(
|
response = await dio.get(
|
||||||
url,
|
url,
|
||||||
@ -232,19 +203,15 @@ class Request {
|
|||||||
token.cancel("cancelled");
|
token.cancel("cancelled");
|
||||||
}
|
}
|
||||||
|
|
||||||
String headerUa({type = 'mob'}) {
|
String headerUa(ua) {
|
||||||
String headerUa = '';
|
String headerUa = '';
|
||||||
if (type == 'mob') {
|
if (ua == 'mob') {
|
||||||
if (Platform.isIOS) {
|
headerUa = Platform.isIOS
|
||||||
headerUa =
|
? 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1'
|
||||||
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Mobile/15E148 Safari/604.1';
|
: 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36';
|
||||||
} else {
|
|
||||||
headerUa =
|
|
||||||
'Mozilla/5.0 (Linux; Android 10; SM-G975F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36';
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
headerUa =
|
headerUa =
|
||||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Safari/605.1.15';
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 Safari/605.1.15';
|
||||||
}
|
}
|
||||||
return headerUa;
|
return headerUa;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,10 +46,7 @@ class ApiInterceptor extends Interceptor {
|
|||||||
void onError(DioException err, ErrorInterceptorHandler handler) async {
|
void onError(DioException err, ErrorInterceptorHandler handler) async {
|
||||||
// 处理网络请求错误
|
// 处理网络请求错误
|
||||||
// handler.next(err);
|
// handler.next(err);
|
||||||
SmartDialog.showToast(
|
SmartDialog.showToast(await dioError(err));
|
||||||
await dioError(err),
|
|
||||||
displayType: SmartToastType.onlyRefresh,
|
|
||||||
);
|
|
||||||
super.onError(err, handler);
|
super.onError(err, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,177 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'dart:math';
|
|
||||||
import 'package:crypto/crypto.dart';
|
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:encrypt/encrypt.dart';
|
|
||||||
import 'package:pilipala/http/index.dart';
|
|
||||||
import 'package:pilipala/models/login/index.dart';
|
|
||||||
import 'package:pilipala/utils/login.dart';
|
|
||||||
import 'package:uuid/uuid.dart';
|
|
||||||
|
|
||||||
class LoginHttp {
|
|
||||||
static Future queryCaptcha() async {
|
|
||||||
var res = await Request().get(Api.getCaptcha);
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
return {
|
|
||||||
'status': true,
|
|
||||||
'data': CaptchaDataModel.fromJson(res.data['data']),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {'status': false, 'data': res.message};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future sendSmsCode({
|
|
||||||
int? cid,
|
|
||||||
required int tel,
|
|
||||||
required String token,
|
|
||||||
required String challenge,
|
|
||||||
required String validate,
|
|
||||||
required String seccode,
|
|
||||||
}) async {
|
|
||||||
var res = await Request().post(
|
|
||||||
Api.appSmsCode,
|
|
||||||
data: {
|
|
||||||
'cid': cid,
|
|
||||||
'tel': tel,
|
|
||||||
"source": "main_web",
|
|
||||||
'token': token,
|
|
||||||
'challenge': challenge,
|
|
||||||
'validate': validate,
|
|
||||||
'seccode': seccode,
|
|
||||||
},
|
|
||||||
options: Options(
|
|
||||||
contentType: Headers.formUrlEncodedContentType,
|
|
||||||
// headers: {'user-agent': ApiConstants.userAgent}
|
|
||||||
),
|
|
||||||
);
|
|
||||||
print(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
// web端验证码
|
|
||||||
static Future sendWebSmsCode({
|
|
||||||
int? cid,
|
|
||||||
required int tel,
|
|
||||||
required String token,
|
|
||||||
required String challenge,
|
|
||||||
required String validate,
|
|
||||||
required String seccode,
|
|
||||||
}) async {
|
|
||||||
Map data = {
|
|
||||||
'cid': cid,
|
|
||||||
'tel': tel,
|
|
||||||
'token': token,
|
|
||||||
'challenge': challenge,
|
|
||||||
'validate': validate,
|
|
||||||
'seccode': seccode,
|
|
||||||
};
|
|
||||||
FormData formData = FormData.fromMap({...data});
|
|
||||||
var res = await Request().post(
|
|
||||||
Api.smsCode,
|
|
||||||
data: formData,
|
|
||||||
options: Options(
|
|
||||||
contentType: Headers.formUrlEncodedContentType,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
print(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
// web端验证码登录
|
|
||||||
static Future loginInByWebSmsCode() async {}
|
|
||||||
|
|
||||||
// web端密码登录
|
|
||||||
static Future liginInByWebPwd() async {}
|
|
||||||
|
|
||||||
// app端验证码
|
|
||||||
static Future sendAppSmsCode({
|
|
||||||
int? cid,
|
|
||||||
required int tel,
|
|
||||||
required String token,
|
|
||||||
required String challenge,
|
|
||||||
required String validate,
|
|
||||||
required String seccode,
|
|
||||||
}) async {
|
|
||||||
Map<String, dynamic> data = {
|
|
||||||
'cid': cid,
|
|
||||||
'tel': tel,
|
|
||||||
'login_session_id': const Uuid().v4().replaceAll('-', ''),
|
|
||||||
'recaptcha_token': token,
|
|
||||||
'gee_challenge': challenge,
|
|
||||||
'gee_validate': validate,
|
|
||||||
'gee_seccode': seccode,
|
|
||||||
'channel': 'bili',
|
|
||||||
'buvid': buvid(),
|
|
||||||
'local_id': buvid(),
|
|
||||||
// 'ts': DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
|
||||||
'statistics': {
|
|
||||||
"appId": 1,
|
|
||||||
"platform": 3,
|
|
||||||
"version": "7.52.0",
|
|
||||||
"abtest": ""
|
|
||||||
},
|
|
||||||
};
|
|
||||||
// FormData formData = FormData.fromMap({...data});
|
|
||||||
var res = await Request().post(
|
|
||||||
Api.appSmsCode,
|
|
||||||
data: data,
|
|
||||||
options: Options(
|
|
||||||
contentType: Headers.formUrlEncodedContentType,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
print(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
static String buvid() {
|
|
||||||
var mac = <String>[];
|
|
||||||
var random = Random();
|
|
||||||
|
|
||||||
for (var i = 0; i < 6; i++) {
|
|
||||||
var min = 0;
|
|
||||||
var max = 0xff;
|
|
||||||
var num = (random.nextInt(max - min + 1) + min).toRadixString(16);
|
|
||||||
mac.add(num);
|
|
||||||
}
|
|
||||||
|
|
||||||
var md5Str = md5.convert(utf8.encode(mac.join(':'))).toString();
|
|
||||||
var md5Arr = md5Str.split('');
|
|
||||||
return 'XY${md5Arr[2]}${md5Arr[12]}${md5Arr[22]}$md5Str';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取盐hash跟PubKey
|
|
||||||
static Future getWebKey() async {
|
|
||||||
var res = await Request().get(Api.getWebKey,
|
|
||||||
data: {'disable_rcmd': 0, 'local_id': LoginUtils.generateBuvid()});
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
return {'status': true, 'data': res.data['data']};
|
|
||||||
} else {
|
|
||||||
return {'status': false, 'data': {}, 'msg': res.data['message']};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// app端密码登录
|
|
||||||
static Future loginInByMobPwd({
|
|
||||||
required String tel,
|
|
||||||
required String password,
|
|
||||||
required String key,
|
|
||||||
required String rhash,
|
|
||||||
}) async {
|
|
||||||
dynamic publicKey = RSAKeyParser().parse(key);
|
|
||||||
String passwordEncryptyed =
|
|
||||||
Encrypter(RSA(publicKey: publicKey)).encrypt(rhash + password).base64;
|
|
||||||
Map<String, dynamic> data = {
|
|
||||||
'username': tel,
|
|
||||||
'password': passwordEncryptyed,
|
|
||||||
'local_id': LoginUtils.generateBuvid(),
|
|
||||||
'disable_rcmd': "0",
|
|
||||||
};
|
|
||||||
var res = await Request().post(
|
|
||||||
Api.loginInByPwdApi,
|
|
||||||
data: data,
|
|
||||||
options: Options(
|
|
||||||
contentType: Headers.formUrlEncodedContentType,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
print(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,16 +1,7 @@
|
|||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
||||||
import 'package:hive/hive.dart';
|
|
||||||
import 'package:pilipala/common/constants.dart';
|
|
||||||
import 'package:pilipala/http/index.dart';
|
import 'package:pilipala/http/index.dart';
|
||||||
import 'package:pilipala/models/dynamics/result.dart';
|
import 'package:pilipala/models/dynamics/result.dart';
|
||||||
import 'package:pilipala/models/follow/result.dart';
|
|
||||||
import 'package:pilipala/models/member/archive.dart';
|
import 'package:pilipala/models/member/archive.dart';
|
||||||
import 'package:pilipala/models/member/coin.dart';
|
|
||||||
import 'package:pilipala/models/member/info.dart';
|
import 'package:pilipala/models/member/info.dart';
|
||||||
import 'package:pilipala/models/member/seasons.dart';
|
|
||||||
import 'package:pilipala/models/member/tags.dart';
|
|
||||||
import 'package:pilipala/utils/storage.dart';
|
|
||||||
import 'package:pilipala/utils/utils.dart';
|
|
||||||
import 'package:pilipala/utils/wbi_sign.dart';
|
import 'package:pilipala/utils/wbi_sign.dart';
|
||||||
|
|
||||||
class MemberHttp {
|
class MemberHttp {
|
||||||
@ -27,7 +18,6 @@ class MemberHttp {
|
|||||||
var res = await Request().get(
|
var res = await Request().get(
|
||||||
Api.memberInfo,
|
Api.memberInfo,
|
||||||
data: params,
|
data: params,
|
||||||
extra: {'ua': 'pc'},
|
|
||||||
);
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {
|
return {
|
||||||
@ -93,7 +83,6 @@ class MemberHttp {
|
|||||||
var res = await Request().get(
|
var res = await Request().get(
|
||||||
Api.memberArchive,
|
Api.memberArchive,
|
||||||
data: params,
|
data: params,
|
||||||
extra: {'ua': 'pc'},
|
|
||||||
);
|
);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {
|
return {
|
||||||
@ -153,312 +142,4 @@ class MemberHttp {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询分组
|
|
||||||
static Future followUpTags() async {
|
|
||||||
var res = await Request().get(Api.followUpTag);
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
return {
|
|
||||||
'status': true,
|
|
||||||
'data': res.data['data']
|
|
||||||
.map<MemberTagItemModel>((e) => MemberTagItemModel.fromJson(e))
|
|
||||||
.toList()
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'data': [],
|
|
||||||
'msg': res.data['message'],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置分组
|
|
||||||
static Future addUsers(int? fids, String? tagids) async {
|
|
||||||
var res = await Request().post(Api.addUsers, queryParameters: {
|
|
||||||
'fids': fids,
|
|
||||||
'tagids': tagids ?? '0',
|
|
||||||
'csrf': await Request.getCsrf(),
|
|
||||||
}, data: {
|
|
||||||
'cross_domain': true
|
|
||||||
});
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
return {'status': true, 'data': [], 'msg': '操作成功'};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'data': [],
|
|
||||||
'msg': res.data['message'],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取某分组下的up
|
|
||||||
static Future followUpGroup(
|
|
||||||
int? mid,
|
|
||||||
int? tagid,
|
|
||||||
int? pn,
|
|
||||||
int? ps,
|
|
||||||
) async {
|
|
||||||
var res = await Request().get(Api.followUpGroup, data: {
|
|
||||||
'mid': mid,
|
|
||||||
'tagid': tagid,
|
|
||||||
'pn': pn,
|
|
||||||
'ps': ps,
|
|
||||||
});
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
// FollowItemModel
|
|
||||||
return {
|
|
||||||
'status': true,
|
|
||||||
'data': res.data['data']
|
|
||||||
.map<FollowItemModel>((e) => FollowItemModel.fromJson(e))
|
|
||||||
.toList()
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'data': [],
|
|
||||||
'msg': res.data['message'],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取up置顶
|
|
||||||
static Future getTopVideo(String? vmid) async {
|
|
||||||
var res = await Request().get(Api.getTopVideoApi);
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
return {
|
|
||||||
'status': true,
|
|
||||||
'data': res.data['data']
|
|
||||||
.map<MemberTagItemModel>((e) => MemberTagItemModel.fromJson(e))
|
|
||||||
.toList()
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'data': [],
|
|
||||||
'msg': res.data['message'],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取uo专栏
|
|
||||||
static Future getMemberSeasons(int? mid, int? pn, int? ps) async {
|
|
||||||
var res = await Request().get(Api.getMemberSeasonsApi, data: {
|
|
||||||
'mid': mid,
|
|
||||||
'page_num': pn,
|
|
||||||
'page_size': ps,
|
|
||||||
});
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
return {
|
|
||||||
'status': true,
|
|
||||||
'data': MemberSeasonsDataModel.fromJson(res.data['data']['items_lists'])
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'data': [],
|
|
||||||
'msg': res.data['message'],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 最近投币
|
|
||||||
static Future getRecentCoinVideo({required int mid}) async {
|
|
||||||
Map params = await WbiSign().makSign({
|
|
||||||
'mid': mid,
|
|
||||||
'gaia_source': 'main_web',
|
|
||||||
'web_location': 333.999,
|
|
||||||
});
|
|
||||||
var res = await Request().get(
|
|
||||||
Api.getRecentCoinVideoApi,
|
|
||||||
data: {
|
|
||||||
'vmid': mid,
|
|
||||||
'gaia_source': 'main_web',
|
|
||||||
'web_location': 333.999,
|
|
||||||
'w_rid': params['w_rid'],
|
|
||||||
'wts': params['wts'],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
return {
|
|
||||||
'status': true,
|
|
||||||
'data': res.data['data']
|
|
||||||
.map<MemberCoinsDataModel>((e) => MemberCoinsDataModel.fromJson(e))
|
|
||||||
.toList(),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'data': [],
|
|
||||||
'msg': res.data['message'],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 最近点赞
|
|
||||||
static Future getRecentLikeVideo({required int mid}) async {
|
|
||||||
Map params = await WbiSign().makSign({
|
|
||||||
'mid': mid,
|
|
||||||
'gaia_source': 'main_web',
|
|
||||||
'web_location': 333.999,
|
|
||||||
});
|
|
||||||
var res = await Request().get(
|
|
||||||
Api.getRecentLikeVideoApi,
|
|
||||||
data: {
|
|
||||||
'vmid': mid,
|
|
||||||
'gaia_source': 'main_web',
|
|
||||||
'web_location': 333.999,
|
|
||||||
'w_rid': params['w_rid'],
|
|
||||||
'wts': params['wts'],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
return {
|
|
||||||
'status': true,
|
|
||||||
'data': MemberSeasonsDataModel.fromJson(res.data['data']['items_lists'])
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'data': [],
|
|
||||||
'msg': res.data['message'],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查看某个专栏
|
|
||||||
static Future getSeasonDetail({
|
|
||||||
required int mid,
|
|
||||||
required int seasonId,
|
|
||||||
bool sortReverse = false,
|
|
||||||
required int pn,
|
|
||||||
required int ps,
|
|
||||||
}) async {
|
|
||||||
var res = await Request().get(
|
|
||||||
Api.getSeasonDetailApi,
|
|
||||||
data: {
|
|
||||||
'mid': mid,
|
|
||||||
'season_id': seasonId,
|
|
||||||
'sort_reverse': sortReverse,
|
|
||||||
'page_num': pn,
|
|
||||||
'page_size': ps,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
try {
|
|
||||||
return {
|
|
||||||
'status': true,
|
|
||||||
'data': MemberSeasonsList.fromJson(res.data['data'])
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
print(err);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'data': [],
|
|
||||||
'msg': res.data['message'],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取TV authCode
|
|
||||||
static Future getTVCode() async {
|
|
||||||
SmartDialog.showLoading();
|
|
||||||
var params = {
|
|
||||||
'appkey': Constants.appKey,
|
|
||||||
'local_id': '0',
|
|
||||||
'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(),
|
|
||||||
};
|
|
||||||
String sign = Utils.appSign(
|
|
||||||
params,
|
|
||||||
Constants.appKey,
|
|
||||||
Constants.appSec,
|
|
||||||
);
|
|
||||||
var res = await Request()
|
|
||||||
.post(Api.getTVCode, queryParameters: {...params, 'sign': sign});
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
return {
|
|
||||||
'status': true,
|
|
||||||
'data': res.data['data']['auth_code'],
|
|
||||||
'msg': '操作成功'
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'data': [],
|
|
||||||
'msg': res.data['message'],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取access_key
|
|
||||||
static Future cookieToKey() async {
|
|
||||||
var authCodeRes = await getTVCode();
|
|
||||||
if (authCodeRes['status']) {
|
|
||||||
var res = await Request().post(Api.cookieToKey, queryParameters: {
|
|
||||||
'auth_code': authCodeRes['data'],
|
|
||||||
'build': 708200,
|
|
||||||
'csrf': await Request.getCsrf(),
|
|
||||||
});
|
|
||||||
await Future.delayed(const Duration(milliseconds: 300));
|
|
||||||
await qrcodePoll(authCodeRes['data']);
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
return {'status': true, 'data': [], 'msg': '操作成功'};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'data': [],
|
|
||||||
'msg': res.data['message'],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future qrcodePoll(authCode) async {
|
|
||||||
var params = {
|
|
||||||
'appkey': Constants.appKey,
|
|
||||||
'auth_code': authCode.toString(),
|
|
||||||
'local_id': '0',
|
|
||||||
'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(),
|
|
||||||
};
|
|
||||||
String sign = Utils.appSign(
|
|
||||||
params,
|
|
||||||
Constants.appKey,
|
|
||||||
Constants.appSec,
|
|
||||||
);
|
|
||||||
var res = await Request()
|
|
||||||
.post(Api.qrcodePoll, queryParameters: {...params, 'sign': sign});
|
|
||||||
SmartDialog.dismiss();
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
String accessKey = res.data['data']['access_token'];
|
|
||||||
Box localCache = GStrorage.localCache;
|
|
||||||
Box userInfoCache = GStrorage.userInfo;
|
|
||||||
var userInfo = userInfoCache.get('userInfoCache');
|
|
||||||
localCache.put(
|
|
||||||
LocalCacheKey.accessKey, {'mid': userInfo.mid, 'value': accessKey});
|
|
||||||
return {'status': true, 'data': [], 'msg': '操作成功'};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'data': [],
|
|
||||||
'msg': res.data['message'],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取up播放数、点赞数
|
|
||||||
static Future memberView({required int mid}) async {
|
|
||||||
var res = await Request().get(Api.getMemberViewApi, data: {'mid': mid});
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
return {'status': true, 'data': res.data['data']};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'data': [],
|
|
||||||
'msg': res.data['message'],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,85 +0,0 @@
|
|||||||
import 'package:pilipala/http/api.dart';
|
|
||||||
import 'package:pilipala/http/init.dart';
|
|
||||||
import 'package:pilipala/models/msg/account.dart';
|
|
||||||
import 'package:pilipala/models/msg/session.dart';
|
|
||||||
import 'package:pilipala/utils/wbi_sign.dart';
|
|
||||||
|
|
||||||
class MsgHttp {
|
|
||||||
// 会话列表
|
|
||||||
static Future sessionList({int? endTs}) async {
|
|
||||||
Map<String, dynamic> params = {
|
|
||||||
'session_type': 1,
|
|
||||||
'group_fold': 1,
|
|
||||||
'unfollow_fold': 0,
|
|
||||||
'sort_rule': 2,
|
|
||||||
'build': 0,
|
|
||||||
'mobi_app': 'web',
|
|
||||||
};
|
|
||||||
if (endTs != null) {
|
|
||||||
params['end_ts'] = endTs;
|
|
||||||
}
|
|
||||||
|
|
||||||
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']),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'date': [],
|
|
||||||
'msg': res.data['message'],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future accountList(uids) async {
|
|
||||||
var res = await Request().get(Api.sessionAccountList, data: {
|
|
||||||
'uids': uids,
|
|
||||||
'build': 0,
|
|
||||||
'mobi_app': 'web',
|
|
||||||
});
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
return {
|
|
||||||
'status': true,
|
|
||||||
'data': res.data['data']
|
|
||||||
.map<AccountListModel>((e) => AccountListModel.fromJson(e))
|
|
||||||
.toList(),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'date': [],
|
|
||||||
'msg': res.data['message'],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future sessionMsg({
|
|
||||||
int? talkerId,
|
|
||||||
}) async {
|
|
||||||
Map params = await WbiSign().makSign({
|
|
||||||
'talker_id': talkerId,
|
|
||||||
'session_type': 1,
|
|
||||||
'size': 20,
|
|
||||||
'sender_device_id': 1,
|
|
||||||
'build': 0,
|
|
||||||
'mobi_app': 'web',
|
|
||||||
});
|
|
||||||
var res = await Request().get(Api.sessionMsg, data: params);
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
return {
|
|
||||||
'status': true,
|
|
||||||
'data': SessionMsgDataModel.fromJson(res.data['data']),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
'status': false,
|
|
||||||
'date': [],
|
|
||||||
'msg': res.data['message'],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -26,7 +26,7 @@ class ReplyHttp {
|
|||||||
Map errMap = {
|
Map errMap = {
|
||||||
-400: '请求错误',
|
-400: '请求错误',
|
||||||
-404: '无此项',
|
-404: '无此项',
|
||||||
12002: '当前页面评论功能已关闭',
|
12002: '当前页面评论功能已关闭"',
|
||||||
12009: '评论主体的type不合法',
|
12009: '评论主体的type不合法',
|
||||||
12061: 'UP主已关闭评论区',
|
12061: 'UP主已关闭评论区',
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import 'package:pilipala/models/user/fav_folder.dart';
|
|||||||
import 'package:pilipala/models/user/history.dart';
|
import 'package:pilipala/models/user/history.dart';
|
||||||
import 'package:pilipala/models/user/info.dart';
|
import 'package:pilipala/models/user/info.dart';
|
||||||
import 'package:pilipala/models/user/stat.dart';
|
import 'package:pilipala/models/user/stat.dart';
|
||||||
import 'package:pilipala/utils/wbi_sign.dart';
|
|
||||||
|
|
||||||
class UserHttp {
|
class UserHttp {
|
||||||
static Future<dynamic> userStat({required int mid}) async {
|
static Future<dynamic> userStat({required int mid}) async {
|
||||||
@ -71,15 +70,14 @@ class UserHttp {
|
|||||||
required int pn,
|
required int pn,
|
||||||
required int ps,
|
required int ps,
|
||||||
String keyword = '',
|
String keyword = '',
|
||||||
String order = 'mtime',
|
String order = 'mtime'}) async {
|
||||||
int type = 0}) async {
|
|
||||||
var res = await Request().get(Api.userFavFolderDetail, data: {
|
var res = await Request().get(Api.userFavFolderDetail, data: {
|
||||||
'media_id': mediaId,
|
'media_id': mediaId,
|
||||||
'pn': pn,
|
'pn': pn,
|
||||||
'ps': ps,
|
'ps': ps,
|
||||||
'keyword': keyword,
|
'keyword': keyword,
|
||||||
'order': order,
|
'order': order,
|
||||||
'type': type,
|
'type': 0,
|
||||||
'tid': 0,
|
'tid': 0,
|
||||||
'platform': 'web'
|
'platform': 'web'
|
||||||
});
|
});
|
||||||
@ -199,7 +197,7 @@ class UserHttp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户凭证 失效
|
// 获取用户凭证
|
||||||
static Future thirdLogin() async {
|
static Future thirdLogin() async {
|
||||||
var res = await Request().get(
|
var res = await Request().get(
|
||||||
'https://passport.bilibili.com/login/app/third',
|
'https://passport.bilibili.com/login/app/third',
|
||||||
@ -251,31 +249,6 @@ class UserHttp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 相互关系查询
|
|
||||||
static Future relationSearch(int mid) async {
|
|
||||||
Map params = await WbiSign().makSign({
|
|
||||||
'mid': mid,
|
|
||||||
'token': '',
|
|
||||||
'platform': 'web',
|
|
||||||
'web_location': 1550101,
|
|
||||||
});
|
|
||||||
var res = await Request().get(
|
|
||||||
Api.relationSearch,
|
|
||||||
data: {
|
|
||||||
'mid': mid,
|
|
||||||
'w_rid': params['w_rid'],
|
|
||||||
'wts': params['wts'],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
// relation 主动状态
|
|
||||||
// 被动状态
|
|
||||||
return {'status': true, 'data': res.data['data']};
|
|
||||||
} else {
|
|
||||||
return {'status': false, 'msg': res.data['message']};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索历史记录
|
// 搜索历史记录
|
||||||
static Future searchHistory(
|
static Future searchHistory(
|
||||||
{required int pn, required String keyword}) async {
|
{required int pn, required String keyword}) async {
|
||||||
|
|||||||
@ -9,11 +9,9 @@ import 'package:pilipala/models/home/rcmd/result.dart';
|
|||||||
import 'package:pilipala/models/model_hot_video_item.dart';
|
import 'package:pilipala/models/model_hot_video_item.dart';
|
||||||
import 'package:pilipala/models/model_rec_video_item.dart';
|
import 'package:pilipala/models/model_rec_video_item.dart';
|
||||||
import 'package:pilipala/models/user/fav_folder.dart';
|
import 'package:pilipala/models/user/fav_folder.dart';
|
||||||
import 'package:pilipala/models/video/ai.dart';
|
|
||||||
import 'package:pilipala/models/video/play/url.dart';
|
import 'package:pilipala/models/video/play/url.dart';
|
||||||
import 'package:pilipala/models/video_detail_res.dart';
|
import 'package:pilipala/models/video_detail_res.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
import 'package:pilipala/utils/wbi_sign.dart';
|
|
||||||
|
|
||||||
/// res.data['code'] == 0 请求正常返回结果
|
/// res.data['code'] == 0 请求正常返回结果
|
||||||
/// res.data['data'] 为结果
|
/// res.data['data'] 为结果
|
||||||
@ -24,7 +22,6 @@ class VideoHttp {
|
|||||||
static Box setting = GStrorage.setting;
|
static Box setting = GStrorage.setting;
|
||||||
static bool enableRcmdDynamic =
|
static bool enableRcmdDynamic =
|
||||||
setting.get(SettingBoxKey.enableRcmdDynamic, defaultValue: true);
|
setting.get(SettingBoxKey.enableRcmdDynamic, defaultValue: true);
|
||||||
static Box userInfoCache = GStrorage.userInfo;
|
|
||||||
|
|
||||||
// 首页推荐视频
|
// 首页推荐视频
|
||||||
static Future rcmdVideoList({required int ps, required int freshIdx}) async {
|
static Future rcmdVideoList({required int ps, required int freshIdx}) async {
|
||||||
@ -136,11 +133,6 @@ class VideoHttp {
|
|||||||
// 'platform': '',
|
// 'platform': '',
|
||||||
// 'high_quality': ''
|
// 'high_quality': ''
|
||||||
};
|
};
|
||||||
// 免登录查看1080p
|
|
||||||
if (userInfoCache.get('userInfoCache') == null &&
|
|
||||||
setting.get(SettingBoxKey.p1080, defaultValue: true)) {
|
|
||||||
data['try_look'] = 1;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
var res = await Request().get(Api.videoUrl, data: data);
|
var res = await Request().get(Api.videoUrl, data: data);
|
||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
@ -422,23 +414,4 @@ class VideoHttp {
|
|||||||
return {'status': true, 'data': res.data['data']};
|
return {'status': true, 'data': res.data['data']};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future aiConclusion({
|
|
||||||
String? bvid,
|
|
||||||
int? cid,
|
|
||||||
int? upMid,
|
|
||||||
}) async {
|
|
||||||
Map params = await WbiSign().makSign({
|
|
||||||
'bvid': bvid,
|
|
||||||
'cid': cid,
|
|
||||||
'up_mid': upMid,
|
|
||||||
});
|
|
||||||
var res = await Request().get(Api.aiConclusion, data: params);
|
|
||||||
if (res.data['code'] == 0) {
|
|
||||||
return {
|
|
||||||
'status': true,
|
|
||||||
'data': AiConclusionModel.fromJson(res.data['data']),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,4 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@ -16,7 +13,6 @@ import 'package:pilipala/pages/search/index.dart';
|
|||||||
import 'package:pilipala/pages/video/detail/index.dart';
|
import 'package:pilipala/pages/video/detail/index.dart';
|
||||||
import 'package:pilipala/router/app_pages.dart';
|
import 'package:pilipala/router/app_pages.dart';
|
||||||
import 'package:pilipala/pages/main/view.dart';
|
import 'package:pilipala/pages/main/view.dart';
|
||||||
import 'package:pilipala/services/service_locator.dart';
|
|
||||||
import 'package:pilipala/utils/app_scheme.dart';
|
import 'package:pilipala/utils/app_scheme.dart';
|
||||||
import 'package:pilipala/utils/data.dart';
|
import 'package:pilipala/utils/data.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
@ -29,7 +25,6 @@ void main() async {
|
|||||||
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown])
|
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown])
|
||||||
.then((_) async {
|
.then((_) async {
|
||||||
await GStrorage.init();
|
await GStrorage.init();
|
||||||
await setupServiceLocator();
|
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
// 小白条、导航栏沉浸
|
// 小白条、导航栏沉浸
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||||
@ -66,23 +61,6 @@ class MyApp extends StatelessWidget {
|
|||||||
double textScale =
|
double textScale =
|
||||||
setting.get(SettingBoxKey.defaultTextScale, defaultValue: 1.0);
|
setting.get(SettingBoxKey.defaultTextScale, defaultValue: 1.0);
|
||||||
|
|
||||||
// 强制设置高帧率
|
|
||||||
if (Platform.isAndroid) {
|
|
||||||
try {
|
|
||||||
late List modes;
|
|
||||||
FlutterDisplayMode.supported.then((value) {
|
|
||||||
modes = value;
|
|
||||||
var storageDisplay = setting.get(SettingBoxKey.displayMode);
|
|
||||||
DisplayMode f = DisplayMode.auto;
|
|
||||||
if (storageDisplay != null) {
|
|
||||||
f = modes.firstWhere((e) => e.toString() == storageDisplay);
|
|
||||||
}
|
|
||||||
DisplayMode preferred = modes.toList().firstWhere((el) => el == f);
|
|
||||||
FlutterDisplayMode.setPreferredMode(preferred);
|
|
||||||
});
|
|
||||||
} catch (_) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
return DynamicColorBuilder(
|
return DynamicColorBuilder(
|
||||||
builder: ((ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
|
builder: ((ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
|
||||||
ColorScheme? lightColorScheme;
|
ColorScheme? lightColorScheme;
|
||||||
|
|||||||
@ -244,9 +244,7 @@ class Vote {
|
|||||||
choiceCnt = json['choice_cnt'];
|
choiceCnt = json['choice_cnt'];
|
||||||
share = json['share'];
|
share = json['share'];
|
||||||
defaultShare = json['default_share'];
|
defaultShare = json['default_share'];
|
||||||
endTime = json['end_time'] is int
|
endTime = json['end_time'];
|
||||||
? json['end_time']
|
|
||||||
: int.parse(json['end_time']);
|
|
||||||
joinNum = json['join_num'];
|
joinNum = json['join_num'];
|
||||||
status = json['status'];
|
status = json['status'];
|
||||||
type = json['type'];
|
type = json['type'];
|
||||||
|
|||||||
@ -8,7 +8,7 @@ class FollowDataModel {
|
|||||||
List<FollowItemModel>? list;
|
List<FollowItemModel>? list;
|
||||||
|
|
||||||
FollowDataModel.fromJson(Map<String, dynamic> json) {
|
FollowDataModel.fromJson(Map<String, dynamic> json) {
|
||||||
total = json['total'] ?? 0;
|
total = json['total'];
|
||||||
list = json['list']
|
list = json['list']
|
||||||
.map<FollowItemModel>((e) => FollowItemModel.fromJson(e))
|
.map<FollowItemModel>((e) => FollowItemModel.fromJson(e))
|
||||||
.toList();
|
.toList();
|
||||||
@ -19,7 +19,7 @@ class FollowItemModel {
|
|||||||
FollowItemModel({
|
FollowItemModel({
|
||||||
this.mid,
|
this.mid,
|
||||||
this.attribute,
|
this.attribute,
|
||||||
// this.mtime,
|
this.mtime,
|
||||||
this.tag,
|
this.tag,
|
||||||
this.special,
|
this.special,
|
||||||
this.uname,
|
this.uname,
|
||||||
@ -30,7 +30,7 @@ class FollowItemModel {
|
|||||||
|
|
||||||
int? mid;
|
int? mid;
|
||||||
int? attribute;
|
int? attribute;
|
||||||
// int? mtime;
|
int? mtime;
|
||||||
List? tag;
|
List? tag;
|
||||||
int? special;
|
int? special;
|
||||||
String? uname;
|
String? uname;
|
||||||
@ -41,7 +41,7 @@ class FollowItemModel {
|
|||||||
FollowItemModel.fromJson(Map<String, dynamic> json) {
|
FollowItemModel.fromJson(Map<String, dynamic> json) {
|
||||||
mid = json['mid'];
|
mid = json['mid'];
|
||||||
attribute = json['attribute'];
|
attribute = json['attribute'];
|
||||||
// mtime = json['mtime'];
|
mtime = json['mtime'];
|
||||||
tag = json['tag'];
|
tag = json['tag'];
|
||||||
special = json['special'];
|
special = json['special'];
|
||||||
uname = json['uname'];
|
uname = json['uname'];
|
||||||
|
|||||||
@ -118,7 +118,7 @@ class RcmdStat {
|
|||||||
|
|
||||||
RcmdStat.fromJson(Map<String, dynamic> json) {
|
RcmdStat.fromJson(Map<String, dynamic> json) {
|
||||||
view = json["cover_left_text_1"];
|
view = json["cover_left_text_1"];
|
||||||
danmu = json['cover_left_text_2'] ?? '-';
|
danmu = json['cover_left_text_2'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,49 +0,0 @@
|
|||||||
class CaptchaDataModel {
|
|
||||||
CaptchaDataModel({
|
|
||||||
this.type,
|
|
||||||
this.token,
|
|
||||||
this.geetest,
|
|
||||||
this.tencent,
|
|
||||||
this.validate,
|
|
||||||
this.seccode,
|
|
||||||
});
|
|
||||||
|
|
||||||
String? type;
|
|
||||||
String? token;
|
|
||||||
GeetestData? geetest;
|
|
||||||
Tencent? tencent;
|
|
||||||
String? validate;
|
|
||||||
String? seccode;
|
|
||||||
|
|
||||||
CaptchaDataModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
type = json["type"];
|
|
||||||
token = json["token"];
|
|
||||||
geetest =
|
|
||||||
json["geetest"] != null ? GeetestData.fromJson(json["geetest"]) : null;
|
|
||||||
tencent =
|
|
||||||
json["tencent"] != null ? Tencent.fromJson(json["tencent"]) : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GeetestData {
|
|
||||||
GeetestData({
|
|
||||||
this.challenge,
|
|
||||||
this.gt,
|
|
||||||
});
|
|
||||||
|
|
||||||
String? challenge;
|
|
||||||
String? gt;
|
|
||||||
|
|
||||||
GeetestData.fromJson(Map<String, dynamic> json) {
|
|
||||||
challenge = json["challenge"];
|
|
||||||
gt = json["gt"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Tencent {
|
|
||||||
Tencent({this.appid});
|
|
||||||
String? appid;
|
|
||||||
Tencent.fromJson(Map<String, dynamic> json) {
|
|
||||||
appid = json["appid"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,89 +0,0 @@
|
|||||||
class MemberCoinsDataModel {
|
|
||||||
MemberCoinsDataModel({
|
|
||||||
this.aid,
|
|
||||||
this.bvid,
|
|
||||||
this.cid,
|
|
||||||
this.coins,
|
|
||||||
this.copyright,
|
|
||||||
this.ctime,
|
|
||||||
this.desc,
|
|
||||||
this.duration,
|
|
||||||
this.owner,
|
|
||||||
this.pic,
|
|
||||||
this.pubLocation,
|
|
||||||
this.pubdate,
|
|
||||||
this.resourceType,
|
|
||||||
this.state,
|
|
||||||
this.subtitle,
|
|
||||||
this.time,
|
|
||||||
this.title,
|
|
||||||
this.tname,
|
|
||||||
this.videos,
|
|
||||||
this.view,
|
|
||||||
this.danmaku,
|
|
||||||
});
|
|
||||||
|
|
||||||
int? aid;
|
|
||||||
String? bvid;
|
|
||||||
int? cid;
|
|
||||||
int? coins;
|
|
||||||
int? copyright;
|
|
||||||
int? ctime;
|
|
||||||
String? desc;
|
|
||||||
int? duration;
|
|
||||||
Owner? owner;
|
|
||||||
String? pic;
|
|
||||||
String? pubLocation;
|
|
||||||
int? pubdate;
|
|
||||||
String? resourceType;
|
|
||||||
int? state;
|
|
||||||
String? subtitle;
|
|
||||||
int? time;
|
|
||||||
String? title;
|
|
||||||
String? tname;
|
|
||||||
int? videos;
|
|
||||||
int? view;
|
|
||||||
int? danmaku;
|
|
||||||
|
|
||||||
MemberCoinsDataModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
aid = json['aid'];
|
|
||||||
bvid = json['bvid'];
|
|
||||||
cid = json['cid'];
|
|
||||||
coins = json['coins'];
|
|
||||||
copyright = json['copyright'];
|
|
||||||
ctime = json['ctime'];
|
|
||||||
desc = json['desc'];
|
|
||||||
duration = json['duration'];
|
|
||||||
owner = Owner.fromJson(json['owner']);
|
|
||||||
pic = json['pic'];
|
|
||||||
pubLocation = json['pub_location'];
|
|
||||||
pubdate = json['pubdate'];
|
|
||||||
resourceType = json['resource_type'];
|
|
||||||
state = json['state'];
|
|
||||||
subtitle = json['subtitle'];
|
|
||||||
time = json['time'];
|
|
||||||
title = json['title'];
|
|
||||||
tname = json['tname'];
|
|
||||||
videos = json['videos'];
|
|
||||||
view = json['stat']['view'];
|
|
||||||
danmaku = json['stat']['danmaku'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Owner {
|
|
||||||
Owner({
|
|
||||||
this.mid,
|
|
||||||
this.name,
|
|
||||||
this.face,
|
|
||||||
});
|
|
||||||
|
|
||||||
int? mid;
|
|
||||||
String? name;
|
|
||||||
String? face;
|
|
||||||
|
|
||||||
Owner.fromJson(Map<String, dynamic> json) {
|
|
||||||
mid = json['mid'];
|
|
||||||
name = json['name'];
|
|
||||||
face = json['face'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,108 +0,0 @@
|
|||||||
class MemberSeasonsDataModel {
|
|
||||||
MemberSeasonsDataModel({
|
|
||||||
this.page,
|
|
||||||
this.seasonsList,
|
|
||||||
});
|
|
||||||
|
|
||||||
Map? page;
|
|
||||||
List<MemberSeasonsList>? seasonsList;
|
|
||||||
|
|
||||||
MemberSeasonsDataModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
page = json['page'];
|
|
||||||
seasonsList = json['seasons_list'] != null
|
|
||||||
? json['seasons_list']
|
|
||||||
.map<MemberSeasonsList>((e) => MemberSeasonsList.fromJson(e))
|
|
||||||
.toList()
|
|
||||||
: [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MemberSeasonsList {
|
|
||||||
MemberSeasonsList({
|
|
||||||
this.archives,
|
|
||||||
this.meta,
|
|
||||||
this.recentAids,
|
|
||||||
this.page,
|
|
||||||
});
|
|
||||||
|
|
||||||
List<MemberArchiveItem>? archives;
|
|
||||||
MamberMeta? meta;
|
|
||||||
List? recentAids;
|
|
||||||
Map? page;
|
|
||||||
|
|
||||||
MemberSeasonsList.fromJson(Map<String, dynamic> json) {
|
|
||||||
archives = json['archives'] != null
|
|
||||||
? json['archives']
|
|
||||||
.map<MemberArchiveItem>((e) => MemberArchiveItem.fromJson(e))
|
|
||||||
.toList()
|
|
||||||
: [];
|
|
||||||
meta = MamberMeta.fromJson(json['meta']);
|
|
||||||
page = json['page'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MemberArchiveItem {
|
|
||||||
MemberArchiveItem({
|
|
||||||
this.aid,
|
|
||||||
this.bvid,
|
|
||||||
this.ctime,
|
|
||||||
this.duration,
|
|
||||||
this.pic,
|
|
||||||
this.cover,
|
|
||||||
this.pubdate,
|
|
||||||
this.view,
|
|
||||||
this.title,
|
|
||||||
});
|
|
||||||
|
|
||||||
int? aid;
|
|
||||||
String? bvid;
|
|
||||||
int? ctime;
|
|
||||||
int? duration;
|
|
||||||
String? pic;
|
|
||||||
String? cover;
|
|
||||||
int? pubdate;
|
|
||||||
int? view;
|
|
||||||
String? title;
|
|
||||||
|
|
||||||
MemberArchiveItem.fromJson(Map<String, dynamic> json) {
|
|
||||||
aid = json['aid'];
|
|
||||||
bvid = json['bvid'];
|
|
||||||
ctime = json['ctime'];
|
|
||||||
duration = json['duration'];
|
|
||||||
pic = json['pic'];
|
|
||||||
cover = json['pic'];
|
|
||||||
pubdate = json['pubdate'];
|
|
||||||
view = json['stat']['view'];
|
|
||||||
title = json['title'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MamberMeta {
|
|
||||||
MamberMeta({
|
|
||||||
this.cover,
|
|
||||||
this.description,
|
|
||||||
this.mid,
|
|
||||||
this.name,
|
|
||||||
this.ptime,
|
|
||||||
this.seasonId,
|
|
||||||
this.total,
|
|
||||||
});
|
|
||||||
|
|
||||||
String? cover;
|
|
||||||
String? description;
|
|
||||||
int? mid;
|
|
||||||
String? name;
|
|
||||||
int? ptime;
|
|
||||||
int? seasonId;
|
|
||||||
int? total;
|
|
||||||
|
|
||||||
MamberMeta.fromJson(Map<String, dynamic> json) {
|
|
||||||
cover = json['cover'];
|
|
||||||
description = json['description'];
|
|
||||||
mid = json['mid'];
|
|
||||||
name = json['name'];
|
|
||||||
ptime = json['ptime'];
|
|
||||||
seasonId = json['season_id'];
|
|
||||||
total = json['total'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
class MemberTagItemModel {
|
|
||||||
MemberTagItemModel({
|
|
||||||
this.count,
|
|
||||||
this.name,
|
|
||||||
this.tagid,
|
|
||||||
this.tip,
|
|
||||||
this.checked,
|
|
||||||
});
|
|
||||||
|
|
||||||
int? count;
|
|
||||||
String? name;
|
|
||||||
int? tagid;
|
|
||||||
String? tip;
|
|
||||||
bool? checked;
|
|
||||||
|
|
||||||
MemberTagItemModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
count = json['count'];
|
|
||||||
name = json['name'];
|
|
||||||
tagid = json['tagid'];
|
|
||||||
tip = json['tip'];
|
|
||||||
checked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,80 +0,0 @@
|
|||||||
class AccountListModel {
|
|
||||||
AccountListModel({
|
|
||||||
this.mid,
|
|
||||||
this.name,
|
|
||||||
this.sex,
|
|
||||||
this.face,
|
|
||||||
this.sign,
|
|
||||||
this.rank,
|
|
||||||
this.level,
|
|
||||||
this.silence,
|
|
||||||
this.vip,
|
|
||||||
this.pendant,
|
|
||||||
this.nameplate,
|
|
||||||
this.official,
|
|
||||||
this.birthday,
|
|
||||||
this.isFakeAccount,
|
|
||||||
this.isDeleted,
|
|
||||||
this.inRegAudit,
|
|
||||||
this.faceNft,
|
|
||||||
this.faceNftNew,
|
|
||||||
this.isSeniorMember,
|
|
||||||
this.digitalId,
|
|
||||||
this.digitalType,
|
|
||||||
this.attestation,
|
|
||||||
this.expertInfo,
|
|
||||||
this.honours,
|
|
||||||
});
|
|
||||||
|
|
||||||
int? mid;
|
|
||||||
String? name;
|
|
||||||
String? sex;
|
|
||||||
String? face;
|
|
||||||
String? sign;
|
|
||||||
int? rank;
|
|
||||||
int? level;
|
|
||||||
int? silence;
|
|
||||||
Map? vip;
|
|
||||||
Map? pendant;
|
|
||||||
Map? nameplate;
|
|
||||||
Map? official;
|
|
||||||
int? birthday;
|
|
||||||
int? isFakeAccount;
|
|
||||||
int? isDeleted;
|
|
||||||
int? inRegAudit;
|
|
||||||
int? faceNft;
|
|
||||||
int? faceNftNew;
|
|
||||||
int? isSeniorMember;
|
|
||||||
String? digitalId;
|
|
||||||
int? digitalType;
|
|
||||||
Map? attestation;
|
|
||||||
Map? expertInfo;
|
|
||||||
Map? honours;
|
|
||||||
|
|
||||||
AccountListModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
mid = json['mid'];
|
|
||||||
name = json['name'] ?? '';
|
|
||||||
sex = json['sex'];
|
|
||||||
face = json['face'];
|
|
||||||
sign = json['sign'];
|
|
||||||
rank = json['rank'];
|
|
||||||
level = json['level'];
|
|
||||||
silence = json['silence'];
|
|
||||||
vip = json['vip'];
|
|
||||||
pendant = json['pendant'];
|
|
||||||
nameplate = json['nameplate'];
|
|
||||||
official = json['official'];
|
|
||||||
birthday = json['birthday'];
|
|
||||||
isFakeAccount = json['is_fake_account'];
|
|
||||||
isDeleted = json['is_deleted'];
|
|
||||||
inRegAudit = json['in_reg_audit'];
|
|
||||||
faceNft = json['face_nft'];
|
|
||||||
faceNftNew = json['face_nft_new'];
|
|
||||||
isSeniorMember = json['is_senior_member'];
|
|
||||||
digitalId = json['digital_id'];
|
|
||||||
digitalType = json['digital_type'];
|
|
||||||
attestation = json['attestation'];
|
|
||||||
expertInfo = json['expert_info'];
|
|
||||||
honours = json['honours'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,226 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:pilipala/models/msg/account.dart';
|
|
||||||
|
|
||||||
class SessionDataModel {
|
|
||||||
SessionDataModel({
|
|
||||||
this.sessionList,
|
|
||||||
this.hasMore,
|
|
||||||
});
|
|
||||||
|
|
||||||
List? sessionList;
|
|
||||||
int? hasMore;
|
|
||||||
|
|
||||||
SessionDataModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
sessionList = json['session_list']
|
|
||||||
?.map<SessionList>((e) => SessionList.fromJson(e))
|
|
||||||
.toList();
|
|
||||||
hasMore = json['has_more'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SessionList {
|
|
||||||
SessionList({
|
|
||||||
this.talkerId,
|
|
||||||
this.sessionType,
|
|
||||||
this.atSeqno,
|
|
||||||
this.topTs,
|
|
||||||
this.groupName,
|
|
||||||
this.groupCover,
|
|
||||||
this.isFollow,
|
|
||||||
this.isDnd,
|
|
||||||
this.ackSeqno,
|
|
||||||
this.ackTs,
|
|
||||||
this.sessionTs,
|
|
||||||
this.unreadCount,
|
|
||||||
this.lastMsg,
|
|
||||||
this.groupType,
|
|
||||||
this.canFold,
|
|
||||||
this.status,
|
|
||||||
this.maxSeqno,
|
|
||||||
this.newPushMsg,
|
|
||||||
this.setting,
|
|
||||||
this.isGuardian,
|
|
||||||
this.isIntercept,
|
|
||||||
this.isTrust,
|
|
||||||
this.systemMsgType,
|
|
||||||
this.liveStatus,
|
|
||||||
this.bizMsgUnreadCount,
|
|
||||||
// this.userLabel,
|
|
||||||
});
|
|
||||||
|
|
||||||
int? talkerId;
|
|
||||||
int? sessionType;
|
|
||||||
int? atSeqno;
|
|
||||||
int? topTs;
|
|
||||||
String? groupName;
|
|
||||||
String? groupCover;
|
|
||||||
int? isFollow;
|
|
||||||
int? isDnd;
|
|
||||||
int? ackSeqno;
|
|
||||||
int? ackTs;
|
|
||||||
int? sessionTs;
|
|
||||||
int? unreadCount;
|
|
||||||
LastMsg? lastMsg;
|
|
||||||
int? groupType;
|
|
||||||
int? canFold;
|
|
||||||
int? status;
|
|
||||||
int? maxSeqno;
|
|
||||||
int? newPushMsg;
|
|
||||||
int? setting;
|
|
||||||
int? isGuardian;
|
|
||||||
int? isIntercept;
|
|
||||||
int? isTrust;
|
|
||||||
int? systemMsgType;
|
|
||||||
int? liveStatus;
|
|
||||||
int? bizMsgUnreadCount;
|
|
||||||
// int? userLabel;
|
|
||||||
AccountListModel? accountInfo;
|
|
||||||
|
|
||||||
SessionList.fromJson(Map<String, dynamic> json) {
|
|
||||||
talkerId = json["talker_id"];
|
|
||||||
sessionType = json["session_type"];
|
|
||||||
atSeqno = json["at_seqno"];
|
|
||||||
topTs = json["top_ts"];
|
|
||||||
groupName = json["group_name"];
|
|
||||||
groupCover = json["group_cover"];
|
|
||||||
isFollow = json["is_follow"];
|
|
||||||
isDnd = json["is_dnd"];
|
|
||||||
ackSeqno = json["ack_seqno"];
|
|
||||||
ackTs = json["ack_ts"];
|
|
||||||
sessionTs = json["session_ts"];
|
|
||||||
unreadCount = json["unread_count"];
|
|
||||||
lastMsg =
|
|
||||||
json["last_msg"] != null ? LastMsg.fromJson(json["last_msg"]) : null;
|
|
||||||
groupType = json["group_type"];
|
|
||||||
canFold = json["can_fold"];
|
|
||||||
status = json["status"];
|
|
||||||
maxSeqno = json["max_seqno"];
|
|
||||||
newPushMsg = json["new_push_msg"];
|
|
||||||
setting = json["setting"];
|
|
||||||
isGuardian = json["is_guardian"];
|
|
||||||
isIntercept = json["is_intercept"];
|
|
||||||
isTrust = json["is_trust"];
|
|
||||||
systemMsgType = json["system_msg_type"];
|
|
||||||
liveStatus = json["live_status"];
|
|
||||||
bizMsgUnreadCount = json["biz_msg_unread_count"];
|
|
||||||
// userLabel = json["user_label"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LastMsg {
|
|
||||||
LastMsg({
|
|
||||||
this.senderIid,
|
|
||||||
this.receiverType,
|
|
||||||
this.receiverId,
|
|
||||||
this.msgType,
|
|
||||||
this.content,
|
|
||||||
this.msgSeqno,
|
|
||||||
this.timestamp,
|
|
||||||
this.atUids,
|
|
||||||
this.msgKey,
|
|
||||||
this.msgStatus,
|
|
||||||
this.notifyCode,
|
|
||||||
this.newFaceVersion,
|
|
||||||
});
|
|
||||||
|
|
||||||
int? senderIid;
|
|
||||||
int? receiverType;
|
|
||||||
int? receiverId;
|
|
||||||
int? msgType;
|
|
||||||
Map? content;
|
|
||||||
int? msgSeqno;
|
|
||||||
int? timestamp;
|
|
||||||
String? atUids;
|
|
||||||
int? msgKey;
|
|
||||||
int? msgStatus;
|
|
||||||
String? notifyCode;
|
|
||||||
int? newFaceVersion;
|
|
||||||
|
|
||||||
LastMsg.fromJson(Map<String, dynamic> json) {
|
|
||||||
senderIid = json['sender_uid'];
|
|
||||||
receiverType = json['receiver_type'];
|
|
||||||
receiverId = json['receiver_id'];
|
|
||||||
msgType = json['msg_type'];
|
|
||||||
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'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SessionMsgDataModel {
|
|
||||||
SessionMsgDataModel({
|
|
||||||
this.messages,
|
|
||||||
this.hasMore,
|
|
||||||
this.minSeqno,
|
|
||||||
this.maxSeqno,
|
|
||||||
this.eInfos,
|
|
||||||
});
|
|
||||||
|
|
||||||
List<MessageItem>? messages;
|
|
||||||
int? hasMore;
|
|
||||||
int? minSeqno;
|
|
||||||
int? maxSeqno;
|
|
||||||
List? eInfos;
|
|
||||||
|
|
||||||
SessionMsgDataModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
messages = json['messages']
|
|
||||||
.map<MessageItem>((e) => MessageItem.fromJson(e))
|
|
||||||
.toList();
|
|
||||||
hasMore = json['has_more'];
|
|
||||||
minSeqno = json['min_seqno'];
|
|
||||||
maxSeqno = json['max_seqno'];
|
|
||||||
eInfos = json['e_infos'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MessageItem {
|
|
||||||
MessageItem({
|
|
||||||
this.senderUid,
|
|
||||||
this.receiverType,
|
|
||||||
this.receiverId,
|
|
||||||
this.msgType,
|
|
||||||
this.content,
|
|
||||||
this.msgSeqno,
|
|
||||||
this.timestamp,
|
|
||||||
this.atUids,
|
|
||||||
this.msgKey,
|
|
||||||
this.msgStatus,
|
|
||||||
this.notifyCode,
|
|
||||||
this.newFaceVersion,
|
|
||||||
});
|
|
||||||
|
|
||||||
int? senderUid;
|
|
||||||
int? receiverType;
|
|
||||||
int? receiverId;
|
|
||||||
int? msgType;
|
|
||||||
Map? content;
|
|
||||||
int? msgSeqno;
|
|
||||||
int? timestamp;
|
|
||||||
List? atUids;
|
|
||||||
int? msgKey;
|
|
||||||
int? msgStatus;
|
|
||||||
String? notifyCode;
|
|
||||||
int? newFaceVersion;
|
|
||||||
|
|
||||||
MessageItem.fromJson(Map<String, dynamic> json) {
|
|
||||||
senderUid = json['sender_uid'];
|
|
||||||
receiverType = json['receiver_type'];
|
|
||||||
receiverId = json['receiver_id'];
|
|
||||||
// 1 文本 2 图片 18 系统提示 10 系统通知
|
|
||||||
msgType = json['msg_type'];
|
|
||||||
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'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,80 +0,0 @@
|
|||||||
class AiConclusionModel {
|
|
||||||
AiConclusionModel({
|
|
||||||
this.code,
|
|
||||||
this.modelResult,
|
|
||||||
this.stid,
|
|
||||||
this.status,
|
|
||||||
this.likeNum,
|
|
||||||
this.dislikeNum,
|
|
||||||
});
|
|
||||||
|
|
||||||
int? code;
|
|
||||||
ModelResult? modelResult;
|
|
||||||
String? stid;
|
|
||||||
int? status;
|
|
||||||
int? likeNum;
|
|
||||||
int? dislikeNum;
|
|
||||||
|
|
||||||
AiConclusionModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
code = json['code'];
|
|
||||||
modelResult = ModelResult.fromJson(json['model_result']);
|
|
||||||
stid = json['stid'];
|
|
||||||
status = json['status'];
|
|
||||||
likeNum = json['like_num'];
|
|
||||||
dislikeNum = json['dislike_num'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ModelResult {
|
|
||||||
ModelResult({
|
|
||||||
this.resultType,
|
|
||||||
this.summary,
|
|
||||||
this.outline,
|
|
||||||
});
|
|
||||||
|
|
||||||
int? resultType;
|
|
||||||
String? summary;
|
|
||||||
List<OutlineItem>? outline;
|
|
||||||
|
|
||||||
ModelResult.fromJson(Map<String, dynamic> json) {
|
|
||||||
resultType = json['result_type'];
|
|
||||||
summary = json['summary'];
|
|
||||||
outline = json['result_type'] == 2
|
|
||||||
? json['outline']
|
|
||||||
.map<OutlineItem>((e) => OutlineItem.fromJson(e))
|
|
||||||
.toList()
|
|
||||||
: <OutlineItem>[];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class OutlineItem {
|
|
||||||
OutlineItem({
|
|
||||||
this.title,
|
|
||||||
this.partOutline,
|
|
||||||
});
|
|
||||||
|
|
||||||
String? title;
|
|
||||||
List<PartOutline>? partOutline;
|
|
||||||
|
|
||||||
OutlineItem.fromJson(Map<String, dynamic> json) {
|
|
||||||
title = json['title'];
|
|
||||||
partOutline = json['part_outline']
|
|
||||||
.map<PartOutline>((e) => PartOutline.fromJson(e))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PartOutline {
|
|
||||||
PartOutline({
|
|
||||||
this.timestamp,
|
|
||||||
this.content,
|
|
||||||
});
|
|
||||||
|
|
||||||
int? timestamp;
|
|
||||||
String? content;
|
|
||||||
|
|
||||||
PartOutline.fromJson(Map<String, dynamic> json) {
|
|
||||||
timestamp = json['timestamp'];
|
|
||||||
content = json['content'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:pilipala/models/video/play/quality.dart';
|
import 'package:pilipala/models/video/play/quality.dart';
|
||||||
|
|
||||||
class PlayUrlModel {
|
class PlayUrlModel {
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
@ -31,6 +34,11 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
Divider(
|
||||||
|
thickness: 8,
|
||||||
|
height: 10,
|
||||||
|
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||||
|
),
|
||||||
Image.asset(
|
Image.asset(
|
||||||
'assets/images/logo/logo_android_2.png',
|
'assets/images/logo/logo_android_2.png',
|
||||||
width: 150,
|
width: 150,
|
||||||
@ -75,9 +83,9 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
Divider(
|
Divider(
|
||||||
thickness: 1,
|
thickness: 8,
|
||||||
height: 30,
|
height: 30,
|
||||||
color: Theme.of(context).colorScheme.outlineVariant,
|
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () => _aboutController.githubUrl(),
|
onTap: () => _aboutController.githubUrl(),
|
||||||
@ -126,6 +134,11 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
title: const Text('赞助'),
|
title: const Text('赞助'),
|
||||||
trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline),
|
trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline),
|
||||||
),
|
),
|
||||||
|
Divider(
|
||||||
|
thickness: 8,
|
||||||
|
height: 30,
|
||||||
|
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -171,7 +184,7 @@ class AboutController extends GetxController {
|
|||||||
|
|
||||||
// 获取远程版本
|
// 获取远程版本
|
||||||
Future getRemoteApp() async {
|
Future getRemoteApp() async {
|
||||||
var result = await Request().get(Api.latestApp, extra: {'ua': 'pc'});
|
var result = await Request().get(Api.latestApp);
|
||||||
data = LatestDataModel.fromJson(result.data);
|
data = LatestDataModel.fromJson(result.data);
|
||||||
remoteAppInfo = data;
|
remoteAppInfo = data;
|
||||||
remoteVersion.value = data.tagName!;
|
remoteVersion.value = data.tagName!;
|
||||||
|
|||||||
@ -22,7 +22,7 @@ class BangumiIntroController extends GetxController {
|
|||||||
? int.parse(Get.parameters['seasonId']!)
|
? int.parse(Get.parameters['seasonId']!)
|
||||||
: null;
|
: null;
|
||||||
var epId = Get.parameters['epId'] != null
|
var epId = Get.parameters['epId'] != null
|
||||||
? int.tryParse(Get.parameters['epId']!)
|
? int.parse(Get.parameters['epId']!)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
// 是否预渲染 骨架屏
|
// 是否预渲染 骨架屏
|
||||||
@ -258,7 +258,7 @@ class BangumiIntroController extends GetxController {
|
|||||||
VideoDetailController videoDetailCtr =
|
VideoDetailController videoDetailCtr =
|
||||||
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
|
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
|
||||||
videoDetailCtr.bvid = bvid;
|
videoDetailCtr.bvid = bvid;
|
||||||
videoDetailCtr.cid.value = cid;
|
videoDetailCtr.cid = cid;
|
||||||
videoDetailCtr.danmakuCid.value = cid;
|
videoDetailCtr.danmakuCid.value = cid;
|
||||||
videoDetailCtr.queryVideoUrl();
|
videoDetailCtr.queryVideoUrl();
|
||||||
// 重新请求评论
|
// 重新请求评论
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
import 'package:pilipala/common/widgets/badge.dart';
|
import 'package:pilipala/common/widgets/badge.dart';
|
||||||
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:pilipala/common/widgets/stat/danmu.dart';
|
import 'package:pilipala/common/widgets/stat/danmu.dart';
|
||||||
import 'package:pilipala/common/widgets/stat/view.dart';
|
import 'package:pilipala/common/widgets/stat/view.dart';
|
||||||
@ -33,12 +34,10 @@ class BangumiIntroPanel extends StatefulWidget {
|
|||||||
|
|
||||||
class _BangumiIntroPanelState extends State<BangumiIntroPanel>
|
class _BangumiIntroPanelState extends State<BangumiIntroPanel>
|
||||||
with AutomaticKeepAliveClientMixin {
|
with AutomaticKeepAliveClientMixin {
|
||||||
late BangumiIntroController bangumiIntroController;
|
final BangumiIntroController bangumiIntroController =
|
||||||
late VideoDetailController videoDetailCtr;
|
Get.put(BangumiIntroController(), tag: Get.arguments['heroTag']);
|
||||||
BangumiInfoModel? bangumiDetail;
|
BangumiInfoModel? bangumiDetail;
|
||||||
late Future _futureBuilderFuture;
|
late Future _futureBuilderFuture;
|
||||||
late int cid;
|
|
||||||
late String heroTag;
|
|
||||||
|
|
||||||
// 添加页面缓存
|
// 添加页面缓存
|
||||||
@override
|
@override
|
||||||
@ -47,19 +46,10 @@ class _BangumiIntroPanelState extends State<BangumiIntroPanel>
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
heroTag = Get.arguments['heroTag'];
|
|
||||||
cid = widget.cid!;
|
|
||||||
bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag);
|
|
||||||
videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag);
|
|
||||||
bangumiIntroController.bangumiDetail.listen((value) {
|
bangumiIntroController.bangumiDetail.listen((value) {
|
||||||
bangumiDetail = value;
|
bangumiDetail = value;
|
||||||
});
|
});
|
||||||
_futureBuilderFuture = bangumiIntroController.queryBangumiIntro();
|
_futureBuilderFuture = bangumiIntroController.queryBangumiIntro();
|
||||||
videoDetailCtr.cid.listen((p0) {
|
|
||||||
print('🐶🐶$p0');
|
|
||||||
cid = p0;
|
|
||||||
setState(() {});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -71,25 +61,22 @@ class _BangumiIntroPanelState extends State<BangumiIntroPanel>
|
|||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
if (snapshot.data['status']) {
|
if (snapshot.data['status']) {
|
||||||
// 请求成功
|
// 请求成功
|
||||||
|
|
||||||
return BangumiInfo(
|
return BangumiInfo(
|
||||||
loadingStatus: false,
|
loadingStatus: false,
|
||||||
bangumiDetail: bangumiDetail,
|
bangumiDetail: bangumiDetail,
|
||||||
cid: cid,
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// 请求错误
|
// 请求错误
|
||||||
// return HttpError(
|
return HttpError(
|
||||||
// errMsg: snapshot.data['msg'],
|
errMsg: snapshot.data['msg'],
|
||||||
// fn: () => Get.back(),
|
fn: () => Get.back(),
|
||||||
// );
|
);
|
||||||
return SizedBox();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return BangumiInfo(
|
return BangumiInfo(
|
||||||
loadingStatus: true,
|
loadingStatus: true,
|
||||||
bangumiDetail: bangumiDetail,
|
bangumiDetail: bangumiDetail,
|
||||||
cid: cid,
|
cid: widget.cid,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -130,12 +117,6 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
bangumiItem = bangumiIntroController.bangumiItem;
|
bangumiItem = bangumiIntroController.bangumiItem;
|
||||||
sheetHeight = localCache.get('sheetHeight');
|
sheetHeight = localCache.get('sheetHeight');
|
||||||
cid = widget.cid!;
|
cid = widget.cid!;
|
||||||
print('cid: $cid');
|
|
||||||
videoDetailCtr.cid.listen((p0) {
|
|
||||||
cid = p0;
|
|
||||||
print('cid: $cid');
|
|
||||||
setState(() {});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 收藏
|
// 收藏
|
||||||
@ -279,15 +260,9 @@ class _BangumiInfoState extends State<BangumiInfo> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
!widget.loadingStatus
|
!widget.loadingStatus
|
||||||
? (widget.bangumiDetail!.areas!
|
? widget.bangumiDetail!.areas!
|
||||||
.isNotEmpty
|
.first['name']
|
||||||
? widget.bangumiDetail!.areas!
|
: bangumiItem!.areas!.first['name'],
|
||||||
.first['name']
|
|
||||||
: '')
|
|
||||||
: (bangumiItem!.areas!.isNotEmpty
|
|
||||||
? bangumiItem!
|
|
||||||
.areas!.first['name']
|
|
||||||
: ''),
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: t.colorScheme.outline,
|
color: t.colorScheme.outline,
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import 'package:flutter/rendering.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
import 'package:pilipala/common/widgets/http_error.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/main/index.dart';
|
||||||
import 'package:pilipala/pages/rcmd/view.dart';
|
import 'package:pilipala/pages/rcmd/view.dart';
|
||||||
|
|
||||||
@ -36,8 +35,6 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
scrollController = _bangumidController.scrollController;
|
scrollController = _bangumidController.scrollController;
|
||||||
StreamController<bool> mainStream =
|
StreamController<bool> mainStream =
|
||||||
Get.find<MainController>().bottomBarStream;
|
Get.find<MainController>().bottomBarStream;
|
||||||
StreamController<bool> searchBarStream =
|
|
||||||
Get.find<HomeController>().searchBarStream;
|
|
||||||
_futureBuilderFuture = _bangumidController.queryBangumiListFeed();
|
_futureBuilderFuture = _bangumidController.queryBangumiListFeed();
|
||||||
_futureBuilderFutureFollow = _bangumidController.queryBangumiFollow();
|
_futureBuilderFutureFollow = _bangumidController.queryBangumiFollow();
|
||||||
scrollController.addListener(
|
scrollController.addListener(
|
||||||
@ -54,10 +51,8 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
scrollController.position.userScrollDirection;
|
scrollController.position.userScrollDirection;
|
||||||
if (direction == ScrollDirection.forward) {
|
if (direction == ScrollDirection.forward) {
|
||||||
mainStream.add(true);
|
mainStream.add(true);
|
||||||
searchBarStream.add(true);
|
|
||||||
} else if (direction == ScrollDirection.reverse) {
|
} else if (direction == ScrollDirection.reverse) {
|
||||||
mainStream.add(false);
|
mainStream.add(false);
|
||||||
searchBarStream.add(false);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -206,7 +201,7 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
LoadingMore()
|
const LoadingMore()
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/models/bangumi/info.dart';
|
import 'package:pilipala/models/bangumi/info.dart';
|
||||||
import 'package:pilipala/pages/video/detail/index.dart';
|
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
class BangumiPanel extends StatefulWidget {
|
class BangumiPanel extends StatefulWidget {
|
||||||
@ -32,28 +30,16 @@ class _BangumiPanelState extends State<BangumiPanel> {
|
|||||||
dynamic userInfo;
|
dynamic userInfo;
|
||||||
// 默认未开通
|
// 默认未开通
|
||||||
int vipStatus = 0;
|
int vipStatus = 0;
|
||||||
late int cid;
|
|
||||||
String heroTag = Get.arguments['heroTag'];
|
|
||||||
late final VideoDetailController videoDetailCtr;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
cid = widget.cid!;
|
currentIndex = widget.pages.indexWhere((e) => e.cid == widget.cid!);
|
||||||
currentIndex = widget.pages.indexWhere((e) => e.cid == cid);
|
|
||||||
scrollToIndex();
|
scrollToIndex();
|
||||||
userInfo = userInfoCache.get('userInfoCache');
|
userInfo = userInfoCache.get('userInfoCache');
|
||||||
if (userInfo != null) {
|
if (userInfo != null) {
|
||||||
vipStatus = userInfo.vipStatus;
|
vipStatus = userInfo.vipStatus;
|
||||||
}
|
}
|
||||||
videoDetailCtr = Get.find<VideoDetailController>(tag: heroTag);
|
|
||||||
|
|
||||||
videoDetailCtr.cid.listen((p0) {
|
|
||||||
cid = p0;
|
|
||||||
setState(() {});
|
|
||||||
currentIndex = widget.pages.indexWhere((e) => e.cid == cid);
|
|
||||||
scrollToIndex();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@ -62,7 +62,7 @@ class BangumiCardV extends StatelessWidget {
|
|||||||
arguments: {
|
arguments: {
|
||||||
'pic': pic,
|
'pic': pic,
|
||||||
'heroTag': heroTag,
|
'heroTag': heroTag,
|
||||||
'videoType': SearchType.media_bangumi,
|
// 'videoType': SearchType.media_bangumi,
|
||||||
'bangumiItem': res['data'],
|
'bangumiItem': res['data'],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -10,34 +10,22 @@ class PlDanmakuController {
|
|||||||
// 按 6min 分段
|
// 按 6min 分段
|
||||||
int segCount = 0;
|
int segCount = 0;
|
||||||
List<DmSegMobileReply> dmSegList = [];
|
List<DmSegMobileReply> dmSegList = [];
|
||||||
// 已请求的段落标记
|
int currentSegIndex = 0;
|
||||||
List<int> hasrequestSeg = [];
|
|
||||||
int currentSegIndex = 1;
|
|
||||||
int currentDmIndex = 0;
|
int currentDmIndex = 0;
|
||||||
|
|
||||||
void calcSegment() {
|
void calcSegment() {
|
||||||
dmSegList.clear();
|
|
||||||
// 视频分段数
|
|
||||||
segCount = (videoDuration.inSeconds / (60 * 6)).ceil();
|
segCount = (videoDuration.inSeconds / (60 * 6)).ceil();
|
||||||
dmSegList = List<DmSegMobileReply>.generate(
|
|
||||||
segCount < 1 ? 1 : segCount, (index) => DmSegMobileReply());
|
|
||||||
// 当前分段
|
|
||||||
try {
|
|
||||||
currentSegIndex =
|
|
||||||
(playerController.position.value.inSeconds / (60 * 6)).ceil();
|
|
||||||
currentSegIndex = currentSegIndex < 1 ? 1 : currentSegIndex;
|
|
||||||
} catch (_) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<DmSegMobileReply>> queryDanmaku() async {
|
Future<List<DmSegMobileReply>> queryDanmaku() async {
|
||||||
// dmSegList.clear();
|
dmSegList.clear();
|
||||||
DmSegMobileReply result =
|
for (int segIndex = 1; segIndex <= segCount; segIndex++) {
|
||||||
await DanmakaHttp.queryDanmaku(cid: cid, segmentIndex: currentSegIndex);
|
DmSegMobileReply result =
|
||||||
if (result.elems.isNotEmpty) {
|
await DanmakaHttp.queryDanmaku(cid: cid, segmentIndex: segIndex);
|
||||||
result.elems.sort((a, b) => (a.progress).compareTo(b.progress));
|
if (result.elems.isNotEmpty) {
|
||||||
// dmSegList.add(result);
|
result.elems.sort((a, b) => (a.progress).compareTo(b.progress));
|
||||||
currentSegIndex = currentSegIndex < 1 ? 1 : currentSegIndex;
|
dmSegList.add(result);
|
||||||
dmSegList[currentSegIndex - 1] = result;
|
}
|
||||||
}
|
}
|
||||||
if (dmSegList.isNotEmpty) {
|
if (dmSegList.isNotEmpty) {
|
||||||
findClosestPositionIndex(playerController.position.value.inMilliseconds);
|
findClosestPositionIndex(playerController.position.value.inMilliseconds);
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import 'package:easy_debounce/easy_throttle.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
@ -86,26 +85,12 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
|||||||
_controller!.onResume();
|
_controller!.onResume();
|
||||||
danmuPlayStatus = true;
|
danmuPlayStatus = true;
|
||||||
}
|
}
|
||||||
if (!playerController.isOpenDanmu.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
PlDanmakuController ctr = _plDanmakuController;
|
PlDanmakuController ctr = _plDanmakuController;
|
||||||
int currentPosition = position.inMilliseconds;
|
int currentPosition = position.inMilliseconds;
|
||||||
blockTypes = playerController.blockTypes;
|
blockTypes = playerController.blockTypes;
|
||||||
// 根据position判断是否有已缓存弹幕。没有则请求对应段
|
|
||||||
int segIndex = (currentPosition / (6 * 60 * 1000)).ceil();
|
if (!playerController.isOpenDanmu.value) {
|
||||||
segIndex = segIndex < 1 ? 1 : segIndex;
|
return;
|
||||||
print('🌹🌹: ${segIndex}');
|
|
||||||
print('🌹🌹: ${ctr.dmSegList.length}');
|
|
||||||
print('🌹🌹: ${ctr.hasrequestSeg.contains(segIndex - 1)}');
|
|
||||||
if (segIndex - 1 >= ctr.dmSegList.length ||
|
|
||||||
(ctr.dmSegList[segIndex - 1].elems.isEmpty &&
|
|
||||||
!ctr.hasrequestSeg.contains(segIndex - 1))) {
|
|
||||||
ctr.hasrequestSeg.add(segIndex - 1);
|
|
||||||
ctr.currentSegIndex = segIndex;
|
|
||||||
EasyThrottle.throttle('follow', const Duration(seconds: 1), () {
|
|
||||||
ctr.queryDanmaku();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
// 超出分段数返回
|
// 超出分段数返回
|
||||||
if (ctr.currentSegIndex >= ctr.dmSegList.length) {
|
if (ctr.currentSegIndex >= ctr.dmSegList.length) {
|
||||||
@ -155,30 +140,23 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return LayoutBuilder(builder: (context, box) {
|
return Obx(
|
||||||
double initDuration = box.maxWidth / 12;
|
() => AnimatedOpacity(
|
||||||
return Obx(
|
opacity: playerController.isOpenDanmu.value ? 1 : 0,
|
||||||
() => AnimatedOpacity(
|
duration: const Duration(milliseconds: 100),
|
||||||
opacity: playerController.isOpenDanmu.value ? 1 : 0,
|
child: DanmakuView(
|
||||||
duration: const Duration(milliseconds: 100),
|
createdController: (DanmakuController e) async {
|
||||||
child: DanmakuView(
|
widget.playerController.danmakuController = _controller = e;
|
||||||
createdController: (DanmakuController e) async {
|
},
|
||||||
widget.playerController.danmakuController = _controller = e;
|
option: DanmakuOption(
|
||||||
},
|
fontSize: 15 * fontSizeVal,
|
||||||
option: DanmakuOption(
|
area: showArea,
|
||||||
fontSize: 15 * fontSizeVal,
|
opacity: opacityVal,
|
||||||
area: showArea,
|
duration: danmakuSpeedVal * widget.playerController.playbackSpeed,
|
||||||
opacity: opacityVal,
|
|
||||||
hideTop: blockTypes.contains(5),
|
|
||||||
hideScroll: blockTypes.contains(2),
|
|
||||||
hideBottom: blockTypes.contains(4),
|
|
||||||
duration: initDuration /
|
|
||||||
(danmakuSpeedVal * widget.playerController.playbackSpeed),
|
|
||||||
),
|
|
||||||
statusChanged: (isPlaying) {},
|
|
||||||
),
|
),
|
||||||
|
statusChanged: (isPlaying) {},
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -149,30 +149,10 @@ class DynamicsController extends GetxController {
|
|||||||
case 'DYNAMIC_TYPE_ARTICLE':
|
case 'DYNAMIC_TYPE_ARTICLE':
|
||||||
String title = item.modules.moduleDynamic.major.opus.title;
|
String title = item.modules.moduleDynamic.major.opus.title;
|
||||||
String url = item.modules.moduleDynamic.major.opus.jumpUrl;
|
String url = item.modules.moduleDynamic.major.opus.jumpUrl;
|
||||||
if (url.contains('opus') || url.contains('read')) {
|
Get.toNamed(
|
||||||
RegExp digitRegExp = RegExp(r'\d+');
|
'/webview',
|
||||||
Iterable<Match> matches = digitRegExp.allMatches(url);
|
parameters: {'url': 'https:$url', 'type': 'note', 'pageTitle': title},
|
||||||
String number = matches.first.group(0)!;
|
);
|
||||||
if (url.contains('read')) {
|
|
||||||
number = 'cv$number';
|
|
||||||
}
|
|
||||||
Get.toNamed('/htmlRender', parameters: {
|
|
||||||
'url': url.startsWith('//') ? url.split('//').last : url,
|
|
||||||
'title': title,
|
|
||||||
'id': number,
|
|
||||||
'dynamicType': url.split('//').last.split('/')[1]
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Get.toNamed(
|
|
||||||
'/webview',
|
|
||||||
parameters: {
|
|
||||||
'url': 'https:$url',
|
|
||||||
'type': 'note',
|
|
||||||
'pageTitle': title
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 'DYNAMIC_TYPE_PGC':
|
case 'DYNAMIC_TYPE_PGC':
|
||||||
print('番剧');
|
print('番剧');
|
||||||
@ -234,7 +214,7 @@ class DynamicsController extends GetxController {
|
|||||||
arguments: {
|
arguments: {
|
||||||
'pic': pic,
|
'pic': pic,
|
||||||
'heroTag': heroTag,
|
'heroTag': heroTag,
|
||||||
'videoType': SearchType.media_bangumi,
|
// 'videoType': SearchType.media_bangumi,
|
||||||
'bangumiItem': res['data'],
|
'bangumiItem': res['data'],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/http/reply.dart';
|
import 'package:pilipala/http/reply.dart';
|
||||||
@ -18,7 +17,6 @@ class DynamicDetailController extends GetxController {
|
|||||||
RxString noMore = ''.obs;
|
RxString noMore = ''.obs;
|
||||||
RxList<ReplyItemModel> replyList = [ReplyItemModel()].obs;
|
RxList<ReplyItemModel> replyList = [ReplyItemModel()].obs;
|
||||||
RxInt acount = 0.obs;
|
RxInt acount = 0.obs;
|
||||||
final ScrollController scrollController = ScrollController();
|
|
||||||
|
|
||||||
ReplySortType _sortType = ReplySortType.time;
|
ReplySortType _sortType = ReplySortType.time;
|
||||||
RxString sortTypeTitle = ReplySortType.time.titles.obs;
|
RxString sortTypeTitle = ReplySortType.time.titles.obs;
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:easy_debounce/easy_throttle.dart';
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/skeleton/video_reply.dart';
|
import 'package:pilipala/common/skeleton/video_reply.dart';
|
||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
@ -10,10 +9,7 @@ import 'package:pilipala/models/common/reply_type.dart';
|
|||||||
import 'package:pilipala/pages/dynamics/deatil/index.dart';
|
import 'package:pilipala/pages/dynamics/deatil/index.dart';
|
||||||
import 'package:pilipala/pages/dynamics/widgets/author_panel.dart';
|
import 'package:pilipala/pages/dynamics/widgets/author_panel.dart';
|
||||||
import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart';
|
import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart';
|
||||||
import 'package:pilipala/pages/video/detail/replyNew/index.dart';
|
|
||||||
import 'package:pilipala/pages/video/detail/replyReply/index.dart';
|
import 'package:pilipala/pages/video/detail/replyReply/index.dart';
|
||||||
import 'package:pilipala/utils/feed_back.dart';
|
|
||||||
import 'package:pilipala/utils/id_utils.dart';
|
|
||||||
|
|
||||||
import '../widgets/dynamic_panel.dart';
|
import '../widgets/dynamic_panel.dart';
|
||||||
|
|
||||||
@ -25,18 +21,15 @@ class DynamicDetailPage extends StatefulWidget {
|
|||||||
State<DynamicDetailPage> createState() => _DynamicDetailPageState();
|
State<DynamicDetailPage> createState() => _DynamicDetailPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DynamicDetailPageState extends State<DynamicDetailPage>
|
class _DynamicDetailPageState extends State<DynamicDetailPage> {
|
||||||
with TickerProviderStateMixin {
|
late DynamicDetailController? _dynamicDetailController;
|
||||||
late DynamicDetailController _dynamicDetailController;
|
|
||||||
late AnimationController fabAnimationCtr;
|
|
||||||
Future? _futureBuilderFuture;
|
Future? _futureBuilderFuture;
|
||||||
late StreamController<bool> titleStreamC; // appBar title
|
late StreamController<bool> titleStreamC; // appBar title
|
||||||
late ScrollController scrollController;
|
final ScrollController scrollController = ScrollController();
|
||||||
bool _visibleTitle = false;
|
bool _visibleTitle = false;
|
||||||
String? action;
|
String? action;
|
||||||
// 回复类型
|
// 回复类型
|
||||||
late int type;
|
late int type;
|
||||||
bool _isFabVisible = true;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -57,30 +50,37 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
}
|
}
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
int commentType = 11;
|
int commentType = Get.arguments['item'].basic!['comment_type'] ?? 11;
|
||||||
try {
|
|
||||||
commentType = Get.arguments['item'].basic!['comment_type'];
|
|
||||||
} catch (_) {}
|
|
||||||
type = (commentType == 0) ? 11 : commentType;
|
type = (commentType == 0) ? 11 : commentType;
|
||||||
|
|
||||||
action =
|
action =
|
||||||
Get.arguments.containsKey('action') ? Get.arguments['action'] : null;
|
Get.arguments.containsKey('action') ? Get.arguments['action'] : null;
|
||||||
_dynamicDetailController =
|
_dynamicDetailController =
|
||||||
Get.put(DynamicDetailController(oid, type), tag: oid.toString());
|
Get.put(DynamicDetailController(oid, type), tag: oid.toString());
|
||||||
_futureBuilderFuture = _dynamicDetailController.queryReplyList();
|
_futureBuilderFuture = _dynamicDetailController!.queryReplyList();
|
||||||
titleStreamC = StreamController<bool>();
|
titleStreamC = StreamController<bool>();
|
||||||
|
scrollController.addListener(_listen);
|
||||||
if (action == 'comment') {
|
if (action == 'comment') {
|
||||||
_visibleTitle = true;
|
_visibleTitle = true;
|
||||||
titleStreamC.add(true);
|
titleStreamC.add(true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fabAnimationCtr = AnimationController(
|
void _listen() async {
|
||||||
vsync: this,
|
if (scrollController.position.pixels >=
|
||||||
duration: const Duration(milliseconds: 300),
|
scrollController.position.maxScrollExtent - 300) {
|
||||||
);
|
EasyThrottle.throttle('replylist', const Duration(seconds: 2), () {
|
||||||
fabAnimationCtr.forward();
|
_dynamicDetailController!.queryReplyList(reqType: 'onLoad');
|
||||||
// 滚动事件监听
|
});
|
||||||
scrollListener();
|
}
|
||||||
|
|
||||||
|
if (scrollController.offset > 55 && !_visibleTitle) {
|
||||||
|
_visibleTitle = true;
|
||||||
|
titleStreamC.add(true);
|
||||||
|
} else if (scrollController.offset <= 55 && _visibleTitle) {
|
||||||
|
_visibleTitle = false;
|
||||||
|
titleStreamC.add(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void replyReply(replyItem) {
|
void replyReply(replyItem) {
|
||||||
@ -107,58 +107,9 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void scrollListener() {
|
|
||||||
scrollController = _dynamicDetailController.scrollController;
|
|
||||||
scrollController.addListener(
|
|
||||||
() {
|
|
||||||
// 分页加载
|
|
||||||
if (scrollController.position.pixels >=
|
|
||||||
scrollController.position.maxScrollExtent - 300) {
|
|
||||||
EasyThrottle.throttle('replylist', const Duration(seconds: 2), () {
|
|
||||||
_dynamicDetailController.queryReplyList(reqType: 'onLoad');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 标题
|
|
||||||
if (scrollController.offset > 55 && !_visibleTitle) {
|
|
||||||
_visibleTitle = true;
|
|
||||||
titleStreamC.add(true);
|
|
||||||
} else if (scrollController.offset <= 55 && _visibleTitle) {
|
|
||||||
_visibleTitle = false;
|
|
||||||
titleStreamC.add(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fab按钮
|
|
||||||
final ScrollDirection direction =
|
|
||||||
scrollController.position.userScrollDirection;
|
|
||||||
if (direction == ScrollDirection.forward) {
|
|
||||||
_showFab();
|
|
||||||
} else if (direction == ScrollDirection.reverse) {
|
|
||||||
_hideFab();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showFab() {
|
|
||||||
if (!_isFabVisible) {
|
|
||||||
_isFabVisible = true;
|
|
||||||
fabAnimationCtr.forward();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _hideFab() {
|
|
||||||
if (_isFabVisible) {
|
|
||||||
_isFabVisible = false;
|
|
||||||
fabAnimationCtr.reverse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
scrollController.removeListener(() {});
|
scrollController.removeListener(() {});
|
||||||
fabAnimationCtr.dispose();
|
|
||||||
scrollController.dispose();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +128,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
return AnimatedOpacity(
|
return AnimatedOpacity(
|
||||||
opacity: snapshot.data ? 1 : 0,
|
opacity: snapshot.data ? 1 : 0,
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
child: AuthorPanel(item: _dynamicDetailController.item),
|
child: author(_dynamicDetailController!.item, context),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -185,206 +136,155 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
|||||||
),
|
),
|
||||||
body: RefreshIndicator(
|
body: RefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
await _dynamicDetailController.queryReplyList();
|
await _dynamicDetailController!.queryReplyList();
|
||||||
},
|
},
|
||||||
child: Stack(
|
child: CustomScrollView(
|
||||||
children: [
|
controller: scrollController,
|
||||||
CustomScrollView(
|
slivers: [
|
||||||
controller: scrollController,
|
if (action != 'comment')
|
||||||
slivers: [
|
SliverToBoxAdapter(
|
||||||
if (action != 'comment')
|
child: DynamicPanel(
|
||||||
SliverToBoxAdapter(
|
item: _dynamicDetailController!.item,
|
||||||
child: DynamicPanel(
|
source: 'detail',
|
||||||
item: _dynamicDetailController.item,
|
),
|
||||||
source: 'detail',
|
),
|
||||||
|
SliverPersistentHeader(
|
||||||
|
delegate: _MySliverPersistentHeaderDelegate(
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
border: Border(
|
||||||
|
top: BorderSide(
|
||||||
|
width: 0.6,
|
||||||
|
color: Theme.of(context).dividerColor.withOpacity(0.05),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverPersistentHeader(
|
height: 45,
|
||||||
delegate: _MySliverPersistentHeaderDelegate(
|
padding: const EdgeInsets.only(left: 12, right: 6),
|
||||||
child: Container(
|
child: Row(
|
||||||
decoration: BoxDecoration(
|
children: [
|
||||||
color: Theme.of(context).colorScheme.surface,
|
Obx(
|
||||||
border: Border(
|
() => AnimatedSwitcher(
|
||||||
top: BorderSide(
|
duration: const Duration(milliseconds: 400),
|
||||||
width: 0.6,
|
transitionBuilder:
|
||||||
color: Theme.of(context)
|
(Widget child, Animation<double> animation) {
|
||||||
.dividerColor
|
return ScaleTransition(
|
||||||
.withOpacity(0.05),
|
scale: animation, child: child);
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
'${_dynamicDetailController!.acount.value}',
|
||||||
|
key: ValueKey<int>(
|
||||||
|
_dynamicDetailController!.acount.value),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
height: 45,
|
const Text('条回复'),
|
||||||
padding: const EdgeInsets.only(left: 12, right: 6),
|
const Spacer(),
|
||||||
child: Row(
|
SizedBox(
|
||||||
children: [
|
height: 35,
|
||||||
Obx(
|
child: TextButton.icon(
|
||||||
() => AnimatedSwitcher(
|
onPressed: () =>
|
||||||
duration: const Duration(milliseconds: 400),
|
_dynamicDetailController!.queryBySort(),
|
||||||
transitionBuilder:
|
icon: const Icon(Icons.sort, size: 16),
|
||||||
(Widget child, Animation<double> animation) {
|
label: Obx(() => Text(
|
||||||
return ScaleTransition(
|
_dynamicDetailController!.sortTypeLabel.value,
|
||||||
scale: animation, child: child);
|
style: const TextStyle(fontSize: 13),
|
||||||
},
|
)),
|
||||||
child: Text(
|
),
|
||||||
'${_dynamicDetailController.acount.value}',
|
)
|
||||||
key: ValueKey<int>(
|
],
|
||||||
_dynamicDetailController.acount.value),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Text('条回复'),
|
|
||||||
const Spacer(),
|
|
||||||
SizedBox(
|
|
||||||
height: 35,
|
|
||||||
child: TextButton.icon(
|
|
||||||
onPressed: () =>
|
|
||||||
_dynamicDetailController.queryBySort(),
|
|
||||||
icon: const Icon(Icons.sort, size: 16),
|
|
||||||
label: Obx(() => Text(
|
|
||||||
_dynamicDetailController
|
|
||||||
.sortTypeLabel.value,
|
|
||||||
style: const TextStyle(fontSize: 13),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
pinned: true,
|
|
||||||
),
|
|
||||||
FutureBuilder(
|
|
||||||
future: _futureBuilderFuture,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
|
||||||
Map data = snapshot.data as Map;
|
|
||||||
if (snapshot.data['status']) {
|
|
||||||
// 请求成功
|
|
||||||
return Obx(
|
|
||||||
() => _dynamicDetailController.replyList.isEmpty &&
|
|
||||||
_dynamicDetailController.isLoadingMore
|
|
||||||
? SliverList(
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
|
||||||
(context, index) {
|
|
||||||
return const VideoReplySkeleton();
|
|
||||||
}, childCount: 8),
|
|
||||||
)
|
|
||||||
: SliverList(
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
|
||||||
(context, index) {
|
|
||||||
if (index ==
|
|
||||||
_dynamicDetailController
|
|
||||||
.replyList.length) {
|
|
||||||
return Container(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
bottom: MediaQuery.of(context)
|
|
||||||
.padding
|
|
||||||
.bottom),
|
|
||||||
height: MediaQuery.of(context)
|
|
||||||
.padding
|
|
||||||
.bottom +
|
|
||||||
100,
|
|
||||||
child: Center(
|
|
||||||
child: Obx(
|
|
||||||
() => Text(
|
|
||||||
_dynamicDetailController
|
|
||||||
.noMore.value,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return ReplyItem(
|
|
||||||
replyItem: _dynamicDetailController
|
|
||||||
.replyList[index],
|
|
||||||
showReplyRow: true,
|
|
||||||
replyLevel: '1',
|
|
||||||
replyReply: (replyItem) =>
|
|
||||||
replyReply(replyItem),
|
|
||||||
replyType: ReplyType.values[type],
|
|
||||||
addReply: (replyItem) {
|
|
||||||
_dynamicDetailController
|
|
||||||
.replyList[index].replies!
|
|
||||||
.add(replyItem);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
childCount: _dynamicDetailController
|
|
||||||
.replyList.length +
|
|
||||||
1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// 请求错误
|
|
||||||
return HttpError(
|
|
||||||
errMsg: data['msg'],
|
|
||||||
fn: () => setState(() {}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 骨架屏
|
|
||||||
return SliverList(
|
|
||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
|
||||||
return const VideoReplySkeleton();
|
|
||||||
}, childCount: 8),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
bottom: MediaQuery.of(context).padding.bottom + 14,
|
|
||||||
right: 14,
|
|
||||||
child: SlideTransition(
|
|
||||||
position: Tween<Offset>(
|
|
||||||
begin: const Offset(0, 2),
|
|
||||||
end: const Offset(0, 0),
|
|
||||||
).animate(CurvedAnimation(
|
|
||||||
parent: fabAnimationCtr,
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
)),
|
|
||||||
child: FloatingActionButton(
|
|
||||||
heroTag: null,
|
|
||||||
onPressed: () {
|
|
||||||
feedBack();
|
|
||||||
showModalBottomSheet(
|
|
||||||
context: context,
|
|
||||||
isScrollControlled: true,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return VideoReplyNewDialog(
|
|
||||||
oid: _dynamicDetailController.oid ??
|
|
||||||
IdUtils.bv2av(Get.parameters['bvid']!),
|
|
||||||
root: 0,
|
|
||||||
parent: 0,
|
|
||||||
replyType: ReplyType.values[type],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
).then(
|
|
||||||
(value) => {
|
|
||||||
// 完成评论,数据添加
|
|
||||||
if (value != null && value['data'] != null)
|
|
||||||
{
|
|
||||||
_dynamicDetailController.replyList
|
|
||||||
.add(value['data']),
|
|
||||||
_dynamicDetailController.acount.value++
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
tooltip: '评论动态',
|
|
||||||
child: const Icon(Icons.reply),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
pinned: true,
|
||||||
),
|
),
|
||||||
|
FutureBuilder(
|
||||||
|
future: _futureBuilderFuture,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
|
Map data = snapshot.data as Map;
|
||||||
|
if (snapshot.data['status']) {
|
||||||
|
// 请求成功
|
||||||
|
return Obx(
|
||||||
|
() => _dynamicDetailController!.replyList.isEmpty &&
|
||||||
|
_dynamicDetailController!.isLoadingMore
|
||||||
|
? SliverList(
|
||||||
|
delegate:
|
||||||
|
SliverChildBuilderDelegate((context, index) {
|
||||||
|
return const VideoReplySkeleton();
|
||||||
|
}, childCount: 8),
|
||||||
|
)
|
||||||
|
: SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(context, index) {
|
||||||
|
if (index ==
|
||||||
|
_dynamicDetailController!
|
||||||
|
.replyList.length) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
bottom: MediaQuery.of(context)
|
||||||
|
.padding
|
||||||
|
.bottom),
|
||||||
|
height: MediaQuery.of(context)
|
||||||
|
.padding
|
||||||
|
.bottom +
|
||||||
|
100,
|
||||||
|
child: Center(
|
||||||
|
child: Obx(
|
||||||
|
() => Text(
|
||||||
|
_dynamicDetailController!
|
||||||
|
.noMore.value,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return ReplyItem(
|
||||||
|
replyItem: _dynamicDetailController!
|
||||||
|
.replyList[index],
|
||||||
|
showReplyRow: true,
|
||||||
|
replyLevel: '1',
|
||||||
|
replyReply: (replyItem) =>
|
||||||
|
replyReply(replyItem),
|
||||||
|
replyType: ReplyType.values[type],
|
||||||
|
addReply: (replyItem) {
|
||||||
|
_dynamicDetailController!
|
||||||
|
.replyList[index].replies!
|
||||||
|
.add(replyItem);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
childCount:
|
||||||
|
_dynamicDetailController!.replyList.length +
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// 请求错误
|
||||||
|
return HttpError(
|
||||||
|
errMsg: data['msg'],
|
||||||
|
fn: () => setState(() {}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 骨架屏
|
||||||
|
return SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
|
return const VideoReplySkeleton();
|
||||||
|
}, childCount: 8),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -231,15 +231,6 @@ class _DynamicsPageState extends State<DynamicsPage>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(
|
|
||||||
child: Container(
|
|
||||||
height: 6,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.onInverseSurface
|
|
||||||
.withOpacity(0.5),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
|
|||||||
@ -1,8 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:pilipala/http/search.dart';
|
|
||||||
|
|
||||||
/// TODO 点击跳转
|
/// TODO 点击跳转
|
||||||
Widget addWidget(item, context, type, {floor = 1}) {
|
Widget addWidget(item, context, type, {floor = 1}) {
|
||||||
@ -22,27 +19,8 @@ Widget addWidget(item, context, type, {floor = 1}) {
|
|||||||
: Theme.of(context).colorScheme.background;
|
: Theme.of(context).colorScheme.background;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'ADDITIONAL_TYPE_UGC':
|
case 'ADDITIONAL_TYPE_UGC':
|
||||||
// 转发的投稿
|
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () async {
|
onTap: () {},
|
||||||
String text = dynamicProperty[type].jumpUrl;
|
|
||||||
RegExp bvRegex = RegExp(r'BV[0-9A-Za-z]{10}', caseSensitive: false);
|
|
||||||
Iterable<Match> matches = bvRegex.allMatches(text);
|
|
||||||
if (matches.isNotEmpty) {
|
|
||||||
Match match = matches.first;
|
|
||||||
String bvid = match.group(0)!;
|
|
||||||
String cover = dynamicProperty[type].cover;
|
|
||||||
try {
|
|
||||||
int cid = await SearchHttp.ab2c(bvid: bvid);
|
|
||||||
Get.toNamed('/video?bvid=$bvid&cid=$cid',
|
|
||||||
arguments: {'pic': cover, 'heroTag': bvid});
|
|
||||||
} catch (err) {
|
|
||||||
SmartDialog.showToast(err.toString());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
print("No match found.");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Container(
|
child: Container(
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.only(left: 12, top: 8, right: 12, bottom: 8),
|
const EdgeInsets.only(left: 12, top: 8, right: 12, bottom: 8),
|
||||||
@ -83,111 +61,101 @@ Widget addWidget(item, context, type, {floor = 1}) {
|
|||||||
);
|
);
|
||||||
case 'ADDITIONAL_TYPE_RESERVE':
|
case 'ADDITIONAL_TYPE_RESERVE':
|
||||||
return dynamicProperty[type].state != -1
|
return dynamicProperty[type].state != -1
|
||||||
? dynamicProperty[type].title != null
|
? Padding(
|
||||||
? Padding(
|
padding: const EdgeInsets.only(top: 8),
|
||||||
padding: const EdgeInsets.only(top: 8),
|
child: InkWell(
|
||||||
child: InkWell(
|
onTap: () {},
|
||||||
onTap: () {},
|
child: Container(
|
||||||
child: Container(
|
width: double.infinity,
|
||||||
width: double.infinity,
|
padding: const EdgeInsets.only(
|
||||||
padding: const EdgeInsets.only(
|
left: 12, top: 10, right: 12, bottom: 10),
|
||||||
left: 12, top: 10, right: 12, bottom: 10),
|
color: bgColor,
|
||||||
color: bgColor,
|
child: Column(
|
||||||
child: Column(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
children: [
|
||||||
children: [
|
Text(
|
||||||
Text(
|
dynamicProperty[type].title,
|
||||||
dynamicProperty[type].title,
|
maxLines: 1,
|
||||||
maxLines: 1,
|
overflow: TextOverflow.ellipsis,
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 1),
|
|
||||||
Text.rich(
|
|
||||||
TextSpan(
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.outline,
|
|
||||||
fontSize: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.labelMedium!
|
|
||||||
.fontSize),
|
|
||||||
children: [
|
|
||||||
if (dynamicProperty[type].desc1 != null)
|
|
||||||
TextSpan(
|
|
||||||
text:
|
|
||||||
dynamicProperty[type].desc1['text']),
|
|
||||||
const TextSpan(text: ' '),
|
|
||||||
if (dynamicProperty[type].desc2 != null)
|
|
||||||
TextSpan(
|
|
||||||
text:
|
|
||||||
dynamicProperty[type].desc2['text']),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
// TextButton(onPressed: () {}, child: Text('123'))
|
const SizedBox(height: 1),
|
||||||
),
|
Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
fontSize: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.labelMedium!
|
||||||
|
.fontSize),
|
||||||
|
children: [
|
||||||
|
TextSpan(text: dynamicProperty[type].desc1['text']),
|
||||||
|
const TextSpan(text: ' '),
|
||||||
|
TextSpan(text: dynamicProperty[type].desc2['text']),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
)
|
// TextButton(onPressed: () {}, child: Text('123'))
|
||||||
: const SizedBox()
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
: const SizedBox();
|
: const SizedBox();
|
||||||
case 'ADDITIONAL_TYPE_GOODS':
|
case 'ADDITIONAL_TYPE_GOODS':
|
||||||
// 商品
|
return Padding(
|
||||||
return const SizedBox();
|
padding: const EdgeInsets.only(top: 6),
|
||||||
// return Padding(
|
child: InkWell(
|
||||||
// padding: const EdgeInsets.only(top: 6),
|
onTap: () {},
|
||||||
// child: InkWell(
|
child: Container(
|
||||||
// onTap: () {},
|
padding:
|
||||||
// child: Container(
|
const EdgeInsets.only(left: 12, top: 8, right: 12, bottom: 8),
|
||||||
// padding:
|
decoration: BoxDecoration(
|
||||||
// const EdgeInsets.only(left: 12, top: 8, right: 12, bottom: 8),
|
color: bgColor,
|
||||||
// decoration: BoxDecoration(
|
borderRadius: const BorderRadius.all(Radius.circular(6)),
|
||||||
// color: bgColor,
|
),
|
||||||
// borderRadius: const BorderRadius.all(Radius.circular(6)),
|
child: Row(
|
||||||
// ),
|
children: [
|
||||||
// child: Row(
|
NetworkImgLayer(
|
||||||
// children: [
|
width: 75,
|
||||||
// NetworkImgLayer(
|
height: 75,
|
||||||
// width: 75,
|
src: dynamicProperty[type].items.first.cover,
|
||||||
// height: 75,
|
),
|
||||||
// src: dynamicProperty[type].items.first.cover,
|
const SizedBox(width: 10),
|
||||||
// ),
|
Expanded(
|
||||||
// const SizedBox(width: 10),
|
child: Column(
|
||||||
// Expanded(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
// child: Column(
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
children: [
|
||||||
// mainAxisAlignment: MainAxisAlignment.start,
|
Text(
|
||||||
// children: [
|
dynamicProperty[type].items.first.name,
|
||||||
// Text(
|
maxLines: 1,
|
||||||
// dynamicProperty[type].items.first.name,
|
overflow: TextOverflow.ellipsis,
|
||||||
// maxLines: 1,
|
),
|
||||||
// overflow: TextOverflow.ellipsis,
|
Text(
|
||||||
// ),
|
dynamicProperty[type].items.first.brief,
|
||||||
// Text(
|
maxLines: 1,
|
||||||
// dynamicProperty[type].items.first.brief,
|
style: TextStyle(
|
||||||
// maxLines: 1,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
// style: TextStyle(
|
fontSize: Theme.of(context)
|
||||||
// color: Theme.of(context).colorScheme.outline,
|
.textTheme
|
||||||
// fontSize: Theme.of(context)
|
.labelMedium!
|
||||||
// .textTheme
|
.fontSize,
|
||||||
// .labelMedium!
|
),
|
||||||
// .fontSize,
|
),
|
||||||
// ),
|
const SizedBox(height: 2),
|
||||||
// ),
|
Text(
|
||||||
// const SizedBox(height: 2),
|
dynamicProperty[type].items.first.price,
|
||||||
// Text(
|
style: TextStyle(
|
||||||
// dynamicProperty[type].items.first.price,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
// style: TextStyle(
|
),
|
||||||
// color: Theme.of(context).colorScheme.primary,
|
),
|
||||||
// ),
|
],
|
||||||
// ),
|
),
|
||||||
// ],
|
),
|
||||||
// ),
|
],
|
||||||
// ),
|
),
|
||||||
// ],
|
),
|
||||||
// ),
|
));
|
||||||
// ),
|
|
||||||
// ),);
|
|
||||||
case 'ADDITIONAL_TYPE_MATCH':
|
case 'ADDITIONAL_TYPE_MATCH':
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
case 'ADDITIONAL_TYPE_COMMON':
|
case 'ADDITIONAL_TYPE_COMMON':
|
||||||
|
|||||||
@ -1,163 +1,65 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:pilipala/http/user.dart';
|
|
||||||
import 'package:pilipala/utils/feed_back.dart';
|
import 'package:pilipala/utils/feed_back.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
class AuthorPanel extends StatelessWidget {
|
Widget author(item, context) {
|
||||||
final dynamic item;
|
String heroTag = Utils.makeHeroTag(item.modules.moduleAuthor.mid);
|
||||||
const AuthorPanel({super.key, required this.item});
|
return Row(
|
||||||
|
children: [
|
||||||
@override
|
GestureDetector(
|
||||||
Widget build(BuildContext context) {
|
onTap: () {
|
||||||
String heroTag = Utils.makeHeroTag(item.modules.moduleAuthor.mid);
|
feedBack();
|
||||||
return Row(
|
Get.toNamed(
|
||||||
children: [
|
'/member?mid=${item.modules.moduleAuthor.mid}',
|
||||||
GestureDetector(
|
arguments: {
|
||||||
onTap: () {
|
'face': item.modules.moduleAuthor.face,
|
||||||
// 番剧
|
'heroTag': heroTag
|
||||||
if (item.modules.moduleAuthor.type == 'AUTHOR_TYPE_PGC') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
feedBack();
|
|
||||||
Get.toNamed(
|
|
||||||
'/member?mid=${item.modules.moduleAuthor.mid}',
|
|
||||||
arguments: {
|
|
||||||
'face': item.modules.moduleAuthor.face,
|
|
||||||
'heroTag': heroTag
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Hero(
|
|
||||||
tag: heroTag,
|
|
||||||
child: NetworkImgLayer(
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
type: 'avatar',
|
|
||||||
src: item.modules.moduleAuthor.face,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
item.modules.moduleAuthor.name,
|
|
||||||
style: TextStyle(
|
|
||||||
color: item.modules.moduleAuthor!.vip != null &&
|
|
||||||
item.modules.moduleAuthor!.vip['status'] > 0
|
|
||||||
? const Color.fromARGB(255, 251, 100, 163)
|
|
||||||
: Theme.of(context).colorScheme.onBackground,
|
|
||||||
fontSize: Theme.of(context).textTheme.titleSmall!.fontSize,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
DefaultTextStyle.merge(
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.outline,
|
|
||||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Text(item.modules.moduleAuthor.pubTime),
|
|
||||||
if (item.modules.moduleAuthor.pubTime != '' &&
|
|
||||||
item.modules.moduleAuthor.pubAction != '')
|
|
||||||
const Text(' '),
|
|
||||||
Text(item.modules.moduleAuthor.pubAction),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
if (item.type == 'DYNAMIC_TYPE_AV')
|
|
||||||
SizedBox(
|
|
||||||
width: 32,
|
|
||||||
height: 32,
|
|
||||||
child: IconButton(
|
|
||||||
style: ButtonStyle(
|
|
||||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
showModalBottomSheet(
|
|
||||||
context: context,
|
|
||||||
useRootNavigator: true,
|
|
||||||
isScrollControlled: true,
|
|
||||||
builder: (context) {
|
|
||||||
return MorePanel(item: item);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.more_vert_outlined, size: 18),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MorePanel extends StatelessWidget {
|
|
||||||
final dynamic item;
|
|
||||||
const MorePanel({super.key, required this.item});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
|
|
||||||
// clipBehavior: Clip.hardEdge,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
InkWell(
|
|
||||||
onTap: () => Get.back(),
|
|
||||||
child: Container(
|
|
||||||
height: 35,
|
|
||||||
padding: const EdgeInsets.only(bottom: 2),
|
|
||||||
child: Center(
|
|
||||||
child: Container(
|
|
||||||
width: 32,
|
|
||||||
height: 3,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.outline,
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(3))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
onTap: () async {
|
|
||||||
try {
|
|
||||||
String bvid = item.modules.moduleDynamic.major.archive.bvid;
|
|
||||||
var res = await UserHttp.toViewLater(bvid: bvid);
|
|
||||||
SmartDialog.showToast(res['msg']);
|
|
||||||
Get.back();
|
|
||||||
} catch (err) {
|
|
||||||
SmartDialog.showToast('出错了:${err.toString()}');
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
minLeadingWidth: 0,
|
);
|
||||||
// dense: true,
|
},
|
||||||
leading: const Icon(Icons.watch_later_outlined, size: 19),
|
child: Hero(
|
||||||
title: Text(
|
tag: heroTag,
|
||||||
'稍后再看',
|
child: NetworkImgLayer(
|
||||||
style: Theme.of(context).textTheme.titleSmall,
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
type: 'avatar',
|
||||||
|
src: item.modules.moduleAuthor.face,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
item.modules.moduleAuthor.name,
|
||||||
|
style: TextStyle(
|
||||||
|
color: item.modules.moduleAuthor!.vip != null &&
|
||||||
|
item.modules.moduleAuthor!.vip['status'] > 0
|
||||||
|
? const Color.fromARGB(255, 251, 100, 163)
|
||||||
|
: Theme.of(context).colorScheme.onBackground,
|
||||||
|
fontSize: Theme.of(context).textTheme.titleSmall!.fontSize,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Divider(thickness: 0.1, height: 1),
|
DefaultTextStyle.merge(
|
||||||
ListTile(
|
style: TextStyle(
|
||||||
onTap: () => Get.back(),
|
color: Theme.of(context).colorScheme.outline,
|
||||||
minLeadingWidth: 0,
|
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||||
dense: true,
|
|
||||||
title: Text(
|
|
||||||
'取消',
|
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
),
|
||||||
),
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(item.modules.moduleAuthor.pubTime),
|
||||||
|
if (item.modules.moduleAuthor.pubTime != '' &&
|
||||||
|
item.modules.moduleAuthor.pubAction != '')
|
||||||
|
const Text(' '),
|
||||||
|
Text(item.modules.moduleAuthor.pubAction),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
],
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,183 +1,42 @@
|
|||||||
// 内容
|
// 内容
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
|
||||||
import 'package:pilipala/models/dynamics/result.dart';
|
|
||||||
import 'package:pilipala/pages/preview/index.dart';
|
|
||||||
|
|
||||||
import 'rich_node_panel.dart';
|
import 'rich_node_panel.dart';
|
||||||
|
|
||||||
// ignore: must_be_immutable
|
Widget content(item, context, source) {
|
||||||
class Content extends StatefulWidget {
|
TextStyle authorStyle =
|
||||||
dynamic item;
|
TextStyle(color: Theme.of(context).colorScheme.primary);
|
||||||
String? source;
|
return Container(
|
||||||
Content({
|
width: double.infinity,
|
||||||
super.key,
|
padding: const EdgeInsets.fromLTRB(12, 0, 12, 6),
|
||||||
this.item,
|
child: Column(
|
||||||
this.source,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
});
|
children: [
|
||||||
|
if (item.modules.moduleDynamic.topic != null) ...[
|
||||||
@override
|
GestureDetector(
|
||||||
State<Content> createState() => _ContentState();
|
child: Text(
|
||||||
}
|
'#${item.modules.moduleDynamic.topic.name}',
|
||||||
|
style: authorStyle,
|
||||||
class _ContentState extends State<Content> {
|
|
||||||
late bool hasPics;
|
|
||||||
List<OpusPicsModel> pics = [];
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
hasPics = widget.item.modules.moduleDynamic.major != null &&
|
|
||||||
widget.item.modules.moduleDynamic.major.opus != null &&
|
|
||||||
widget.item.modules.moduleDynamic.major.opus.pics.isNotEmpty;
|
|
||||||
if (hasPics) {
|
|
||||||
pics = widget.item.modules.moduleDynamic.major.opus.pics;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InlineSpan picsNodes() {
|
|
||||||
List<InlineSpan> spanChilds = [];
|
|
||||||
int len = pics.length;
|
|
||||||
List<String> picList = [];
|
|
||||||
|
|
||||||
if (len == 1) {
|
|
||||||
OpusPicsModel pictureItem = pics.first;
|
|
||||||
picList.add(pictureItem.url!);
|
|
||||||
spanChilds.add(const TextSpan(text: '\n'));
|
|
||||||
spanChilds.add(
|
|
||||||
WidgetSpan(
|
|
||||||
child: LayoutBuilder(
|
|
||||||
builder: (context, BoxConstraints box) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
showDialog(
|
|
||||||
useSafeArea: false,
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return ImagePreview(initialPage: 0, imgList: picList);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 4),
|
|
||||||
child: NetworkImgLayer(
|
|
||||||
src: pictureItem.url,
|
|
||||||
width: box.maxWidth / 2,
|
|
||||||
height: box.maxWidth *
|
|
||||||
0.5 *
|
|
||||||
(pictureItem.height != null && pictureItem.width != null
|
|
||||||
? pictureItem.height! / pictureItem.width!
|
|
||||||
: 1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (len > 1) {
|
|
||||||
List<Widget> list = [];
|
|
||||||
for (var i = 0; i < len; i++) {
|
|
||||||
picList.add(pics[i].url!);
|
|
||||||
list.add(
|
|
||||||
LayoutBuilder(
|
|
||||||
builder: (context, BoxConstraints box) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
showDialog(
|
|
||||||
useSafeArea: false,
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return ImagePreview(initialPage: i, imgList: picList);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: NetworkImgLayer(
|
|
||||||
src: pics[i].url,
|
|
||||||
width: box.maxWidth,
|
|
||||||
height: box.maxWidth,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
spanChilds.add(
|
|
||||||
WidgetSpan(
|
|
||||||
child: LayoutBuilder(
|
|
||||||
builder: (context, BoxConstraints box) {
|
|
||||||
double maxWidth = box.maxWidth;
|
|
||||||
double crossCount = len < 3 ? 2 : 3;
|
|
||||||
double height = maxWidth /
|
|
||||||
crossCount *
|
|
||||||
(len % crossCount == 0
|
|
||||||
? len ~/ crossCount
|
|
||||||
: len ~/ crossCount + 1) +
|
|
||||||
6;
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.only(top: 6),
|
|
||||||
height: height,
|
|
||||||
child: GridView.count(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
crossAxisCount: crossCount.toInt(),
|
|
||||||
mainAxisSpacing: 4.0,
|
|
||||||
crossAxisSpacing: 4.0,
|
|
||||||
childAspectRatio: 1,
|
|
||||||
children: list,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return TextSpan(
|
|
||||||
children: spanChilds,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
TextStyle authorStyle =
|
|
||||||
TextStyle(color: Theme.of(context).colorScheme.primary);
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
width: double.infinity,
|
|
||||||
padding: const EdgeInsets.fromLTRB(12, 0, 12, 6),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
if (widget.item.modules.moduleDynamic.topic != null) ...[
|
|
||||||
GestureDetector(
|
|
||||||
child: Text(
|
|
||||||
'#${widget.item.modules.moduleDynamic.topic.name}',
|
|
||||||
style: authorStyle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
IgnorePointer(
|
|
||||||
// 禁用SelectableRegion的触摸交互功能
|
|
||||||
ignoring: widget.source == 'detail' ? false : true,
|
|
||||||
child: SelectableRegion(
|
|
||||||
magnifierConfiguration: const TextMagnifierConfiguration(),
|
|
||||||
focusNode: FocusNode(),
|
|
||||||
selectionControls: MaterialTextSelectionControls(),
|
|
||||||
child: Text.rich(
|
|
||||||
/// fix 默认20px高度
|
|
||||||
style: const TextStyle(height: 0),
|
|
||||||
richNode(widget.item, context),
|
|
||||||
maxLines: widget.source == 'detail' ? 999 : 3,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (hasPics) ...[
|
|
||||||
Text.rich(picsNodes()),
|
|
||||||
]
|
|
||||||
],
|
],
|
||||||
),
|
IgnorePointer(
|
||||||
);
|
// 禁用SelectableRegion的触摸交互功能
|
||||||
}
|
ignoring: source == 'detail' ? false : true,
|
||||||
|
child: SelectableRegion(
|
||||||
|
magnifierConfiguration: const TextMagnifierConfiguration(),
|
||||||
|
focusNode: FocusNode(),
|
||||||
|
selectionControls: MaterialTextSelectionControls(),
|
||||||
|
child: Text.rich(
|
||||||
|
/// fix 默认20px高度
|
||||||
|
style: const TextStyle(height: 0),
|
||||||
|
richNode(item, context),
|
||||||
|
maxLines: source == 'detail' ? 999 : 3,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,11 +39,11 @@ class DynamicPanel extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 8),
|
padding: const EdgeInsets.fromLTRB(12, 12, 12, 8),
|
||||||
child: AuthorPanel(item: item),
|
child: author(item, context),
|
||||||
),
|
),
|
||||||
if (item!.modules!.moduleDynamic!.desc != null ||
|
if (item!.modules!.moduleDynamic!.desc != null ||
|
||||||
item!.modules!.moduleDynamic!.major != null)
|
item!.modules!.moduleDynamic!.major != null)
|
||||||
Content(item: item, source: source),
|
content(item, context, source),
|
||||||
forWard(item, context, _dynamicsController, source),
|
forWard(item, context, _dynamicsController, source),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
if (source == null) ActionPanel(item: item),
|
if (source == null) ActionPanel(item: item),
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
|
import 'package:pilipala/models/dynamics/result.dart';
|
||||||
|
import 'package:pilipala/pages/preview/index.dart';
|
||||||
|
|
||||||
// 富文本
|
// 富文本
|
||||||
InlineSpan richNode(item, context) {
|
InlineSpan richNode(item, context) {
|
||||||
@ -9,11 +11,13 @@ InlineSpan richNode(item, context) {
|
|||||||
TextStyle authorStyle =
|
TextStyle authorStyle =
|
||||||
TextStyle(color: Theme.of(context).colorScheme.primary);
|
TextStyle(color: Theme.of(context).colorScheme.primary);
|
||||||
List<InlineSpan> spanChilds = [];
|
List<InlineSpan> spanChilds = [];
|
||||||
|
String contentType = 'desc';
|
||||||
|
|
||||||
dynamic richTextNodes;
|
dynamic richTextNodes;
|
||||||
if (item.modules.moduleDynamic.desc != null) {
|
if (item.modules.moduleDynamic.desc != null) {
|
||||||
richTextNodes = item.modules.moduleDynamic.desc.richTextNodes;
|
richTextNodes = item.modules.moduleDynamic.desc.richTextNodes;
|
||||||
} else if (item.modules.moduleDynamic.major != null) {
|
} else if (item.modules.moduleDynamic.major != null) {
|
||||||
|
contentType = 'major';
|
||||||
// 动态页面 richTextNodes 层级可能与主页动态层级不同
|
// 动态页面 richTextNodes 层级可能与主页动态层级不同
|
||||||
richTextNodes =
|
richTextNodes =
|
||||||
item.modules.moduleDynamic.major.opus.summary.richTextNodes;
|
item.modules.moduleDynamic.major.opus.summary.richTextNodes;
|
||||||
@ -23,9 +27,8 @@ InlineSpan richNode(item, context) {
|
|||||||
} else {
|
} else {
|
||||||
for (var i in richTextNodes) {
|
for (var i in richTextNodes) {
|
||||||
/// fix 渲染专栏时内容会重复
|
/// fix 渲染专栏时内容会重复
|
||||||
// if (item.modules.moduleDynamic.major.opus.title == null &&
|
if (item.modules.moduleDynamic.major.opus.title == null &&
|
||||||
// i.type == 'RICH_TEXT_NODE_TYPE_TEXT') {
|
i.type == 'RICH_TEXT_NODE_TYPE_TEXT') {
|
||||||
if (i.type == 'RICH_TEXT_NODE_TYPE_TEXT') {
|
|
||||||
spanChilds.add(
|
spanChilds.add(
|
||||||
TextSpan(text: i.origText, style: const TextStyle(height: 1.65)));
|
TextSpan(text: i.origText, style: const TextStyle(height: 1.65)));
|
||||||
}
|
}
|
||||||
@ -106,18 +109,16 @@ InlineSpan richNode(item, context) {
|
|||||||
alignment: PlaceholderAlignment.middle,
|
alignment: PlaceholderAlignment.middle,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
try {
|
String dynamicId = item.basic['comment_id_str'];
|
||||||
String dynamicId = item.basic['comment_id_str'];
|
Get.toNamed(
|
||||||
Get.toNamed(
|
'/webview',
|
||||||
'/webview',
|
parameters: {
|
||||||
parameters: {
|
'url':
|
||||||
'url':
|
'https://t.bilibili.com/vote/h5/index/#/result?vote_id=${i.rid}&dynamic_id=$dynamicId&isWeb=1',
|
||||||
'https://t.bilibili.com/vote/h5/index/#/result?vote_id=${i.rid}&dynamic_id=$dynamicId&isWeb=1',
|
'type': 'vote',
|
||||||
'type': 'vote',
|
'pageTitle': '投票'
|
||||||
'pageTitle': '投票'
|
},
|
||||||
},
|
);
|
||||||
);
|
|
||||||
} catch (_) {}
|
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
'投票:${i.text}',
|
'投票:${i.text}',
|
||||||
@ -192,118 +193,116 @@ InlineSpan richNode(item, context) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if (contentType == 'major' &&
|
if (contentType == 'major' &&
|
||||||
// item.modules.moduleDynamic.major.opus.pics.isNotEmpty) {
|
item.modules.moduleDynamic.major.opus.pics.isNotEmpty) {
|
||||||
// // 图片可能跟其他widget重复渲染
|
// 图片可能跟其他widget重复渲染
|
||||||
// List<OpusPicsModel> pics = item.modules.moduleDynamic.major.opus.pics;
|
List<OpusPicsModel> pics = item.modules.moduleDynamic.major.opus.pics;
|
||||||
// int len = pics.length;
|
int len = pics.length;
|
||||||
// List<String> picList = [];
|
List<String> picList = [];
|
||||||
|
|
||||||
// if (len == 1) {
|
if (len == 1) {
|
||||||
// OpusPicsModel pictureItem = pics.first;
|
OpusPicsModel pictureItem = pics.first;
|
||||||
// picList.add(pictureItem.url!);
|
picList.add(pictureItem.url!);
|
||||||
// spanChilds.add(const TextSpan(text: '\n'));
|
spanChilds.add(const TextSpan(text: '\n'));
|
||||||
// spanChilds.add(
|
spanChilds.add(
|
||||||
// WidgetSpan(
|
WidgetSpan(
|
||||||
// child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
// builder: (context, BoxConstraints box) {
|
builder: (context, BoxConstraints box) {
|
||||||
// return GestureDetector(
|
return GestureDetector(
|
||||||
// onTap: () {
|
onTap: () {
|
||||||
// showDialog(
|
showDialog(
|
||||||
// useSafeArea: false,
|
useSafeArea: false,
|
||||||
// context: context,
|
context: context,
|
||||||
// builder: (context) {
|
builder: (context) {
|
||||||
// return ImagePreview(initialPage: 0, imgList: picList);
|
return ImagePreview(initialPage: 0, imgList: picList);
|
||||||
// },
|
},
|
||||||
// );
|
);
|
||||||
// },
|
},
|
||||||
// child: Padding(
|
child: Padding(
|
||||||
// padding: const EdgeInsets.only(top: 4),
|
padding: const EdgeInsets.only(top: 4),
|
||||||
// child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
// src: pictureItem.url,
|
src: pictureItem.url,
|
||||||
// width: box.maxWidth / 2,
|
width: box.maxWidth / 2,
|
||||||
// height: box.maxWidth *
|
height: box.maxWidth *
|
||||||
// 0.5 *
|
0.5 *
|
||||||
// (pictureItem.height != null &&
|
pictureItem.height! /
|
||||||
// pictureItem.width != null
|
pictureItem.width!,
|
||||||
// ? pictureItem.height! / pictureItem.width!
|
),
|
||||||
// : 1),
|
),
|
||||||
// ),
|
);
|
||||||
// ),
|
},
|
||||||
// );
|
),
|
||||||
// },
|
),
|
||||||
// ),
|
);
|
||||||
// ),
|
}
|
||||||
// );
|
if (len > 1) {
|
||||||
// }
|
List<Widget> list = [];
|
||||||
// if (len > 1) {
|
for (var i = 0; i < len; i++) {
|
||||||
// List<Widget> list = [];
|
picList.add(pics[i].url!);
|
||||||
// for (var i = 0; i < len; i++) {
|
list.add(
|
||||||
// picList.add(pics[i].url!);
|
LayoutBuilder(
|
||||||
// list.add(
|
builder: (context, BoxConstraints box) {
|
||||||
// LayoutBuilder(
|
return GestureDetector(
|
||||||
// builder: (context, BoxConstraints box) {
|
onTap: () {
|
||||||
// return GestureDetector(
|
showDialog(
|
||||||
// onTap: () {
|
useSafeArea: false,
|
||||||
// showDialog(
|
context: context,
|
||||||
// useSafeArea: false,
|
builder: (context) {
|
||||||
// context: context,
|
return ImagePreview(initialPage: i, imgList: picList);
|
||||||
// builder: (context) {
|
},
|
||||||
// return ImagePreview(initialPage: i, imgList: picList);
|
);
|
||||||
// },
|
},
|
||||||
// );
|
child: NetworkImgLayer(
|
||||||
// },
|
src: pics[i].url,
|
||||||
// child: NetworkImgLayer(
|
width: box.maxWidth,
|
||||||
// src: pics[i].url,
|
height: box.maxWidth,
|
||||||
// width: box.maxWidth,
|
),
|
||||||
// height: box.maxWidth,
|
);
|
||||||
// ),
|
},
|
||||||
// );
|
),
|
||||||
// },
|
);
|
||||||
// ),
|
}
|
||||||
// );
|
spanChilds.add(
|
||||||
// }
|
WidgetSpan(
|
||||||
// spanChilds.add(
|
child: LayoutBuilder(
|
||||||
// WidgetSpan(
|
builder: (context, BoxConstraints box) {
|
||||||
// child: LayoutBuilder(
|
double maxWidth = box.maxWidth;
|
||||||
// builder: (context, BoxConstraints box) {
|
double crossCount = len < 3 ? 2 : 3;
|
||||||
// double maxWidth = box.maxWidth;
|
double height = maxWidth /
|
||||||
// double crossCount = len < 3 ? 2 : 3;
|
crossCount *
|
||||||
// double height = maxWidth /
|
(len % crossCount == 0
|
||||||
// crossCount *
|
? len ~/ crossCount
|
||||||
// (len % crossCount == 0
|
: len ~/ crossCount + 1) +
|
||||||
// ? len ~/ crossCount
|
6;
|
||||||
// : len ~/ crossCount + 1) +
|
return Container(
|
||||||
// 6;
|
padding: const EdgeInsets.only(top: 6),
|
||||||
// return Container(
|
height: height,
|
||||||
// padding: const EdgeInsets.only(top: 6),
|
child: GridView.count(
|
||||||
// height: height,
|
padding: EdgeInsets.zero,
|
||||||
// child: GridView.count(
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
// padding: EdgeInsets.zero,
|
crossAxisCount: crossCount.toInt(),
|
||||||
// physics: const NeverScrollableScrollPhysics(),
|
mainAxisSpacing: 4.0,
|
||||||
// crossAxisCount: crossCount.toInt(),
|
crossAxisSpacing: 4.0,
|
||||||
// mainAxisSpacing: 4.0,
|
childAspectRatio: 1,
|
||||||
// crossAxisSpacing: 4.0,
|
children: list,
|
||||||
// childAspectRatio: 1,
|
),
|
||||||
// children: list,
|
);
|
||||||
// ),
|
},
|
||||||
// );
|
),
|
||||||
// },
|
),
|
||||||
// ),
|
);
|
||||||
// ),
|
}
|
||||||
// );
|
// spanChilds.add(
|
||||||
// }
|
// WidgetSpan(
|
||||||
// spanChilds.add(
|
// child: NetworkImgLayer(
|
||||||
// WidgetSpan(
|
// src: pics.first.url,
|
||||||
// child: NetworkImgLayer(
|
// type: 'emote',
|
||||||
// src: pics.first.url,
|
// width: 100,
|
||||||
// type: 'emote',
|
// height: 200,
|
||||||
// width: 100,
|
// ),
|
||||||
// height: 200,
|
// ),
|
||||||
// ),
|
// );
|
||||||
// ),
|
}
|
||||||
// );
|
|
||||||
// }
|
|
||||||
return TextSpan(
|
return TextSpan(
|
||||||
children: spanChilds,
|
children: spanChilds,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -56,73 +56,64 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
floating: true,
|
floating: true,
|
||||||
pinned: false,
|
pinned: false,
|
||||||
delegate: _SliverHeaderDelegate(
|
delegate: _SliverHeaderDelegate(
|
||||||
height: 124,
|
height: 90,
|
||||||
child: Column(
|
child: Container(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
height: 90,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
color: Theme.of(context).colorScheme.background,
|
||||||
children: [
|
child: Row(
|
||||||
Container(
|
children: [
|
||||||
color: Theme.of(context).colorScheme.background,
|
Expanded(
|
||||||
padding: const EdgeInsets.only(left: 16, right: 16),
|
child: ListView(
|
||||||
child: Row(
|
scrollDirection: Axis.horizontal,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
controller: scrollController,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
children: [
|
||||||
children: [
|
const SizedBox(width: 10),
|
||||||
const Text('最新关注'),
|
if (liveList.isNotEmpty) ...[
|
||||||
GestureDetector(
|
for (int i = 0; i < liveList.length; i++) ...[
|
||||||
onTap: () {
|
upItemBuild(liveList[i], i)
|
||||||
feedBack();
|
],
|
||||||
Get.toNamed('/follow?mid=${userInfo.mid}');
|
VerticalDivider(
|
||||||
},
|
indent: 20,
|
||||||
child: Container(
|
endIndent: 40,
|
||||||
padding: const EdgeInsets.only(top: 5, bottom: 5),
|
width: 26,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary
|
||||||
|
.withOpacity(0.5),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
for (int i = 0; i < upList.length; i++) ...[
|
||||||
|
upItemBuild(upList[i], i)
|
||||||
|
],
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Material(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => {feedBack(), Get.toNamed('/follow')},
|
||||||
|
child: Container(
|
||||||
|
height: 100,
|
||||||
|
padding: const EdgeInsets.only(left: 10, right: 10),
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.secondaryContainer
|
||||||
|
.withOpacity(0.3),
|
||||||
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'查看全部',
|
'全部',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.outline),
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onSecondaryContainer),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
Container(
|
)),
|
||||||
height: 90,
|
),
|
||||||
color: Theme.of(context).colorScheme.background,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: ListView(
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
controller: scrollController,
|
|
||||||
children: [
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
if (liveList.isNotEmpty) ...[
|
|
||||||
for (int i = 0; i < liveList.length; i++) ...[
|
|
||||||
upItemBuild(liveList[i], i)
|
|
||||||
],
|
|
||||||
VerticalDivider(
|
|
||||||
indent: 20,
|
|
||||||
endIndent: 40,
|
|
||||||
width: 26,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.primary
|
|
||||||
.withOpacity(0.5),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
for (int i = 0; i < upList.length; i++) ...[
|
|
||||||
upItemBuild(upList[i], i)
|
|
||||||
],
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,16 +16,13 @@ class FansPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _FansPageState extends State<FansPage> {
|
class _FansPageState extends State<FansPage> {
|
||||||
late String mid;
|
final FansController _fansController = Get.put(FansController());
|
||||||
late FansController _fansController;
|
|
||||||
final ScrollController scrollController = ScrollController();
|
final ScrollController scrollController = ScrollController();
|
||||||
Future? _futureBuilderFuture;
|
Future? _futureBuilderFuture;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
mid = Get.parameters['mid']!;
|
|
||||||
_fansController = Get.put(FansController(), tag: mid);
|
|
||||||
_futureBuilderFuture = _fansController.queryFans('init');
|
_futureBuilderFuture = _fansController.queryFans('init');
|
||||||
scrollController.addListener(
|
scrollController.addListener(
|
||||||
() async {
|
() async {
|
||||||
|
|||||||
@ -44,14 +44,6 @@ class _FavPageState extends State<FavPage> {
|
|||||||
'我的收藏',
|
'我的收藏',
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
actions: [
|
|
||||||
IconButton(
|
|
||||||
onPressed: () => Get.toNamed(
|
|
||||||
'/favSearch?searchType=1&mediaId=${_favController.favFolderData.value.list!.first.id}'),
|
|
||||||
icon: const Icon(Icons.search_outlined),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
body: FutureBuilder(
|
body: FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
|
|||||||
@ -92,18 +92,13 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
actions: [
|
// actions: [
|
||||||
IconButton(
|
// IconButton(
|
||||||
onPressed: () => Get.toNamed(
|
// onPressed: () {},
|
||||||
'/favSearch?searchType=0&mediaId=${Get.parameters['mediaId']!}'),
|
// icon: const Icon(Icons.more_vert),
|
||||||
icon: const Icon(Icons.search_outlined),
|
// ),
|
||||||
),
|
// const SizedBox(width: 4)
|
||||||
// IconButton(
|
// ],
|
||||||
// onPressed: () {},
|
|
||||||
// icon: const Icon(Icons.more_vert),
|
|
||||||
// ),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
],
|
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
flexibleSpace: FlexibleSpaceBar(
|
||||||
background: Container(
|
background: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
|||||||
@ -49,8 +49,8 @@ class FavVideoCardH extends StatelessWidget {
|
|||||||
Get.toNamed('/video', parameters: parameters, arguments: {
|
Get.toNamed('/video', parameters: parameters, arguments: {
|
||||||
'videoItem': videoItem,
|
'videoItem': videoItem,
|
||||||
'heroTag': heroTag,
|
'heroTag': heroTag,
|
||||||
'videoType':
|
// 'videoType':
|
||||||
epId != null ? SearchType.media_bangumi : SearchType.video,
|
// epId != null ? SearchType.media_bangumi : SearchType.video,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -142,21 +142,15 @@ class VideoContent extends StatelessWidget {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Text(
|
|
||||||
videoItem.owner.name,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
|
||||||
color: Theme.of(context).colorScheme.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
StatView(
|
Text(
|
||||||
theme: 'gray',
|
videoItem.owner.name,
|
||||||
view: videoItem.cntInfo['play'],
|
style: TextStyle(
|
||||||
|
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
|
||||||
StatDanMu(theme: 'gray', danmu: videoItem.cntInfo['danmaku']),
|
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 26,
|
width: 26,
|
||||||
|
|||||||
@ -1,75 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:pilipala/http/user.dart';
|
|
||||||
import 'package:pilipala/models/user/fav_detail.dart';
|
|
||||||
|
|
||||||
class FavSearchController extends GetxController {
|
|
||||||
final ScrollController scrollController = ScrollController();
|
|
||||||
Rx<TextEditingController> controller = TextEditingController().obs;
|
|
||||||
final FocusNode searchFocusNode = FocusNode();
|
|
||||||
RxString searchKeyWord = ''.obs; // 搜索词
|
|
||||||
String hintText = '请输入已收藏视频名称'; // 默认
|
|
||||||
RxBool loadingStatus = false.obs; // 加载状态
|
|
||||||
RxString loadingText = '加载中...'.obs; // 加载提示
|
|
||||||
bool hasMore = false;
|
|
||||||
late int searchType;
|
|
||||||
late int mediaId;
|
|
||||||
|
|
||||||
int currentPage = 1; // 当前页
|
|
||||||
int count = 0; // 总数
|
|
||||||
RxList<FavDetailItemData> favList = <FavDetailItemData>[].obs;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onInit() {
|
|
||||||
super.onInit();
|
|
||||||
searchType = int.parse(Get.parameters['searchType']!);
|
|
||||||
mediaId = int.parse(Get.parameters['mediaId']!);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清空搜索
|
|
||||||
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 = true;
|
|
||||||
currentPage = 1;
|
|
||||||
searchFav();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索收藏夹视频
|
|
||||||
Future searchFav({type = 'init'}) async {
|
|
||||||
var res = await await UserHttp.userFavFolderDetail(
|
|
||||||
pn: currentPage,
|
|
||||||
ps: 20,
|
|
||||||
mediaId: mediaId,
|
|
||||||
keyword: searchKeyWord.value,
|
|
||||||
type: searchType,
|
|
||||||
);
|
|
||||||
if (res['status']) {
|
|
||||||
if (currentPage == 1 && type == 'init') {
|
|
||||||
favList.value = res['data'].medias;
|
|
||||||
} else if (type == 'onLoad') {
|
|
||||||
favList.addAll(res['data'].medias);
|
|
||||||
}
|
|
||||||
hasMore = res['data'].hasMore;
|
|
||||||
}
|
|
||||||
currentPage += 1;
|
|
||||||
loadingStatus.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
onLoad() {
|
|
||||||
if (!hasMore) return;
|
|
||||||
searchFav(type: 'onLoad');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
library fav_search;
|
|
||||||
|
|
||||||
export './controller.dart';
|
|
||||||
export './view.dart';
|
|
||||||
@ -1,116 +0,0 @@
|
|||||||
import 'package:easy_debounce/easy_throttle.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:pilipala/common/skeleton/video_card_h.dart';
|
|
||||||
import 'package:pilipala/common/widgets/no_data.dart';
|
|
||||||
import 'package:pilipala/pages/favDetail/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});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<FavSearchPage> createState() => _FavSearchPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _FavSearchPageState extends State<FavSearchPage> {
|
|
||||||
final FavSearchController _favSearchCtr = Get.put(FavSearchController());
|
|
||||||
late ScrollController scrollController;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
scrollController = _favSearchCtr.scrollController;
|
|
||||||
scrollController.addListener(
|
|
||||||
() {
|
|
||||||
if (scrollController.position.pixels >=
|
|
||||||
scrollController.position.maxScrollExtent - 300) {
|
|
||||||
EasyThrottle.throttle('fav', const Duration(seconds: 1), () {
|
|
||||||
_favSearchCtr.onLoad();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
scrollController.removeListener(() {});
|
|
||||||
scrollController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
titleSpacing: 0,
|
|
||||||
actions: [
|
|
||||||
IconButton(
|
|
||||||
onPressed: () => _favSearchCtr.submit(),
|
|
||||||
icon: const Icon(Icons.search_outlined, size: 22)),
|
|
||||||
const SizedBox(width: 10)
|
|
||||||
],
|
|
||||||
title: Obx(
|
|
||||||
() => TextField(
|
|
||||||
autofocus: true,
|
|
||||||
focusNode: _favSearchCtr.searchFocusNode,
|
|
||||||
controller: _favSearchCtr.controller.value,
|
|
||||||
textInputAction: TextInputAction.search,
|
|
||||||
onChanged: (value) => _favSearchCtr.onChange(value),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: _favSearchCtr.hintText,
|
|
||||||
border: InputBorder.none,
|
|
||||||
suffixIcon: IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
Icons.clear,
|
|
||||||
size: 22,
|
|
||||||
color: Theme.of(context).colorScheme.outline,
|
|
||||||
),
|
|
||||||
onPressed: () => _favSearchCtr.onClear(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onSubmitted: (String value) => _favSearchCtr.submit(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body: Obx(
|
|
||||||
() => _favSearchCtr.loadingStatus.value && _favSearchCtr.favList.isEmpty
|
|
||||||
? ListView.builder(
|
|
||||||
itemCount: 10,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return const VideoCardHSkeleton();
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: _favSearchCtr.favList.isNotEmpty
|
|
||||||
? ListView.builder(
|
|
||||||
controller: scrollController,
|
|
||||||
itemCount: _favSearchCtr.favList.length + 1,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
if (index == _favSearchCtr.favList.length) {
|
|
||||||
return Container(
|
|
||||||
height: MediaQuery.of(context).padding.bottom + 60,
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
bottom: MediaQuery.of(context).padding.bottom),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return FavVideoCardH(
|
|
||||||
videoItem: _favSearchCtr.favList[index],
|
|
||||||
callFn: () => null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: const CustomScrollView(
|
|
||||||
slivers: <Widget>[
|
|
||||||
NoData(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,28 +1,20 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/http/follow.dart';
|
import 'package:pilipala/http/follow.dart';
|
||||||
import 'package:pilipala/http/member.dart';
|
|
||||||
import 'package:pilipala/models/follow/result.dart';
|
import 'package:pilipala/models/follow/result.dart';
|
||||||
import 'package:pilipala/models/member/tags.dart';
|
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
/// 查看自己的关注时,可以查看分类
|
class FollowController extends GetxController {
|
||||||
/// 查看其他人的关注时,只可以看全部
|
|
||||||
class FollowController extends GetxController with GetTickerProviderStateMixin {
|
|
||||||
Box userInfoCache = GStrorage.userInfo;
|
Box userInfoCache = GStrorage.userInfo;
|
||||||
int pn = 1;
|
int pn = 1;
|
||||||
int ps = 20;
|
int ps = 20;
|
||||||
int total = 0;
|
int total = 0;
|
||||||
RxList<FollowItemModel> followList = <FollowItemModel>[].obs;
|
RxList<FollowItemModel> followList = [FollowItemModel()].obs;
|
||||||
late int mid;
|
late int mid;
|
||||||
late String name;
|
late String name;
|
||||||
var userInfo;
|
var userInfo;
|
||||||
RxString loadingText = '加载中...'.obs;
|
RxString loadingText = '加载中...'.obs;
|
||||||
RxBool isOwner = false.obs;
|
|
||||||
late List<MemberTagItemModel> followTags;
|
|
||||||
late TabController tabController;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
@ -31,7 +23,6 @@ class FollowController extends GetxController with GetTickerProviderStateMixin {
|
|||||||
mid = Get.parameters['mid'] != null
|
mid = Get.parameters['mid'] != null
|
||||||
? int.parse(Get.parameters['mid']!)
|
? int.parse(Get.parameters['mid']!)
|
||||||
: userInfo.mid;
|
: userInfo.mid;
|
||||||
isOwner.value = mid == userInfo.mid;
|
|
||||||
name = Get.parameters['name'] ?? userInfo.uname;
|
name = Get.parameters['name'] ?? userInfo.uname;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,20 +56,4 @@ class FollowController extends GetxController with GetTickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当查看当前用户的关注时,请求关注分组
|
|
||||||
Future followUpTags() async {
|
|
||||||
if (userInfo != null && mid == userInfo.mid) {
|
|
||||||
var res = await MemberHttp.followUpTags();
|
|
||||||
if (res['status']) {
|
|
||||||
followTags = res['data'];
|
|
||||||
tabController = TabController(
|
|
||||||
initialIndex: 0,
|
|
||||||
length: res['data'].length,
|
|
||||||
vsync: this,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,12 @@
|
|||||||
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
|
import 'package:pilipala/common/widgets/no_data.dart';
|
||||||
|
import 'package:pilipala/models/follow/result.dart';
|
||||||
|
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
import 'widgets/follow_list.dart';
|
import 'widgets/follow_item.dart';
|
||||||
import 'widgets/owner_follow_list.dart';
|
|
||||||
|
|
||||||
class FollowPage extends StatefulWidget {
|
class FollowPage extends StatefulWidget {
|
||||||
const FollowPage({super.key});
|
const FollowPage({super.key});
|
||||||
@ -12,15 +16,30 @@ class FollowPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _FollowPageState extends State<FollowPage> {
|
class _FollowPageState extends State<FollowPage> {
|
||||||
late String mid;
|
final FollowController _followController = Get.put(FollowController());
|
||||||
late FollowController _followController;
|
|
||||||
final ScrollController scrollController = ScrollController();
|
final ScrollController scrollController = ScrollController();
|
||||||
|
Future? _futureBuilderFuture;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
mid = Get.parameters['mid']!;
|
_futureBuilderFuture = _followController.queryFollowings('init');
|
||||||
_followController = Get.put(FollowController(), tag: mid);
|
scrollController.addListener(
|
||||||
|
() async {
|
||||||
|
if (scrollController.position.pixels >=
|
||||||
|
scrollController.position.maxScrollExtent - 200) {
|
||||||
|
EasyThrottle.throttle('follow', const Duration(seconds: 1), () {
|
||||||
|
_followController.queryFollowings('onLoad');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
scrollController.removeListener(() {});
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -32,57 +51,73 @@ class _FollowPageState extends State<FollowPage> {
|
|||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
title: Text(
|
title: Text(
|
||||||
_followController.isOwner.value
|
'${_followController.name}的关注',
|
||||||
? '我的关注'
|
|
||||||
: '${_followController.name}的关注',
|
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: Obx(
|
body: RefreshIndicator(
|
||||||
() => !_followController.isOwner.value
|
onRefresh: () async =>
|
||||||
? FollowList(ctr: _followController)
|
await _followController.queryFollowings('init'),
|
||||||
: FutureBuilder(
|
child: FutureBuilder(
|
||||||
future: _followController.followUpTags(),
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
var data = snapshot.data;
|
var data = snapshot.data;
|
||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
return Column(
|
List<FollowItemModel> list = _followController.followList;
|
||||||
children: [
|
return Obx(
|
||||||
TabBar(
|
() => list.isNotEmpty
|
||||||
controller: _followController.tabController,
|
? ListView.builder(
|
||||||
isScrollable: true,
|
controller: scrollController,
|
||||||
tabs: [
|
itemCount: list.length + 1,
|
||||||
for (var i in data['data']) ...[
|
itemBuilder: (BuildContext context, int index) {
|
||||||
Tab(text: i.name),
|
if (index == list.length) {
|
||||||
]
|
return Container(
|
||||||
]),
|
height:
|
||||||
Expanded(
|
MediaQuery.of(context).padding.bottom +
|
||||||
child: TabBarView(
|
60,
|
||||||
controller: _followController.tabController,
|
padding: EdgeInsets.only(
|
||||||
children: [
|
bottom: MediaQuery.of(context)
|
||||||
for (var i = 0;
|
.padding
|
||||||
i < _followController.tabController.length;
|
.bottom),
|
||||||
i++) ...[
|
child: Center(
|
||||||
OwnerFollowList(
|
child: Obx(
|
||||||
ctr: _followController,
|
() => Text(
|
||||||
tagItem: _followController.followTags[i],
|
_followController.loadingText.value,
|
||||||
)
|
style: TextStyle(
|
||||||
]
|
color: Theme.of(context)
|
||||||
],
|
.colorScheme
|
||||||
),
|
.outline,
|
||||||
|
fontSize: 13),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return followItem(item: list[index]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: const CustomScrollView(
|
||||||
|
slivers: [NoData()],
|
||||||
),
|
),
|
||||||
],
|
);
|
||||||
);
|
} else {
|
||||||
} else {
|
return CustomScrollView(
|
||||||
return const SizedBox();
|
slivers: [
|
||||||
}
|
HttpError(
|
||||||
} else {
|
errMsg: data['msg'],
|
||||||
return const SizedBox();
|
fn: () => _followController.queryFollowings('init'),
|
||||||
}
|
)
|
||||||
},
|
],
|
||||||
),
|
);
|
||||||
),
|
}
|
||||||
|
} else {
|
||||||
|
// 骨架屏
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user