Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31a639400e | ||
|
|
d6b24561fa | ||
|
|
7ba9646d38 | ||
|
|
58a7cf1e75 | ||
|
|
1a327198f7 | ||
|
|
e4fe91ef92 | ||
|
|
afcf817c4f | ||
|
|
21550815db | ||
|
|
02af3a18ff | ||
|
|
a5a13b45cf | ||
|
|
0fd232ab3a | ||
|
|
8d83143ca6 | ||
|
|
74452cd622 | ||
|
|
cf2e8cec54 | ||
|
|
5231faf254 | ||
|
|
959d4de78a | ||
|
|
f5d7dc6b6a | ||
|
|
b761c35d10 | ||
|
|
7f3f7f6bdd | ||
|
|
c5877b7c5e | ||
|
|
9e4187ef17 | ||
|
|
bf7ce3e5a2 | ||
|
|
2c55314491 | ||
|
|
d28efef672 | ||
|
|
49b631d560 | ||
|
|
896510f852 | ||
|
|
1d8e469a46 | ||
|
|
caee40a5d9 | ||
|
|
7de051e6bb | ||
|
|
18cec3c752 | ||
|
|
3b46655051 | ||
|
|
f72ad572fb | ||
|
|
a57ea2adb6 |
10
README.md
@@ -47,6 +47,14 @@
|
||||
|
||||
## feat
|
||||
|
||||
- [x] 滑动跳转预览视频缩略图
|
||||
- [x] Live Photo
|
||||
- [x] 复制/移动收藏夹/稍后再看视频
|
||||
- [x] 超分辨率
|
||||
- [x] 合并弹幕
|
||||
- [x] 会员彩色弹幕
|
||||
- [x] 播放全部/继续播放/倒序播放
|
||||
- [x] Cookie登录
|
||||
- [x] 显示视频分段信息
|
||||
- [x] 调节字幕大小
|
||||
- [x] 调节全屏弹幕大小
|
||||
@@ -74,7 +82,7 @@
|
||||
- [x] 转发动态
|
||||
- [x] 合集图片
|
||||
- [x] 删除/置顶私信
|
||||
- [x] 举报用户/评论/视频
|
||||
- [x] 举报用户/评论/视频/动态
|
||||
- [x] 删除/发布文本/图片动态
|
||||
- [x] 其他
|
||||
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.example.piliplus">
|
||||
<!-- The INTERNET permission is required for development. Specifically,
|
||||
the Flutter tool needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission
|
||||
android:name="android.permission.INTERNET"
|
||||
/>
|
||||
|
||||
<application
|
||||
android:label="PiliPlus Debug"
|
||||
tools:replace="android:label">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:resizeableActivity="true"
|
||||
>
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<intent-filter android:label="PiliPlus Debug">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="http"/>
|
||||
<data android:scheme="https"/>
|
||||
<data android:host="*.bilibili.com"/>
|
||||
<data android:host="*.bilibili.cn"/>
|
||||
<data android:host="*.bilibili.tv"/>
|
||||
<data android:host="bilibili.com"/>
|
||||
<data android:host="bilibili.cn"/>
|
||||
<data android:host="bilibili.tv"/>
|
||||
<data android:host="b23.tv" />
|
||||
<!--<data android:host="live.bilibili.com"/>-->
|
||||
<!--<data android:host="www.bilibili.com"/>-->
|
||||
<!--<data android:host="www.bilibili.tv"/>-->
|
||||
<!--<data android:host="www.bilibili.cn"/>-->
|
||||
<!--<data android:host="m.bilibili.cn"/>-->
|
||||
<!--<data android:host="m.bilibili.com"/>-->
|
||||
<!--<data android:host="bilibili.cn"/>-->
|
||||
<!--<data android:host="bilibili.com"/>-->
|
||||
<!--<data android:host="bangumi.bilibili.com"/>-->
|
||||
<!--<data android:host="space.bilibili.com"/>-->
|
||||
</intent-filter>
|
||||
<intent-filter android:label="PiliPlus Debug">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<action android:name="android.intent.action.SEARCH" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="bilibili"/>
|
||||
<data android:host="forward" />
|
||||
<data android:host="comment"
|
||||
android:pathPattern="/detail/.*/.*/.*" />
|
||||
<data android:host="uper" />
|
||||
<data android:host="article"
|
||||
android:pathPattern="/readlist" />
|
||||
<data android:host="advertise" android:path="/home" />
|
||||
<data android:host="clip" />
|
||||
<data android:host="search" />
|
||||
<data android:host="stardust-search" />
|
||||
<data android:host="music" />
|
||||
<data android:host="bangumi"
|
||||
android:pathPattern="/season.*" />
|
||||
<data android:host="bangumi" android:pathPattern="/.*" />
|
||||
<data android:host="pictureshow"
|
||||
android:pathPrefix="/creative_center" />
|
||||
<data android:host="cliparea" />
|
||||
<data android:host="im" />
|
||||
<data android:host="im" android:path="/notifications" />
|
||||
<data android:host="following" />
|
||||
<data android:host="following"
|
||||
android:pathPattern="/detail/.*" />
|
||||
<data android:host="following"
|
||||
android:path="/publishInfo/" />
|
||||
<data android:host="laser" android:pathPattern="/.*" />
|
||||
<data android:host="livearea" />
|
||||
<data android:host="live" />
|
||||
<data android:host="catalog" />
|
||||
<data android:host="browser" />
|
||||
<data android:host="user_center" />
|
||||
<data android:host="login" />
|
||||
<data android:host="space" />
|
||||
<data android:host="author" />
|
||||
<data android:host="tag" />
|
||||
<data android:host="rank" />
|
||||
<data android:host="external" />
|
||||
<data android:host="blank" />
|
||||
<data android:host="home" />
|
||||
<data android:host="root" />
|
||||
<data android:host="video" />
|
||||
<data android:host="story" />
|
||||
<data android:host="podcast" />
|
||||
<data android:host="search" />
|
||||
<data android:host="main" android:path="/favorite" />
|
||||
<data android:host="pgc" android:path="/theater/match" />
|
||||
<data android:host="pgc" android:path="/theater/square" />
|
||||
<data android:host="m.bilibili.com"
|
||||
android:path="/topic-detail" />
|
||||
<data android:host="article" />
|
||||
<data android:host="pegasus"
|
||||
android:pathPattern="/channel/v2/.*" />
|
||||
<data android:host="feed" android:pathPattern="/channel" />
|
||||
<data android:host="vip" />
|
||||
<data android:host="user_center" android:path="/vip" />
|
||||
<data android:host="history" />
|
||||
<data android:host="charge" android:path="/rank" />
|
||||
<data android:host="assistant" />
|
||||
<data android:host="assistant" />
|
||||
<data android:host="feedback" />
|
||||
<data android:host="auth" android:path="/launch" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="512dp"
|
||||
android:width="512dp"
|
||||
android:viewportWidth="512.0"
|
||||
android:viewportHeight="512.0">
|
||||
<path
|
||||
android:fillColor="#FF5CB67B"
|
||||
android:pathData="M456.65,256C456.65,366.81 366.81,456.65 256,456.65 145.19,456.65 55.35,366.81 55.35,256 55.35,145.18 145.19,55.35 256,55.35 366.81,55.35 456.65,145.18 456.65,256Z"
|
||||
android:strokeWidth="0.783784"
|
||||
android:fillType="evenOdd" />
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M270.04,256L156.1,256l15.61,-76.8h98.32c21.21,0 38.4,17.19 38.4,38.4 0,21.21 -17.19,38.4 -38.4,38.4zM270.04,128L202.46,128l-50.1,256h52.76l15.18,-76.8h49.73c49.49,0 89.6,-40.12 89.6,-89.6 0,-49.49 -40.11,-89.6 -89.6,-89.6z"
|
||||
android:fillType="evenOdd" />
|
||||
</vector>
|
||||
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 7.0 KiB |
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FF5CB67B</color>
|
||||
</resources>
|
||||
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 8.8 KiB |
@@ -1,16 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="512dp"
|
||||
android:width="512dp"
|
||||
android:viewportWidth="512.0"
|
||||
android:viewportHeight="512.0">
|
||||
android:height="108dp"
|
||||
android:width="108dp"
|
||||
android:viewportWidth="108.0"
|
||||
android:viewportHeight="108.0">
|
||||
<path
|
||||
android:fillColor="#FF5CB67B"
|
||||
android:pathData="M456.65,256C456.65,366.81 366.81,456.65 256,456.65 145.19,456.65 55.35,366.81 55.35,256 55.35,145.18 145.19,55.35 256,55.35 366.81,55.35 456.65,145.18 456.65,256Z"
|
||||
android:strokeWidth="0.783784"
|
||||
android:fillColor="@color/ic_launcher_foreground"
|
||||
android:pathData="M56,54L39.78,54l2.22,-10.94h14c3.02,0 5.47,2.45 5.47,5.47 0,3.02 -2.45,5.47 -5.47,5.47zM56,35.77h-9.62l-7.13,36.45h7.51L48.92,61.29h7.08c7.05,0 12.76,-5.71 12.76,-12.76 0,-7.05 -5.71,-12.76 -12.76,-12.76z"
|
||||
android:fillType="evenOdd" />
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M270.04,256L156.1,256l15.61,-76.8h98.32c21.21,0 38.4,17.19 38.4,38.4 0,21.21 -17.19,38.4 -38.4,38.4zM270.04,128L202.46,128l-50.1,256h52.76l15.18,-76.8h49.73c49.49,0 89.6,-40.12 89.6,-89.6 0,-49.49 -40.11,-89.6 -89.6,-89.6z"
|
||||
android:fillType="evenOdd" />
|
||||
</vector>
|
||||
</vector>
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="512dp"
|
||||
android:width="512dp"
|
||||
android:viewportWidth="512.0"
|
||||
android:viewportHeight="512.0">
|
||||
<path
|
||||
android:fillColor="#FF5CB67B"
|
||||
android:pathData="M456.65,256C456.65,366.81 366.81,456.65 256,456.65 145.19,456.65 55.35,366.81 55.35,256 55.35,145.18 145.19,55.35 256,55.35 366.81,55.35 456.65,145.18 456.65,256Z"
|
||||
android:strokeWidth="0.783784"
|
||||
android:fillType="evenOdd" />
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M270.04,256L156.1,256l15.61,-76.8h98.32c21.21,0 38.4,17.19 38.4,38.4 0,21.21 -17.19,38.4 -38.4,38.4zM270.04,128L202.46,128l-50.1,256h52.76l15.18,-76.8h49.73c49.49,0 89.6,-40.12 89.6,-89.6 0,-49.49 -40.11,-89.6 -89.6,-89.6z"
|
||||
android:fillType="evenOdd" />
|
||||
</vector>
|
||||
@@ -1,16 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="512.0"
|
||||
android:viewportHeight="512.0">
|
||||
android:height="108dp"
|
||||
android:width="108dp"
|
||||
android:viewportWidth="108.0"
|
||||
android:viewportHeight="108.0">
|
||||
<path
|
||||
android:fillColor="#FF5CB67B"
|
||||
android:pathData="M456.65,256C456.65,366.81 366.81,456.65 256,456.65 145.19,456.65 55.35,366.81 55.35,256 55.35,145.18 145.19,55.35 256,55.35 366.81,55.35 456.65,145.18 456.65,256Z"
|
||||
android:strokeWidth="0.783784"
|
||||
android:fillType="evenOdd" />
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M270.04,256L156.1,256l15.61,-76.8h98.32c21.21,0 38.4,17.19 38.4,38.4 0,21.21 -17.19,38.4 -38.4,38.4zM270.04,128L202.46,128l-50.1,256h52.76l15.18,-76.8h49.73c49.49,0 89.6,-40.12 89.6,-89.6 0,-49.49 -40.11,-89.6 -89.6,-89.6z"
|
||||
android:pathData="M56,54L39.78,54l2.22,-10.94h14c3.02,0 5.47,2.45 5.47,5.47 0,3.02 -2.45,5.47 -5.47,5.47zM56,35.77h-9.62l-7.13,36.45h7.51L48.92,61.29h7.08c7.05,0 12.76,-5.71 12.76,-12.76 0,-7.05 -5.71,-12.76 -12.76,-12.76z"
|
||||
android:fillType="evenOdd" />
|
||||
</vector>
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground>
|
||||
<inset
|
||||
android:drawable="@drawable/ic_launcher_foreground"
|
||||
android:inset="16%" />
|
||||
</foreground>
|
||||
<monochrome>
|
||||
<inset
|
||||
android:drawable="@drawable/ic_launcher_monochrome"
|
||||
android:inset="16%" />
|
||||
</monochrome>
|
||||
</adaptive-icon>
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 914 B |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 3.4 KiB |
5
android/app/src/main/res/values-night-v31/colors.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_foreground">@android:color/system_accent1_100</color>
|
||||
<color name="ic_launcher_background">@android:color/system_neutral1_800</color>
|
||||
</resources>
|
||||
5
android/app/src/main/res/values-v31/colors.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_foreground">@android:color/system_neutral2_700</color>
|
||||
<color name="ic_launcher_background">@android:color/system_accent1_100</color>
|
||||
</resources>
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FF5CB67B</color>
|
||||
<color name="ic_launcher_foreground">#FF5CB67B</color>
|
||||
<color name="ic_launcher_background">#FFFFFFFF</color>
|
||||
</resources>
|
||||
BIN
assets/fonts/digital_id_num.ttf
Normal file
BIN
assets/images/logo/logo_2.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
assets/images/logo/logo_3.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
@@ -39,51 +39,55 @@ class VideoCardHSkeleton extends StatelessWidget {
|
||||
),
|
||||
// VideoContent(videoItem: videoItem)
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 4, 6, 4),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
width: 200,
|
||||
height: 11,
|
||||
margin: const EdgeInsets.only(bottom: 5),
|
||||
),
|
||||
Container(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
width: 150,
|
||||
height: 13,
|
||||
),
|
||||
const Spacer(),
|
||||
Container(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
width: 100,
|
||||
height: 13,
|
||||
margin: const EdgeInsets.only(bottom: 5),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface,
|
||||
width: 40,
|
||||
height: 13,
|
||||
margin: const EdgeInsets.only(right: 8),
|
||||
),
|
||||
Container(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface,
|
||||
width: 40,
|
||||
height: 13,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 4, 6, 4),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
color:
|
||||
Theme.of(context).colorScheme.onInverseSurface,
|
||||
width: 200,
|
||||
height: 11,
|
||||
margin: const EdgeInsets.only(bottom: 5),
|
||||
),
|
||||
Container(
|
||||
color:
|
||||
Theme.of(context).colorScheme.onInverseSurface,
|
||||
width: 150,
|
||||
height: 13,
|
||||
),
|
||||
const Spacer(),
|
||||
Container(
|
||||
color:
|
||||
Theme.of(context).colorScheme.onInverseSurface,
|
||||
width: 100,
|
||||
height: 13,
|
||||
margin: const EdgeInsets.only(bottom: 5),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface,
|
||||
width: 40,
|
||||
height: 13,
|
||||
margin: const EdgeInsets.only(right: 8),
|
||||
),
|
||||
Container(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface,
|
||||
width: 40,
|
||||
height: 13,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
|
||||
show SourceModel;
|
||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/dynamics/article_content_model.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
@@ -68,7 +70,8 @@ Widget articleContent({
|
||||
} else {
|
||||
context.imageView(
|
||||
initialPage: imgList.indexOf(item.pic!.pics!.first.url!),
|
||||
imgList: imgList,
|
||||
imgList:
|
||||
imgList.map((url) => SourceModel(url: url)).toList(),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
32
lib/common/widgets/dialog.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
void showConfirmDialog({
|
||||
required BuildContext context,
|
||||
required String title,
|
||||
String? content,
|
||||
required VoidCallback onConfirm,
|
||||
}) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(title),
|
||||
content: content == null ? null : Text(content),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: Get.back,
|
||||
child: Text(
|
||||
'取消',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: onConfirm,
|
||||
child: Text('确认'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
|
||||
show SourceModel;
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_html/flutter_html.dart';
|
||||
@@ -54,7 +56,7 @@ Widget htmlRender({
|
||||
callback([imgUrl], 0);
|
||||
} else {
|
||||
context.imageView(
|
||||
imgList: [imgUrl],
|
||||
imgList: [SourceModel(url: imgUrl)],
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,9 +2,12 @@ import 'dart:math';
|
||||
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
|
||||
show SourceModel, SourceType;
|
||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/nine_grid_view.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ImageModel {
|
||||
@@ -12,16 +15,20 @@ class ImageModel {
|
||||
required this.width,
|
||||
required this.height,
|
||||
required this.url,
|
||||
this.liveUrl,
|
||||
});
|
||||
|
||||
dynamic width;
|
||||
dynamic height;
|
||||
String url;
|
||||
String? liveUrl;
|
||||
bool? _isLongPic;
|
||||
bool? _isLivePhoto;
|
||||
|
||||
dynamic get safeWidth => width ?? 1;
|
||||
dynamic get safeHeight => height ?? 1;
|
||||
bool get isLongPic => _isLongPic ??= (safeHeight / safeWidth) > (22 / 9);
|
||||
bool get isLivePhoto => _isLivePhoto ??= liveUrl?.isNotEmpty == true;
|
||||
}
|
||||
|
||||
Widget imageview(
|
||||
@@ -83,6 +90,17 @@ Widget imageview(
|
||||
);
|
||||
}
|
||||
|
||||
late final enableLivePhoto = GStorage.enableLivePhoto;
|
||||
|
||||
int parseSize(size) {
|
||||
return switch (size) {
|
||||
int() => size,
|
||||
double() => size.round(),
|
||||
String() => int.tryParse(size) ?? 1,
|
||||
_ => 1,
|
||||
};
|
||||
}
|
||||
|
||||
return NineGridView(
|
||||
type: NineGridType.weiBo,
|
||||
margin: const EdgeInsets.only(top: 6),
|
||||
@@ -102,7 +120,19 @@ Widget imageview(
|
||||
onViewImage?.call();
|
||||
context.imageView(
|
||||
initialPage: index,
|
||||
imgList: picArr.map((item) => item.url).toList(),
|
||||
imgList: picArr.map(
|
||||
(item) {
|
||||
bool isLive = item.isLivePhoto && enableLivePhoto;
|
||||
return SourceModel(
|
||||
sourceType:
|
||||
isLive ? SourceType.livePhoto : SourceType.networkImage,
|
||||
url: item.url,
|
||||
liveUrl: isLive ? item.liveUrl : null,
|
||||
width: isLive ? parseSize(item.width) : null,
|
||||
height: isLive ? parseSize(item.height) : null,
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
onDismissed: onDismissed,
|
||||
);
|
||||
}
|
||||
@@ -143,7 +173,14 @@ Widget imageview(
|
||||
},
|
||||
),
|
||||
),
|
||||
if (picArr[index].isLongPic)
|
||||
if (picArr[index].isLivePhoto)
|
||||
const PBadge(
|
||||
text: 'Live',
|
||||
right: 8,
|
||||
bottom: 8,
|
||||
type: 'gray',
|
||||
)
|
||||
else if (picArr[index].isLongPic)
|
||||
const PBadge(
|
||||
text: '长图',
|
||||
right: 8,
|
||||
|
||||
@@ -10,6 +10,8 @@ import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:media_kit/media_kit.dart';
|
||||
import 'package:media_kit_video/media_kit_video.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:status_bar_control/status_bar_control.dart';
|
||||
@@ -33,6 +35,24 @@ typedef IndexedFocusedWidgetBuilder = Widget Function(
|
||||
|
||||
typedef IndexedTagStringBuilder = String Function(int index);
|
||||
|
||||
enum SourceType { fileImage, networkImage, livePhoto }
|
||||
|
||||
class SourceModel {
|
||||
final SourceType sourceType;
|
||||
final String url;
|
||||
final String? liveUrl;
|
||||
final int? width;
|
||||
final int? height;
|
||||
|
||||
const SourceModel({
|
||||
this.sourceType = SourceType.networkImage,
|
||||
required this.url,
|
||||
this.liveUrl,
|
||||
this.width,
|
||||
this.height,
|
||||
});
|
||||
}
|
||||
|
||||
class InteractiveviewerGallery<T> extends StatefulWidget {
|
||||
const InteractiveviewerGallery({
|
||||
super.key,
|
||||
@@ -45,17 +65,14 @@ class InteractiveviewerGallery<T> extends StatefulWidget {
|
||||
this.onDismissed,
|
||||
this.setStatusBar,
|
||||
this.onClose,
|
||||
this.isFile,
|
||||
});
|
||||
|
||||
final bool? isFile;
|
||||
|
||||
final VoidCallback? onClose;
|
||||
|
||||
final bool? setStatusBar;
|
||||
|
||||
/// The sources to show.
|
||||
final List<String> sources;
|
||||
final List<SourceModel> sources;
|
||||
|
||||
/// The index of the first source in [sources] to show.
|
||||
final int initIndex;
|
||||
@@ -92,7 +109,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
|
||||
late Offset _doubleTapLocalPosition;
|
||||
|
||||
int? currentIndex;
|
||||
late final RxInt currentIndex = widget.initIndex.obs;
|
||||
|
||||
late List<bool> _thumbList;
|
||||
late final int _quality = GStorage.previewQ;
|
||||
@@ -115,10 +132,13 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
_animation?.value ?? Matrix4.identity();
|
||||
});
|
||||
|
||||
currentIndex = widget.initIndex;
|
||||
if (widget.setStatusBar != false) {
|
||||
setStatusBar();
|
||||
}
|
||||
|
||||
if (widget.sources[currentIndex.value].sourceType == SourceType.livePhoto) {
|
||||
_onPlay(currentIndex.value);
|
||||
}
|
||||
}
|
||||
|
||||
setStatusBar() async {
|
||||
@@ -132,6 +152,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
|
||||
@override
|
||||
void dispose() async {
|
||||
_player?.dispose();
|
||||
_pageController?.dispose();
|
||||
_animationController.removeListener(() {});
|
||||
_animationController.dispose();
|
||||
@@ -140,8 +161,8 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
StatusBarControl.setHidden(false, animation: StatusBarAnimation.FADE);
|
||||
}
|
||||
}
|
||||
if (widget.isFile != true) {
|
||||
for (int index = 0; index < widget.sources.length; index++) {
|
||||
for (int index = 0; index < widget.sources.length; index++) {
|
||||
if (widget.sources[index].sourceType == SourceType.networkImage) {
|
||||
CachedNetworkImageProvider(_getActualUrl(index)).evict();
|
||||
}
|
||||
}
|
||||
@@ -201,14 +222,22 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
}
|
||||
}
|
||||
|
||||
void _onPlay(int index) {
|
||||
_player ??= Player();
|
||||
_videoController ??= VideoController(_player!);
|
||||
_player!.open(Media(widget.sources[index].liveUrl!));
|
||||
}
|
||||
|
||||
/// When the page view changed its page, the source will animate back into the
|
||||
/// original scale if it was scaled up.
|
||||
///
|
||||
/// Additionally the swipe up / down to dismiss gets enabled.
|
||||
void _onPageChanged(int page) {
|
||||
setState(() {
|
||||
currentIndex = page;
|
||||
});
|
||||
_player?.pause();
|
||||
currentIndex.value = page;
|
||||
if (widget.sources[page].sourceType == SourceType.livePhoto) {
|
||||
_onPlay(page);
|
||||
}
|
||||
widget.onPageChanged?.call(page);
|
||||
if (_transformationController!.value != Matrix4.identity()) {
|
||||
// animate the reset for the transformation of the interactive viewer
|
||||
@@ -225,8 +254,8 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
}
|
||||
|
||||
String _getActualUrl(int index) => _thumbList[index] && _quality != 100
|
||||
? '${widget.sources[index]}@${_quality}q.webp'.http2https
|
||||
: widget.sources[index].http2https;
|
||||
? '${widget.sources[index].url}@${_quality}q.webp'.http2https
|
||||
: widget.sources[index].url.http2https;
|
||||
|
||||
void onClose() {
|
||||
if (widget.onClose != null) {
|
||||
@@ -237,6 +266,9 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
}
|
||||
}
|
||||
|
||||
Player? _player;
|
||||
VideoController? _videoController;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
@@ -272,12 +304,15 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
_doubleTapLocalPosition = details.localPosition;
|
||||
},
|
||||
onDoubleTap: onDoubleTap,
|
||||
onLongPress: widget.isFile == true ? null : onLongPress,
|
||||
onLongPress:
|
||||
widget.sources[index].sourceType == SourceType.fileImage
|
||||
? null
|
||||
: onLongPress,
|
||||
child: widget.itemBuilder != null
|
||||
? widget.itemBuilder!(
|
||||
context,
|
||||
index,
|
||||
index == currentIndex,
|
||||
index == currentIndex.value,
|
||||
_enablePageView,
|
||||
)
|
||||
: _itemBuilder(index),
|
||||
@@ -321,51 +356,70 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
if (widget.sources.length > 1)
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
"${currentIndex! + 1}/${widget.sources.length}",
|
||||
style: const TextStyle(color: Colors.white),
|
||||
child: Obx(
|
||||
() => Text(
|
||||
"${currentIndex.value + 1}/${widget.sources.length}",
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.isFile != true)
|
||||
if (widget.sources[currentIndex.value].sourceType !=
|
||||
SourceType.fileImage)
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: PopupMenuButton(
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
PopupMenuItem(
|
||||
value: 0,
|
||||
onTap: () =>
|
||||
onShareImg(widget.sources[currentIndex!]),
|
||||
onTap: () => onShareImg(
|
||||
widget.sources[currentIndex.value].url),
|
||||
child: const Text("分享图片"),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 1,
|
||||
onTap: () {
|
||||
Utils.copyText(widget.sources[currentIndex!]);
|
||||
Utils.copyText(
|
||||
widget.sources[currentIndex.value].url);
|
||||
},
|
||||
child: const Text("复制链接"),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 2,
|
||||
onTap: () {
|
||||
DownloadUtils.downloadImg(
|
||||
context,
|
||||
[widget.sources[currentIndex!]],
|
||||
[widget.sources[currentIndex.value].url],
|
||||
);
|
||||
},
|
||||
child: const Text("保存图片"),
|
||||
),
|
||||
if (widget.sources.length > 1)
|
||||
PopupMenuItem(
|
||||
value: 3,
|
||||
onTap: () {
|
||||
DownloadUtils.downloadImg(
|
||||
context,
|
||||
widget.sources,
|
||||
widget.sources
|
||||
.map((item) => item.url)
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
child: const Text("保存全部图片"),
|
||||
),
|
||||
if (widget.sources[currentIndex.value].sourceType ==
|
||||
SourceType.livePhoto)
|
||||
PopupMenuItem(
|
||||
onTap: () {
|
||||
DownloadUtils.downloadLivePhoto(
|
||||
context: context,
|
||||
url: widget.sources[currentIndex.value].url,
|
||||
liveUrl: widget
|
||||
.sources[currentIndex.value].liveUrl!,
|
||||
width:
|
||||
widget.sources[currentIndex.value].width!,
|
||||
height: widget
|
||||
.sources[currentIndex.value].height!,
|
||||
);
|
||||
},
|
||||
child: const Text("保存 Live Photo"),
|
||||
),
|
||||
];
|
||||
},
|
||||
child: const Icon(Icons.more_horiz, color: Colors.white),
|
||||
@@ -396,34 +450,43 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
Widget _itemBuilder(index) {
|
||||
return Center(
|
||||
child: Hero(
|
||||
tag: widget.sources[index],
|
||||
child: widget.isFile == true
|
||||
? Image(
|
||||
filterQuality: FilterQuality.low,
|
||||
image: FileImage(File(widget.sources[index])),
|
||||
)
|
||||
: CachedNetworkImage(
|
||||
fadeInDuration: const Duration(milliseconds: 0),
|
||||
fadeOutDuration: const Duration(milliseconds: 0),
|
||||
imageUrl: _getActualUrl(index),
|
||||
// fit: BoxFit.contain,
|
||||
progressIndicatorBuilder: (context, url, progress) {
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: 150.0,
|
||||
child: LinearProgressIndicator(
|
||||
value: progress.progress ?? 0),
|
||||
),
|
||||
);
|
||||
},
|
||||
// errorListener: (value) {
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// setState(() {
|
||||
// _thumbList[index] = false;
|
||||
// });
|
||||
// });
|
||||
// },
|
||||
),
|
||||
tag: widget.sources[index].url,
|
||||
child: switch (widget.sources[index].sourceType) {
|
||||
SourceType.fileImage => Image(
|
||||
filterQuality: FilterQuality.low,
|
||||
image: FileImage(File(widget.sources[index].url)),
|
||||
),
|
||||
SourceType.networkImage => CachedNetworkImage(
|
||||
fadeInDuration: const Duration(milliseconds: 0),
|
||||
fadeOutDuration: const Duration(milliseconds: 0),
|
||||
imageUrl: _getActualUrl(index),
|
||||
// fit: BoxFit.contain,
|
||||
progressIndicatorBuilder: (context, url, progress) {
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: 150.0,
|
||||
child:
|
||||
LinearProgressIndicator(value: progress.progress ?? 0),
|
||||
),
|
||||
);
|
||||
},
|
||||
// errorListener: (value) {
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// setState(() {
|
||||
// _thumbList[index] = false;
|
||||
// });
|
||||
// });
|
||||
// },
|
||||
),
|
||||
SourceType.livePhoto => Obx(() => currentIndex.value == index
|
||||
? IgnorePointer(
|
||||
child: Video(
|
||||
controller: _videoController!,
|
||||
fill: Colors.transparent,
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink()),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -487,7 +550,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
children: [
|
||||
ListTile(
|
||||
onTap: () {
|
||||
onShareImg(widget.sources[currentIndex!]);
|
||||
onShareImg(widget.sources[currentIndex.value].url);
|
||||
Get.back();
|
||||
},
|
||||
dense: true,
|
||||
@@ -496,7 +559,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
ListTile(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
Utils.copyText(widget.sources[currentIndex!]);
|
||||
Utils.copyText(widget.sources[currentIndex.value].url);
|
||||
},
|
||||
dense: true,
|
||||
title: const Text('复制链接', style: TextStyle(fontSize: 14)),
|
||||
@@ -506,7 +569,7 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
Get.back();
|
||||
DownloadUtils.downloadImg(
|
||||
context,
|
||||
[widget.sources[currentIndex!]],
|
||||
[widget.sources[currentIndex.value].url],
|
||||
);
|
||||
},
|
||||
dense: true,
|
||||
@@ -518,12 +581,31 @@ class _InteractiveviewerGalleryState extends State<InteractiveviewerGallery>
|
||||
Get.back();
|
||||
DownloadUtils.downloadImg(
|
||||
context,
|
||||
widget.sources,
|
||||
widget.sources.map((item) => item.url).toList(),
|
||||
);
|
||||
},
|
||||
dense: true,
|
||||
title: const Text('保存全部图片', style: TextStyle(fontSize: 14)),
|
||||
),
|
||||
if (widget.sources[currentIndex.value].sourceType ==
|
||||
SourceType.livePhoto)
|
||||
ListTile(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
DownloadUtils.downloadLivePhoto(
|
||||
context: context,
|
||||
url: widget.sources[currentIndex.value].url,
|
||||
liveUrl: widget.sources[currentIndex.value].liveUrl!,
|
||||
width: widget.sources[currentIndex.value].width!,
|
||||
height: widget.sources[currentIndex.value].height!,
|
||||
);
|
||||
},
|
||||
dense: true,
|
||||
title: const Text(
|
||||
'保存 Live Photo',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -410,7 +410,7 @@ class _NineGridViewState extends State<NineGridView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget? child = Container();
|
||||
Widget? child;
|
||||
double? realWidth = widget.width;
|
||||
double? realHeight = widget.height;
|
||||
switch (widget.type) {
|
||||
|
||||
25
lib/common/widgets/radio_widget.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
Widget radioWidget<T>({
|
||||
required T value,
|
||||
T? groupValue,
|
||||
required ValueChanged onChanged,
|
||||
required String title,
|
||||
double? paddingStart,
|
||||
}) {
|
||||
return InkWell(
|
||||
onTap: () => onChanged(value),
|
||||
child: Row(
|
||||
children: [
|
||||
if (paddingStart != null) SizedBox(width: paddingStart),
|
||||
Radio(
|
||||
value: value,
|
||||
groupValue: groupValue,
|
||||
onChanged: onChanged,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
Text(title),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -151,7 +151,8 @@ class VideoCardH extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
videoContent(context)
|
||||
const SizedBox(width: 10),
|
||||
videoContent(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -176,107 +177,102 @@ class VideoCardH extends StatelessWidget {
|
||||
: '';
|
||||
if (pubdate != '') pubdate += ' ';
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (videoItem.title is String)
|
||||
Expanded(
|
||||
child: Text(
|
||||
videoItem.title as String,
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||
height: 1.42,
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (videoItem.title is String)
|
||||
Expanded(
|
||||
child: Text(
|
||||
videoItem.title,
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||
height: 1.42,
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
)
|
||||
else
|
||||
Expanded(
|
||||
child: RichText(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
textScaler: MediaQuery.textScalerOf(context),
|
||||
text: TextSpan(
|
||||
children: [
|
||||
for (final i in videoItem.title) ...[
|
||||
TextSpan(
|
||||
text: i['text'] as String,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.fontSize,
|
||||
letterSpacing: 0.3,
|
||||
color: i['type'] == 'em'
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
)
|
||||
else
|
||||
Expanded(
|
||||
child: Text.rich(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
TextSpan(
|
||||
children: [
|
||||
for (final i in videoItem.title) ...[
|
||||
TextSpan(
|
||||
text: i['text'] as String,
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||
height: 1.42,
|
||||
letterSpacing: 0.3,
|
||||
color: i['type'] == 'em'
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
// const Spacer(),
|
||||
// if (videoItem.rcmdReason != null &&
|
||||
// videoItem.rcmdReason.content != '')
|
||||
// Container(
|
||||
// padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(4),
|
||||
// border: Border.all(
|
||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
||||
// ),
|
||||
// child: Text(
|
||||
// videoItem.rcmdReason.content,
|
||||
// style: TextStyle(
|
||||
// fontSize: 9,
|
||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 4),
|
||||
if (showOwner || showPubdate)
|
||||
Expanded(
|
||||
flex: 0,
|
||||
child: Text(
|
||||
"$pubdate ${showOwner ? videoItem.owner.name : ''}",
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
Row(
|
||||
children: [
|
||||
if (showView) ...[
|
||||
statView(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
view: videoItem.stat.view as int,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
if (showDanmaku)
|
||||
statDanMu(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
danmu: videoItem.stat.danmu as int,
|
||||
),
|
||||
const Spacer(),
|
||||
if (source == 'normal') const SizedBox(width: 24),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
// const Spacer(),
|
||||
// if (videoItem.rcmdReason != null &&
|
||||
// videoItem.rcmdReason.content != '')
|
||||
// Container(
|
||||
// padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(4),
|
||||
// border: Border.all(
|
||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
||||
// ),
|
||||
// child: Text(
|
||||
// videoItem.rcmdReason.content,
|
||||
// style: TextStyle(
|
||||
// fontSize: 9,
|
||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 4),
|
||||
if (showOwner || showPubdate)
|
||||
Expanded(
|
||||
flex: 0,
|
||||
child: Text(
|
||||
"$pubdate ${showOwner ? videoItem.owner.name : ''}",
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
Row(
|
||||
children: [
|
||||
if (showView) ...[
|
||||
statView(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
view: videoItem.stat.view as int,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
if (showDanmaku)
|
||||
statDanMu(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
danmu: videoItem.stat.danmu as int,
|
||||
),
|
||||
const Spacer(),
|
||||
if (source == 'normal') const SizedBox(width: 24),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -111,7 +111,8 @@ class VideoCardHGrpc extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
videoContent(context)
|
||||
const SizedBox(width: 10),
|
||||
videoContent(context),
|
||||
],
|
||||
);
|
||||
},
|
||||
@@ -133,58 +134,43 @@ class VideoCardHGrpc extends StatelessWidget {
|
||||
|
||||
Widget videoContent(context) {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
...[
|
||||
Expanded(
|
||||
child: Text(
|
||||
videoItem.smallCoverV5.base.title,
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||
height: 1.42,
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
videoItem.smallCoverV5.base.title,
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||
height: 1.42,
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
],
|
||||
// const Spacer(),
|
||||
// if (videoItem.rcmdReason != null &&
|
||||
// videoItem.rcmdReason.content != '')
|
||||
// Container(
|
||||
// padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(4),
|
||||
// border: Border.all(
|
||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
||||
// ),
|
||||
// child: Text(
|
||||
// videoItem.rcmdReason.content,
|
||||
// style: TextStyle(
|
||||
// fontSize: 9,
|
||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 4),
|
||||
if (showOwner || showPubdate)
|
||||
Text(
|
||||
videoItem.smallCoverV5.rightDesc1,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
// const Spacer(),
|
||||
// if (videoItem.rcmdReason != null &&
|
||||
// videoItem.rcmdReason.content != '')
|
||||
// Container(
|
||||
// padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(4),
|
||||
// border: Border.all(
|
||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
||||
// ),
|
||||
// child: Text(
|
||||
// videoItem.rcmdReason.content,
|
||||
// style: TextStyle(
|
||||
// fontSize: 9,
|
||||
// color: Theme.of(context).colorScheme.surfaceTint),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 4),
|
||||
if (showOwner || showPubdate)
|
||||
Text(
|
||||
videoItem.smallCoverV5.rightDesc2,
|
||||
videoItem.smallCoverV5.rightDesc1,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
@@ -193,26 +179,36 @@ class VideoCardHGrpc extends StatelessWidget {
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
// Row(
|
||||
// children: [
|
||||
// if (showView) ...[
|
||||
// StatView(
|
||||
// theme: 'gray',
|
||||
// view: videoItem.stat.view as int,
|
||||
// ),
|
||||
// const SizedBox(width: 8),
|
||||
// ],
|
||||
// if (showDanmaku)
|
||||
// StatDanMu(
|
||||
// theme: 'gray',
|
||||
// danmu: videoItem.stat.danmu as int,
|
||||
// ),
|
||||
// const Spacer(),
|
||||
// if (source == 'normal') const SizedBox(width: 24),
|
||||
// ],
|
||||
// ),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
Text(
|
||||
videoItem.smallCoverV5.rightDesc2,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
// Row(
|
||||
// children: [
|
||||
// if (showView) ...[
|
||||
// StatView(
|
||||
// theme: 'gray',
|
||||
// view: videoItem.stat.view as int,
|
||||
// ),
|
||||
// const SizedBox(width: 8),
|
||||
// ],
|
||||
// if (showDanmaku)
|
||||
// StatDanMu(
|
||||
// theme: 'gray',
|
||||
// danmu: videoItem.stat.danmu as int,
|
||||
// ),
|
||||
// const Spacer(),
|
||||
// if (source == 'normal') const SizedBox(width: 24),
|
||||
// ],
|
||||
// ),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -103,7 +103,8 @@ class VideoCardHMemberVideo extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
videoContent(context)
|
||||
const SizedBox(width: 10),
|
||||
videoContent(context),
|
||||
],
|
||||
);
|
||||
},
|
||||
@@ -125,66 +126,61 @@ class VideoCardHMemberVideo extends StatelessWidget {
|
||||
|
||||
Widget videoContent(context) {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
...[
|
||||
Expanded(
|
||||
child: Text(
|
||||
// videoItem.season?['title'] ?? videoItem.title ?? '',
|
||||
videoItem.title ?? '',
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontWeight: videoItem.bvid != null && videoItem.bvid == bvid
|
||||
? FontWeight.bold
|
||||
: null,
|
||||
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||
height: 1.42,
|
||||
letterSpacing: 0.3,
|
||||
color: videoItem.bvid != null && videoItem.bvid == bvid
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: null,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
// videoItem.season?['title'] ?? videoItem.title ?? '',
|
||||
videoItem.title ?? '',
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontWeight: videoItem.bvid != null && videoItem.bvid == bvid
|
||||
? FontWeight.bold
|
||||
: null,
|
||||
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||
height: 1.42,
|
||||
letterSpacing: 0.3,
|
||||
color: videoItem.bvid != null && videoItem.bvid == bvid
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: null,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
videoItem.season != null
|
||||
? Utils.dateFormat(videoItem.season?['mtime'])
|
||||
: videoItem.publishTimeText ?? '',
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
Row(
|
||||
children: [
|
||||
statView(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
// view: videoItem.season?['view_content'] ??
|
||||
// videoItem.viewContent,
|
||||
view: videoItem.viewContent,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
statDanMu(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
// danmu: videoItem.season?['danmaku'] ?? videoItem.danmaku,
|
||||
danmu: videoItem.danmaku,
|
||||
),
|
||||
],
|
||||
Text(
|
||||
videoItem.season != null
|
||||
? Utils.dateFormat(videoItem.season?['mtime'])
|
||||
: videoItem.publishTimeText ?? '',
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
Row(
|
||||
children: [
|
||||
statView(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
// view: videoItem.season?['view_content'] ??
|
||||
// videoItem.viewContent,
|
||||
view: videoItem.viewContent,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
statDanMu(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
// danmu: videoItem.season?['danmaku'] ?? videoItem.danmaku,
|
||||
danmu: videoItem.danmaku,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -314,10 +314,9 @@ class VideoCardV extends StatelessWidget {
|
||||
const Spacer(),
|
||||
Expanded(
|
||||
flex: 0,
|
||||
child: RichText(
|
||||
child: Text.rich(
|
||||
maxLines: 1,
|
||||
textScaler: MediaQuery.textScalerOf(context),
|
||||
text: TextSpan(
|
||||
TextSpan(
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
@@ -337,10 +336,9 @@ class VideoCardV extends StatelessWidget {
|
||||
const Spacer(),
|
||||
Expanded(
|
||||
flex: 0,
|
||||
child: RichText(
|
||||
child: Text.rich(
|
||||
maxLines: 1,
|
||||
textScaler: MediaQuery.textScalerOf(context),
|
||||
text: TextSpan(
|
||||
TextSpan(
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
|
||||
@@ -121,7 +121,15 @@ class Api {
|
||||
// up_mid num 目标用户mid 必要
|
||||
// type num 目标内容属性 非必要 默认为全部 0:全部 2:视频稿件
|
||||
// rid num 目标 视频稿件avid
|
||||
static const String videoInFolder = '/x/v3/fav/folder/created/list-all';
|
||||
static const String favFolder = '/x/v3/fav/folder/created/list-all';
|
||||
|
||||
static const String copyFav = '/x/v3/fav/resource/copy';
|
||||
|
||||
static const String moveFav = '/x/v3/fav/resource/move';
|
||||
|
||||
static const String copyToview = '/x/v2/history/toview/copy';
|
||||
|
||||
static const String moveToview = '/x/v2/history/toview/move';
|
||||
|
||||
// 视频详情页 相关视频
|
||||
static const String relatedList = '/x/web-interface/archive/related';
|
||||
@@ -182,6 +190,8 @@ class Api {
|
||||
|
||||
static const String deleteFolder = '/x/v3/fav/folder/del';
|
||||
|
||||
static const String cleanFav = '/x/v3/fav/resource/clean';
|
||||
|
||||
/// 收藏夹 详情
|
||||
/// media_id 当前收藏夹id 搜索全部时为默认收藏夹id
|
||||
/// pn int 当前页
|
||||
|
||||
@@ -14,7 +14,7 @@ class DynamicsHttp {
|
||||
'type': type ?? 'all',
|
||||
'timezone_offset': '-480',
|
||||
'offset': offset,
|
||||
'features': 'itemOpusStyle'
|
||||
'features': 'itemOpusStyle,listOnlyfans'
|
||||
};
|
||||
if (mid != -1) {
|
||||
data['host_mid'] = mid;
|
||||
|
||||
@@ -404,7 +404,7 @@ class MemberHttp {
|
||||
'offset': offset ?? '',
|
||||
'host_mid': mid,
|
||||
'timezone_offset': '-480',
|
||||
'features': 'itemOpusStyle',
|
||||
'features': 'itemOpusStyle,listOnlyfans',
|
||||
'platform': 'web',
|
||||
'web_location': '333.999',
|
||||
'dm_img_list': '[]',
|
||||
|
||||
@@ -62,6 +62,27 @@ class UserHttp {
|
||||
}
|
||||
}
|
||||
|
||||
static Future cleanFav({
|
||||
required dynamic mediaId,
|
||||
}) async {
|
||||
var res = await Request().post(
|
||||
Api.cleanFav,
|
||||
data: {
|
||||
'media_id': mediaId,
|
||||
'platform': 'web',
|
||||
'csrf': await Request.getCsrf(),
|
||||
},
|
||||
options: Options(
|
||||
contentType: Headers.formUrlEncodedContentType,
|
||||
),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true, 'data': res.data['data']};
|
||||
} else {
|
||||
return {'status': false, 'msg': res.data['message']};
|
||||
}
|
||||
}
|
||||
|
||||
static Future deleteFolder({
|
||||
required List<dynamic> mediaIds,
|
||||
}) async {
|
||||
|
||||
@@ -676,6 +676,52 @@ class VideoHttp {
|
||||
// }
|
||||
// }
|
||||
|
||||
static Future copyOrMoveFav({
|
||||
required bool isCopy,
|
||||
required bool isFav,
|
||||
required dynamic srcMediaId,
|
||||
required dynamic tarMediaId,
|
||||
dynamic mid,
|
||||
required List resources,
|
||||
}) async {
|
||||
var res = await Request().post(
|
||||
isFav
|
||||
? isCopy
|
||||
? Api.copyFav
|
||||
: Api.moveFav
|
||||
: isCopy
|
||||
? Api.copyToview
|
||||
: Api.moveToview,
|
||||
data: {
|
||||
if (srcMediaId != null) 'src_media_id': srcMediaId,
|
||||
'tar_media_id': tarMediaId,
|
||||
if (mid != null) 'mid': mid,
|
||||
'resources': resources.join(','),
|
||||
'platform': 'web',
|
||||
'csrf': await Request.getCsrf(),
|
||||
},
|
||||
options: Options(contentType: Headers.formUrlEncodedContentType),
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true};
|
||||
} else {
|
||||
return {'status': false, 'msg': res.data['message']};
|
||||
}
|
||||
}
|
||||
|
||||
static Future allFavFolders(mid) async {
|
||||
var res = await Request().get(
|
||||
Api.favFolder,
|
||||
queryParameters: {'up_mid': mid},
|
||||
);
|
||||
if (res.data['code'] == 0) {
|
||||
FavFolderData data = FavFolderData.fromJson(res.data['data']);
|
||||
return {'status': true, 'data': data};
|
||||
} else {
|
||||
return {'status': false, 'msg': res.data['message']};
|
||||
}
|
||||
}
|
||||
|
||||
// 查看视频被收藏在哪个文件夹
|
||||
static Future videoInFolder({
|
||||
dynamic mid,
|
||||
@@ -683,7 +729,7 @@ class VideoHttp {
|
||||
dynamic type,
|
||||
}) async {
|
||||
var res = await Request().get(
|
||||
Api.videoInFolder,
|
||||
Api.favFolder,
|
||||
queryParameters: {
|
||||
'up_mid': mid,
|
||||
'rid': rid,
|
||||
|
||||
@@ -275,6 +275,12 @@ class MyApp extends StatelessWidget {
|
||||
progressIndicatorTheme: ProgressIndicatorThemeData(
|
||||
refreshBackgroundColor: colorScheme.onSecondary,
|
||||
),
|
||||
dialogTheme: DialogTheme(
|
||||
titleTextStyle: TextStyle(
|
||||
fontSize: 18,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
|
||||
class DynamicsDataModel {
|
||||
DynamicsDataModel({
|
||||
this.hasMore,
|
||||
@@ -126,6 +128,8 @@ class ModuleAuthorModel {
|
||||
this.pubTs,
|
||||
this.type,
|
||||
this.vip,
|
||||
this.decorate,
|
||||
this.pendant,
|
||||
});
|
||||
|
||||
String? face;
|
||||
@@ -139,6 +143,8 @@ class ModuleAuthorModel {
|
||||
int? pubTs;
|
||||
String? type;
|
||||
Map? vip;
|
||||
Map? decorate;
|
||||
Map? pendant;
|
||||
|
||||
ModuleAuthorModel.fromJson(Map<String, dynamic> json) {
|
||||
face = json['face'];
|
||||
@@ -152,7 +158,13 @@ class ModuleAuthorModel {
|
||||
pubTs = json['pub_ts'] == 0 ? null : json['pub_ts'];
|
||||
type = json['type'];
|
||||
vip = json['vip'];
|
||||
if (showDynDecorate) {
|
||||
decorate = json['decorate'];
|
||||
pendant = json['pendant'];
|
||||
}
|
||||
}
|
||||
|
||||
static bool showDynDecorate = GStorage.showDynDecorate;
|
||||
}
|
||||
|
||||
// 单个动态详情 - 动态信息
|
||||
@@ -421,6 +433,7 @@ class DynamicMajorModel {
|
||||
this.courses,
|
||||
this.common,
|
||||
this.music,
|
||||
this.blocked,
|
||||
});
|
||||
|
||||
DynamicArchiveModel? archive;
|
||||
@@ -438,6 +451,7 @@ class DynamicMajorModel {
|
||||
Map? courses;
|
||||
Map? common;
|
||||
Map? music;
|
||||
Map? blocked;
|
||||
|
||||
DynamicMajorModel.fromJson(Map<String, dynamic> json) {
|
||||
archive = json['archive'] != null
|
||||
@@ -463,6 +477,7 @@ class DynamicMajorModel {
|
||||
courses = json['courses'] ?? {};
|
||||
common = json['common'] ?? {};
|
||||
music = json['music'] ?? {};
|
||||
blocked = json['blocked'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,6 +673,7 @@ class OpusPicsModel {
|
||||
int? size;
|
||||
String? src;
|
||||
String? url;
|
||||
String? liveUrl;
|
||||
|
||||
OpusPicsModel.fromJson(Map<String, dynamic> json) {
|
||||
width = json['width'];
|
||||
@@ -665,6 +681,7 @@ class OpusPicsModel {
|
||||
size = json['size'] != null ? json['size'].toInt() : 0;
|
||||
src = json['src'];
|
||||
url = json['url'];
|
||||
liveUrl = json['live_url'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,8 @@ class _AboutPageState extends State<AboutPage> {
|
||||
TextStyle subTitleStyle =
|
||||
TextStyle(fontSize: 13, color: Theme.of(context).colorScheme.outline);
|
||||
return Scaffold(
|
||||
appBar: widget.showAppBar == false ? null : AppBar(title: Text('关于')),
|
||||
appBar:
|
||||
widget.showAppBar == false ? null : AppBar(title: const Text('关于')),
|
||||
body: ListView(
|
||||
children: [
|
||||
GestureDetector(
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:PiliPlus/http/user.dart';
|
||||
import 'package:PiliPlus/pages/common/common_controller.dart';
|
||||
import 'package:PiliPlus/pages/video/detail/introduction/controller.dart';
|
||||
import 'package:PiliPlus/pages/video/detail/introduction/pay_coins_page.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
@@ -59,8 +60,7 @@ class BangumiIntroController extends CommonController {
|
||||
dynamic videoTags;
|
||||
bool isLogin = false;
|
||||
Rx<FavFolderData> favFolderData = FavFolderData().obs;
|
||||
List addMediaIdsNew = [];
|
||||
List delMediaIdsNew = [];
|
||||
List? favIds;
|
||||
dynamic userInfo;
|
||||
|
||||
late final enableQuickFav =
|
||||
@@ -314,12 +314,20 @@ class BangumiIntroController extends CommonController {
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
List addMediaIdsNew = [];
|
||||
List delMediaIdsNew = [];
|
||||
try {
|
||||
for (var i in favFolderData.value.list!.toList()) {
|
||||
bool isFaved = favIds?.contains(i.id) == true;
|
||||
if (i.favState == 1) {
|
||||
addMediaIdsNew.add(i.id);
|
||||
if (isFaved.not) {
|
||||
addMediaIdsNew.add(i.id);
|
||||
}
|
||||
} else {
|
||||
delMediaIdsNew.add(i.id);
|
||||
if (isFaved) {
|
||||
delMediaIdsNew.add(i.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
@@ -330,8 +338,6 @@ class BangumiIntroController extends CommonController {
|
||||
delIds: delMediaIdsNew.join(','),
|
||||
);
|
||||
if (result['status']) {
|
||||
addMediaIdsNew = [];
|
||||
delMediaIdsNew = [];
|
||||
SmartDialog.showToast('操作成功');
|
||||
Get.back();
|
||||
Future.delayed(const Duration(milliseconds: 255), () {
|
||||
@@ -394,14 +400,10 @@ class BangumiIntroController extends CommonController {
|
||||
onChoose(bool checkValue, int index) {
|
||||
feedBack();
|
||||
List<FavFolderItemData> datalist = favFolderData.value.list!;
|
||||
for (var i = 0; i < datalist.length; i++) {
|
||||
if (i == index) {
|
||||
datalist[i].favState = checkValue == true ? 1 : 0;
|
||||
datalist[i].mediaCount = checkValue == true
|
||||
? datalist[i].mediaCount! + 1
|
||||
: datalist[i].mediaCount! - 1;
|
||||
}
|
||||
}
|
||||
datalist[index].favState = checkValue ? 1 : 0;
|
||||
datalist[index].mediaCount = checkValue
|
||||
? datalist[index].mediaCount! + 1
|
||||
: datalist[index].mediaCount! - 1;
|
||||
favFolderData.value.list = datalist;
|
||||
favFolderData.refresh();
|
||||
}
|
||||
@@ -416,11 +418,7 @@ class BangumiIntroController extends CommonController {
|
||||
Get.find<VideoDetailController>(tag: Get.arguments['heroTag'])
|
||||
..plPlayerController.pause()
|
||||
..makeHeartBeat()
|
||||
..playedTime = null
|
||||
..videoUrl = null
|
||||
..audioUrl = null
|
||||
..vttSubtitlesIndex = null
|
||||
..savedDanmaku = null
|
||||
..onReset()
|
||||
..epId = epId
|
||||
..bvid = bvid
|
||||
..cid.value = cid
|
||||
@@ -487,6 +485,7 @@ class BangumiIntroController extends CommonController {
|
||||
}
|
||||
|
||||
Future queryVideoInFolder() async {
|
||||
favIds = null;
|
||||
var result = await VideoHttp.videoInFolder(
|
||||
mid: userInfo.mid,
|
||||
rid: epId, // bangumi
|
||||
@@ -494,6 +493,10 @@ class BangumiIntroController extends CommonController {
|
||||
);
|
||||
if (result['status']) {
|
||||
favFolderData.value = result['data'];
|
||||
favIds = favFolderData.value.list
|
||||
?.where((item) => item.favState == 1)
|
||||
.map((item) => item.id)
|
||||
.toList();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:PiliPlus/common/widgets/http_error.dart';
|
||||
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
|
||||
show SourceModel;
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -17,7 +19,6 @@ import 'package:PiliPlus/pages/bangumi/widgets/bangumi_panel.dart';
|
||||
import 'package:PiliPlus/pages/video/detail/index.dart';
|
||||
import 'package:PiliPlus/pages/video/detail/introduction/widgets/action_item.dart';
|
||||
import 'package:PiliPlus/pages/video/detail/introduction/widgets/action_row_item.dart';
|
||||
import 'package:PiliPlus/pages/video/detail/introduction/widgets/fav_panel.dart';
|
||||
import 'package:PiliPlus/utils/feed_back.dart';
|
||||
|
||||
import '../../../utils/utils.dart';
|
||||
@@ -185,42 +186,13 @@ class _BangumiInfoState extends State<BangumiInfo>
|
||||
if (type == 'tap') {
|
||||
bangumiIntroController.actionFavVideo(type: 'default');
|
||||
} else {
|
||||
_showFavBottomSheet();
|
||||
Utils.showFavBottomSheet(context: context, ctr: bangumiIntroController);
|
||||
}
|
||||
} else if (type != 'longPress') {
|
||||
_showFavBottomSheet();
|
||||
Utils.showFavBottomSheet(context: context, ctr: bangumiIntroController);
|
||||
}
|
||||
}
|
||||
|
||||
_showFavBottomSheet() {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
useSafeArea: true,
|
||||
isScrollControlled: true,
|
||||
transitionAnimationController: AnimationController(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
vsync: this,
|
||||
),
|
||||
sheetAnimationStyle: AnimationStyle(curve: Curves.ease),
|
||||
builder: (BuildContext context) {
|
||||
return DraggableScrollableSheet(
|
||||
minChildSize: 0,
|
||||
maxChildSize: 1,
|
||||
initialChildSize: 0.7,
|
||||
snap: true,
|
||||
expand: false,
|
||||
snapSizes: const [0.7],
|
||||
builder: (BuildContext context, ScrollController scrollController) {
|
||||
return FavPanel(
|
||||
ctr: bangumiIntroController,
|
||||
scrollController: scrollController,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// 视频介绍
|
||||
showIntroDetail() {
|
||||
feedBack();
|
||||
@@ -254,9 +226,11 @@ class _BangumiInfoState extends State<BangumiInfo>
|
||||
videoDetailCtr.onViewImage();
|
||||
context.imageView(
|
||||
imgList: [
|
||||
!widget.isLoading
|
||||
? widget.bangumiDetail!.cover!
|
||||
: bangumiItem!.cover!
|
||||
SourceModel(
|
||||
url: !widget.isLoading
|
||||
? widget.bangumiDetail!.cover!
|
||||
: bangumiItem!.cover!,
|
||||
)
|
||||
],
|
||||
onDismissed: videoDetailCtr.onDismissed,
|
||||
);
|
||||
|
||||
@@ -4,6 +4,8 @@ import 'dart:math';
|
||||
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/icon_button.dart';
|
||||
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
|
||||
show SourceModel, SourceType;
|
||||
import 'package:PiliPlus/http/msg.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:chat_bottom_container/chat_bottom_container.dart';
|
||||
@@ -274,8 +276,12 @@ abstract class CommonPublishPageState<T extends CommonPublishPage>
|
||||
onTap: () {
|
||||
controller.keepChatPanel();
|
||||
context.imageView(
|
||||
isFile: true,
|
||||
imgList: pathList,
|
||||
imgList: pathList
|
||||
.map((path) => SourceModel(
|
||||
url: path,
|
||||
sourceType: SourceType.fileImage,
|
||||
))
|
||||
.toList(),
|
||||
initialPage: index,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -43,7 +43,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
||||
AnimationController? _fabAnimationCtr;
|
||||
late StreamController<bool> _titleStreamC; // appBar title
|
||||
bool _visibleTitle = false;
|
||||
String? action;
|
||||
// String? action;
|
||||
// 回复类型
|
||||
late int replyType;
|
||||
bool _isFabVisible = true;
|
||||
@@ -89,10 +89,10 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
||||
// floor 1原创 2转发
|
||||
init();
|
||||
_titleStreamC = StreamController<bool>();
|
||||
if (action == 'comment') {
|
||||
_visibleTitle = true;
|
||||
_titleStreamC.add(true);
|
||||
}
|
||||
// if (action == 'comment') {
|
||||
// _visibleTitle = true;
|
||||
// _titleStreamC.add(true);
|
||||
// }
|
||||
|
||||
_fabAnimationCtr = AnimationController(
|
||||
vsync: this,
|
||||
@@ -109,7 +109,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
||||
// 楼层
|
||||
int floor = args['floor'];
|
||||
// 从action栏点击进入
|
||||
action = args.containsKey('action') ? args['action'] : null;
|
||||
// action = args.containsKey('action') ? args['action'] : null;
|
||||
// 评论类型
|
||||
int commentType = args['item'].basic!['comment_type'] ?? 11;
|
||||
replyType = (commentType == 0) ? 11 : commentType;
|
||||
@@ -156,7 +156,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
|
||||
Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
appBar: AppBar(
|
||||
title: Text('评论详情'),
|
||||
title: const Text('评论详情'),
|
||||
titleSpacing: automaticallyImplyLeading ? null : 12,
|
||||
automaticallyImplyLeading: automaticallyImplyLeading,
|
||||
),
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import 'package:PiliPlus/common/widgets/radio_widget.dart';
|
||||
import 'package:PiliPlus/http/index.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:PiliPlus/pages/member/view.dart' show radioWidget;
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -30,127 +31,205 @@ class AuthorPanel extends StatelessWidget {
|
||||
this.onRemove,
|
||||
});
|
||||
|
||||
Widget _buildAvatar(double size) => NetworkImgLayer(
|
||||
width: size,
|
||||
height: size,
|
||||
type: 'avatar',
|
||||
src: item.modules.moduleAuthor.face,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String heroTag = Utils.makeHeroTag(item.modules.moduleAuthor.mid);
|
||||
return Row(
|
||||
return Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// 番剧
|
||||
if (item.modules.moduleAuthor.type == 'AUTHOR_TYPE_PGC' ||
|
||||
item.modules.moduleAuthor.type == 'AUTHOR_TYPE_UGC_SEASON') {
|
||||
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: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
item.modules.moduleAuthor.name,
|
||||
// semanticsLabel: "UP主:${item.modules.moduleAuthor.name}",
|
||||
style: TextStyle(
|
||||
color: item.modules.moduleAuthor!.vip != null &&
|
||||
item.modules.moduleAuthor!.vip['status'] > 0 &&
|
||||
item.modules.moduleAuthor!.vip['type'] == 2
|
||||
? context.vipColor
|
||||
: Theme.of(context).colorScheme.onSurface,
|
||||
fontSize: Theme.of(context).textTheme.titleSmall!.fontSize,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
DefaultTextStyle.merge(
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// 番剧
|
||||
if (item.modules.moduleAuthor.type == 'AUTHOR_TYPE_PGC' ||
|
||||
item.modules.moduleAuthor.type ==
|
||||
'AUTHOR_TYPE_UGC_SEASON') {
|
||||
return;
|
||||
}
|
||||
feedBack();
|
||||
Get.toNamed(
|
||||
'/member?mid=${item.modules.moduleAuthor.mid}',
|
||||
arguments: {
|
||||
'face': item.modules.moduleAuthor.face,
|
||||
},
|
||||
);
|
||||
},
|
||||
child: (item.modules.moduleAuthor?.pendant?['image'] as String?)
|
||||
?.isNotEmpty ==
|
||||
true
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(3),
|
||||
child: _buildAvatar(34),
|
||||
)
|
||||
: _buildAvatar(40),
|
||||
),
|
||||
child: Row(
|
||||
const SizedBox(width: 10),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(item is ItemOrigModel
|
||||
? Utils.dateFormat(item.modules.moduleAuthor.pubTs)
|
||||
: item.modules.moduleAuthor.pubTime),
|
||||
if (item.modules.moduleAuthor.pubTime != '' &&
|
||||
item.modules.moduleAuthor.pubAction != '')
|
||||
const Text(' '),
|
||||
Text(item.modules.moduleAuthor.pubAction),
|
||||
Text(
|
||||
item.modules.moduleAuthor.name,
|
||||
// semanticsLabel: "UP主:${item.modules.moduleAuthor.name}",
|
||||
style: TextStyle(
|
||||
color: item.modules.moduleAuthor!.vip != null &&
|
||||
item.modules.moduleAuthor!.vip['status'] > 0 &&
|
||||
item.modules.moduleAuthor!.vip['type'] == 2
|
||||
? context.vipColor
|
||||
: Theme.of(context).colorScheme.onSurface,
|
||||
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 is ItemOrigModel
|
||||
? Utils.dateFormat(item.modules.moduleAuthor.pubTs)
|
||||
: item.modules.moduleAuthor.pubTime),
|
||||
if (item.modules.moduleAuthor.pubTime != '' &&
|
||||
item.modules.moduleAuthor.pubAction != '')
|
||||
const Text(' '),
|
||||
Text(item.modules.moduleAuthor.pubAction),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
if (source != 'detail' && item.modules?.moduleTag?.text != null)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
||||
border: Border.all(
|
||||
width: 1.25,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
item.modules.moduleTag.text,
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
strutStyle: const StrutStyle(
|
||||
leading: 0,
|
||||
height: 1,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 32,
|
||||
height: 32,
|
||||
child: IconButton(
|
||||
tooltip: '更多',
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
||||
),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
return morePanel(context);
|
||||
},
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.more_vert_outlined, size: 18),
|
||||
// const Spacer(),
|
||||
// if (source != 'detail' && item.modules?.moduleTag?.text != null)
|
||||
// Container(
|
||||
// padding:
|
||||
// const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
||||
// decoration: BoxDecoration(
|
||||
// color: Theme.of(context).colorScheme.surface,
|
||||
// borderRadius: const BorderRadius.all(Radius.circular(4)),
|
||||
// border: Border.all(
|
||||
// width: 1.25,
|
||||
// color: Theme.of(context).colorScheme.primary,
|
||||
// ),
|
||||
// ),
|
||||
// child: Text(
|
||||
// item.modules.moduleTag.text,
|
||||
// style: TextStyle(
|
||||
// height: 1,
|
||||
// fontSize: 12,
|
||||
// color: Theme.of(context).colorScheme.primary,
|
||||
// ),
|
||||
// strutStyle: const StrutStyle(
|
||||
// leading: 0,
|
||||
// height: 1,
|
||||
// fontSize: 12,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: item.modules.moduleAuthor.decorate != null
|
||||
? Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// GestureDetector(
|
||||
// onTap:
|
||||
// item.modules.moduleAuthor.decorate['jump_url'] != null
|
||||
// ? () {
|
||||
// Get.toNamed(
|
||||
// '/webview',
|
||||
// parameters: {
|
||||
// 'url':
|
||||
// '${item.modules.moduleAuthor.decorate['jump_url']}'
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
// : null,
|
||||
// child:
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
alignment: Alignment.centerRight,
|
||||
children: [
|
||||
CachedNetworkImage(
|
||||
height: 32,
|
||||
imageUrl:
|
||||
item.modules.moduleAuthor.decorate['card_url'],
|
||||
),
|
||||
if ((item.modules.moduleAuthor.decorate?['fan']
|
||||
?['num_str'] as String?)
|
||||
?.isNotEmpty ==
|
||||
true)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 32),
|
||||
child: Text(
|
||||
'${item.modules.moduleAuthor.decorate['fan']['num_str']}',
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontFamily: 'digital_id_num',
|
||||
color:
|
||||
(item.modules.moduleAuthor.decorate?['fan']
|
||||
?['color'] as String?)
|
||||
?.startsWith('#') ==
|
||||
true
|
||||
? Color(
|
||||
int.parse(
|
||||
item.modules.moduleAuthor
|
||||
.decorate['fan']['color']
|
||||
.replaceFirst('#', '0xFF'),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// ),
|
||||
_moreWidget(context),
|
||||
],
|
||||
)
|
||||
: _moreWidget(context),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _moreWidget(context) => SizedBox(
|
||||
width: 32,
|
||||
height: 32,
|
||||
child: IconButton(
|
||||
tooltip: '更多',
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
||||
),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
return morePanel(context);
|
||||
},
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.more_vert_outlined, size: 18),
|
||||
),
|
||||
);
|
||||
|
||||
Widget morePanel(context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
|
||||
@@ -309,10 +388,7 @@ class AuthorPanel extends StatelessWidget {
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
'举报动态',
|
||||
style: TextStyle(fontSize: 18),
|
||||
),
|
||||
title: const Text('举报动态'),
|
||||
titlePadding: const EdgeInsets.only(left: 22, top: 16, right: 22),
|
||||
contentPadding: const EdgeInsets.only(top: 5),
|
||||
actionsPadding:
|
||||
|
||||
@@ -16,6 +16,7 @@ Widget content(context, item, source, callback) {
|
||||
width: item.width,
|
||||
height: item.height,
|
||||
url: item.url ?? '',
|
||||
liveUrl: item.liveUrl,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/common/widgets/image_save.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'action_panel.dart';
|
||||
import 'author_panel.dart';
|
||||
@@ -79,27 +80,50 @@ class DynamicPanel extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 6),
|
||||
child: AuthorPanel(
|
||||
item: item,
|
||||
source: source,
|
||||
onRemove: onRemove,
|
||||
),
|
||||
),
|
||||
if (item!.modules!.moduleDynamic!.desc != null ||
|
||||
item!.modules!.moduleDynamic!.major != null)
|
||||
content(context, item, source, callback),
|
||||
forWard(item, context, source, callback),
|
||||
const SizedBox(height: 2),
|
||||
if (source == null) ActionPanel(item: item),
|
||||
],
|
||||
),
|
||||
child: (item.modules.moduleAuthor?.pendant?['image'] as String?)
|
||||
?.isNotEmpty ==
|
||||
true
|
||||
? Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
_buildContent(context, item, source, callback),
|
||||
Positioned(
|
||||
left: 2,
|
||||
top: 2,
|
||||
child: IgnorePointer(
|
||||
child: CachedNetworkImage(
|
||||
width: 60,
|
||||
height: 60,
|
||||
imageUrl: item.modules.moduleAuthor.pendant['image'],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: _buildContent(context, item, source, callback),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContent(context, item, source, callback) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 6),
|
||||
child: AuthorPanel(
|
||||
item: item,
|
||||
source: source,
|
||||
onRemove: onRemove,
|
||||
),
|
||||
),
|
||||
if (item!.modules!.moduleDynamic!.desc != null ||
|
||||
item!.modules!.moduleDynamic!.major != null)
|
||||
content(context, item, source, callback),
|
||||
forWard(item, context, source, callback),
|
||||
const SizedBox(height: 2),
|
||||
if (source == null) ActionPanel(item: item),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ InlineSpan picsNodes(List<OpusPicsModel> pics, callback) {
|
||||
width: item.width,
|
||||
height: item.height,
|
||||
url: item.url ?? '',
|
||||
liveUrl: item.liveUrl,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
@@ -37,21 +38,14 @@ InlineSpan picsNodes(List<OpusPicsModel> pics, callback) {
|
||||
}
|
||||
|
||||
Widget forWard(item, context, source, callback, {floor = 1}) {
|
||||
TextStyle authorStyle =
|
||||
TextStyle(color: Theme.of(context).colorScheme.primary);
|
||||
|
||||
List<OpusPicsModel> pics = [];
|
||||
|
||||
bool hasPics = item.modules.moduleDynamic.major != null &&
|
||||
item.modules.moduleDynamic.major.opus != null &&
|
||||
item.modules.moduleDynamic.major.opus.pics.isNotEmpty;
|
||||
if (hasPics) {
|
||||
pics = item.modules.moduleDynamic.major.opus.pics;
|
||||
}
|
||||
InlineSpan? richNodes = richNode(item, context);
|
||||
switch (item.type) {
|
||||
// 图文
|
||||
case 'DYNAMIC_TYPE_DRAW':
|
||||
bool hasPics = item.modules.moduleDynamic.major != null &&
|
||||
item.modules.moduleDynamic.major.opus != null &&
|
||||
item.modules.moduleDynamic.major.opus.pics.isNotEmpty;
|
||||
|
||||
InlineSpan? richNodes = richNode(item, context);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -64,7 +58,8 @@ Widget forWard(item, context, source, callback, {floor = 1}) {
|
||||
arguments: {'face': item.modules.moduleAuthor.face}),
|
||||
child: Text(
|
||||
'@${item.modules.moduleAuthor.name}',
|
||||
style: authorStyle,
|
||||
style:
|
||||
TextStyle(color: Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
@@ -93,6 +88,7 @@ Widget forWard(item, context, source, callback, {floor = 1}) {
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
|
||||
if (richNodes != null)
|
||||
Text.rich(
|
||||
richNodes,
|
||||
@@ -104,11 +100,11 @@ Widget forWard(item, context, source, callback, {floor = 1}) {
|
||||
),
|
||||
if (hasPics) ...[
|
||||
Text.rich(
|
||||
picsNodes(pics, callback),
|
||||
picsNodes(item.modules.moduleDynamic.major.opus.pics, callback),
|
||||
// semanticsLabel: '动态图片',
|
||||
),
|
||||
if (item.modules.moduleDynamic.additional != null)
|
||||
const SizedBox(height: 4),
|
||||
// if (item.modules.moduleDynamic.additional != null)
|
||||
// const SizedBox(height: 4),
|
||||
],
|
||||
const SizedBox(height: 4),
|
||||
],
|
||||
@@ -126,7 +122,38 @@ Widget forWard(item, context, source, callback, {floor = 1}) {
|
||||
context,
|
||||
item.modules.moduleDynamic.additional.type,
|
||||
floor: floor,
|
||||
)
|
||||
),
|
||||
|
||||
if (item.modules.moduleDynamic.major.blocked != null) ...[
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.only(
|
||||
left: 12, right: 12, bottom: source == 'detail' ? 8 : 0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (item.modules.moduleDynamic.major.blocked['title'] != null)
|
||||
Text(
|
||||
item.modules.moduleDynamic.major.blocked['title'],
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
if (item.modules.moduleDynamic.major
|
||||
.blocked['hint_message'] !=
|
||||
null)
|
||||
Text(
|
||||
item.modules.moduleDynamic.major.blocked['hint_message'],
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
// 视频
|
||||
@@ -198,6 +225,7 @@ Widget forWard(item, context, source, callback, {floor = 1}) {
|
||||
case 'DYNAMIC_TYPE_UGC_SEASON':
|
||||
return videoSeasonWidget(item, context, 'ugcSeason');
|
||||
case 'DYNAMIC_TYPE_WORD':
|
||||
InlineSpan? richNodes = richNode(item, context);
|
||||
return floor == 2
|
||||
? Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -210,7 +238,8 @@ Widget forWard(item, context, source, callback, {floor = 1}) {
|
||||
arguments: {'face': item.modules.moduleAuthor.face}),
|
||||
child: Text(
|
||||
'@${item.modules.moduleAuthor.name}',
|
||||
style: authorStyle,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
|
||||
@@ -2,8 +2,8 @@ import 'package:PiliPlus/common/widgets/imageview.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
Widget picWidget(item, context, callback) {
|
||||
String type = item.modules.moduleDynamic.major.type;
|
||||
if (type == 'MAJOR_TYPE_OPUS') {
|
||||
if (item.modules.moduleDynamic.major?.draw?.items == null ||
|
||||
item.modules.moduleDynamic.major.type == 'MAJOR_TYPE_OPUS') {
|
||||
/// fix 图片跟rich_node_panel重复
|
||||
// pictures = item.modules.moduleDynamic.major.opus.pics;
|
||||
return const SizedBox();
|
||||
@@ -17,6 +17,7 @@ Widget picWidget(item, context, callback) {
|
||||
width: item.width,
|
||||
height: item.height,
|
||||
url: item.url ?? '',
|
||||
liveUrl: item.liveUrl,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
|
||||
@@ -26,7 +26,7 @@ class _FavPageState extends State<FavPage> {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('我的收藏'),
|
||||
title: const Text('我的收藏'),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
|
||||
@@ -53,7 +53,8 @@ class FavItem extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
videoContent(context)
|
||||
const SizedBox(width: 10),
|
||||
videoContent(context),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -65,43 +66,40 @@ class FavItem extends StatelessWidget {
|
||||
|
||||
Widget videoContent(context) {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 2, 6, 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
favFolderItem.title ?? '',
|
||||
textAlign: TextAlign.start,
|
||||
style: const TextStyle(
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
favFolderItem.title ?? '',
|
||||
textAlign: TextAlign.start,
|
||||
style: const TextStyle(
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
if (favFolderItem.intro?.isNotEmpty == true)
|
||||
Text(
|
||||
favFolderItem.intro!,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (favFolderItem.intro?.isNotEmpty == true)
|
||||
Text(
|
||||
'${favFolderItem.mediaCount}个内容',
|
||||
favFolderItem.intro!,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
Utils.isPublicText(favFolderItem.attr ?? 0),
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
Text(
|
||||
'${favFolderItem.mediaCount}个内容',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
Utils.isPublicText(favFolderItem.attr ?? 0),
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ class FavDetailController extends MultiSelectController {
|
||||
RxBool isOwner = false.obs;
|
||||
RxBool titleCtr = false.obs;
|
||||
|
||||
dynamic mid;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// item = Get.arguments;
|
||||
@@ -26,6 +28,8 @@ class FavDetailController extends MultiSelectController {
|
||||
}
|
||||
super.onInit();
|
||||
|
||||
mid = GStorage.userInfo.get('userInfoCache')?.mid;
|
||||
|
||||
queryData();
|
||||
}
|
||||
|
||||
@@ -33,8 +37,7 @@ class FavDetailController extends MultiSelectController {
|
||||
bool customHandleResponse(Success response) {
|
||||
if (currentPage == 1) {
|
||||
item.value = response.response.info;
|
||||
isOwner.value = response.response.info.mid ==
|
||||
GStorage.userInfo.get('userInfoCache')?.mid;
|
||||
isOwner.value = response.response.info.mid == mid;
|
||||
}
|
||||
if (response.response.medias.isEmpty) {
|
||||
isEnd = true;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:PiliPlus/common/widgets/dialog.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/user.dart';
|
||||
import 'package:PiliPlus/models/user/fav_folder.dart';
|
||||
@@ -60,6 +61,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
}
|
||||
},
|
||||
child: Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
floatingActionButton: Obx(
|
||||
() => (_favDetailController.item.value.mediaCount ?? -1) > 0
|
||||
? FloatingActionButton.extended(
|
||||
@@ -85,7 +87,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
pinned: true,
|
||||
title: _favDetailController.enableMultiSelect.value
|
||||
? Text(
|
||||
'已选择${_favDetailController.checkedCount.value}项',
|
||||
'已选: ${_favDetailController.checkedCount.value}',
|
||||
)
|
||||
: Obx(
|
||||
() => AnimatedOpacity(
|
||||
@@ -110,11 +112,59 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
actions: _favDetailController.enableMultiSelect.value
|
||||
? [
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
visualDensity:
|
||||
VisualDensity(horizontal: -2, vertical: -2),
|
||||
),
|
||||
onPressed: () =>
|
||||
_favDetailController.handleSelect(true),
|
||||
child: const Text('全选'),
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
visualDensity:
|
||||
VisualDensity(horizontal: -2, vertical: -2),
|
||||
),
|
||||
onPressed: () => Utils.onCopyOrMove(
|
||||
context: context,
|
||||
isCopy: true,
|
||||
ctr: _favDetailController,
|
||||
mediaId: _favDetailController.mediaId,
|
||||
),
|
||||
child: Text(
|
||||
'复制',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
visualDensity:
|
||||
VisualDensity(horizontal: -2, vertical: -2),
|
||||
),
|
||||
onPressed: () => Utils.onCopyOrMove(
|
||||
context: context,
|
||||
isCopy: false,
|
||||
ctr: _favDetailController,
|
||||
mediaId: _favDetailController.mediaId,
|
||||
),
|
||||
child: Text(
|
||||
'移动',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
visualDensity:
|
||||
VisualDensity(horizontal: -2, vertical: -2),
|
||||
),
|
||||
onPressed: () =>
|
||||
_favDetailController.onDelChecked(context),
|
||||
child: Text(
|
||||
@@ -159,23 +209,55 @@ class _FavDetailPageState extends State<FavDetailPage> {
|
||||
},
|
||||
child: Text('编辑信息'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
onTap: () {
|
||||
UserHttp.cleanFav(mediaId: mediaId)
|
||||
.then((data) {
|
||||
if (data['status']) {
|
||||
SmartDialog.showToast('清除成功');
|
||||
Future.delayed(
|
||||
const Duration(
|
||||
milliseconds: 200), () {
|
||||
_favDetailController.onReload();
|
||||
});
|
||||
} else {
|
||||
SmartDialog.showToast(data['msg']);
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Text('清除失效内容'),
|
||||
),
|
||||
if (!Utils.isDefault(
|
||||
_favDetailController.item.value.attr ??
|
||||
0))
|
||||
PopupMenuItem(
|
||||
onTap: () {
|
||||
UserHttp.deleteFolder(
|
||||
mediaIds: [mediaId]).then((data) {
|
||||
if (data['status']) {
|
||||
SmartDialog.showToast('删除成功');
|
||||
Get.back(result: true);
|
||||
} else {
|
||||
SmartDialog.showToast(
|
||||
data['msg']);
|
||||
}
|
||||
});
|
||||
showConfirmDialog(
|
||||
context: context,
|
||||
title: '确定删除该收藏夹?',
|
||||
onConfirm: () {
|
||||
UserHttp.deleteFolder(
|
||||
mediaIds: [mediaId])
|
||||
.then((data) {
|
||||
if (data['status']) {
|
||||
SmartDialog.showToast('删除成功');
|
||||
Get.back(result: true);
|
||||
} else {
|
||||
SmartDialog.showToast(
|
||||
data['msg']);
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Text('删除'),
|
||||
child: Text(
|
||||
'删除',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.error,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -137,7 +137,8 @@ class FavVideoCardH extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
videoContent(context)
|
||||
const SizedBox(width: 10),
|
||||
videoContent(context),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -149,113 +150,106 @@ class FavVideoCardH extends StatelessWidget {
|
||||
|
||||
Widget videoContent(context) {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 2, 6, 0),
|
||||
child: Stack(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
videoItem.title,
|
||||
textAlign: TextAlign.start,
|
||||
style: const TextStyle(
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
child: Stack(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
videoItem.title,
|
||||
textAlign: TextAlign.start,
|
||||
style: const TextStyle(
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
if (videoItem.ogv != null) ...[
|
||||
Text(
|
||||
videoItem.intro,
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
const Spacer(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
if (videoItem.ogv != null) ...[
|
||||
Text(
|
||||
Utils.dateFormat(videoItem.favTime),
|
||||
videoItem.intro,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
if (videoItem.owner.name != '') ...[
|
||||
Text(
|
||||
videoItem.owner.name,
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 2),
|
||||
child: Row(
|
||||
children: [
|
||||
statView(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
view: videoItem.cntInfo['play'],
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
statDanMu(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
danmu: videoItem.cntInfo['danmaku'],
|
||||
),
|
||||
const Spacer(),
|
||||
],
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (searchType != 1 && isOwner)
|
||||
Positioned(
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: iconButton(
|
||||
context: context,
|
||||
icon: Icons.clear,
|
||||
tooltip: '取消收藏',
|
||||
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
bgColor: Colors.transparent,
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: Get.context!,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: const Text('提示'),
|
||||
content: const Text('要取消收藏吗?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: Get.back,
|
||||
child: Text(
|
||||
'取消',
|
||||
style: TextStyle(
|
||||
color:
|
||||
Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
await callFn?.call();
|
||||
Get.back();
|
||||
},
|
||||
child: const Text('确定取消'),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
const Spacer(),
|
||||
Text(
|
||||
Utils.dateFormat(videoItem.favTime),
|
||||
style: TextStyle(
|
||||
fontSize: 11, color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
if (videoItem.owner.name != '') ...[
|
||||
Text(
|
||||
videoItem.owner.name,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 2),
|
||||
child: Row(
|
||||
children: [
|
||||
statView(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
view: videoItem.cntInfo['play'],
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
statDanMu(
|
||||
context: context,
|
||||
theme: 'gray',
|
||||
danmu: videoItem.cntInfo['danmaku'],
|
||||
),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
if (searchType != 1 && isOwner)
|
||||
Positioned(
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: iconButton(
|
||||
context: context,
|
||||
icon: Icons.clear,
|
||||
tooltip: '取消收藏',
|
||||
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
bgColor: Colors.transparent,
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: Get.context!,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: const Text('提示'),
|
||||
content: const Text('要取消收藏吗?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: Get.back,
|
||||
child: Text(
|
||||
'取消',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
await callFn?.call();
|
||||
Get.back();
|
||||
},
|
||||
child: const Text('确定取消'),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ class _HistoryPageState extends State<HistoryPage> {
|
||||
appBar: AppBarWidget(
|
||||
visible: _historyController.enableMultiSelect.value,
|
||||
child1: AppBar(
|
||||
title: Text('观看记录'),
|
||||
title: const Text('观看记录'),
|
||||
actions: [
|
||||
IconButton(
|
||||
tooltip: '搜索',
|
||||
@@ -103,7 +103,7 @@ class _HistoryPageState extends State<HistoryPage> {
|
||||
),
|
||||
title: Obx(
|
||||
() => Text(
|
||||
'已选择${_historyController.checkedCount.value}项',
|
||||
'已选: ${_historyController.checkedCount.value}',
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
|
||||
@@ -291,7 +291,8 @@ class HistoryItem extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
videoContent(context)
|
||||
const SizedBox(width: 10),
|
||||
videoContent(context),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -303,113 +304,108 @@ class HistoryItem extends StatelessWidget {
|
||||
|
||||
Widget videoContent(context) {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 2, 6, 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
videoItem.title,
|
||||
textAlign: TextAlign.start,
|
||||
style: const TextStyle(
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
maxLines: videoItem.videos > 1 ? 1 : 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
if (videoItem.isFullScreen != null) ...[
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
videoItem.title,
|
||||
videoItem.isFullScreen,
|
||||
textAlign: TextAlign.start,
|
||||
style: const TextStyle(
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
maxLines: videoItem.videos > 1 ? 1 : 2,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
if (videoItem.isFullScreen != null) ...[
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
videoItem.isFullScreen,
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
const Spacer(),
|
||||
if (videoItem.authorName != '')
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
videoItem.authorName,
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
const Spacer(),
|
||||
if (videoItem.authorName != '')
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
Utils.dateFormat(videoItem.viewAt!),
|
||||
videoItem.authorName,
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: PopupMenuButton<String>(
|
||||
padding: EdgeInsets.zero,
|
||||
tooltip: '功能菜单',
|
||||
icon: Icon(
|
||||
Icons.more_vert_outlined,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
size: 18,
|
||||
),
|
||||
position: PopupMenuPosition.under,
|
||||
// constraints: const BoxConstraints(maxHeight: 35),
|
||||
onSelected: (String type) {},
|
||||
itemBuilder: (BuildContext context) =>
|
||||
<PopupMenuEntry<String>>[
|
||||
if (videoItem.history?.business != 'pgc' &&
|
||||
videoItem.badge != '番剧' &&
|
||||
!videoItem.tagName.contains('动画') &&
|
||||
videoItem.history.business != 'live' &&
|
||||
!videoItem.history.business.contains('article'))
|
||||
PopupMenuItem<String>(
|
||||
onTap: () async {
|
||||
var res = await UserHttp.toViewLater(
|
||||
bvid: videoItem.history.bvid);
|
||||
SmartDialog.showToast(res['msg']);
|
||||
},
|
||||
value: 'pause',
|
||||
height: 35,
|
||||
child: const Row(
|
||||
children: [
|
||||
Icon(Icons.watch_later_outlined, size: 16),
|
||||
SizedBox(width: 6),
|
||||
Text('稍后再看', style: TextStyle(fontSize: 13))
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem<String>(
|
||||
onTap: () => ctr!.delHistory(
|
||||
videoItem.kid, videoItem.history.business),
|
||||
value: 'pause',
|
||||
height: 35,
|
||||
child: const Row(
|
||||
children: [
|
||||
Icon(Icons.close_outlined, size: 16),
|
||||
SizedBox(width: 6),
|
||||
Text('删除记录', style: TextStyle(fontSize: 13))
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
Utils.dateFormat(videoItem.viewAt!),
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: PopupMenuButton<String>(
|
||||
padding: EdgeInsets.zero,
|
||||
tooltip: '功能菜单',
|
||||
icon: Icon(
|
||||
Icons.more_vert_outlined,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
size: 18,
|
||||
),
|
||||
position: PopupMenuPosition.under,
|
||||
// constraints: const BoxConstraints(maxHeight: 35),
|
||||
onSelected: (String type) {},
|
||||
itemBuilder: (BuildContext context) =>
|
||||
<PopupMenuEntry<String>>[
|
||||
if (videoItem.history?.business != 'pgc' &&
|
||||
videoItem.badge != '番剧' &&
|
||||
!videoItem.tagName.contains('动画') &&
|
||||
videoItem.history.business != 'live' &&
|
||||
!videoItem.history.business.contains('article'))
|
||||
PopupMenuItem<String>(
|
||||
onTap: () async {
|
||||
var res = await UserHttp.toViewLater(
|
||||
bvid: videoItem.history.bvid);
|
||||
SmartDialog.showToast(res['msg']);
|
||||
},
|
||||
value: 'pause',
|
||||
height: 35,
|
||||
child: const Row(
|
||||
children: [
|
||||
Icon(Icons.watch_later_outlined, size: 16),
|
||||
SizedBox(width: 6),
|
||||
Text('稍后再看', style: TextStyle(fontSize: 13))
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem<String>(
|
||||
onTap: () => ctr!.delHistory(
|
||||
videoItem.kid, videoItem.history.business),
|
||||
value: 'pause',
|
||||
height: 35,
|
||||
child: const Row(
|
||||
children: [
|
||||
Icon(Icons.close_outlined, size: 16),
|
||||
SizedBox(width: 6),
|
||||
Text('删除记录', style: TextStyle(fontSize: 13))
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ class _HtmlRenderPageState extends State<HtmlRenderPage>
|
||||
Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
appBar: AppBar(
|
||||
title: Text('评论详情'),
|
||||
title: const Text('评论详情'),
|
||||
titleSpacing: automaticallyImplyLeading ? null : 12,
|
||||
automaticallyImplyLeading: automaticallyImplyLeading,
|
||||
),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:PiliPlus/common/widgets/dialog.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/model_hot_video_item.dart';
|
||||
import 'package:PiliPlus/pages/common/multi_select_controller.dart';
|
||||
@@ -11,9 +12,12 @@ import 'package:PiliPlus/http/user.dart';
|
||||
class LaterController extends MultiSelectController {
|
||||
RxInt count = (-1).obs;
|
||||
|
||||
dynamic mid;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
mid = GStorage.userInfo.get('userInfoCache')?.mid;
|
||||
queryData();
|
||||
}
|
||||
|
||||
@@ -79,34 +83,18 @@ class LaterController extends MultiSelectController {
|
||||
}
|
||||
|
||||
// 一键清空
|
||||
Future toViewClear(BuildContext context) async {
|
||||
await showDialog(
|
||||
void toViewClear(BuildContext context) {
|
||||
showConfirmDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: const Text('清空确认'),
|
||||
content: const Text('确定要清空你的稍后再看列表吗?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Get.back(),
|
||||
child: Text(
|
||||
'取消',
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
var res = await UserHttp.toViewClear();
|
||||
if (res['status']) {
|
||||
loadingState.value = LoadingState.success([]);
|
||||
}
|
||||
Get.back();
|
||||
SmartDialog.showToast(res['msg']);
|
||||
},
|
||||
child: const Text('确认'),
|
||||
)
|
||||
],
|
||||
);
|
||||
title: '清空确认',
|
||||
content: '确定要清空你的稍后再看列表吗?',
|
||||
onConfirm: () async {
|
||||
var res = await UserHttp.toViewClear();
|
||||
if (res['status']) {
|
||||
loadingState.value = LoadingState.success([]);
|
||||
}
|
||||
Get.back();
|
||||
SmartDialog.showToast(res['msg']);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -185,7 +173,7 @@ class LaterController extends MultiSelectController {
|
||||
'sourceType': 'watchLater',
|
||||
'count': list.length,
|
||||
'favTitle': '稍后再看',
|
||||
'mediaId': GStorage.userInfo.get('userInfoCache')?.mid,
|
||||
'mediaId': mid,
|
||||
'desc': false,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:PiliPlus/common/widgets/icon_button.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/history/view.dart' show AppBarWidget;
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPlus/common/skeleton/video_card_h.dart';
|
||||
@@ -33,6 +34,7 @@ class _LaterPageState extends State<LaterPage> {
|
||||
}
|
||||
},
|
||||
child: Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
appBar: AppBarWidget(
|
||||
visible: _laterController.enableMultiSelect.value,
|
||||
child1: AppBar(
|
||||
@@ -75,15 +77,55 @@ class _LaterPageState extends State<LaterPage> {
|
||||
),
|
||||
title: Obx(
|
||||
() => Text(
|
||||
'已选择${_laterController.checkedCount.value}项',
|
||||
'已选: ${_laterController.checkedCount.value}',
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
visualDensity: VisualDensity(horizontal: -2, vertical: -2),
|
||||
),
|
||||
onPressed: () => _laterController.handleSelect(true),
|
||||
child: const Text('全选'),
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
visualDensity: VisualDensity(horizontal: -2, vertical: -2),
|
||||
),
|
||||
onPressed: () => Utils.onCopyOrMove(
|
||||
context: context,
|
||||
isCopy: true,
|
||||
ctr: _laterController,
|
||||
mediaId: null,
|
||||
),
|
||||
child: Text(
|
||||
'复制',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
visualDensity: VisualDensity(horizontal: -2, vertical: -2),
|
||||
),
|
||||
onPressed: () => Utils.onCopyOrMove(
|
||||
context: context,
|
||||
isCopy: false,
|
||||
ctr: _laterController,
|
||||
mediaId: null,
|
||||
),
|
||||
child: Text(
|
||||
'移动',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
visualDensity: VisualDensity(horizontal: -2, vertical: -2),
|
||||
),
|
||||
onPressed: () => _laterController.onDelChecked(context),
|
||||
child: Text(
|
||||
'移除',
|
||||
|
||||
@@ -313,7 +313,6 @@ class LoginPageController extends GetxController
|
||||
title: const Text(
|
||||
"本次登录需要验证您的手机号",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 18),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
||||
@@ -58,7 +58,8 @@ class SeasonSeriesCard extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
videoContent(context)
|
||||
const SizedBox(width: 10),
|
||||
videoContent(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -67,36 +68,33 @@ class SeasonSeriesCard extends StatelessWidget {
|
||||
|
||||
Widget videoContent(context) {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item['meta']['name'],
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||
height: 1.42,
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item['meta']['name'],
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
|
||||
height: 1.42,
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
Utils.dateFormat(item['meta']['ptime']),
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
Utils.dateFormat(item['meta']['ptime']),
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -276,10 +276,7 @@ class _EditProfilePageState extends State<EditProfilePage> {
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
'修改$title',
|
||||
style: TextStyle(fontSize: 18),
|
||||
),
|
||||
title: Text('修改$title'),
|
||||
content: TextField(
|
||||
controller: _textController,
|
||||
minLines: type == ProfileType.uname ? 1 : 4,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
|
||||
show SourceModel;
|
||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
||||
import 'package:PiliPlus/models/dynamics/result.dart';
|
||||
import 'package:PiliPlus/models/space/card.dart' as space;
|
||||
import 'package:PiliPlus/models/space/images.dart' as space;
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
@@ -80,7 +83,7 @@ class UserInfoCard extends StatelessWidget {
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
context.imageView(
|
||||
imgList: [imgUrl ?? 'bgTag'],
|
||||
imgList: [SourceModel(url: imgUrl ?? 'bgTag')],
|
||||
);
|
||||
},
|
||||
child: CachedNetworkImage(
|
||||
@@ -444,22 +447,22 @@ class UserInfoCard extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
|
||||
_buildAvatar(BuildContext context) => Hero(
|
||||
tag: card.face ?? 'avatarTag',
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
context.imageView(
|
||||
imgList: [card.face ?? 'avatarTag'],
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 2.5,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
_buildAvatar(BuildContext context) => Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 2.5,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Hero(
|
||||
tag: card.face ?? 'avatarTag',
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
context.imageView(
|
||||
imgList: [SourceModel(url: card.face ?? 'avatarTag')],
|
||||
);
|
||||
},
|
||||
child: NetworkImgLayer(
|
||||
src: card.face,
|
||||
type: 'avatar',
|
||||
@@ -489,6 +492,19 @@ class UserInfoCard extends StatelessWidget {
|
||||
left: 20,
|
||||
child: _buildAvatar(context),
|
||||
),
|
||||
if (ModuleAuthorModel.showDynDecorate &&
|
||||
card.pendant?.image?.isNotEmpty == true)
|
||||
Positioned(
|
||||
top: 82.5,
|
||||
left: -7.5,
|
||||
child: IgnorePointer(
|
||||
child: CachedNetworkImage(
|
||||
width: 140,
|
||||
height: 140,
|
||||
imageUrl: card.pendant!.image!,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (card.officialVerify?.icon?.isNotEmpty == true ||
|
||||
(card.vip?.vipStatus ?? -1) > 0)
|
||||
Positioned(
|
||||
@@ -572,6 +588,19 @@ class UserInfoCard extends StatelessWidget {
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
_buildAvatar(context),
|
||||
if (ModuleAuthorModel.showDynDecorate &&
|
||||
card.pendant?.image?.isNotEmpty == true)
|
||||
Positioned(
|
||||
top: -27.5,
|
||||
left: -27.5,
|
||||
child: IgnorePointer(
|
||||
child: CachedNetworkImage(
|
||||
width: 140,
|
||||
height: 140,
|
||||
imageUrl: card.pendant!.image!,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (card.officialVerify?.icon?.isNotEmpty == true ||
|
||||
(card.vip?.vipStatus ?? -1) > 0)
|
||||
Positioned(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:PiliPlus/common/widgets/radio_widget.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
@@ -649,27 +650,3 @@ Widget _checkBoxWidget(
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget radioWidget<T>({
|
||||
required T value,
|
||||
T? groupValue,
|
||||
required ValueChanged onChanged,
|
||||
required String title,
|
||||
double? paddingStart,
|
||||
}) {
|
||||
return InkWell(
|
||||
onTap: () => onChanged(value),
|
||||
child: Row(
|
||||
children: [
|
||||
if (paddingStart != null) SizedBox(width: paddingStart),
|
||||
Radio(
|
||||
value: value,
|
||||
groupValue: groupValue,
|
||||
onChanged: onChanged,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
Text(title),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
|
||||
show SourceModel;
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -28,7 +30,10 @@ class ProfilePanel extends StatelessWidget {
|
||||
GestureDetector(
|
||||
onTap: () => context.imageView(
|
||||
imgList: [
|
||||
!loadingStatus ? memberInfo.face : ctr.face.value
|
||||
SourceModel(
|
||||
url:
|
||||
!loadingStatus ? memberInfo.face : ctr.face.value,
|
||||
)
|
||||
],
|
||||
),
|
||||
child: NetworkImgLayer(
|
||||
|
||||
@@ -53,7 +53,7 @@ class _MemberArchivePageState extends State<MemberArchivePage> {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Ta的投稿'),
|
||||
title: const Text('Ta的投稿'),
|
||||
actions: [
|
||||
Obx(
|
||||
() => TextButton.icon(
|
||||
|
||||
@@ -46,7 +46,7 @@ class _MemberDynamicsPageState extends State<MemberDynamicsPage>
|
||||
super.build(context);
|
||||
return widget.mid == null
|
||||
? Scaffold(
|
||||
appBar: AppBar(title: Text('Ta的动态')),
|
||||
appBar: AppBar(title: const Text('Ta的动态')),
|
||||
body: _buildBody,
|
||||
)
|
||||
: _buildBody;
|
||||
|
||||
@@ -47,7 +47,7 @@ class _MemberSeasonsPageState extends State<MemberSeasonsPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('Ta的专栏')),
|
||||
appBar: AppBar(title: const Text('Ta的专栏')),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: StyleString.safeSpace,
|
||||
|
||||
@@ -69,7 +69,11 @@ class _SearchPanelState extends State<SearchPanel>
|
||||
SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
mainAxisSpacing: 2,
|
||||
maxCrossAxisExtent: Grid.smallCardWidth * 2,
|
||||
maxCrossAxisExtent: (widget.searchType == SearchType.video ||
|
||||
widget.searchType == SearchType.article
|
||||
? Grid.mediumCardWidth
|
||||
: Grid.smallCardWidth) *
|
||||
2,
|
||||
childAspectRatio: StyleString.aspectRatio *
|
||||
(widget.searchType == SearchType.media_bangumi ||
|
||||
widget.searchType == SearchType.media_ft
|
||||
|
||||
@@ -153,61 +153,54 @@ Widget searchArticlePanel(context, searchPanelCtr, LoadingState loadingState) {
|
||||
);
|
||||
}),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
10, 2, 6, 0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
RichText(
|
||||
maxLines: 2,
|
||||
textScaler:
|
||||
MediaQuery.textScalerOf(
|
||||
context),
|
||||
text: TextSpan(
|
||||
children: [
|
||||
for (var i in loadingState
|
||||
.response[index]
|
||||
.title) ...[
|
||||
TextSpan(
|
||||
text: i['text'],
|
||||
style: TextStyle(
|
||||
color: i['type'] == 'em'
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.primary
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface,
|
||||
),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
Utils.dateFormat(
|
||||
loadingState.response[index]
|
||||
.pubTime,
|
||||
formatType: 'detail'),
|
||||
style: textStyle),
|
||||
Row(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text.rich(
|
||||
maxLines: 2,
|
||||
TextSpan(
|
||||
children: [
|
||||
Text(
|
||||
'${loadingState.response[index].view}浏览',
|
||||
style: textStyle),
|
||||
Text(' • ', style: textStyle),
|
||||
Text(
|
||||
'${loadingState.response[index].reply}评论',
|
||||
style: textStyle),
|
||||
for (var i in loadingState
|
||||
.response[index].title) ...[
|
||||
TextSpan(
|
||||
text: i['text'],
|
||||
style: TextStyle(
|
||||
color: i['type'] == 'em'
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.primary
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface,
|
||||
),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
Utils.dateFormat(
|
||||
loadingState
|
||||
.response[index].pubTime,
|
||||
formatType: 'detail'),
|
||||
style: textStyle),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'${loadingState.response[index].view}浏览',
|
||||
style: textStyle),
|
||||
Text(' • ', style: textStyle),
|
||||
Text(
|
||||
'${loadingState.response[index].reply}评论',
|
||||
style: textStyle),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -127,9 +127,8 @@ class LiveContent extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
RichText(
|
||||
textScaler: MediaQuery.textScalerOf(context),
|
||||
text: TextSpan(
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
for (var i in liveItem.title) ...[
|
||||
TextSpan(
|
||||
|
||||
@@ -75,12 +75,10 @@ Widget searchBangumiPanel(context, ctr, LoadingState loadingState) {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 4),
|
||||
RichText(
|
||||
Text.rich(
|
||||
// maxLines: 1,
|
||||
// overflow: TextOverflow.ellipsis,
|
||||
textScaler:
|
||||
MediaQuery.textScalerOf(context),
|
||||
text: TextSpan(
|
||||
TextSpan(
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
|
||||
@@ -95,7 +95,7 @@ class _LogsPageState extends State<LogsPage> {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('日志'),
|
||||
title: const Text('日志'),
|
||||
actions: [
|
||||
PopupMenuButton<String>(
|
||||
onSelected: (String type) {
|
||||
|
||||
@@ -191,7 +191,7 @@ class _PlaySpeedPageState extends State<PlaySpeedPage> {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('倍速设置'),
|
||||
title: const Text('倍速设置'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
|
||||
@@ -23,7 +23,9 @@ class _PlaySettingState extends State<PlaySetting> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: widget.showAppBar == false ? null : AppBar(title: Text('播放器设置')),
|
||||
appBar: widget.showAppBar == false
|
||||
? null
|
||||
: AppBar(title: const Text('播放器设置')),
|
||||
body: ListView(
|
||||
children: [
|
||||
...playSettings.map((item) => item.widget),
|
||||
|
||||
@@ -9,7 +9,7 @@ class PrivacySetting extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: showAppBar == false ? null : AppBar(title: Text('隐私设置')),
|
||||
appBar: showAppBar == false ? null : AppBar(title: const Text('隐私设置')),
|
||||
body: ListView(
|
||||
children: [
|
||||
...privacySettings.map((item) => item.widget),
|
||||
|
||||