diff --git a/README.md b/README.md
index 470e9a35..228d17bb 100644
--- a/README.md
+++ b/README.md
@@ -94,7 +94,7 @@ QQ频道: https://pd.qq.com/s/365esodk3
- [x] 音质选择(视视频而定)
- [x] 解码格式选择(视视频而定)
- [x] 弹幕
- - [ ] 字幕
+ - [x] 字幕
- [x] 记忆播放
- [x] 视频比例:高度/宽度适应、填充、包含等
diff --git a/android/build.gradle b/android/build.gradle
index 713d7f6e..674e96f4 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,5 +1,5 @@
buildscript {
- ext.kotlin_version = '1.7.10'
+ ext.kotlin_version = '1.9.0'
repositories {
google()
mavenCentral()
diff --git a/assets/loading.json b/assets/loading.json
new file mode 100644
index 00000000..38bccbed
--- /dev/null
+++ b/assets/loading.json
@@ -0,0 +1 @@
+{"v":"5.7.11","fr":60,"ip":0,"op":81,"w":1920,"h":1080,"nm":"Loading Dots","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Dot4","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":25,"s":[25]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":39,"s":[100]},{"t":55,"s":[25]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":25,"s":[1142,540,0],"to":[0,-6.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":39,"s":[1142,500,0],"to":[0,0,0],"ti":[0,-6.667,0]},{"t":55,"s":[1142,540,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-284,92,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":25,"s":[50,50,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":39,"s":[75,75,100]},{"t":55,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[120,120],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.0039,0.6157,0.5686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-284,92],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Dot3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":17,"s":[25]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":31,"s":[100]},{"t":47,"s":[25]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":17,"s":[1022,540,0],"to":[0,-6.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":31,"s":[1022,500,0],"to":[0,0,0],"ti":[0,-6.667,0]},{"t":47,"s":[1022,540,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-284,92,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":17,"s":[50,50,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":31,"s":[75,75,100]},{"t":47,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[120,120],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.0039,0.6157,0.5686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-284,92],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Dot2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":9,"s":[25]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":23,"s":[100]},{"t":39,"s":[25]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":9,"s":[902,540,0],"to":[0,-6.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":23,"s":[902,500,0],"to":[0,0,0],"ti":[0,0,0]},{"t":39,"s":[902,540,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-284,92,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":9,"s":[50,50,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":23,"s":[75,75,100]},{"t":39,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[120,120],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.0039,0.6157,0.5686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-284,92],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Dot1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[25]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":14,"s":[100]},{"t":30,"s":[25]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[782,540,0],"to":[0,-6.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":14,"s":[782,500,0],"to":[0,0,0],"ti":[0,-6.667,0]},{"t":30,"s":[782,540,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-284,92,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[50,50,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":14,"s":[75,75,100]},{"t":30,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[120,120],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.0039,0.6157,0.5686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-284,92],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/assets/trail_loading.json b/assets/trail_loading.json
new file mode 100644
index 00000000..9fb39ea6
--- /dev/null
+++ b/assets/trail_loading.json
@@ -0,0 +1 @@
+{"v":"4.6.8","fr":60,"ip":0,"op":106,"w":500,"h":500,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 5","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":20,"s":[0],"e":[360]},{"t":110}]},"p":{"a":0,"k":[251,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[10,10]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":20,"op":620,"st":20,"bm":0,"sr":1},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 4","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":15,"s":[0],"e":[360]},{"t":105}]},"p":{"a":0,"k":[251,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":15,"op":615,"st":15,"bm":0,"sr":1},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 3","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":10,"s":[0],"e":[360]},{"t":100}]},"p":{"a":0,"k":[251,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[30,30]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":10,"op":610,"st":10,"bm":0,"sr":1},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 2","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":5,"s":[0],"e":[360]},{"t":95}]},"p":{"a":0,"k":[251,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[40,40]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":5,"op":605,"st":5,"bm":0,"sr":1},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 1","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":0,"s":[0],"e":[360]},{"t":90}]},"p":{"a":0,"k":[250,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"n":["0p667_1_0p333_0","0p667_1_0p333_0"],"t":0,"s":[50,50],"e":[40,40]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"n":["0p667_1_0p333_0","0p667_1_0p333_0"],"t":84,"s":[40,40],"e":[50,50]},{"t":100}]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":600,"st":0,"bm":0,"sr":1}]}
\ No newline at end of file
diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist
index 9625e105..7c569640 100644
--- a/ios/Flutter/AppFrameworkInfo.plist
+++ b/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
CFBundleVersion
1.0
MinimumOSVersion
- 11.0
+ 12.0
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 2c1a635b..04fe6670 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -10,7 +10,6 @@ PODS:
- connectivity_plus (0.0.1):
- Flutter
- FlutterMacOS
- - ReachabilitySwift
- device_info_plus (0.0.1):
- Flutter
- Flutter (1.0.0)
@@ -41,7 +40,6 @@ PODS:
- FlutterMacOS
- permission_handler_apple (9.3.0):
- Flutter
- - ReachabilitySwift (5.0.0)
- saver_gallery (0.0.1):
- Flutter
- screen_brightness_ios (0.1.0):
@@ -101,7 +99,6 @@ SPEC REPOS:
trunk:
- FMDB
- GT3Captcha-iOS
- - ReachabilitySwift
- Toast
EXTERNAL SOURCES:
@@ -167,9 +164,9 @@ SPEC CHECKSUMS:
audio_service: f509d65da41b9521a61f1c404dd58651f265a567
audio_session: 4f3e461722055d21515cf3261b64c973c062f345
auto_orientation: 102ed811a5938d52c86520ddd7ecd3a126b5d39d
- connectivity_plus: e2dad488011aeb593e219360e804c43cc1af5770
+ connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
- Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
+ Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83
flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529
fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265
@@ -182,7 +179,6 @@ SPEC CHECKSUMS:
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
- ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
saver_gallery: 2b4e584106fde2407ab51560f3851564963e6b78
screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
@@ -190,11 +186,11 @@ SPEC CHECKSUMS:
status_bar_control: 7c84146799e6a076315cc1550f78ef53aae3e446
system_proxy: bec1a5c5af67dd3e3ebf43979400a8756c04cc44
Toast: ec33c32b8688982cecc6348adeae667c1b9938da
- url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b
+ url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9
- wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47
+ wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
webview_cookie_manager: eaf920722b493bd0f7611b5484771ca53fed03f7
- webview_flutter_wkwebview: 4f3e50f7273d31e5500066ed267e3ae4309c5ae4
+ webview_flutter_wkwebview: be0f0d33777f1bfd0c9fdcb594786704dbf65f36
PODFILE CHECKSUM: 637cd290bed23275b5f5ffcc7eb1e73d0a5fb2be
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index bac856d2..55565d40 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -156,7 +156,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 1430;
+ LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index a6b826db..5e31d3d3 100644
--- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -1,6 +1,6 @@
colorObject = {
'white': Colors.white,
'gray': Theme.of(context).colorScheme.outline,
- 'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8),
+ 'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8),
};
Color color = colorObject[theme]!;
return Row(
diff --git a/lib/common/widgets/stat/view.dart b/lib/common/widgets/stat/view.dart
index 2665e2d4..5359c979 100644
--- a/lib/common/widgets/stat/view.dart
+++ b/lib/common/widgets/stat/view.dart
@@ -14,7 +14,7 @@ class StatView extends StatelessWidget {
Map colorObject = {
'white': Colors.white,
'gray': Theme.of(context).colorScheme.outline,
- 'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8),
+ 'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8),
};
Color color = colorObject[theme]!;
return Row(
diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart
index 7b56152f..14476cdf 100644
--- a/lib/common/widgets/video_card_v.dart
+++ b/lib/common/widgets/video_card_v.dart
@@ -20,11 +20,13 @@ import 'network_img_layer.dart';
class VideoCardV extends StatelessWidget {
final dynamic videoItem;
final int crossAxisCount;
+ final Function? blockUserCb;
const VideoCardV({
Key? key,
required this.videoItem,
required this.crossAxisCount,
+ this.blockUserCb,
}) : super(key: key);
bool isStringNumeric(String str) {
@@ -157,7 +159,11 @@ class VideoCardV extends StatelessWidget {
);
}),
),
- VideoContent(videoItem: videoItem, crossAxisCount: crossAxisCount)
+ VideoContent(
+ videoItem: videoItem,
+ crossAxisCount: crossAxisCount,
+ blockUserCb: blockUserCb,
+ )
],
),
);
@@ -167,9 +173,14 @@ class VideoCardV extends StatelessWidget {
class VideoContent extends StatelessWidget {
final dynamic videoItem;
final int crossAxisCount;
- const VideoContent(
- {Key? key, required this.videoItem, required this.crossAxisCount})
- : super(key: key);
+ final Function? blockUserCb;
+
+ const VideoContent({
+ Key? key,
+ required this.videoItem,
+ required this.crossAxisCount,
+ this.blockUserCb,
+ }) : super(key: key);
Widget _buildBadge(String text, String type, [double fs = 12]) {
return PBadge(
@@ -241,7 +252,10 @@ class VideoContent extends StatelessWidget {
useRootNavigator: true,
isScrollControlled: true,
builder: (context) {
- return MorePanel(videoItem: videoItem);
+ return MorePanel(
+ videoItem: videoItem,
+ blockUserCb: blockUserCb,
+ );
},
);
},
@@ -297,11 +311,17 @@ class VideoStat extends StatelessWidget {
class MorePanel extends StatelessWidget {
final dynamic videoItem;
- const MorePanel({super.key, required this.videoItem});
+ final Function? blockUserCb;
+ const MorePanel({
+ super.key,
+ required this.videoItem,
+ this.blockUserCb,
+ });
Future menuActionHandler(String type) async {
switch (type) {
case 'block':
+ Get.back();
blockUser();
break;
case 'watchLater':
@@ -338,7 +358,10 @@ class MorePanel extends StatelessWidget {
reSrc: 11,
);
SmartDialog.dismiss();
- SmartDialog.showToast(res['msg'] ?? '成功');
+ if (res['status']) {
+ blockUserCb?.call(videoItem.owner.mid);
+ }
+ SmartDialog.showToast(res['msg']);
},
child: const Text('确认'),
)
diff --git a/lib/http/api.dart b/lib/http/api.dart
index 77c168d0..e519d91c 100644
--- a/lib/http/api.dart
+++ b/lib/http/api.dart
@@ -400,12 +400,24 @@ class Api {
'${HttpString.passBaseUrl}/x/passport-login/captcha?source=main_web';
// web端短信验证码
- static const String smsCode =
+ static const String webSmsCode =
'${HttpString.passBaseUrl}/x/passport-login/web/sms/send';
// web端验证码登录
+ static const String webSmsLogin =
+ '${HttpString.passBaseUrl}/x/passport-login/web/login/sms';
// web端密码登录
+ static const String loginInByWebPwd =
+ '${HttpString.passBaseUrl}/x/passport-login/web/login';
+
+ // web端二维码
+ static const String qrCodeApi =
+ '${HttpString.passBaseUrl}/x/passport-login/web/qrcode/generate';
+
+ // 扫码登录
+ static const String loginInByQrcode =
+ '${HttpString.passBaseUrl}/x/passport-login/web/qrcode/poll';
// app端短信验证码
static const String appSmsCode =
diff --git a/lib/http/login.dart b/lib/http/login.dart
index ff3fee23..2437b72a 100644
--- a/lib/http/login.dart
+++ b/lib/http/login.dart
@@ -3,6 +3,7 @@ import 'dart:math';
import 'package:crypto/crypto.dart';
import 'package:dio/dio.dart';
import 'package:encrypt/encrypt.dart';
+import 'package:pilipala/http/constants.dart';
import 'package:uuid/uuid.dart';
import '../models/login/index.dart';
import '../utils/login.dart';
@@ -21,32 +22,32 @@ class LoginHttp {
}
}
- 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);
- }
+ // 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({
@@ -60,6 +61,7 @@ class LoginHttp {
Map data = {
'cid': cid,
'tel': tel,
+ "source": "main_web",
'token': token,
'challenge': challenge,
'validate': validate,
@@ -67,17 +69,56 @@ class LoginHttp {
};
FormData formData = FormData.fromMap({...data});
var res = await Request().post(
- Api.smsCode,
+ Api.webSmsCode,
data: formData,
options: Options(
contentType: Headers.formUrlEncodedContentType,
),
);
- print(res);
+ if (res.data['code'] == 0) {
+ return {
+ 'status': true,
+ 'data': res.data['data'],
+ };
+ } else {
+ return {'status': false, 'data': [], 'msg': res.data['message']};
+ }
}
// web端验证码登录
- static Future loginInByWebSmsCode() async {}
+ static Future loginInByWebSmsCode({
+ int? cid,
+ required int tel,
+ required int code,
+ required String captchaKey,
+ }) async {
+ // webSmsLogin
+ Map data = {
+ "cid": cid,
+ "tel": tel,
+ "code": code,
+ "source": "main_mini",
+ "keep": 0,
+ "captcha_key": captchaKey,
+ "go_url": HttpString.baseUrl
+ };
+ FormData formData = FormData.fromMap({...data});
+ var res = await Request().post(
+ Api.webSmsLogin,
+ data: formData,
+ options: Options(
+ contentType: Headers.formUrlEncodedContentType,
+ ),
+ );
+ if (res.data['code'] == 0) {
+ return {
+ 'status': true,
+ 'data': res.data['data'],
+ };
+ } else {
+ return {'status': false, 'data': [], 'msg': res.data['message']};
+ }
+ }
// web端密码登录
static Future liginInByWebPwd() async {}
@@ -173,4 +214,69 @@ class LoginHttp {
);
print(res);
}
+
+ // web端密码登录
+ static Future loginInByWebPwd({
+ required int username,
+ required String password,
+ required String token,
+ required String challenge,
+ required String validate,
+ required String seccode,
+ }) async {
+ Map data = {
+ 'username': username,
+ 'password': password,
+ 'keep': 0,
+ 'token': token,
+ 'challenge': challenge,
+ 'validate': validate,
+ 'seccode': seccode,
+ 'source': 'main-fe-header',
+ "go_url": HttpString.baseUrl
+ };
+ FormData formData = FormData.fromMap({...data});
+ var res = await Request().post(
+ Api.loginInByWebPwd,
+ data: formData,
+ options: Options(
+ contentType: Headers.formUrlEncodedContentType,
+ ),
+ );
+ if (res.data['code'] == 0) {
+ return {
+ 'status': true,
+ 'data': res.data['data'],
+ };
+ } else {
+ return {'status': false, 'data': [], 'msg': res.data['message']};
+ }
+ }
+
+ // web端登录二维码
+ static Future getWebQrcode() async {
+ var res = await Request().get(Api.qrCodeApi);
+ if (res.data['code'] == 0) {
+ return {
+ 'status': true,
+ 'data': res.data['data'],
+ };
+ } else {
+ return {'status': false, 'data': [], 'msg': res.data['message']};
+ }
+ }
+
+ // web端二维码轮询登录状态
+ static Future queryWebQrcodeStatus(String qrcodeKey) async {
+ var res = await Request()
+ .get(Api.loginInByQrcode, data: {'qrcode_key': qrcodeKey});
+ if (res.data['data']['code'] == 0) {
+ return {
+ 'status': true,
+ 'data': res.data['data'],
+ };
+ } else {
+ return {'status': false, 'data': [], 'msg': res.data['message']};
+ }
+ }
}
diff --git a/lib/http/video.dart b/lib/http/video.dart
index bf5921d6..7c1d9ba6 100644
--- a/lib/http/video.dart
+++ b/lib/http/video.dart
@@ -387,9 +387,15 @@ class VideoHttp {
'csrf': await Request.getCsrf(),
});
if (res.data['code'] == 0) {
- return {'status': true, 'data': res.data['data']};
+ if (act == 5) {
+ List blackMidsList =
+ setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]);
+ blackMidsList.add(mid);
+ setting.put(SettingBoxKey.blackMidsList, blackMidsList);
+ }
+ return {'status': true, 'data': res.data['data'], 'msg': '成功'};
} else {
- return {'status': false, 'data': []};
+ return {'status': false, 'data': [], 'msg': res.data['message']};
}
}
diff --git a/lib/main.dart b/lib/main.dart
index 066bb599..1a2ce989 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -23,7 +23,7 @@ import 'package:pilipala/utils/app_scheme.dart';
import 'package:pilipala/utils/data.dart';
import 'package:pilipala/utils/global_data.dart';
import 'package:pilipala/utils/storage.dart';
-import 'package:media_kit/media_kit.dart'; // Provides [Player], [Media], [Playlist] etc.
+import 'package:media_kit/media_kit.dart';
import 'package:pilipala/utils/recommend_filter.dart';
import 'package:catcher_2/catcher_2.dart';
import './services/loggeer.dart';
@@ -31,59 +31,42 @@ import './services/loggeer.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
MediaKit.ensureInitialized();
- SystemChrome.setPreferredOrientations(
- [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown])
- .then((_) async {
- await GStrorage.init();
- await setupServiceLocator();
- clearLogs();
- Request();
- await Request.setCookie();
- RecommendFilter();
+ await SystemChrome.setPreferredOrientations(
+ [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
+ await GStrorage.init();
+ await setupServiceLocator();
+ clearLogs();
+ Request();
+ await Request.setCookie();
- // 异常捕获 logo记录
- final Catcher2Options debugConfig = Catcher2Options(
- SilentReportMode(),
- [
- FileHandler(await getLogsPath()),
- ConsoleHandler(
- enableDeviceParameters: false,
- enableApplicationParameters: false,
- )
- ],
- );
+ // 异常捕获 logo记录
+ final Catcher2Options releaseConfig = Catcher2Options(
+ SilentReportMode(),
+ [FileHandler(await getLogsPath())],
+ );
- final Catcher2Options releaseConfig = Catcher2Options(
- SilentReportMode(),
- [FileHandler(await getLogsPath())],
- );
+ Catcher2(
+ releaseConfig: releaseConfig,
+ runAppFunction: () {
+ runApp(const MyApp());
+ },
+ );
- Catcher2(
- debugConfig: debugConfig,
- releaseConfig: releaseConfig,
- runAppFunction: () {
- runApp(const MyApp());
- },
- );
-
- // 小白条、导航栏沉浸
- if (Platform.isAndroid) {
- final androidInfo = await DeviceInfoPlugin().androidInfo;
- if (androidInfo.version.sdkInt >= 29) {
- SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
- }
- SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
- systemNavigationBarColor: Colors.transparent,
- systemNavigationBarDividerColor: Colors.transparent,
- statusBarColor: Colors.transparent,
- ));
+ // 小白条、导航栏沉浸
+ if (Platform.isAndroid) {
+ final androidInfo = await DeviceInfoPlugin().androidInfo;
+ if (androidInfo.version.sdkInt >= 29) {
+ SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
}
+ SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
+ systemNavigationBarColor: Colors.transparent,
+ systemNavigationBarDividerColor: Colors.transparent,
+ statusBarColor: Colors.transparent,
+ ));
+ }
- Data.init();
- GlobalData();
- PiliSchame.init();
- DisableBatteryOpt();
- });
+ PiliSchame.init();
+ DisableBatteryOpt();
}
class MyApp extends StatelessWidget {
@@ -124,6 +107,39 @@ class MyApp extends StatelessWidget {
} catch (_) {}
}
+ if (Platform.isAndroid) {
+ return AndroidApp(
+ brandColor: brandColor,
+ isDynamicColor: isDynamicColor,
+ currentThemeValue: currentThemeValue,
+ textScale: textScale,
+ );
+ } else {
+ return OtherApp(
+ brandColor: brandColor,
+ currentThemeValue: currentThemeValue,
+ textScale: textScale,
+ );
+ }
+ }
+}
+
+class AndroidApp extends StatelessWidget {
+ const AndroidApp({
+ super.key,
+ required this.brandColor,
+ required this.isDynamicColor,
+ required this.currentThemeValue,
+ required this.textScale,
+ });
+
+ final Color brandColor;
+ final bool isDynamicColor;
+ final ThemeType currentThemeValue;
+ final double textScale;
+
+ @override
+ Widget build(BuildContext context) {
return DynamicColorBuilder(
builder: ((ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
ColorScheme? lightColorScheme;
@@ -143,96 +159,120 @@ class MyApp extends StatelessWidget {
brightness: Brightness.dark,
);
}
-
- // ThemeData themeData = ThemeData(
- // colorScheme: currentThemeValue == ThemeType.dark
- // ? darkColorScheme
- // : lightColorScheme,
- // );
-
- // // 小白条、导航栏沉浸
- // if (Platform.isAndroid) {
- // List versionParts = Platform.version.split('.');
- // int androidVersion = int.parse(versionParts[0]);
- // if (androidVersion >= 29) {
- // SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
- // }
- // SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
- // systemNavigationBarColor: GlobalData().enableMYBar
- // ? const Color(0x00010000)
- // : themeData.canvasColor,
- // systemNavigationBarDividerColor: GlobalData().enableMYBar
- // ? const Color(0x00010000)
- // : themeData.canvasColor,
- // systemNavigationBarIconBrightness:
- // currentThemeValue == ThemeType.dark
- // ? Brightness.light
- // : Brightness.dark,
- // statusBarColor: Colors.transparent,
- // ));
- // }
-
- // 图片缓存
- // PaintingBinding.instance.imageCache.maximumSizeBytes = 1000 << 20;
- return GetMaterialApp(
- title: 'PiliPala',
- theme: ThemeData(
- colorScheme: currentThemeValue == ThemeType.dark
- ? darkColorScheme
- : lightColorScheme,
- snackBarTheme: SnackBarThemeData(
- actionTextColor: lightColorScheme.primary,
- backgroundColor: lightColorScheme.secondaryContainer,
- closeIconColor: lightColorScheme.secondary,
- contentTextStyle: TextStyle(color: lightColorScheme.secondary),
- elevation: 20,
- ),
- pageTransitionsTheme: const PageTransitionsTheme(
- builders: {
- TargetPlatform.android: ZoomPageTransitionsBuilder(
- allowEnterRouteSnapshotting: false,
- ),
- },
- ),
- ),
- darkTheme: ThemeData(
- colorScheme: currentThemeValue == ThemeType.light
- ? lightColorScheme
- : darkColorScheme,
- snackBarTheme: SnackBarThemeData(
- actionTextColor: darkColorScheme.primary,
- backgroundColor: darkColorScheme.secondaryContainer,
- closeIconColor: darkColorScheme.secondary,
- contentTextStyle: TextStyle(color: darkColorScheme.secondary),
- elevation: 20,
- ),
- ),
- localizationsDelegates: const [
- GlobalCupertinoLocalizations.delegate,
- GlobalMaterialLocalizations.delegate,
- GlobalWidgetsLocalizations.delegate,
- ],
- locale: const Locale("zh", "CN"),
- supportedLocales: const [Locale("zh", "CN"), Locale("en", "US")],
- fallbackLocale: const Locale("zh", "CN"),
- getPages: Routes.getPages,
- home: const MainApp(),
- builder: (BuildContext context, Widget? child) {
- return FlutterSmartDialog(
- toastBuilder: (String msg) => CustomToast(msg: msg),
- child: MediaQuery(
- data: MediaQuery.of(context)
- .copyWith(textScaler: TextScaler.linear(textScale)),
- child: child!,
- ),
- );
- },
- navigatorObservers: [
- VideoDetailPage.routeObserver,
- SearchPage.routeObserver,
- ],
+ return BuildMainApp(
+ lightColorScheme: lightColorScheme,
+ darkColorScheme: darkColorScheme,
+ currentThemeValue: currentThemeValue,
+ textScale: textScale,
);
}),
);
}
}
+
+class OtherApp extends StatelessWidget {
+ const OtherApp({
+ super.key,
+ required this.brandColor,
+ required this.currentThemeValue,
+ required this.textScale,
+ });
+
+ final Color brandColor;
+ final ThemeType currentThemeValue;
+ final double textScale;
+
+ @override
+ Widget build(BuildContext context) {
+ return BuildMainApp(
+ lightColorScheme: ColorScheme.fromSeed(
+ seedColor: brandColor,
+ brightness: Brightness.light,
+ ),
+ darkColorScheme: ColorScheme.fromSeed(
+ seedColor: brandColor,
+ brightness: Brightness.dark,
+ ),
+ currentThemeValue: currentThemeValue,
+ textScale: textScale,
+ );
+ }
+}
+
+class BuildMainApp extends StatelessWidget {
+ const BuildMainApp({
+ super.key,
+ required this.lightColorScheme,
+ required this.darkColorScheme,
+ required this.currentThemeValue,
+ required this.textScale,
+ });
+
+ final ColorScheme lightColorScheme;
+ final ColorScheme darkColorScheme;
+ final ThemeType currentThemeValue;
+ final double textScale;
+
+ @override
+ Widget build(BuildContext context) {
+ final SnackBarThemeData snackBarTheme = SnackBarThemeData(
+ actionTextColor: lightColorScheme.primary,
+ backgroundColor: lightColorScheme.secondaryContainer,
+ closeIconColor: lightColorScheme.secondary,
+ contentTextStyle: TextStyle(color: lightColorScheme.secondary),
+ elevation: 20,
+ );
+
+ return GetMaterialApp(
+ title: 'PiliPala',
+ theme: ThemeData(
+ colorScheme: currentThemeValue == ThemeType.dark
+ ? darkColorScheme
+ : lightColorScheme,
+ snackBarTheme: snackBarTheme,
+ pageTransitionsTheme: const PageTransitionsTheme(
+ builders: {
+ TargetPlatform.android: ZoomPageTransitionsBuilder(
+ allowEnterRouteSnapshotting: false,
+ ),
+ },
+ ),
+ ),
+ darkTheme: ThemeData(
+ colorScheme: currentThemeValue == ThemeType.light
+ ? lightColorScheme
+ : darkColorScheme,
+ snackBarTheme: snackBarTheme,
+ ),
+ localizationsDelegates: const [
+ GlobalCupertinoLocalizations.delegate,
+ GlobalMaterialLocalizations.delegate,
+ GlobalWidgetsLocalizations.delegate,
+ ],
+ locale: const Locale("zh", "CN"),
+ supportedLocales: const [Locale("zh", "CN"), Locale("en", "US")],
+ fallbackLocale: const Locale("zh", "CN"),
+ getPages: Routes.getPages,
+ home: const MainApp(),
+ builder: (BuildContext context, Widget? child) {
+ return FlutterSmartDialog(
+ toastBuilder: (String msg) => CustomToast(msg: msg),
+ child: MediaQuery(
+ data: MediaQuery.of(context)
+ .copyWith(textScaler: TextScaler.linear(textScale)),
+ child: child!,
+ ),
+ );
+ },
+ navigatorObservers: [
+ VideoDetailPage.routeObserver,
+ SearchPage.routeObserver,
+ ],
+ onInit: () {
+ RecommendFilter();
+ Data.init();
+ GlobalData();
+ },
+ );
+ }
+}
diff --git a/lib/models/dynamics/result.dart b/lib/models/dynamics/result.dart
index bc7105d1..64a6e5b1 100644
--- a/lib/models/dynamics/result.dart
+++ b/lib/models/dynamics/result.dart
@@ -415,6 +415,7 @@ class DynamicMajorModel {
this.type,
this.courses,
this.common,
+ this.music,
});
DynamicArchiveModel? archive;
@@ -431,6 +432,7 @@ class DynamicMajorModel {
String? type;
Map? courses;
Map? common;
+ Map? music;
DynamicMajorModel.fromJson(Map json) {
archive = json['archive'] != null
@@ -455,6 +457,7 @@ class DynamicMajorModel {
type = json['type'];
courses = json['courses'] ?? {};
common = json['common'] ?? {};
+ music = json['music'] ?? {};
}
}
diff --git a/lib/models/home/rcmd/result.dart b/lib/models/home/rcmd/result.dart
index 78747d1a..0098fe95 100644
--- a/lib/models/home/rcmd/result.dart
+++ b/lib/models/home/rcmd/result.dart
@@ -69,9 +69,10 @@ class RecVideoItemAppModel {
: null;
// 由于app端api并不会直接返回与owner的关注状态
// 所以借用推荐原因是否为“已关注”、“新关注”等判别关注状态,从而与web端接口等效
+ RegExp regex = RegExp(r'已关注|新关注');
isFollowed = rcmdReason != null &&
rcmdReason!.content != null &&
- rcmdReason!.content!.contains('关注')
+ regex.hasMatch(rcmdReason!.content!)
? 1
: 0;
// 如果是,就无需再显示推荐原因,交由view统一处理即可
diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart
index 6876da79..95d4d898 100644
--- a/lib/pages/bangumi/introduction/view.dart
+++ b/lib/pages/bangumi/introduction/view.dart
@@ -194,7 +194,8 @@ class _BangumiInfoState extends State {
src: widget.bangumiDetail!.cover!,
),
PBadge(
- text: '评分 ${widget.bangumiDetail!.rating!['score']!}',
+ text:
+ '评分 ${widget.bangumiDetail?.rating?['score']! ?? '暂无'}',
top: null,
right: 6,
bottom: 6,
diff --git a/lib/pages/bangumi/introduction/widgets/intro_detail.dart b/lib/pages/bangumi/introduction/widgets/intro_detail.dart
index c5bbd566..07684a86 100644
--- a/lib/pages/bangumi/introduction/widgets/intro_detail.dart
+++ b/lib/pages/bangumi/introduction/widgets/intro_detail.dart
@@ -20,10 +20,10 @@ class IntroDetail extends StatelessWidget {
sheetHeight = localCache.get('sheetHeight');
TextStyle smallTitle = TextStyle(
fontSize: 12,
- color: Theme.of(context).colorScheme.onBackground,
+ color: Theme.of(context).colorScheme.onSurface,
);
return Container(
- color: Theme.of(context).colorScheme.background,
+ color: Theme.of(context).colorScheme.surface,
padding: const EdgeInsets.only(left: 14, right: 14),
height: sheetHeight,
child: Column(
diff --git a/lib/pages/bangumi/view.dart b/lib/pages/bangumi/view.dart
index 8759af65..3adfdc1f 100644
--- a/lib/pages/bangumi/view.dart
+++ b/lib/pages/bangumi/view.dart
@@ -3,7 +3,6 @@ import 'dart:async';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
-import 'package:nil/nil.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/utils/main_stream.dart';
@@ -142,10 +141,10 @@ class _BangumiPageState extends State
),
);
} else {
- return nil;
+ return const SizedBox();
}
} else {
- return nil;
+ return const SizedBox();
}
},
),
@@ -216,7 +215,7 @@ class _BangumiPageState extends State
(BuildContext context, int index) {
return bangumiList!.isNotEmpty
? BangumiCardV(bangumiItem: bangumiList[index])
- : nil;
+ : const SizedBox();
},
childCount: bangumiList!.isNotEmpty ? bangumiList!.length : 10,
),
diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart
index da15239d..258ad531 100644
--- a/lib/pages/dynamics/view.dart
+++ b/lib/pages/dynamics/view.dart
@@ -162,13 +162,12 @@ class _DynamicsPageState extends State
decoration: BoxDecoration(
color: Theme.of(context)
.colorScheme
- .surfaceVariant
+ .surfaceContainerHighest
.withOpacity(0.7),
borderRadius: BorderRadius.circular(20),
),
thumbDecoration: BoxDecoration(
- color:
- Theme.of(context).colorScheme.background,
+ color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(20),
),
duration: const Duration(milliseconds: 300),
diff --git a/lib/pages/dynamics/widgets/additional_panel.dart b/lib/pages/dynamics/widgets/additional_panel.dart
index fa11f217..50e1b6d3 100644
--- a/lib/pages/dynamics/widgets/additional_panel.dart
+++ b/lib/pages/dynamics/widgets/additional_panel.dart
@@ -19,7 +19,7 @@ Widget addWidget(item, context, type, {floor = 1}) {
};
Color bgColor = floor == 1
? Theme.of(context).dividerColor.withOpacity(0.08)
- : Theme.of(context).colorScheme.background;
+ : Theme.of(context).colorScheme.surface;
switch (type) {
case 'ADDITIONAL_TYPE_UGC':
// 转发的投稿
diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart
index 0d3baecd..8acdc26a 100644
--- a/lib/pages/dynamics/widgets/author_panel.dart
+++ b/lib/pages/dynamics/widgets/author_panel.dart
@@ -52,7 +52,7 @@ class AuthorPanel extends StatelessWidget {
color: item.modules.moduleAuthor!.vip != null &&
item.modules.moduleAuthor!.vip['status'] > 0
? const Color.fromARGB(255, 251, 100, 163)
- : Theme.of(context).colorScheme.onBackground,
+ : Theme.of(context).colorScheme.onSurface,
fontSize: Theme.of(context).textTheme.titleSmall!.fontSize,
),
),
diff --git a/lib/pages/dynamics/widgets/forward_panel.dart b/lib/pages/dynamics/widgets/forward_panel.dart
index 76c7b24e..f8b90a81 100644
--- a/lib/pages/dynamics/widgets/forward_panel.dart
+++ b/lib/pages/dynamics/widgets/forward_panel.dart
@@ -238,6 +238,61 @@ Widget forWard(item, context, ctr, source, {floor = 1}) {
),
),
);
+ case 'DYNAMIC_TYPE_MUSIC':
+ final Map music = item.modules.moduleDynamic.major.music;
+ return Padding(
+ padding: const EdgeInsets.only(top: 8),
+ child: InkWell(
+ onTap: () {
+ Get.toNamed('/webview', parameters: {
+ 'url': "https:${music['jump_url']}",
+ 'type': 'url',
+ 'pageTitle': music['title']
+ });
+ },
+ child: Container(
+ width: double.infinity,
+ padding:
+ const EdgeInsets.only(left: 12, top: 10, right: 12, bottom: 10),
+ color: Theme.of(context).dividerColor.withOpacity(0.08),
+ child: Row(
+ children: [
+ NetworkImgLayer(
+ width: 45,
+ height: 45,
+ src: music['cover'],
+ ),
+ const SizedBox(width: 10),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ music['title'],
+ style: TextStyle(
+ color: Theme.of(context).colorScheme.primary,
+ ),
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ ),
+ const SizedBox(height: 2),
+ Text(
+ music['label'],
+ style: TextStyle(
+ color: Theme.of(context).colorScheme.outline,
+ fontSize:
+ Theme.of(context).textTheme.labelMedium!.fontSize,
+ ),
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ ),
+ ],
+ )
+ ],
+ ),
+ // TextButton(onPressed: () {}, child: Text('123'))
+ ),
+ ),
+ );
default:
return const SizedBox(
width: double.infinity,
diff --git a/lib/pages/dynamics/widgets/up_panel.dart b/lib/pages/dynamics/widgets/up_panel.dart
index fd0ae642..f8c973a0 100644
--- a/lib/pages/dynamics/widgets/up_panel.dart
+++ b/lib/pages/dynamics/widgets/up_panel.dart
@@ -1,3 +1,4 @@
+import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
@@ -30,6 +31,31 @@ class _UpPanelState extends State {
liveList = widget.upData.liveList!;
}
+ void onClickUp(data, i) {
+ currentMid = data.mid;
+ Get.find().mid.value = data.mid;
+ Get.find().upInfo.value = data;
+ Get.find().onSelectUp(data.mid);
+ int liveLen = liveList.length;
+ int upLen = upList.length;
+ double itemWidth = contentWidth + itemPadding.horizontal;
+ double screenWidth = MediaQuery.sizeOf(context).width;
+ double moveDistance = 0.0;
+ if (itemWidth * (upList.length + liveList.length) <= screenWidth) {
+ } else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) {
+ moveDistance = (i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2;
+ } else {
+ moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth;
+ }
+ data.hasUpdate = false;
+ scrollController.animateTo(
+ moveDistance,
+ duration: const Duration(milliseconds: 200),
+ curve: Curves.linear,
+ );
+ setState(() {});
+ }
+
@override
Widget build(BuildContext context) {
listFormat();
@@ -43,7 +69,7 @@ class _UpPanelState extends State {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
- color: Theme.of(context).colorScheme.background,
+ color: Theme.of(context).colorScheme.surface,
padding: const EdgeInsets.only(left: 16, right: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -69,7 +95,7 @@ class _UpPanelState extends State {
),
Container(
height: 90,
- color: Theme.of(context).colorScheme.background,
+ color: Theme.of(context).colorScheme.surface,
child: Row(
children: [
Flexible(
@@ -120,30 +146,10 @@ class _UpPanelState extends State {
onTap: () {
feedBack();
if (data.type == 'up') {
- currentMid = data.mid;
- Get.find().mid.value = data.mid;
- Get.find().upInfo.value = data;
- Get.find().onSelectUp(data.mid);
- int liveLen = liveList.length;
- int upLen = upList.length;
- double itemWidth = contentWidth + itemPadding.horizontal;
- double screenWidth = MediaQuery.sizeOf(context).width;
- double moveDistance = 0.0;
- if (itemWidth * (upList.length + liveList.length) <= screenWidth) {
- } else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) {
- moveDistance =
- (i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2;
- } else {
- moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth;
- }
- data.hasUpdate = false;
- scrollController.animateTo(
- moveDistance,
- duration: const Duration(milliseconds: 500),
- curve: Curves.easeInOut,
- );
-
- setState(() {});
+ EasyThrottle.throttle('follow', const Duration(milliseconds: 300),
+ () {
+ onClickUp(data, i);
+ });
} else if (data.type == 'live') {
LiveItemModel liveItem = LiveItemModel.fromJson({
'title': data.title,
diff --git a/lib/pages/fav/view.dart b/lib/pages/fav/view.dart
index b8d37f50..4f48213e 100644
--- a/lib/pages/fav/view.dart
+++ b/lib/pages/fav/view.dart
@@ -1,6 +1,7 @@
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/http_error.dart';
import 'package:pilipala/pages/fav/index.dart';
import 'package:pilipala/pages/fav/widgets/item.dart';
@@ -93,7 +94,12 @@ class _FavPageState extends State {
}
} else {
// 骨架屏
- return const Text('请求中');
+ return ListView.builder(
+ itemBuilder: (context, index) {
+ return const VideoCardHSkeleton();
+ },
+ itemCount: 10,
+ );
}
},
),
diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart
index ba5f6b5c..baebfedb 100644
--- a/lib/pages/history/widgets/item.dart
+++ b/lib/pages/history/widgets/item.dart
@@ -198,7 +198,8 @@ class HistoryItem extends StatelessWidget {
duration: const Duration(milliseconds: 200),
child: Container(
decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(12),
+ borderRadius: BorderRadius.circular(
+ StyleString.imgRadius.x),
color: Colors.black.withOpacity(
ctr!.enableMultiple.value &&
videoItem.checked
@@ -243,7 +244,7 @@ class HistoryItem extends StatelessWidget {
),
),
),
- videoItem.progress != 0
+ videoItem.progress != 0 && videoItem.duration != 0
? Positioned(
left: 3,
right: 3,
diff --git a/lib/pages/history_search/controller.dart b/lib/pages/history_search/controller.dart
index 90ac7a02..a6c79e6a 100644
--- a/lib/pages/history_search/controller.dart
+++ b/lib/pages/history_search/controller.dart
@@ -10,9 +10,8 @@ class HistorySearchController extends GetxController {
final FocusNode searchFocusNode = FocusNode();
RxString searchKeyWord = ''.obs;
String hintText = '搜索';
- RxString loadingStatus = 'init'.obs;
+ RxBool loadingStatus = false.obs;
RxString loadingText = '加载中...'.obs;
- bool hasRequest = false;
late int mid;
RxString uname = ''.obs;
int pn = 1;
@@ -36,8 +35,7 @@ class HistorySearchController extends GetxController {
// 提交搜索内容
void submit() {
- loadingStatus.value = 'loading';
- if (hasRequest) {
+ if (!loadingStatus.value) {
pn = 1;
searchHistories();
}
@@ -48,6 +46,7 @@ class HistorySearchController extends GetxController {
if (type == 'onLoad' && loadingText.value == '没有更多了') {
return;
}
+ loadingStatus.value = true;
var res = await UserHttp.searchHistory(
pn: pn,
keyword: controller.value.text,
@@ -63,9 +62,8 @@ class HistorySearchController extends GetxController {
loadingText.value = '没有更多了';
}
pn += 1;
- hasRequest = true;
}
- loadingStatus.value = 'finish';
+ loadingStatus.value = false;
return res;
}
@@ -86,6 +84,6 @@ class HistorySearchController extends GetxController {
historyList.removeWhere((e) => e.kid == kid);
SmartDialog.showToast(res['msg']);
}
- loadingStatus.value = 'finish';
+ // loadingStatus.value = fasle;
}
}
diff --git a/lib/pages/history_search/view.dart b/lib/pages/history_search/view.dart
index 5bde691d..f5bcae64 100644
--- a/lib/pages/history_search/view.dart
+++ b/lib/pages/history_search/view.dart
@@ -2,7 +2,6 @@ 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/http_error.dart';
import 'package:pilipala/common/widgets/no_data.dart';
import 'package:pilipala/pages/history/widgets/item.dart';
@@ -16,20 +15,19 @@ class HistorySearchPage extends StatefulWidget {
}
class _HistorySearchPageState extends State {
- final HistorySearchController _historySearchCtr =
- Get.put(HistorySearchController());
+ final HistorySearchController _hisCtr = Get.put(HistorySearchController());
late ScrollController scrollController;
@override
void initState() {
super.initState();
- scrollController = _historySearchCtr.scrollController;
+ scrollController = _hisCtr.scrollController;
scrollController.addListener(
() {
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 300) {
EasyThrottle.throttle('history', const Duration(seconds: 1), () {
- _historySearchCtr.onLoad();
+ _hisCtr.onLoad();
});
}
},
@@ -50,19 +48,19 @@ class _HistorySearchPageState extends State {
titleSpacing: 0,
actions: [
IconButton(
- onPressed: () => _historySearchCtr.submit(),
+ onPressed: () => _hisCtr.submit(),
icon: const Icon(Icons.search_outlined, size: 22)),
const SizedBox(width: 10)
],
title: Obx(
() => TextField(
autofocus: true,
- focusNode: _historySearchCtr.searchFocusNode,
- controller: _historySearchCtr.controller.value,
+ focusNode: _hisCtr.searchFocusNode,
+ controller: _hisCtr.controller.value,
textInputAction: TextInputAction.search,
- onChanged: (value) => _historySearchCtr.onChange(value),
+ onChanged: (value) => _hisCtr.onChange(value),
decoration: InputDecoration(
- hintText: _historySearchCtr.hintText,
+ hintText: _hisCtr.hintText,
border: InputBorder.none,
suffixIcon: IconButton(
icon: Icon(
@@ -70,103 +68,61 @@ class _HistorySearchPageState extends State {
size: 22,
color: Theme.of(context).colorScheme.outline,
),
- onPressed: () => _historySearchCtr.onClear(),
+ onPressed: () => _hisCtr.onClear(),
),
),
- onSubmitted: (String value) => _historySearchCtr.submit(),
+ onSubmitted: (String value) => _hisCtr.submit(),
),
),
),
body: Obx(
- () => Column(
- children: _historySearchCtr.loadingStatus.value == 'init'
- ? [const SizedBox()]
- : [
- Expanded(
- child: FutureBuilder(
- future: _historySearchCtr.searchHistories(),
- builder: (context, snapshot) {
- if (snapshot.connectionState == ConnectionState.done) {
- Map data = snapshot.data as Map;
- if (data['status']) {
- return Obx(
- () => _historySearchCtr.historyList.isNotEmpty
- ? ListView.builder(
- controller: scrollController,
- itemCount:
- _historySearchCtr.historyList.length +
- 1,
- itemBuilder: (context, index) {
- if (index ==
- _historySearchCtr
- .historyList.length) {
- return Container(
- height: MediaQuery.of(context)
- .padding
- .bottom +
- 60,
- padding: EdgeInsets.only(
- bottom: MediaQuery.of(context)
- .padding
- .bottom),
- child: Center(
- child: Obx(
- () => Text(
- _historySearchCtr
- .loadingText.value,
- style: TextStyle(
- color: Theme.of(context)
- .colorScheme
- .outline,
- fontSize: 13),
- ),
- ),
- ),
- );
- } else {
- return HistoryItem(
- videoItem: _historySearchCtr
- .historyList[index],
- ctr: _historySearchCtr,
- onChoose: null,
- onUpdateMultiple: () => null,
- );
- }
- },
- )
- : _historySearchCtr.loadingStatus.value ==
- 'loading'
- ? const SizedBox(child: Text('加载中...'))
- : const CustomScrollView(
- slivers: [
- NoData(),
- ],
- ),
- );
- } else {
- return CustomScrollView(
- slivers: [
- HttpError(
- errMsg: data['msg'],
- fn: () => setState(() {}),
- )
- ],
- );
- }
+ () {
+ return _hisCtr.loadingStatus.value && _hisCtr.historyList.isEmpty
+ ? ListView.builder(
+ itemCount: 10,
+ itemBuilder: (context, index) {
+ return const VideoCardHSkeleton();
+ },
+ )
+ : _hisCtr.historyList.isNotEmpty
+ ? ListView.builder(
+ controller: scrollController,
+ itemCount: _hisCtr.historyList.length + 1,
+ itemBuilder: (context, index) {
+ if (index == _hisCtr.historyList.length) {
+ return Container(
+ height: MediaQuery.of(context).padding.bottom + 60,
+ padding: EdgeInsets.only(
+ bottom: MediaQuery.of(context).padding.bottom),
+ child: Center(
+ child: Obx(
+ () => Text(
+ _hisCtr.loadingText.value,
+ style: TextStyle(
+ color:
+ Theme.of(context).colorScheme.outline,
+ fontSize: 13,
+ ),
+ ),
+ ),
+ ),
+ );
} else {
- // 骨架屏
- return ListView.builder(
- itemCount: 10,
- itemBuilder: (context, index) {
- return const VideoCardHSkeleton();
- },
+ return HistoryItem(
+ videoItem: _hisCtr.historyList[index],
+ ctr: _hisCtr,
+ onChoose: null,
+ onUpdateMultiple: () => null,
);
}
},
- ),
- ),
- ],
- ),
+ )
+ : const CustomScrollView(
+ slivers: [
+ NoData(),
+ ],
+ );
+ },
),
);
}
diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart
index 5c2a9800..4e67fa2c 100644
--- a/lib/pages/live_room/controller.dart
+++ b/lib/pages/live_room/controller.dart
@@ -17,8 +17,7 @@ class LiveRoomController extends GetxController {
double volume = 0.0;
// 静音状态
RxBool volumeOff = false.obs;
- PlPlayerController plPlayerController =
- PlPlayerController.getInstance(videoType: 'live');
+ PlPlayerController plPlayerController = PlPlayerController(videoType: 'live');
Rx roomInfoH5 = RoomInfoH5Model().obs;
late bool enableCDN;
late int currentQn;
diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart
index e5a9d6c9..4dd7c538 100644
--- a/lib/pages/live_room/widgets/bottom_control.dart
+++ b/lib/pages/live_room/widgets/bottom_control.dart
@@ -153,7 +153,8 @@ class _BottomControlState extends State {
size: 20,
color: Colors.white,
),
- fuc: () => widget.controller!.triggerFullScreen(),
+ fuc: () => widget.controller!.triggerFullScreen(
+ status: !(widget.controller!.isFullScreen.value)),
),
],
),
diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart
index c002fdf9..b5ddba16 100644
--- a/lib/pages/login/controller.dart
+++ b/lib/pages/login/controller.dart
@@ -1,11 +1,14 @@
+import 'dart:async';
import 'dart:io';
+import 'package:encrypt/encrypt.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:pilipala/http/login.dart';
import 'package:gt3_flutter_plugin/gt3_flutter_plugin.dart';
import 'package:pilipala/models/login/index.dart';
+import 'package:pilipala/utils/login.dart';
class LoginPageController extends GetxController {
final GlobalKey mobFormKey = GlobalKey();
@@ -26,9 +29,23 @@ class LoginPageController extends GetxController {
final Gt3FlutterPlugin captcha = Gt3FlutterPlugin();
+ // 倒计时60s
+ RxInt seconds = 60.obs;
+ late Timer timer;
+ RxBool smsCodeSendStatus = false.obs;
+
// 默认密码登录
RxInt loginType = 0.obs;
+ late String captchaKey;
+
+ late int tel;
+ late int webSmsCode;
+
+ RxInt validSeconds = 180.obs;
+ late Timer validTimer;
+ late String qrcodeKey;
+
// 监听pageView切换
void onPageChange(int index) {
currentIndex.value = index;
@@ -43,6 +60,7 @@ class LoginPageController extends GetxController {
curve: Curves.easeInOut,
);
passwordTextFieldNode.requestFocus();
+ (mobFormKey.currentState as FormState).save();
}
}
@@ -86,18 +104,64 @@ class LoginPageController extends GetxController {
}
}
- // 验证码登录
- void loginInByCode() {
- if ((msgCodeFormKey.currentState as FormState).validate()) {}
+ // web端密码登录
+ void loginInByWebPassword() async {
+ if ((passwordFormKey.currentState as FormState).validate()) {
+ getCaptcha((data) async {
+ CaptchaDataModel captchaData = data;
+ var webKeyRes = await LoginHttp.getWebKey();
+ if (webKeyRes['status']) {
+ String rhash = webKeyRes['data']['hash'];
+ String key = webKeyRes['data']['key'];
+ dynamic publicKey = RSAKeyParser().parse(key);
+ String passwordEncryptyed = Encrypter(RSA(publicKey: publicKey))
+ .encrypt(rhash + passwordTextController.text)
+ .base64;
+ var res = await LoginHttp.loginInByWebPwd(
+ username: tel,
+ password: passwordEncryptyed,
+ token: captchaData.token!,
+ challenge: captchaData.geetest!.challenge!,
+ validate: captchaData.validate!,
+ seccode: captchaData.seccode!,
+ );
+ if (res['status']) {
+ await LoginUtils.confirmLogin('', null);
+ } else {
+ SmartDialog.showToast(res['msg']);
+ }
+ } else {
+ SmartDialog.showToast(webKeyRes['msg']);
+ }
+ });
+ }
}
- // app端验证码
- void getMsgCode() async {
+ // web端验证码登录
+ void loginInByCode() async {
+ if ((msgCodeFormKey.currentState as FormState).validate()) {
+ (msgCodeFormKey.currentState as FormState).save();
+ var res = await LoginHttp.loginInByWebSmsCode(
+ cid: 86,
+ tel: tel,
+ code: webSmsCode,
+ captchaKey: captchaKey,
+ );
+ if (res['status']) {
+ await LoginUtils.confirmLogin('', null);
+ } else {
+ SmartDialog.showToast(res['msg']);
+ }
+ }
+ }
+
+ // 获取app端验证码
+ void getAppMsgCode() async {
getCaptcha((data) async {
CaptchaDataModel captchaData = data;
var res = await LoginHttp.sendAppSmsCode(
cid: 86,
- tel: 13734077064,
+ tel: tel,
token: captchaData.token!,
challenge: captchaData.geetest!.challenge!,
validate: captchaData.validate!,
@@ -121,7 +185,7 @@ class LoginPageController extends GetxController {
captcha.addEventHandler(onShow: (Map message) async {
SmartDialog.dismiss();
}, onClose: (Map message) async {
- SmartDialog.showToast('关闭验证');
+ SmartDialog.showToast('取消验证');
}, onResult: (Map message) async {
debugPrint("Captcha result: $message");
String code = message["code"];
@@ -201,4 +265,72 @@ class LoginPageController extends GetxController {
captcha.startCaptcha(registerData);
} else {}
}
+
+ // 获取web端验证码
+ void getWebMsgCode() async {
+ getCaptcha((data) async {
+ CaptchaDataModel captchaData = data;
+ var res = await LoginHttp.sendWebSmsCode(
+ cid: 86,
+ tel: tel,
+ token: captchaData.token!,
+ challenge: captchaData.geetest!.challenge!,
+ validate: captchaData.validate!,
+ seccode: captchaData.seccode!,
+ );
+ if (res['status']) {
+ captchaKey = res['data']['captcha_key'];
+ SmartDialog.showToast('验证码已发送');
+ // 倒计时60s
+ smsCodeSendStatus.value = true;
+ startTimer();
+ } else {
+ SmartDialog.showToast(res['msg']);
+ }
+ });
+ }
+
+ // 验证码倒计时
+ void startTimer() {
+ timer = Timer.periodic(const Duration(seconds: 1), (timer) {
+ if (seconds.value > 0) {
+ seconds.value--;
+ } else {
+ seconds.value = 60;
+ smsCodeSendStatus.value = false;
+ timer.cancel();
+ }
+ });
+ }
+
+ // 获取登录二维码
+ Future getWebQrcode() async {
+ var res = await LoginHttp.getWebQrcode();
+ validSeconds.value = 180;
+ if (res['status']) {
+ qrcodeKey = res['data']['qrcode_key'];
+ validTimer = Timer.periodic(const Duration(seconds: 1), (validTimer) {
+ if (validSeconds.value > 0) {
+ validSeconds.value--;
+ queryWebQrcodeStatus();
+ } else {
+ getWebQrcode();
+ validTimer.cancel();
+ }
+ });
+ return res;
+ } else {
+ SmartDialog.showToast(res['msg']);
+ }
+ }
+
+ // 轮询二维码登录状态
+ Future queryWebQrcodeStatus() async {
+ var res = await LoginHttp.queryWebQrcodeStatus(qrcodeKey);
+ if (res['status']) {
+ await LoginUtils.confirmLogin('', null);
+ validTimer.cancel();
+ Get.back();
+ }
+ }
}
diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart
index 6521e9d9..cd91ea26 100644
--- a/lib/pages/login/view.dart
+++ b/lib/pages/login/view.dart
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
+import 'package:qr_flutter/qr_flutter.dart';
import 'controller.dart';
@@ -37,6 +38,105 @@ class _LoginPageState extends State {
icon: const Icon(Icons.arrow_back),
),
),
+ actions: [
+ IconButton(
+ tooltip: '浏览器打开',
+ onPressed: () {
+ Get.offNamed(
+ '/webview',
+ parameters: {
+ 'url': 'https://passport.bilibili.com/h5-app/passport/login',
+ 'type': 'login',
+ 'pageTitle': '登录bilibili',
+ },
+ );
+ },
+ icon: const Icon(Icons.language),
+ ),
+ IconButton(
+ tooltip: '二维码登录',
+ onPressed: () {
+ showDialog(
+ context: context,
+ builder: (context) {
+ return StatefulBuilder(
+ builder: (context, StateSetter setState) {
+ return AlertDialog(
+ title: Row(
+ children: [
+ const Text('扫码登录'),
+ IconButton(
+ onPressed: () {
+ setState(() {});
+ },
+ icon: const Icon(Icons.refresh),
+ ),
+ ],
+ ),
+ contentPadding: const EdgeInsets.fromLTRB(0, 0, 0, 4),
+ content: AspectRatio(
+ aspectRatio: 1,
+ child: Container(
+ width: 200,
+ padding: const EdgeInsets.all(12),
+ child: FutureBuilder(
+ future: _loginPageCtr.getWebQrcode(),
+ builder: (context, snapshot) {
+ if (snapshot.connectionState ==
+ ConnectionState.done) {
+ if (snapshot.data == null) {
+ return const SizedBox();
+ }
+ Map data = snapshot.data as Map;
+ return QrImageView(
+ data: data['data']['url'],
+ backgroundColor: Colors.transparent,
+ );
+ } else {
+ return const Center(
+ child: SizedBox(
+ width: 40,
+ height: 40,
+ child: CircularProgressIndicator(),
+ ),
+ );
+ }
+ },
+ ),
+ ),
+ ),
+ actions: [
+ TextButton(
+ onPressed: () {},
+ child: Obx(() {
+ return Text(
+ '有效期: ${_loginPageCtr.validSeconds.value}s',
+ style: Theme.of(context).textTheme.titleMedium,
+ );
+ }),
+ ),
+ TextButton(
+ onPressed: () {},
+ child: Text(
+ '检查登录状态',
+ style: TextStyle(
+ fontSize: Theme.of(context)
+ .textTheme
+ .titleMedium!
+ .fontSize,
+ ),
+ ),
+ )
+ ],
+ );
+ });
+ },
+ );
+ },
+ icon: const Icon(Icons.qr_code),
+ ),
+ const SizedBox(width: 22),
+ ],
),
body: PageView(
physics: const NeverScrollableScrollPhysics(),
@@ -93,35 +193,12 @@ class _LoginPageState extends State {
validator: (v) {
return v!.trim().isNotEmpty ? null : "手机号码不能为空";
},
- onSaved: (val) {
- print(val);
- },
+ onSaved: (val) => _loginPageCtr.tel = int.parse(val!),
onEditingComplete: () {
_loginPageCtr.nextStep();
},
),
),
- GestureDetector(
- onTap: () {
- Get.offNamed(
- '/webview',
- parameters: {
- 'url':
- 'https://passport.bilibili.com/h5-app/passport/login',
- 'type': 'login',
- 'pageTitle': '登录bilibili',
- },
- );
- },
- child: Padding(
- padding: const EdgeInsets.only(left: 2),
- child: Text(
- '使用网页端登录',
- style: TextStyle(
- color: Theme.of(context).colorScheme.primary),
- ),
- ),
- ),
const Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -236,7 +313,7 @@ class _LoginPageState extends State {
.primary, // 设置按钮背景色
),
onPressed: () =>
- _loginPageCtr.loginInByAppPassword(),
+ _loginPageCtr.loginInByWebPassword(),
child: const Text('确认登录'),
)
],
@@ -308,21 +385,28 @@ class _LoginPageState extends State {
? null
: "验证码不能为空";
},
- onSaved: (val) {
- print(val);
- },
+ onSaved: (val) => _loginPageCtr.webSmsCode =
+ int.parse(val!),
),
- Positioned(
- right: 8,
- top: 4,
- child: Center(
- child: TextButton(
- onPressed: () =>
- _loginPageCtr.getMsgCode(),
- child: const Text('获取验证码'),
+ Obx(() {
+ return Positioned(
+ right: 8,
+ top: 0,
+ child: Center(
+ child: TextButton(
+ onPressed: _loginPageCtr
+ .smsCodeSendStatus.value
+ ? null
+ : () =>
+ _loginPageCtr.getWebMsgCode(),
+ child: _loginPageCtr
+ .smsCodeSendStatus.value
+ ? Text(
+ '重新获取(${_loginPageCtr.seconds.value}s)')
+ : const Text('获取验证码')),
),
- ),
- ),
+ );
+ })
],
),
),
diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart
index 6541680a..cc413e59 100644
--- a/lib/pages/media/view.dart
+++ b/lib/pages/media/view.dart
@@ -105,7 +105,7 @@ class _MediaPageState extends State
color: Theme.of(context).dividerColor.withOpacity(0.1),
),
ListTile(
- onTap: () {},
+ onTap: () => Get.toNamed('/fav'),
leading: null,
dense: true,
title: Padding(
diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart
index a61bb820..153a7162 100644
--- a/lib/pages/mine/controller.dart
+++ b/lib/pages/mine/controller.dart
@@ -6,7 +6,6 @@ import 'package:pilipala/http/user.dart';
import 'package:pilipala/models/common/theme_type.dart';
import 'package:pilipala/models/user/info.dart';
import 'package:pilipala/models/user/stat.dart';
-import 'package:pilipala/utils/route_push.dart';
import 'package:pilipala/utils/storage.dart';
class MineController extends GetxController {
@@ -34,8 +33,8 @@ class MineController extends GetxController {
onLogin() async {
if (!userLogin.value) {
- RoutePush.loginPush();
- // Get.toNamed('/loginPage');
+ // RoutePush.loginPush();
+ Get.toNamed('/loginPage');
} else {
int mid = userInfo.value.mid!;
String face = userInfo.value.face!;
diff --git a/lib/pages/rcmd/controller.dart b/lib/pages/rcmd/controller.dart
index 28ff055b..2d606b12 100644
--- a/lib/pages/rcmd/controller.dart
+++ b/lib/pages/rcmd/controller.dart
@@ -1,4 +1,5 @@
import 'package:flutter/cupertino.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/http/video.dart';
@@ -106,4 +107,10 @@ class RcmdController extends GetxController {
duration: const Duration(milliseconds: 500), curve: Curves.easeInOut);
}
}
+
+ void blockUserCb(mid) {
+ videoList.removeWhere((e) => e.owner.mid == mid);
+ videoList.refresh();
+ SmartDialog.showToast('已移除相关视频');
+ }
}
diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart
index 67567870..29a8d469 100644
--- a/lib/pages/rcmd/view.dart
+++ b/lib/pages/rcmd/view.dart
@@ -146,6 +146,7 @@ class _RcmdPageState extends State
? VideoCardV(
videoItem: videoList[index],
crossAxisCount: crossAxisCount,
+ blockUserCb: (mid) => ctr.blockUserCb(mid),
)
: const VideoCardVSkeleton();
},
diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart
index 5e91a4cd..fbd1cfc6 100644
--- a/lib/pages/search/widgets/search_text.dart
+++ b/lib/pages/search/widgets/search_text.dart
@@ -18,7 +18,10 @@ class SearchText extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Material(
- color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5),
+ color: Theme.of(context)
+ .colorScheme
+ .surfaceContainerHighest
+ .withOpacity(0.5),
borderRadius: BorderRadius.circular(6),
child: Padding(
padding: EdgeInsets.zero,
diff --git a/lib/pages/setting/pages/font_size_select.dart b/lib/pages/setting/pages/font_size_select.dart
index 4985c83f..f5ca6be3 100644
--- a/lib/pages/setting/pages/font_size_select.dart
+++ b/lib/pages/setting/pages/font_size_select.dart
@@ -66,7 +66,7 @@ class _FontSizeSelectPageState extends State {
.colorScheme
.primary
.withOpacity(0.3))),
- color: Theme.of(context).colorScheme.background,
+ color: Theme.of(context).colorScheme.surface,
),
child: Row(
children: [
diff --git a/lib/pages/setting/pages/logs.dart b/lib/pages/setting/pages/logs.dart
index 0958edb8..f497aee5 100644
--- a/lib/pages/setting/pages/logs.dart
+++ b/lib/pages/setting/pages/logs.dart
@@ -41,7 +41,8 @@ class _LogsPageState extends State {
.replaceAll('DEVICE INFO', '设备信息')
.replaceAll('APP INFO', '应用信息')
.replaceAll('ERROR', '错误信息')
- .replaceAll('STACK TRACE', '错误堆栈');
+ .replaceAll('STACK TRACE', '错误堆栈')
+ .replaceAll('#', 'Line');
}).toList();
List