Compare commits

...

27 Commits

Author SHA1 Message Date
dom
62c2c081d9 Revert "fix: macOS Media Control not activated & remove Background Play switch on desktop (#1872)"
This reverts commit 8f00ca5680.
2026-04-19 20:18:14 +08:00
dom
82483b33fc opt live emote
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-30 00:02:58 +08:00
My-Responsitories
886c53c7d8 opt: m3e loading (#1877)
* opt: loading

* feat: refresh m3e

* restore refreshIndicator

---------

Co-authored-by: dom <githubaccount56556@proton.me>
2026-03-29 23:34:04 +08:00
dom
f0050dd6e6 fix pendent offset
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-29 11:47:58 +08:00
dom
e6a2f65b4e fix reply up badge
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-29 11:47:58 +08:00
dom
2fc3f9864f opt ui
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-29 10:51:52 +08:00
dom
64c05a1b06 upgrade deps
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-29 10:02:00 +08:00
ninatan777
7c4e20f96c safely parse up list (#1876)
* fix: 兼容 mid/roomId/count 字段为字符串或数字类型

* update

---------

Co-authored-by: dom <githubaccount56556@proton.me>
2026-03-29 10:01:10 +08:00
dom
ace286753c flutter 3.41.6
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-27 10:33:15 +08:00
dom
f0430eba9f opt player bar
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-26 18:34:57 +08:00
dom
bbcceb72a7 upgrade deps
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-26 10:27:21 +08:00
dom
be4fa6ad2c build
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-25 20:25:54 +08:00
dom
50e1f77e10 log player error instead of toast
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-25 20:22:07 +08:00
dom
ba56b45038 clamp archive page
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-25 15:01:35 +08:00
dom
b4b3764e5f web archive
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-25 12:59:29 +08:00
dom
2220372e4f tweaks
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-25 12:58:49 +08:00
dom
0957dfc66e fix ios build
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-23 14:50:57 +08:00
dom
9578f948b4 tweaks
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-23 13:47:20 +08:00
dom
1724f0d202 upgrade deps
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-23 11:14:31 +08:00
dom
2bebf200df show user medal
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-23 11:03:24 +08:00
dom
fc7fc18b14 tweaks
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-22 16:35:08 +08:00
0x535A
8f00ca5680 fix: macOS Media Control not activated & remove Background Play switch on desktop (#1872)
* fix: macOS Media Control not activated

* fix: remove Background Play switch on desktop

asdf

Update lib/main.dart

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-22 16:25:31 +08:00
dom
236b524445 opt image viewer gesture
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-22 13:53:49 +08:00
dom
ae59d257c3 show medal wall
show user follow time

show top image title

Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-22 13:53:44 +08:00
dom
662ccfcf0a upgrade deps
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-19 20:34:36 +08:00
dom
b7ab3655c4 flutter 3.41.5
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-19 20:34:36 +08:00
Starfallen
eda04b32a4 fix(player): clamp loudnorm measured_thresh parameters to valid range (#1871)
Co-authored-by: ci <example@example.com>
2026-03-19 08:30:17 +00:00
258 changed files with 4100 additions and 2240 deletions

2
.fvmrc
View File

@@ -1,3 +1,3 @@
{ {
"flutter": "3.41.4" "flutter": "3.41.6"
} }

View File

@@ -13,7 +13,7 @@ on:
jobs: jobs:
build-macos-app: build-macos-app:
name: Release IOS name: Release IOS
runs-on: macos-latest runs-on: macos-26
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6 uses: actions/checkout@v6

View File

@@ -18,8 +18,10 @@ android {
targetCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17
} }
kotlinOptions { kotlin {
jvmTarget = JavaVersion.VERSION_17.toString() compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
}
} }
defaultConfig { defaultConfig {
@@ -62,10 +64,10 @@ android {
value = "PiliPlus dev", value = "PiliPlus dev",
) )
} }
proguardFiles( // proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), // getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro" // "proguard-rules.pro"
) // )
} }
debug { debug {
applicationIdSuffix = ".debug" applicationIdSuffix = ".debug"

View File

@@ -20,7 +20,5 @@
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0</string> <string>1.0</string>
<key>MinimumOSVersion</key>
<string>13.0</string>
</dict> </dict>
</plist> </plist>

View File

@@ -8,6 +8,8 @@ PODS:
- Flutter - Flutter
- auto_orientation (0.0.1): - auto_orientation (0.0.1):
- Flutter - Flutter
- battery_plus (1.0.0):
- Flutter
- chat_bottom_container (0.0.1): - chat_bottom_container (0.0.1):
- Flutter - Flutter
- connectivity_plus (0.0.1): - connectivity_plus (0.0.1):
@@ -68,9 +70,9 @@ PODS:
- Flutter - Flutter
- GT3Captcha-iOS - GT3Captcha-iOS
- GT3Captcha-iOS (0.15.8.3) - GT3Captcha-iOS (0.15.8.3)
- image_cropper (0.0.4): - image_cropper (0.0.5):
- Flutter - Flutter
- TOCropViewController (~> 2.8.0) - TOCropViewController (~> 3.1.1)
- image_picker_ios (0.0.1): - image_picker_ios (0.0.1):
- Flutter - Flutter
- live_photo_maker (0.0.3): - live_photo_maker (0.0.3):
@@ -84,9 +86,6 @@ PODS:
- OrderedSet (6.0.3) - OrderedSet (6.0.3)
- package_info_plus (0.4.5): - package_info_plus (0.4.5):
- Flutter - Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- permission_handler_apple (9.3.0): - permission_handler_apple (9.3.0):
- Flutter - Flutter
- saver_gallery (0.0.1): - saver_gallery (0.0.1):
@@ -105,7 +104,7 @@ PODS:
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- SwiftyGif (5.4.5) - SwiftyGif (5.4.5)
- TOCropViewController (2.8.0) - TOCropViewController (3.1.1)
- url_launcher_ios (0.0.1): - url_launcher_ios (0.0.1):
- Flutter - Flutter
- wakelock_plus (0.0.1): - wakelock_plus (0.0.1):
@@ -116,6 +115,7 @@ DEPENDENCIES:
- audio_service (from `.symlinks/plugins/audio_service/darwin`) - audio_service (from `.symlinks/plugins/audio_service/darwin`)
- audio_session (from `.symlinks/plugins/audio_session/ios`) - audio_session (from `.symlinks/plugins/audio_session/ios`)
- auto_orientation (from `.symlinks/plugins/auto_orientation/ios`) - auto_orientation (from `.symlinks/plugins/auto_orientation/ios`)
- battery_plus (from `.symlinks/plugins/battery_plus/ios`)
- chat_bottom_container (from `.symlinks/plugins/chat_bottom_container/ios`) - chat_bottom_container (from `.symlinks/plugins/chat_bottom_container/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
@@ -134,7 +134,6 @@ DEPENDENCIES:
- media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`) - media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`)
- media_kit_video (from `.symlinks/plugins/media_kit_video/ios`) - media_kit_video (from `.symlinks/plugins/media_kit_video/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- saver_gallery (from `.symlinks/plugins/saver_gallery/ios`) - saver_gallery (from `.symlinks/plugins/saver_gallery/ios`)
- screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`) - screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`)
@@ -163,6 +162,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/audio_session/ios" :path: ".symlinks/plugins/audio_session/ios"
auto_orientation: auto_orientation:
:path: ".symlinks/plugins/auto_orientation/ios" :path: ".symlinks/plugins/auto_orientation/ios"
battery_plus:
:path: ".symlinks/plugins/battery_plus/ios"
chat_bottom_container: chat_bottom_container:
:path: ".symlinks/plugins/chat_bottom_container/ios" :path: ".symlinks/plugins/chat_bottom_container/ios"
connectivity_plus: connectivity_plus:
@@ -199,8 +200,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/media_kit_video/ios" :path: ".symlinks/plugins/media_kit_video/ios"
package_info_plus: package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios" :path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple: permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios" :path: ".symlinks/plugins/permission_handler_apple/ios"
saver_gallery: saver_gallery:
@@ -219,44 +218,44 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/wakelock_plus/ios" :path: ".symlinks/plugins/wakelock_plus/ios"
SPEC CHECKSUMS: SPEC CHECKSUMS:
app_links: 6d01271b3907b0ee7325c5297c75d697c4226c4d app_links: a754cbec3c255bd4bbb4d236ecc06f28cd9a7ce8
audio_service: cab6c1a0eaf01b5a35b567e11fa67d3cc1956910 audio_service: aa99a6ba2ae7565996015322b0bb024e1d25c6fd
audio_session: 19e9480dbdd4e5f6c4543826b2e8b0e4ab6145fe audio_session: 9bb7f6c970f21241b19f5a3658097ae459681ba0
auto_orientation: 102ed811a5938d52c86520ddd7ecd3a126b5d39d auto_orientation: a1600c9ed72e6e96982fb4e1214463343342432a
chat_bottom_container: d8b077152c91b0ab90001e900748ea50353a5520 battery_plus: b42253f6d2dde71712f8c36fef456d99121c5977
connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d chat_bottom_container: f1eb8323db77a87db50f361142c679f11e892d1b
device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342 connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49 file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4 flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83 flutter_mailer: 3a8cd4f36c960fb04528d5471097270c19fec1c4
flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29 flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529 flutter_volume_controller: c2be490cb0487e8b88d0d9fc2b7e1c139a4ebccb
fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
gt3_flutter_plugin: 5bd2c08d3c19cbb6ee3b08f4358439e54c8ab2ee gt3_flutter_plugin: 37090e5fa66ff2a52939eb9d208fc36fa49d36e5
GT3Captcha-iOS: 5e3b1077834d8a9d6f4d64a447a30af3e14affe6 GT3Captcha-iOS: 5e3b1077834d8a9d6f4d64a447a30af3e14affe6
image_cropper: b8ef14d3fcff4040b0f9da2ca28d98219a5cba0e image_cropper: e405d3e44183f8e8edbec2e49b01ff9c819c7ac8
image_picker_ios: 4f2f91b01abdb52842a8e277617df877e40f905b image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
live_photo_maker: 7d57bfc70a120b4673c10871f354f4b1b6fde5fd live_photo_maker: 29280ca88323bd5a33aafd00d98624d5cf522176
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1 media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a media_kit_native_event_loop: 5fba1a849a6c87a34985f1e178a0de5bd444a0cf
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94 OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 saver_gallery: af2d0c762dafda254e0ad025ef0dabd6506cd490
saver_gallery: 76172dc4bf6b40e66d694948ada9ff402304dd87 screen_brightness_ios: 9953fd7da5bd480f1a93990daeec2eb42d4f3b52
screen_brightness_ios: 6a6f7794b67f07c4f1e24f6374b2d8ad367ffb39
SDWebImage: 16309af6d214ba3f77a7c6f6fdda888cb313a50a SDWebImage: 16309af6d214ba3f77a7c6f6fdda888cb313a50a
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6 shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
TOCropViewController: 797deaf39c90e6e9ddd848d88817f6b9a8a09888 TOCropViewController: 9002a9b12d8104d7478cdc306d80f0efea7fe2c5
url_launcher_ios: bb13df5870e8c4234ca12609d04010a21be43dfa url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
wakelock_plus: 76957ab028e12bfa4e66813c99e46637f367fc7e wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
PODFILE CHECKSUM: f62db4fb414ebdecb264109948f76dfef35fdc3d PODFILE CHECKSUM: f62db4fb414ebdecb264109948f76dfef35fdc3d

56
lib/common/assets.dart Normal file
View File

@@ -0,0 +1,56 @@
abstract final class Assets {
static const digitalNum = 'digital_id_num';
static const logo = 'assets/images/logo/logo.png';
static const logo2 = 'assets/images/logo/logo_2.png';
static const logoIco = 'assets/images/logo/ico/app_icon.ico';
static const logoLarge = 'assets/images/logo/desktop/logo_large.png';
static const vipIcon = 'assets/images/big-vip.png';
static const avatarPlaceHolder = 'assets/images/noface.jpeg';
static const loading = 'assets/images/loading.png';
static const buffering = 'assets/images/loading.webp';
static const play = 'assets/images/play.png';
static const topicHeader = 'assets/images/topic-header-bg.png';
static const trendingBanner = 'assets/images/trending_banner.png';
static const ai = 'assets/images/ai.png';
static const livingChart = 'assets/images/live.gif';
static const livingStatic = 'assets/images/live.png';
static const livingRect = 'assets/images/live/live.gif';
static const livingBackground = 'assets/images/live/default_bg.webp';
static const thunder1 = 'assets/images/paycoins/ic_thunder_1.png';
static const thunder2 = 'assets/images/paycoins/ic_thunder_2.png';
static const thunder3 = 'assets/images/paycoins/ic_thunder_3.png';
static const notEnough = 'assets/images/paycoins/ic_22_not_enough_pay.png';
static const mario = 'assets/images/paycoins/ic_22_mario.png';
static const gunSister = 'assets/images/paycoins/ic_22_gun_sister.png';
static const payBox = 'assets/images/paycoins/ic_pay_coins_box.png';
static const coinsOne = 'assets/images/paycoins/ic_coins_one.png';
static const coinsTwo = 'assets/images/paycoins/ic_coins_two.png';
static const left = 'assets/images/paycoins/ic_left.png';
static const leftDisable = 'assets/images/paycoins/ic_left_disable.png';
static const right = 'assets/images/paycoins/ic_right.png';
static const rightDisable = 'assets/images/paycoins/ic_right_disable.png';
static const panelClose = 'assets/images/paycoins/ic_panel_close.png';
static const List<String> mpvAnime4KShaders = [
'Anime4K_Clamp_Highlights.glsl',
'Anime4K_Restore_CNN_VL.glsl',
'Anime4K_Upscale_CNN_x2_VL.glsl',
'Anime4K_AutoDownscalePre_x2.glsl',
'Anime4K_AutoDownscalePre_x4.glsl',
'Anime4K_Upscale_CNN_x2_M.glsl',
];
static const mpvAnime4KShadersLite = [
'Anime4K_Clamp_Highlights.glsl',
'Anime4K_Restore_CNN_M.glsl',
'Anime4K_Restore_CNN_S.glsl',
'Anime4K_Upscale_CNN_x2_M.glsl',
'Anime4K_AutoDownscalePre_x2.glsl',
'Anime4K_AutoDownscalePre_x4.glsl',
'Anime4K_Upscale_CNN_x2_S.glsl',
];
}

View File

@@ -1,30 +1,3 @@
import 'package:flutter/material.dart';
abstract final class StyleString {
static const double cardSpace = 8;
static const double safeSpace = 12;
static const BorderRadius mdRadius = BorderRadius.all(imgRadius);
static const Radius imgRadius = Radius.circular(10);
static const double aspectRatio = 16 / 10;
static const double aspectRatio16x9 = 16 / 9;
static const double imgMaxRatio = 2.6;
static const bottomSheetRadius = BorderRadius.vertical(
top: Radius.circular(18),
);
static const dialogFixedConstraints = BoxConstraints(
minWidth: 420,
maxWidth: 420,
);
static const topBarHeight = 52.0;
static const buttonStyle = ButtonStyle(
visualDensity: VisualDensity(
horizontal: -2,
vertical: -1.25,
),
tapTargetSize: .shrinkWrap,
);
}
abstract final class Constants { abstract final class Constants {
static const appName = 'PiliPlus'; static const appName = 'PiliPlus';
static const sourceCodeUrl = 'https://github.com/bggRGjQaUbCoE/PiliPlus'; static const sourceCodeUrl = 'https://github.com/bggRGjQaUbCoE/PiliPlus';
@@ -68,244 +41,4 @@ abstract final class Constants {
// 'itemOpusStyle,opusBigCover,onlyfansVote,endFooterHidden,decorationCard,onlyfansAssetsV2,ugcDelete,onlyfansQaCard,editable,opusPrivateVisible,avatarAutoTheme,sunflowerStyle,cardsEnhance,eva3CardOpus,eva3CardVideo,eva3CardComment,eva3CardVote,eva3CardUser' // 'itemOpusStyle,opusBigCover,onlyfansVote,endFooterHidden,decorationCard,onlyfansAssetsV2,ugcDelete,onlyfansQaCard,editable,opusPrivateVisible,avatarAutoTheme,sunflowerStyle,cardsEnhance,eva3CardOpus,eva3CardVideo,eva3CardComment,eva3CardVote,eva3CardUser'
static const dynFeatures = 'itemOpusStyle,listOnlyfans,onlyfansQaCard'; static const dynFeatures = 'itemOpusStyle,listOnlyfans,onlyfansQaCard';
// 超分辨率滤镜
static const List<String> mpvAnime4KShaders = [
'Anime4K_Clamp_Highlights.glsl',
'Anime4K_Restore_CNN_VL.glsl',
'Anime4K_Upscale_CNN_x2_VL.glsl',
'Anime4K_AutoDownscalePre_x2.glsl',
'Anime4K_AutoDownscalePre_x4.glsl',
'Anime4K_Upscale_CNN_x2_M.glsl',
];
// 超分辨率滤镜 (轻量)
static const mpvAnime4KShadersLite = [
'Anime4K_Clamp_Highlights.glsl',
'Anime4K_Restore_CNN_M.glsl',
'Anime4K_Restore_CNN_S.glsl',
'Anime4K_Upscale_CNN_x2_M.glsl',
'Anime4K_AutoDownscalePre_x2.glsl',
'Anime4K_AutoDownscalePre_x4.glsl',
'Anime4K_Upscale_CNN_x2_S.glsl',
];
//内容来自 https://passport.bilibili.com/web/generic/country/list
static const internationalDialingPrefix = [
(id: 1, cname: "中国大陆", countryId: 86),
(id: 5, cname: "中国香港特别行政区", countryId: 852),
(id: 2, cname: "中国澳门特别行政区", countryId: 853),
(id: 3, cname: "中国台湾", countryId: 886),
(id: 4, cname: "美国", countryId: 1),
(id: 6, cname: "比利时", countryId: 32),
(id: 7, cname: "澳大利亚", countryId: 61),
(id: 8, cname: "法国", countryId: 33),
(id: 9, cname: "加拿大", countryId: 1),
(id: 10, cname: "日本", countryId: 81),
(id: 11, cname: "新加坡", countryId: 65),
(id: 12, cname: "韩国", countryId: 82),
(id: 13, cname: "马来西亚", countryId: 60),
(id: 14, cname: "英国", countryId: 44),
(id: 15, cname: "意大利", countryId: 39),
(id: 16, cname: "德国", countryId: 49),
(id: 18, cname: "俄罗斯", countryId: 7),
(id: 19, cname: "新西兰", countryId: 64),
(id: 153, cname: "瓦利斯群岛和富图纳群岛", countryId: 1681),
(id: 152, cname: "葡萄牙", countryId: 351),
(id: 151, cname: "帕劳", countryId: 680),
(id: 150, cname: "诺福克岛", countryId: 672),
(id: 149, cname: "挪威", countryId: 47),
(id: 148, cname: "纽埃岛", countryId: 683),
(id: 147, cname: "尼日利亚", countryId: 234),
(id: 146, cname: "尼日尔", countryId: 227),
(id: 145, cname: "尼加拉瓜", countryId: 505),
(id: 144, cname: "尼泊尔", countryId: 977),
(id: 143, cname: "瑙鲁", countryId: 674),
(id: 154, cname: "格鲁吉亚", countryId: 995),
(id: 155, cname: "瑞典", countryId: 46),
(id: 165, cname: "沙特阿拉伯", countryId: 966),
(id: 164, cname: "桑给巴尔岛", countryId: 259),
(id: 163, cname: "塞舌尔共和国", countryId: 248),
(id: 162, cname: "塞浦路斯", countryId: 357),
(id: 161, cname: "塞内加尔", countryId: 221),
(id: 160, cname: "塞拉利昂", countryId: 232),
(id: 159, cname: "萨摩亚,东部", countryId: 684),
(id: 158, cname: "萨摩亚,西部", countryId: 685),
(id: 157, cname: "萨尔瓦多", countryId: 503),
(id: 156, cname: "瑞士", countryId: 41),
(id: 166, cname: "圣多美和普林西比", countryId: 239),
(id: 142, cname: "塞尔维亚", countryId: 381),
(id: 141, cname: "南非", countryId: 27),
(id: 128, cname: "毛里塔尼亚", countryId: 222),
(id: 127, cname: "毛里求斯", countryId: 230),
(id: 126, cname: "马歇尔岛", countryId: 692),
(id: 125, cname: "马提尼克岛", countryId: 596),
(id: 124, cname: "马其顿", countryId: 389),
(id: 123, cname: "马里亚纳岛", countryId: 1670),
(id: 122, cname: "马里", countryId: 223),
(id: 121, cname: "马拉维", countryId: 265),
(id: 120, cname: "马耳他", countryId: 356),
(id: 119, cname: "马尔代夫", countryId: 960),
(id: 129, cname: "蒙古", countryId: 976),
(id: 130, cname: "蒙特塞拉特岛", countryId: 1664),
(id: 140, cname: "纳米比亚", countryId: 264),
(id: 139, cname: "墨西哥", countryId: 52),
(id: 138, cname: "莫桑比克", countryId: 258),
(id: 137, cname: "摩纳哥", countryId: 377),
(id: 136, cname: "摩洛哥", countryId: 212),
(id: 135, cname: "摩尔多瓦", countryId: 373),
(id: 134, cname: "缅甸", countryId: 95),
(id: 133, cname: "密克罗尼西亚", countryId: 691),
(id: 132, cname: "秘鲁", countryId: 51),
(id: 131, cname: "孟加拉国", countryId: 880),
(id: 118, cname: "马达加斯加", countryId: 261),
(id: 167, cname: "圣卢西亚", countryId: 1784),
(id: 216, cname: "智利", countryId: 56),
(id: 203, cname: "牙买加", countryId: 1876),
(id: 202, cname: "叙利亚", countryId: 963),
(id: 201, cname: "匈牙利", countryId: 36),
(id: 200, cname: "科特迪瓦", countryId: 225),
(id: 199, cname: "希腊", countryId: 30),
(id: 198, cname: "西班牙", countryId: 34),
(id: 197, cname: "乌兹别克斯坦", countryId: 998),
(id: 196, cname: "乌拉圭", countryId: 598),
(id: 195, cname: "乌克兰", countryId: 380),
(id: 194, cname: "乌干达", countryId: 256),
(id: 204, cname: "亚美尼亚", countryId: 374),
(id: 205, cname: "也门", countryId: 967),
(id: 215, cname: "直布罗陀", countryId: 350),
(id: 214, cname: "乍得", countryId: 235),
(id: 213, cname: "赞比亚", countryId: 260),
(id: 212, cname: "越南", countryId: 84),
(id: 211, cname: "约旦", countryId: 962),
(id: 210, cname: "印尼", countryId: 62),
(id: 209, cname: "印度", countryId: 91),
(id: 208, cname: "以色列", countryId: 972),
(id: 207, cname: "伊朗", countryId: 98),
(id: 206, cname: "伊拉克", countryId: 964),
(id: 193, cname: "文莱", countryId: 673),
(id: 192, cname: "委内瑞拉", countryId: 58),
(id: 191, cname: "维珍群岛(英属)", countryId: 1284),
(id: 178, cname: "泰国", countryId: 66),
(id: 177, cname: "索马里", countryId: 252),
(id: 176, cname: "所罗门群岛", countryId: 677),
(id: 175, cname: "苏里南", countryId: 597),
(id: 174, cname: "苏丹", countryId: 249),
(id: 173, cname: "斯威士兰", countryId: 268),
(id: 172, cname: "斯洛文尼亚", countryId: 386),
(id: 171, cname: "斯洛伐克", countryId: 421),
(id: 170, cname: "斯里兰卡", countryId: 94),
(id: 169, cname: "圣皮埃尔和密克隆群岛", countryId: 508),
(id: 179, cname: "坦桑尼亚", countryId: 255),
(id: 180, cname: "汤加", countryId: 676),
(id: 190, cname: "维珍群岛(美属)", countryId: 1340),
(id: 189, cname: "瓦努阿图", countryId: 678),
(id: 188, cname: "托克劳岛", countryId: 690),
(id: 187, cname: "土库曼斯坦", countryId: 993),
(id: 186, cname: "土耳其", countryId: 90),
(id: 185, cname: "图瓦卢", countryId: 688),
(id: 184, cname: "突尼斯", countryId: 216),
(id: 183, cname: "阿森松岛", countryId: 247),
(id: 182, cname: "特立尼达和多巴哥", countryId: 1868),
(id: 181, cname: "特克斯和凯科斯", countryId: 1649),
(id: 168, cname: "圣马力诺", countryId: 378),
(id: 67, cname: "法属圭亚那", countryId: 594),
(id: 54, cname: "不丹", countryId: 975),
(id: 53, cname: "博茨瓦纳", countryId: 267),
(id: 52, cname: "伯利兹", countryId: 501),
(id: 51, cname: "玻利维亚", countryId: 591),
(id: 50, cname: "波兰", countryId: 48),
(id: 49, cname: "波黑", countryId: 387),
(id: 48, cname: "波多黎各", countryId: 1787),
(id: 47, cname: "冰岛", countryId: 354),
(id: 46, cname: "贝宁", countryId: 229),
(id: 45, cname: "保加利亚", countryId: 359),
(id: 55, cname: "布基纳法索", countryId: 226),
(id: 56, cname: "布隆迪", countryId: 257),
(id: 66, cname: "法属波利尼西亚", countryId: 689),
(id: 65, cname: "法罗岛", countryId: 298),
(id: 64, cname: "厄立特里亚", countryId: 291),
(id: 63, cname: "厄瓜多尔", countryId: 593),
(id: 62, cname: "多米尼加代表", countryId: 1809),
(id: 61, cname: "多米尼加", countryId: 1767),
(id: 60, cname: "多哥", countryId: 228),
(id: 59, cname: "迪戈加西亚岛", countryId: 246),
(id: 58, cname: "丹麦", countryId: 45),
(id: 57, cname: "赤道几内亚", countryId: 240),
(id: 44, cname: "百慕大群岛", countryId: 1441),
(id: 43, cname: "白俄罗斯", countryId: 375),
(id: 42, cname: "巴西", countryId: 55),
(id: 29, cname: "爱尔兰", countryId: 353),
(id: 28, cname: "埃塞俄比亚", countryId: 251),
(id: 27, cname: "埃及", countryId: 20),
(id: 26, cname: "阿塞拜疆", countryId: 994),
(id: 25, cname: "阿曼", countryId: 968),
(id: 24, cname: "阿联酋", countryId: 971),
(id: 23, cname: "阿根廷", countryId: 54),
(id: 22, cname: "阿富汗", countryId: 93),
(id: 21, cname: "阿尔及利亚", countryId: 213),
(id: 20, cname: "阿尔巴尼亚", countryId: 355),
(id: 30, cname: "爱沙尼亚", countryId: 372),
(id: 31, cname: "安道尔", countryId: 376),
(id: 41, cname: "巴拿马", countryId: 507),
(id: 40, cname: "巴林", countryId: 973),
(id: 39, cname: "巴拉圭", countryId: 595),
(id: 38, cname: "巴基斯坦", countryId: 92),
(id: 37, cname: "巴哈马群岛", countryId: 1242),
(id: 36, cname: "巴布亚新几内亚", countryId: 675),
(id: 35, cname: "巴巴多斯", countryId: 1246),
(id: 34, cname: "奥地利", countryId: 43),
(id: 33, cname: "安提瓜岛和巴布达", countryId: 1268),
(id: 32, cname: "安哥拉", countryId: 244),
(id: 68, cname: "非洲中部", countryId: 236),
(id: 117, cname: "罗马尼亚", countryId: 40),
(id: 104, cname: "科威特", countryId: 965),
(id: 103, cname: "科摩罗", countryId: 269),
(id: 102, cname: "开曼群岛", countryId: 1345),
(id: 101, cname: "卡塔尔", countryId: 974),
(id: 100, cname: "喀麦隆", countryId: 237),
(id: 99, cname: "聚会岛", countryId: 262),
(id: 98, cname: "津巴布韦", countryId: 263),
(id: 97, cname: "捷克", countryId: 420),
(id: 96, cname: "柬埔寨", countryId: 855),
(id: 95, cname: "加蓬", countryId: 241),
(id: 105, cname: "克罗地亚", countryId: 385),
(id: 106, cname: "肯尼亚", countryId: 254),
(id: 116, cname: "卢旺达", countryId: 250),
(id: 115, cname: "卢森堡", countryId: 352),
(id: 114, cname: "利比亚", countryId: 218),
(id: 113, cname: "利比里亚", countryId: 231),
(id: 112, cname: "立陶宛", countryId: 370),
(id: 111, cname: "黎巴嫩", countryId: 961),
(id: 110, cname: "老挝", countryId: 856),
(id: 109, cname: "莱索托", countryId: 266),
(id: 108, cname: "拉脱维亚", countryId: 371),
(id: 107, cname: "库克岛", countryId: 682),
(id: 94, cname: "加纳", countryId: 233),
(id: 93, cname: "几内亚比绍", countryId: 245),
(id: 92, cname: "几内亚", countryId: 224),
(id: 79, cname: "格林纳达", countryId: 1473),
(id: 78, cname: "哥斯达黎加", countryId: 506),
(id: 77, cname: "哥伦比亚", countryId: 57),
(id: 76, cname: "刚果(金)", countryId: 243),
(id: 75, cname: "刚果", countryId: 242),
(id: 74, cname: "冈比亚", countryId: 220),
(id: 73, cname: "福克兰岛", countryId: 500),
(id: 72, cname: "佛得角", countryId: 238),
(id: 71, cname: "芬兰", countryId: 358),
(id: 70, cname: "斐济", countryId: 679),
(id: 80, cname: "格陵兰岛", countryId: 299),
(id: 81, cname: "古巴", countryId: 53),
(id: 91, cname: "吉尔吉斯斯坦", countryId: 996),
(id: 90, cname: "吉布提", countryId: 253),
(id: 89, cname: "基里巴斯", countryId: 686),
(id: 88, cname: "维克岛", countryId: 1808),
(id: 87, cname: "洪都拉斯", countryId: 504),
(id: 86, cname: "荷兰", countryId: 31),
(id: 85, cname: "朝鲜", countryId: 850),
(id: 84, cname: "海地", countryId: 509),
(id: 83, cname: "关岛", countryId: 1671),
(id: 82, cname: "瓜德罗普岛", countryId: 590),
(id: 69, cname: "菲律宾", countryId: 63),
];
} }

220
lib/common/dial_prefix.dart Normal file
View File

@@ -0,0 +1,220 @@
abstract final class Login {
//内容来自 https://passport.bilibili.com/web/generic/country/list
static const dialPrefix = [
(id: 1, cname: "中国大陆", countryId: 86),
(id: 5, cname: "中国香港特别行政区", countryId: 852),
(id: 2, cname: "中国澳门特别行政区", countryId: 853),
(id: 3, cname: "中国台湾", countryId: 886),
(id: 4, cname: "美国", countryId: 1),
(id: 6, cname: "比利时", countryId: 32),
(id: 7, cname: "澳大利亚", countryId: 61),
(id: 8, cname: "法国", countryId: 33),
(id: 9, cname: "加拿大", countryId: 1),
(id: 10, cname: "日本", countryId: 81),
(id: 11, cname: "新加坡", countryId: 65),
(id: 12, cname: "韩国", countryId: 82),
(id: 13, cname: "马来西亚", countryId: 60),
(id: 14, cname: "英国", countryId: 44),
(id: 15, cname: "意大利", countryId: 39),
(id: 16, cname: "德国", countryId: 49),
(id: 18, cname: "俄罗斯", countryId: 7),
(id: 19, cname: "新西兰", countryId: 64),
(id: 153, cname: "瓦利斯群岛和富图纳群岛", countryId: 1681),
(id: 152, cname: "葡萄牙", countryId: 351),
(id: 151, cname: "帕劳", countryId: 680),
(id: 150, cname: "诺福克岛", countryId: 672),
(id: 149, cname: "挪威", countryId: 47),
(id: 148, cname: "纽埃岛", countryId: 683),
(id: 147, cname: "尼日利亚", countryId: 234),
(id: 146, cname: "尼日尔", countryId: 227),
(id: 145, cname: "尼加拉瓜", countryId: 505),
(id: 144, cname: "尼泊尔", countryId: 977),
(id: 143, cname: "瑙鲁", countryId: 674),
(id: 154, cname: "格鲁吉亚", countryId: 995),
(id: 155, cname: "瑞典", countryId: 46),
(id: 165, cname: "沙特阿拉伯", countryId: 966),
(id: 164, cname: "桑给巴尔岛", countryId: 259),
(id: 163, cname: "塞舌尔共和国", countryId: 248),
(id: 162, cname: "塞浦路斯", countryId: 357),
(id: 161, cname: "塞内加尔", countryId: 221),
(id: 160, cname: "塞拉利昂", countryId: 232),
(id: 159, cname: "萨摩亚,东部", countryId: 684),
(id: 158, cname: "萨摩亚,西部", countryId: 685),
(id: 157, cname: "萨尔瓦多", countryId: 503),
(id: 156, cname: "瑞士", countryId: 41),
(id: 166, cname: "圣多美和普林西比", countryId: 239),
(id: 142, cname: "塞尔维亚", countryId: 381),
(id: 141, cname: "南非", countryId: 27),
(id: 128, cname: "毛里塔尼亚", countryId: 222),
(id: 127, cname: "毛里求斯", countryId: 230),
(id: 126, cname: "马歇尔岛", countryId: 692),
(id: 125, cname: "马提尼克岛", countryId: 596),
(id: 124, cname: "马其顿", countryId: 389),
(id: 123, cname: "马里亚纳岛", countryId: 1670),
(id: 122, cname: "马里", countryId: 223),
(id: 121, cname: "马拉维", countryId: 265),
(id: 120, cname: "马耳他", countryId: 356),
(id: 119, cname: "马尔代夫", countryId: 960),
(id: 129, cname: "蒙古", countryId: 976),
(id: 130, cname: "蒙特塞拉特岛", countryId: 1664),
(id: 140, cname: "纳米比亚", countryId: 264),
(id: 139, cname: "墨西哥", countryId: 52),
(id: 138, cname: "莫桑比克", countryId: 258),
(id: 137, cname: "摩纳哥", countryId: 377),
(id: 136, cname: "摩洛哥", countryId: 212),
(id: 135, cname: "摩尔多瓦", countryId: 373),
(id: 134, cname: "缅甸", countryId: 95),
(id: 133, cname: "密克罗尼西亚", countryId: 691),
(id: 132, cname: "秘鲁", countryId: 51),
(id: 131, cname: "孟加拉国", countryId: 880),
(id: 118, cname: "马达加斯加", countryId: 261),
(id: 167, cname: "圣卢西亚", countryId: 1784),
(id: 216, cname: "智利", countryId: 56),
(id: 203, cname: "牙买加", countryId: 1876),
(id: 202, cname: "叙利亚", countryId: 963),
(id: 201, cname: "匈牙利", countryId: 36),
(id: 200, cname: "科特迪瓦", countryId: 225),
(id: 199, cname: "希腊", countryId: 30),
(id: 198, cname: "西班牙", countryId: 34),
(id: 197, cname: "乌兹别克斯坦", countryId: 998),
(id: 196, cname: "乌拉圭", countryId: 598),
(id: 195, cname: "乌克兰", countryId: 380),
(id: 194, cname: "乌干达", countryId: 256),
(id: 204, cname: "亚美尼亚", countryId: 374),
(id: 205, cname: "也门", countryId: 967),
(id: 215, cname: "直布罗陀", countryId: 350),
(id: 214, cname: "乍得", countryId: 235),
(id: 213, cname: "赞比亚", countryId: 260),
(id: 212, cname: "越南", countryId: 84),
(id: 211, cname: "约旦", countryId: 962),
(id: 210, cname: "印尼", countryId: 62),
(id: 209, cname: "印度", countryId: 91),
(id: 208, cname: "以色列", countryId: 972),
(id: 207, cname: "伊朗", countryId: 98),
(id: 206, cname: "伊拉克", countryId: 964),
(id: 193, cname: "文莱", countryId: 673),
(id: 192, cname: "委内瑞拉", countryId: 58),
(id: 191, cname: "维珍群岛(英属)", countryId: 1284),
(id: 178, cname: "泰国", countryId: 66),
(id: 177, cname: "索马里", countryId: 252),
(id: 176, cname: "所罗门群岛", countryId: 677),
(id: 175, cname: "苏里南", countryId: 597),
(id: 174, cname: "苏丹", countryId: 249),
(id: 173, cname: "斯威士兰", countryId: 268),
(id: 172, cname: "斯洛文尼亚", countryId: 386),
(id: 171, cname: "斯洛伐克", countryId: 421),
(id: 170, cname: "斯里兰卡", countryId: 94),
(id: 169, cname: "圣皮埃尔和密克隆群岛", countryId: 508),
(id: 179, cname: "坦桑尼亚", countryId: 255),
(id: 180, cname: "汤加", countryId: 676),
(id: 190, cname: "维珍群岛(美属)", countryId: 1340),
(id: 189, cname: "瓦努阿图", countryId: 678),
(id: 188, cname: "托克劳岛", countryId: 690),
(id: 187, cname: "土库曼斯坦", countryId: 993),
(id: 186, cname: "土耳其", countryId: 90),
(id: 185, cname: "图瓦卢", countryId: 688),
(id: 184, cname: "突尼斯", countryId: 216),
(id: 183, cname: "阿森松岛", countryId: 247),
(id: 182, cname: "特立尼达和多巴哥", countryId: 1868),
(id: 181, cname: "特克斯和凯科斯", countryId: 1649),
(id: 168, cname: "圣马力诺", countryId: 378),
(id: 67, cname: "法属圭亚那", countryId: 594),
(id: 54, cname: "不丹", countryId: 975),
(id: 53, cname: "博茨瓦纳", countryId: 267),
(id: 52, cname: "伯利兹", countryId: 501),
(id: 51, cname: "玻利维亚", countryId: 591),
(id: 50, cname: "波兰", countryId: 48),
(id: 49, cname: "波黑", countryId: 387),
(id: 48, cname: "波多黎各", countryId: 1787),
(id: 47, cname: "冰岛", countryId: 354),
(id: 46, cname: "贝宁", countryId: 229),
(id: 45, cname: "保加利亚", countryId: 359),
(id: 55, cname: "布基纳法索", countryId: 226),
(id: 56, cname: "布隆迪", countryId: 257),
(id: 66, cname: "法属波利尼西亚", countryId: 689),
(id: 65, cname: "法罗岛", countryId: 298),
(id: 64, cname: "厄立特里亚", countryId: 291),
(id: 63, cname: "厄瓜多尔", countryId: 593),
(id: 62, cname: "多米尼加代表", countryId: 1809),
(id: 61, cname: "多米尼加", countryId: 1767),
(id: 60, cname: "多哥", countryId: 228),
(id: 59, cname: "迪戈加西亚岛", countryId: 246),
(id: 58, cname: "丹麦", countryId: 45),
(id: 57, cname: "赤道几内亚", countryId: 240),
(id: 44, cname: "百慕大群岛", countryId: 1441),
(id: 43, cname: "白俄罗斯", countryId: 375),
(id: 42, cname: "巴西", countryId: 55),
(id: 29, cname: "爱尔兰", countryId: 353),
(id: 28, cname: "埃塞俄比亚", countryId: 251),
(id: 27, cname: "埃及", countryId: 20),
(id: 26, cname: "阿塞拜疆", countryId: 994),
(id: 25, cname: "阿曼", countryId: 968),
(id: 24, cname: "阿联酋", countryId: 971),
(id: 23, cname: "阿根廷", countryId: 54),
(id: 22, cname: "阿富汗", countryId: 93),
(id: 21, cname: "阿尔及利亚", countryId: 213),
(id: 20, cname: "阿尔巴尼亚", countryId: 355),
(id: 30, cname: "爱沙尼亚", countryId: 372),
(id: 31, cname: "安道尔", countryId: 376),
(id: 41, cname: "巴拿马", countryId: 507),
(id: 40, cname: "巴林", countryId: 973),
(id: 39, cname: "巴拉圭", countryId: 595),
(id: 38, cname: "巴基斯坦", countryId: 92),
(id: 37, cname: "巴哈马群岛", countryId: 1242),
(id: 36, cname: "巴布亚新几内亚", countryId: 675),
(id: 35, cname: "巴巴多斯", countryId: 1246),
(id: 34, cname: "奥地利", countryId: 43),
(id: 33, cname: "安提瓜岛和巴布达", countryId: 1268),
(id: 32, cname: "安哥拉", countryId: 244),
(id: 68, cname: "非洲中部", countryId: 236),
(id: 117, cname: "罗马尼亚", countryId: 40),
(id: 104, cname: "科威特", countryId: 965),
(id: 103, cname: "科摩罗", countryId: 269),
(id: 102, cname: "开曼群岛", countryId: 1345),
(id: 101, cname: "卡塔尔", countryId: 974),
(id: 100, cname: "喀麦隆", countryId: 237),
(id: 99, cname: "聚会岛", countryId: 262),
(id: 98, cname: "津巴布韦", countryId: 263),
(id: 97, cname: "捷克", countryId: 420),
(id: 96, cname: "柬埔寨", countryId: 855),
(id: 95, cname: "加蓬", countryId: 241),
(id: 105, cname: "克罗地亚", countryId: 385),
(id: 106, cname: "肯尼亚", countryId: 254),
(id: 116, cname: "卢旺达", countryId: 250),
(id: 115, cname: "卢森堡", countryId: 352),
(id: 114, cname: "利比亚", countryId: 218),
(id: 113, cname: "利比里亚", countryId: 231),
(id: 112, cname: "立陶宛", countryId: 370),
(id: 111, cname: "黎巴嫩", countryId: 961),
(id: 110, cname: "老挝", countryId: 856),
(id: 109, cname: "莱索托", countryId: 266),
(id: 108, cname: "拉脱维亚", countryId: 371),
(id: 107, cname: "库克岛", countryId: 682),
(id: 94, cname: "加纳", countryId: 233),
(id: 93, cname: "几内亚比绍", countryId: 245),
(id: 92, cname: "几内亚", countryId: 224),
(id: 79, cname: "格林纳达", countryId: 1473),
(id: 78, cname: "哥斯达黎加", countryId: 506),
(id: 77, cname: "哥伦比亚", countryId: 57),
(id: 76, cname: "刚果(金)", countryId: 243),
(id: 75, cname: "刚果", countryId: 242),
(id: 74, cname: "冈比亚", countryId: 220),
(id: 73, cname: "福克兰岛", countryId: 500),
(id: 72, cname: "佛得角", countryId: 238),
(id: 71, cname: "芬兰", countryId: 358),
(id: 70, cname: "斐济", countryId: 679),
(id: 80, cname: "格陵兰岛", countryId: 299),
(id: 81, cname: "古巴", countryId: 53),
(id: 91, cname: "吉尔吉斯斯坦", countryId: 996),
(id: 90, cname: "吉布提", countryId: 253),
(id: 89, cname: "基里巴斯", countryId: 686),
(id: 88, cname: "维克岛", countryId: 1808),
(id: 87, cname: "洪都拉斯", countryId: 504),
(id: 86, cname: "荷兰", countryId: 31),
(id: 85, cname: "朝鲜", countryId: 850),
(id: 84, cname: "海地", countryId: 509),
(id: 83, cname: "关岛", countryId: 1671),
(id: 82, cname: "瓜德罗普岛", countryId: 590),
(id: 69, cname: "菲律宾", countryId: 63),
];
}

View File

@@ -1,5 +1,5 @@
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/skeleton/skeleton.dart'; import 'package:PiliPlus/common/skeleton/skeleton.dart';
import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart'; import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
import 'package:flutter/material.dart' hide LayoutBuilder; import 'package:flutter/material.dart' hide LayoutBuilder;
@@ -12,7 +12,7 @@ class FavPgcItemSkeleton extends StatelessWidget {
return Skeleton( return Skeleton(
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: StyleString.safeSpace, horizontal: Style.safeSpace,
vertical: 5, vertical: 5,
), ),
child: Row( child: Row(

View File

@@ -1,5 +1,5 @@
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/skeleton/skeleton.dart'; import 'package:PiliPlus/common/skeleton/skeleton.dart';
import 'package:PiliPlus/common/style.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class MediaPgcSkeleton extends StatefulWidget { class MediaPgcSkeleton extends StatefulWidget {
@@ -15,11 +15,9 @@ class _MediaPgcSkeletonState extends State<MediaPgcSkeleton> {
Color bgColor = Theme.of(context).colorScheme.onInverseSurface; Color bgColor = Theme.of(context).colorScheme.onInverseSurface;
return Skeleton( return Skeleton(
child: Padding( child: Padding(
padding: const EdgeInsets.fromLTRB( padding: const .symmetric(
StyleString.safeSpace, horizontal: Style.safeSpace,
7, vertical: 7,
StyleString.safeSpace,
7,
), ),
child: Row( child: Row(
children: [ children: [

View File

@@ -1,5 +1,5 @@
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/skeleton/skeleton.dart'; import 'package:PiliPlus/common/skeleton/skeleton.dart';
import 'package:PiliPlus/common/style.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class VideoCardHSkeleton extends StatelessWidget { class VideoCardHSkeleton extends StatelessWidget {
@@ -10,25 +10,25 @@ class VideoCardHSkeleton extends StatelessWidget {
final color = Theme.of(context).colorScheme.onInverseSurface; final color = Theme.of(context).colorScheme.onInverseSurface;
return Skeleton( return Skeleton(
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const .symmetric(
horizontal: StyleString.safeSpace, horizontal: Style.safeSpace,
vertical: 5, vertical: 5,
), ),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
AspectRatio( AspectRatio(
aspectRatio: StyleString.aspectRatio, aspectRatio: Style.aspectRatio,
child: DecoratedBox( child: DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
color: color, color: color,
borderRadius: StyleString.mdRadius, borderRadius: Style.mdRadius,
), ),
), ),
), ),
Expanded( Expanded(
child: Padding( child: Padding(
padding: const EdgeInsets.fromLTRB(10, 4, 6, 4), padding: const .fromLTRB(10, 4, 6, 4),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [

View File

@@ -1,5 +1,5 @@
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/skeleton/skeleton.dart'; import 'package:PiliPlus/common/skeleton/skeleton.dart';
import 'package:PiliPlus/common/style.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class VideoCardVSkeleton extends StatelessWidget { class VideoCardVSkeleton extends StatelessWidget {
@@ -13,11 +13,11 @@ class VideoCardVSkeleton extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
AspectRatio( AspectRatio(
aspectRatio: StyleString.aspectRatio, aspectRatio: Style.aspectRatio,
child: DecoratedBox( child: DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
color: color, color: color,
borderRadius: StyleString.mdRadius, borderRadius: Style.mdRadius,
), ),
), ),
), ),

24
lib/common/style.dart Normal file
View File

@@ -0,0 +1,24 @@
import 'package:flutter/material.dart'
show BorderRadius, Radius, BoxConstraints, ButtonStyle, VisualDensity;
abstract final class Style {
static const cardSpace = 8.0;
static const safeSpace = 12.0;
static const mdRadius = BorderRadius.all(imgRadius);
static const imgRadius = Radius.circular(10);
static const aspectRatio = 16 / 10;
static const aspectRatio16x9 = 16 / 9;
static const imgMaxRatio = 2.6;
static const bottomSheetRadius = BorderRadius.vertical(
top: Radius.circular(18),
);
static const dialogFixedConstraints = BoxConstraints(
minWidth: 420,
maxWidth: 420,
);
static const topBarHeight = 52.0;
static const buttonStyle = ButtonStyle(
visualDensity: VisualDensity(horizontal: -2, vertical: -1.25),
tapTargetSize: .shrinkWrap,
);
}

View File

@@ -1,4 +1,4 @@
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/style.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ColorPalette extends StatelessWidget { class ColorPalette extends StatelessWidget {
@@ -62,7 +62,7 @@ class ColorPalette extends StatelessWidget {
padding: const EdgeInsets.all(6), padding: const EdgeInsets.all(6),
decoration: BoxDecoration( decoration: BoxDecoration(
color: colorScheme.onInverseSurface, color: colorScheme.onInverseSurface,
borderRadius: StyleString.mdRadius, borderRadius: Style.mdRadius,
), ),
child: child, child: child,
); );

View File

@@ -10,23 +10,21 @@ class CustomToast extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context); final colorScheme = ColorScheme.of(context);
return Container( return Container(
margin: EdgeInsets.only( margin: EdgeInsets.only(
bottom: MediaQuery.viewPaddingOf(context).bottom + 30, bottom: MediaQuery.viewPaddingOf(context).bottom + 30,
), ),
padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 10), padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 10),
decoration: BoxDecoration( decoration: BoxDecoration(
color: theme.colorScheme.primaryContainer.withValues( color: colorScheme.primaryContainer.withValues(alpha: toastOpacity),
alpha: toastOpacity,
),
borderRadius: const BorderRadius.all(Radius.circular(20)), borderRadius: const BorderRadius.all(Radius.circular(20)),
), ),
child: Text( child: Text(
msg, msg,
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
color: theme.colorScheme.onPrimaryContainer, color: colorScheme.onPrimaryContainer,
), ),
), ),
); );
@@ -41,7 +39,7 @@ class LoadingWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context); final theme = Theme.of(context);
final onSurfaceVariant = theme.colorScheme.onSurfaceVariant; final onSurfaceVariant = theme.colorScheme.onSurfaceVariant;
return Container( return Container(
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20), padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20),
@@ -58,7 +56,6 @@ class LoadingWidget extends StatelessWidget {
strokeWidth: 3, strokeWidth: 3,
valueColor: AlwaysStoppedAnimation(onSurfaceVariant), valueColor: AlwaysStoppedAnimation(onSurfaceVariant),
), ),
//msg //msg
Text(msg, style: TextStyle(color: onSurfaceVariant)), Text(msg, style: TextStyle(color: onSurfaceVariant)),
], ],

View File

@@ -3,21 +3,16 @@ import 'package:get/get.dart';
Future<bool> showConfirmDialog({ Future<bool> showConfirmDialog({
required BuildContext context, required BuildContext context,
required String title, required Widget title,
Object? content, Widget? content,
// @Deprecated('use `bool result = await showConfirmDialog()` instead') // @Deprecated('use `bool result = await showConfirmDialog()` instead')
VoidCallback? onConfirm, VoidCallback? onConfirm,
}) async { }) async {
assert(content is String? || content is Widget);
return await showDialog<bool>( return await showDialog<bool>(
context: context, context: context,
builder: (context) => AlertDialog( builder: (context) => AlertDialog(
title: Text(title), title: title,
content: content is String content: content,
? Text(content)
: content is Widget
? content
: null,
actions: [ actions: [
TextButton( TextButton(
onPressed: Get.back, onPressed: Get.back,

View File

@@ -2,7 +2,7 @@ import 'dart:async' show FutureOr;
import 'dart:convert' show utf8, jsonDecode; import 'dart:convert' show utf8, jsonDecode;
import 'dart:io' show File; import 'dart:io' show File;
import 'package:PiliPlus/common/constants.dart' show StyleString; import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/utils/extension/context_ext.dart'; import 'package:PiliPlus/utils/extension/context_ext.dart';
import 'package:PiliPlus/utils/utils.dart'; import 'package:PiliPlus/utils/utils.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
@@ -155,7 +155,7 @@ void importFromInput<T>(
context: context, context: context,
builder: (context) => AlertDialog( builder: (context) => AlertDialog(
title: Text('输入$title'), title: Text('输入$title'),
constraints: StyleString.dialogFixedConstraints, constraints: Style.dialogFixedConstraints,
content: TextFormField( content: TextFormField(
key: key, key: key,
minLines: 4, minLines: 4,

View File

@@ -0,0 +1,31 @@
import 'package:flutter/rendering.dart' show RenderProxyBox, BoxHitTestResult;
import 'package:flutter/widgets.dart';
class ExtraHitTestWidget extends SingleChildRenderObjectWidget {
const ExtraHitTestWidget({
super.key,
required this.width,
required Widget super.child,
});
final double width;
@override
RenderObject createRenderObject(BuildContext context) {
return RenderExtraHitTestWidget(width: width);
}
}
class RenderExtraHitTestWidget extends RenderProxyBox {
RenderExtraHitTestWidget({
required double width,
}) : _width = width;
final double _width;
@override
bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
return super.hitTestChildren(result, position: position) ||
position.dx <= _width;
}
}

View File

@@ -217,8 +217,8 @@ class RefreshIndicatorState extends State<RefreshIndicator>
RefreshIndicatorStatus? _status; RefreshIndicatorStatus? _status;
late Future<void> _pendingRefreshFuture; late Future<void> _pendingRefreshFuture;
double? _dragOffset; double? _dragOffset;
late Color _effectiveValueColor = late Color _effectiveValueColor;
widget.color ?? Theme.of(context).colorScheme.primary; // late Color _backgroundColor;
static final Animatable<double> _threeQuarterTween = Tween<double>( static final Animatable<double> _threeQuarterTween = Tween<double>(
begin: 0.0, begin: 0.0,
@@ -274,9 +274,10 @@ class RefreshIndicatorState extends State<RefreshIndicator>
} }
void _setupColorTween() { void _setupColorTween() {
final colorScheme = ColorScheme.of(context);
// _backgroundColor = colorScheme.surfaceContainerHighest;
// Reset the current value color. // Reset the current value color.
_effectiveValueColor = _effectiveValueColor = widget.color ?? colorScheme.primary;
widget.color ?? Theme.of(context).colorScheme.primary;
final Color color = _effectiveValueColor; final Color color = _effectiveValueColor;
if (color.a == 0) { if (color.a == 0) {
// Set an always stopped animation instead of a driven tween. // Set an always stopped animation instead of a driven tween.
@@ -558,14 +559,52 @@ class RefreshIndicatorState extends State<RefreshIndicator>
} }
bool _onDrag(double offset, double viewportDimension) { bool _onDrag(double offset, double viewportDimension) {
if (_positionController.value > 0.0 && if (_positionController.value > 0.0 && _status == .drag) {
_status == RefreshIndicatorStatus.drag) {
_dragOffset = _dragOffset! + offset; _dragOffset = _dragOffset! + offset;
_checkDragOffset(viewportDimension); _checkDragOffset(viewportDimension);
return true; return true;
} }
return false; return false;
} }
// late final _refreshKey = GlobalKey();
// Widget _m3eRefreshProgressIndicator(bool showIndeterminateIndicator) {
// const indicatorMargin = EdgeInsets.all(4);
// const indicatorPadding = EdgeInsets.all(6);
// const indicatorSize = 41.0;
// final progress = _value.value;
// return Padding(
// padding: indicatorMargin,
// child: SizedBox(
// width: indicatorSize,
// height: indicatorSize,
// child: Material(
// type: MaterialType.circle,
// color: _backgroundColor,
// elevation: widget.elevation,
// child: Padding(
// padding: indicatorPadding,
// child: showIndeterminateIndicator
// ? M3ELoadingIndicator(
// childKey: _refreshKey,
// color: _effectiveValueColor,
// morphs: Morphs.refreshMorphs,
// size: null,
// )
// : RawM3ELoadingIndicator(
// key: _refreshKey,
// morph: Morphs.manualMorph,
// progress: progress,
// angle: -progress * math.pi,
// color: _valueColor.value!,
// size: null,
// ),
// ),
// ),
// ),
// );
// }
} }
// ignore: camel_case_types // ignore: camel_case_types

View File

@@ -1,26 +1,29 @@
import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
class CustomHorizontalDragGestureRecognizer mixin InitialPositionMixin on GestureRecognizer {
extends HorizontalDragGestureRecognizer {
CustomHorizontalDragGestureRecognizer({
super.debugOwner,
super.supportedDevices,
super.allowedButtonsFilter,
});
Offset? _initialPosition; Offset? _initialPosition;
Offset? get initialPosition => _initialPosition; Offset? get initialPosition => _initialPosition;
@override
DeviceGestureSettings get gestureSettings => _gestureSettings;
final _gestureSettings = DeviceGestureSettings(touchSlop: touchSlopH);
@override @override
void addAllowedPointer(PointerDownEvent event) { void addAllowedPointer(PointerDownEvent event) {
super.addAllowedPointer(event); super.addAllowedPointer(event);
_initialPosition = event.position; _initialPosition = event.position;
} }
}
class CustomHorizontalDragGestureRecognizer
extends HorizontalDragGestureRecognizer
with InitialPositionMixin {
CustomHorizontalDragGestureRecognizer({
super.debugOwner,
super.supportedDevices,
super.allowedButtonsFilter,
});
@override
DeviceGestureSettings get gestureSettings => _gestureSettings;
final _gestureSettings = DeviceGestureSettings(touchSlop: touchSlopH);
@override @override
bool hasSufficientGlobalDistanceToAccept( bool hasSufficientGlobalDistanceToAccept(
@@ -41,7 +44,7 @@ double touchSlopH = Pref.touchSlopH;
bool _computeHitSlop( bool _computeHitSlop(
double globalDistanceMoved, double globalDistanceMoved,
DeviceGestureSettings? settings, DeviceGestureSettings settings,
PointerDeviceKind kind, PointerDeviceKind kind,
Offset? initialPosition, Offset? initialPosition,
Offset lastPosition, Offset lastPosition,
@@ -53,10 +56,10 @@ bool _computeHitSlop(
case PointerDeviceKind.invertedStylus: case PointerDeviceKind.invertedStylus:
case PointerDeviceKind.unknown: case PointerDeviceKind.unknown:
case PointerDeviceKind.touch: case PointerDeviceKind.touch:
return globalDistanceMoved > touchSlopH && return globalDistanceMoved > settings.touchSlop! &&
_calc(initialPosition!, lastPosition); _calc(initialPosition!, lastPosition);
case PointerDeviceKind.trackpad: case PointerDeviceKind.trackpad:
return globalDistanceMoved > (settings?.touchSlop ?? kTouchSlop); return globalDistanceMoved > settings.touchSlop!;
} }
} }

View File

@@ -1,22 +1,22 @@
import 'package:PiliPlus/common/widgets/gesture/horizontal_drag_gesture_recognizer.dart'; import 'package:PiliPlus/common/widgets/gesture/horizontal_drag_gesture_recognizer.dart';
import 'package:PiliPlus/utils/platform_utils.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
mixin ImageGestureRecognizerMixin on GestureRecognizer { mixin ImageGestureRecognizerMixin on GestureRecognizer {
int? _pointer; int? _pointer;
@override @override
void addPointer(PointerDownEvent event) { void addPointer(PointerDownEvent event, {bool isPointerAllowed = true}) {
if (_pointer == event.pointer) { if (_pointer == event.pointer) {
return; return;
} }
_pointer = event.pointer; _pointer = event.pointer;
super.addPointer(event); if (isPointerAllowed) {
super.addPointer(event);
}
} }
} }
typedef IsBoundaryAllowed =
bool Function(Offset? initialPosition, OffsetPair lastPosition);
class ImageHorizontalDragGestureRecognizer class ImageHorizontalDragGestureRecognizer
extends CustomHorizontalDragGestureRecognizer extends CustomHorizontalDragGestureRecognizer
with ImageGestureRecognizerMixin { with ImageGestureRecognizerMixin {
@@ -26,23 +26,62 @@ class ImageHorizontalDragGestureRecognizer
super.allowedButtonsFilter, super.allowedButtonsFilter,
}); });
IsBoundaryAllowed? isBoundaryAllowed; static final double _touchSlop = PlatformUtils.isDesktop
? kPrecisePointerHitSlop
: 3.0;
@override @override
bool hasSufficientGlobalDistanceToAccept( DeviceGestureSettings get gestureSettings => _gestureSettings;
PointerDeviceKind pointerDeviceKind, final _gestureSettings = DeviceGestureSettings(touchSlop: _touchSlop);
double? deviceTouchSlop,
) { bool isAtLeftEdge = false;
return super.hasSufficientGlobalDistanceToAccept( bool isAtRightEdge = false;
pointerDeviceKind,
deviceTouchSlop, void setAtBothEdges() {
) && isAtLeftEdge = isAtRightEdge = true;
(isBoundaryAllowed?.call(initialPosition, lastPosition) ?? true); }
bool _isEdgeAllowed(double dx) {
if ((initialPosition!.dx - dx).abs() < _touchSlop) return true;
if (isAtLeftEdge) {
if (isAtRightEdge) {
return _hasAcceptedOrChecked = true;
}
_hasAcceptedOrChecked = true;
return initialPosition!.dx < dx;
} else if (isAtRightEdge) {
_hasAcceptedOrChecked = true;
return initialPosition!.dx > dx;
}
return true;
} }
@override @override
void dispose() { void handleEvent(PointerEvent event) {
isBoundaryAllowed = null; if (!_hasAcceptedOrChecked &&
super.dispose(); event is PointerMoveEvent &&
_pointer == event.pointer) {
if (!_isEdgeAllowed(event.position.dx)) {
rejectGesture(event.pointer);
return;
}
}
super.handleEvent(event);
}
bool _hasAcceptedOrChecked = false;
@override
void acceptGesture(int pointer) {
_hasAcceptedOrChecked = true;
super.acceptGesture(pointer);
}
@override
void stopTrackingPointer(int pointer) {
_hasAcceptedOrChecked = false;
isAtLeftEdge = false;
isAtRightEdge = false;
super.stopTrackingPointer(pointer);
} }
} }

View File

@@ -1,4 +1,4 @@
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/button/icon_button.dart'; import 'package:PiliPlus/common/widgets/button/icon_button.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/http/user.dart'; import 'package:PiliPlus/http/user.dart';
@@ -22,10 +22,10 @@ void imageSaveDialog({
final theme = Theme.of(context); final theme = Theme.of(context);
return Container( return Container(
width: imgWidth, width: imgWidth,
margin: const .symmetric(horizontal: StyleString.safeSpace), margin: const .symmetric(horizontal: Style.safeSpace),
decoration: BoxDecoration( decoration: BoxDecoration(
color: theme.colorScheme.surface, color: theme.colorScheme.surface,
borderRadius: StyleString.mdRadius, borderRadius: Style.mdRadius,
), ),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@@ -39,8 +39,8 @@ void imageSaveDialog({
src: cover, src: cover,
quality: 100, quality: 100,
width: imgWidth, width: imgWidth,
height: imgWidth / StyleString.aspectRatio16x9, height: imgWidth / Style.aspectRatio16x9,
borderRadius: const .vertical(top: StyleString.imgRadius), borderRadius: const .vertical(top: Style.imgRadius),
), ),
), ),
Positioned( Positioned(

View File

@@ -1,4 +1,5 @@
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/assets.dart';
import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/models/common/image_type.dart'; import 'package:PiliPlus/models/common/image_type.dart';
import 'package:PiliPlus/utils/extension/num_ext.dart'; import 'package:PiliPlus/utils/extension/num_ext.dart';
import 'package:PiliPlus/utils/image_utils.dart'; import 'package:PiliPlus/utils/image_utils.dart';
@@ -16,7 +17,7 @@ class NetworkImgLayer extends StatelessWidget {
this.fadeOutDuration = const Duration(milliseconds: 120), this.fadeOutDuration = const Duration(milliseconds: 120),
this.fadeInDuration = const Duration(milliseconds: 120), this.fadeInDuration = const Duration(milliseconds: 120),
this.quality = 1, this.quality = 1,
this.borderRadius = StyleString.mdRadius, this.borderRadius = Style.mdRadius,
this.getPlaceHolder, this.getPlaceHolder,
this.fit = .cover, this.fit = .cover,
this.alignment = .center, this.alignment = .center,
@@ -108,7 +109,7 @@ class NetworkImgLayer extends StatelessWidget {
), ),
child: Center( child: Center(
child: Image.asset( child: Image.asset(
isAvatar ? 'assets/images/noface.jpeg' : 'assets/images/loading.png', isAvatar ? Assets.avatarPlaceHolder : Assets.loading,
width: width, width: width,
height: height, height: height,
cacheWidth: width.cacheSize(context), cacheWidth: width.cacheSize(context),

View File

@@ -18,7 +18,7 @@
import 'dart:collection' show HashSet; import 'dart:collection' show HashSet;
import 'dart:math' as math; import 'dart:math' as math;
import 'package:PiliPlus/common/constants.dart' show StyleString; import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/image_grid/image_grid_view.dart' import 'package:PiliPlus/common/widgets/image_grid/image_grid_view.dart'
show ImageModel; show ImageModel;
import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/foundation.dart' show kDebugMode;
@@ -505,7 +505,7 @@ class ImageGridRenderObjectElement extends RenderObjectElement {
if (width != 1) { if (width != 1) {
imageWidth = math.min(imageWidth, width.toDouble()); imageWidth = math.min(imageWidth, width.toDouble());
} }
imageHeight = imageWidth * math.min(ratioHW, StyleString.imgMaxRatio); imageHeight = imageWidth * math.min(ratioHW, Style.imgMaxRatio);
} }
} }

View File

@@ -17,7 +17,8 @@
import 'dart:io' show Platform; import 'dart:io' show Platform;
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/assets.dart';
import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/common/widgets/image_grid/image_grid_builder.dart'; import 'package:PiliPlus/common/widgets/image_grid/image_grid_builder.dart';
@@ -53,8 +54,7 @@ class ImageModel {
bool? _isLongPic; bool? _isLongPic;
bool? _isLivePhoto; bool? _isLivePhoto;
bool get isLongPic => bool get isLongPic => _isLongPic ??= (height / width) > Style.imgMaxRatio;
_isLongPic ??= (height / width) > StyleString.imgMaxRatio;
bool get isLivePhoto => bool get isLivePhoto =>
_isLivePhoto ??= enableLivePhoto && liveUrl?.isNotEmpty == true; _isLivePhoto ??= enableLivePhoto && liveUrl?.isNotEmpty == true;
@@ -116,9 +116,9 @@ class ImageGridView extends StatelessWidget {
int col, int col,
int length, int length,
int index, { int index, {
Radius r = StyleString.imgRadius, Radius r = Style.imgRadius,
}) { }) {
if (length == 1) return StyleString.mdRadius; if (length == 1) return Style.mdRadius;
final bool hasUp = index - col >= 0; final bool hasUp = index - col >= 0;
final bool hasDown = index + col < length; final bool hasDown = index + col < length;
@@ -213,7 +213,7 @@ class ImageGridView extends StatelessWidget {
).colorScheme.onInverseSurface.withValues(alpha: 0.4), ).colorScheme.onInverseSurface.withValues(alpha: 0.4),
), ),
child: Image.asset( child: Image.asset(
'assets/images/loading.png', Assets.loading,
width: width, width: width,
height: height, height: height,
cacheWidth: width.cacheSize(context), cacheWidth: width.cacheSize(context),

View File

@@ -5,7 +5,7 @@
import 'dart:io' show File; import 'dart:io' show File;
import 'dart:math' as math; import 'dart:math' as math;
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/gesture/image_horizontal_drag_gesture_recognizer.dart'; import 'package:PiliPlus/common/widgets/gesture/image_horizontal_drag_gesture_recognizer.dart';
import 'package:PiliPlus/common/widgets/image_viewer/viewer.dart'; import 'package:PiliPlus/common/widgets/image_viewer/viewer.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@@ -603,7 +603,7 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
final imgHeight = _imageInfo!.image.height.toDouble(); final imgHeight = _imageInfo!.image.height.toDouble();
final imgRatio = imgHeight / imgWidth; final imgRatio = imgHeight / imgWidth;
isLongPic = isLongPic =
imgRatio > StyleString.imgMaxRatio && imgRatio > Style.imgMaxRatio &&
imgHeight > widget.containerSize.height; imgHeight > widget.containerSize.height;
if (isLongPic) { if (isLongPic) {
final compatWidth = math.min(650.0, widget.containerSize.width); final compatWidth = math.min(650.0, widget.containerSize.width);

View File

@@ -15,10 +15,10 @@
* along with PiliPlus. If not, see <https://www.gnu.org/licenses/>. * along with PiliPlus. If not, see <https://www.gnu.org/licenses/>.
*/ */
import 'dart:io' show Platform;
import 'dart:math' show pi; import 'dart:math' show pi;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/semantics.dart' show SemanticsConfiguration;
/// ///
/// created by dom on 2026/02/14 /// created by dom on 2026/02/14
@@ -74,6 +74,7 @@ class RenderLoadingIndicator extends RenderBox {
if (_progress == value) return; if (_progress == value) return;
_progress = value; _progress = value;
markNeedsPaint(); markNeedsPaint();
markNeedsSemanticsUpdate();
} }
@override @override
@@ -95,53 +96,39 @@ class RenderLoadingIndicator extends RenderBox {
final radius = size.width / 2 - strokeWidth; final radius = size.width / 2 - strokeWidth;
final center = size.center(.zero); final center = size.center(.zero);
// TODO: remove context.canvas
// https://github.com/flutter/flutter/issues/182708 ..drawCircle(
// https://github.com/flutter/flutter/issues/183083 center,
if (Platform.isIOS) { radius,
context.canvas paint
..drawCircle( ..style = .fill
center, ..color = const Color(0x80000000),
radius, )
paint..color = Colors.white, ..drawCircle(
) center,
..drawCircle( radius,
center, paint
radius - strokeWidth, ..style = .stroke
paint..color = const Color(0x80000000), ..strokeWidth = strokeWidth
) ..color = Colors.white,
..drawArc( )
Rect.fromCircle(center: center, radius: radius - padding), ..drawArc(
startAngle, Rect.fromCircle(center: center, radius: radius - padding),
progress * 2 * pi, startAngle,
true, progress * 2 * pi,
paint..color = Colors.white, true,
); paint..style = .fill,
} else { );
context.canvas }
..drawCircle(
center, @override
radius, void describeSemanticsConfiguration(SemanticsConfiguration config) {
paint super.describeSemanticsConfiguration(config);
..style = .fill config
..color = const Color(0x80000000), ..role = .progressBar
) ..minValue = '0'
..drawCircle( ..maxValue = '100'
center, ..value = (_progress * 100).round().toString();
radius,
paint
..style = .stroke
..strokeWidth = strokeWidth
..color = Colors.white,
)
..drawArc(
Rect.fromCircle(center: center, radius: radius - padding),
startAngle,
progress * 2 * pi,
true,
paint..style = .fill,
);
}
} }
@override @override

View File

@@ -26,6 +26,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/physics.dart' show FrictionSimulation; import 'package:flutter/physics.dart' show FrictionSimulation;
import 'package:flutter/scheduler.dart' show SchedulerBinding;
import 'package:flutter/services.dart' show HardwareKeyboard; import 'package:flutter/services.dart' show HardwareKeyboard;
/// ///
@@ -69,7 +70,6 @@ class Viewer extends StatefulWidget {
} }
class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin { class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
double get _interactionEndFrictionCoefficient => 0.0001 * _scale; // 0.0000135
static const double _scaleFactor = kDefaultMouseScrollToScaleFactor; static const double _scaleFactor = kDefaultMouseScrollToScaleFactor;
_GestureType? _gestureType; _GestureType? _gestureType;
@@ -171,6 +171,7 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
@override @override
void dispose() { void dispose() {
_stopFling();
_animationController _animationController
..removeListener(_listener) ..removeListener(_listener)
..dispose(); ..dispose();
@@ -234,6 +235,7 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
void _handleDoubleTap() { void _handleDoubleTap() {
if (!mounted) return; if (!mounted) return;
if (_animationController.isAnimating) return; if (_animationController.isAnimating) return;
_stopFling();
_scaleFrom = _scale; _scaleFrom = _scale;
_positionFrom = _position; _positionFrom = _position;
@@ -265,6 +267,8 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
} }
void _onScaleStart(ScaleStartDetails details) { void _onScaleStart(ScaleStartDetails details) {
_stopFling();
if (_animationController.isAnimating) { if (_animationController.isAnimating) {
_animationController.stop(); _animationController.stop();
} }
@@ -329,40 +333,106 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
} }
} }
/// ref https://github.com/ahnaineh/custom_interactive_viewer
int? _flingFrameCallbackId;
Simulation? _flingSimulation;
Duration? _flingStartTime;
double _lastFlingElapsedSeconds = 0.0;
Offset _flingDirection = Offset.zero;
/// Calculate appropriate friction based on velocity magnitude
double _calculateDynamicFriction(double velocityMagnitude) {
// Use higher friction for faster flicks
// These values can be tuned for the feel you want
if (velocityMagnitude > 5000) {
return 0.03; // Higher friction for very fast flicks
} else if (velocityMagnitude > 3000) {
return 0.02; // Medium friction for moderate flicks
} else {
return 0.01; // Lower friction for gentle movements
}
}
void _startFling(Velocity velocity) {
_stopFling();
final double velocityMagnitude = velocity.pixelsPerSecond.distance;
final double frictionCoefficient = _calculateDynamicFriction(
velocityMagnitude,
);
_flingSimulation = FrictionSimulation(
frictionCoefficient,
0.0,
velocityMagnitude,
);
_flingDirection = velocityMagnitude > 0
? velocity.pixelsPerSecond / velocityMagnitude
: Offset.zero;
_flingStartTime = null;
_lastFlingElapsedSeconds = 0.0;
_scheduleFlingFrame();
}
void _scheduleFlingFrame() {
_flingFrameCallbackId = SchedulerBinding.instance.scheduleFrameCallback(
_handleFlingFrame,
);
}
void _handleFlingFrame(Duration timeStamp) {
if (_flingSimulation == null) return;
_flingStartTime ??= timeStamp;
final double elapsedSeconds =
(timeStamp - _flingStartTime!).inMicroseconds / 1e6;
final double distance = _flingSimulation!.x(elapsedSeconds);
final double prevDistance = _flingSimulation!.x(_lastFlingElapsedSeconds);
final double delta = distance - prevDistance;
_lastFlingElapsedSeconds = elapsedSeconds;
if ((prevDistance != 0.0 && delta.abs() < 0.1) ||
_flingSimulation!.isDone(elapsedSeconds)) {
_stopFling();
return;
}
final Offset movement = _flingDirection * delta;
_position = _clampPosition(_position + movement, _scale);
setState(() {});
if (_flingSimulation!.isDone(elapsedSeconds)) {
_stopFling();
} else {
_scheduleFlingFrame();
}
}
void _stopFling() {
if (_flingFrameCallbackId != null) {
SchedulerBinding.instance.cancelFrameCallbackWithId(
_flingFrameCallbackId!,
);
_flingFrameCallbackId = null;
}
_flingStartTime = null;
_lastFlingElapsedSeconds = 0.0;
_flingSimulation = null;
}
/// ref [InteractiveViewer] /// ref [InteractiveViewer]
void _onScaleEnd(ScaleEndDetails details) { void _onScaleEnd(ScaleEndDetails details) {
switch (_gestureType) { switch (_gestureType) {
case _GestureType.pan: case _GestureType.pan:
if (details.velocity.pixelsPerSecond.distance < kMinFlingVelocity) { final double velocityMagnitude =
return; details.velocity.pixelsPerSecond.distance;
if (velocityMagnitude >= 200.0) {
_startFling(details.velocity);
} }
final drag = _interactionEndFrictionCoefficient;
final FrictionSimulation frictionSimulationX = FrictionSimulation(
drag,
_position.dx,
details.velocity.pixelsPerSecond.dx,
);
final FrictionSimulation frictionSimulationY = FrictionSimulation(
drag,
_position.dy,
details.velocity.pixelsPerSecond.dy,
);
final double tFinal = _getFinalTime(
details.velocity.pixelsPerSecond.distance,
drag,
);
final position = _clampPosition(
Offset(frictionSimulationX.finalX, frictionSimulationY.finalX),
_scale,
);
_scaleFrom = _scaleTo = _scale;
_positionFrom = _position;
_positionTo = position;
_animationController
..duration = Duration(milliseconds: (tFinal * 1000).round())
..forward(from: 0);
case _GestureType.scale: case _GestureType.scale:
// if (details.scaleVelocity.abs() < 0.1) { // if (details.scaleVelocity.abs() < 0.1) {
// return; // return;
@@ -417,9 +487,10 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
_doubleTapGestureRecognizer _doubleTapGestureRecognizer
..onDoubleTapDown = _onDoubleTapDown ..onDoubleTapDown = _onDoubleTapDown
..onDoubleTap = _onDoubleTap; ..onDoubleTap = _onDoubleTap;
_horizontalDragGestureRecognizer _horizontalDragGestureRecognizer.addPointer(
..isBoundaryAllowed = _isBoundaryAllowed event,
..addPointer(event); isPointerAllowed: _isAtEdge(event.localPosition),
);
_scaleGestureRecognizer.addPointer(event); _scaleGestureRecognizer.addPointer(event);
} }
@@ -427,25 +498,28 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
_scaleGestureRecognizer.addPointerPanZoom(event); _scaleGestureRecognizer.addPointerPanZoom(event);
} }
bool _isBoundaryAllowed(Offset? initialPosition, OffsetPair lastPosition) { bool _isAtEdge(Offset position) {
if (initialPosition == null) {
return true;
}
if (_scale <= widget.minScale) { if (_scale <= widget.minScale) {
_horizontalDragGestureRecognizer.setAtBothEdges();
return true; return true;
} }
final containerWidth = widget.containerSize.width; final containerWidth = widget.containerSize.width;
final imageWidth = _imageSize.width * _scale; final imageWidth = _imageSize.width * _scale;
if (imageWidth <= containerWidth) { if (imageWidth <= containerWidth) {
_horizontalDragGestureRecognizer.setAtBothEdges();
return true; return true;
} }
final dx = (1 - _scale) * containerWidth / 2; final dx = (1 - _scale) * containerWidth / 2;
final dxOffset = (imageWidth - containerWidth) / 2; final dxOffset = (imageWidth - containerWidth) / 2;
if (initialPosition.dx < lastPosition.global.dx) { if (_position.dx.equals(dx + dxOffset, 1e-6)) {
return _position.dx.equals(dx + dxOffset, 1e-6); _horizontalDragGestureRecognizer.isAtLeftEdge = true;
} else { return true;
return _position.dx.equals(dx - dxOffset, 1e-6);
} }
if (_position.dx.equals(dx - dxOffset, 1e-6)) {
_horizontalDragGestureRecognizer.isAtRightEdge = true;
return true;
}
return false;
} }
void _onPointerSignal(PointerSignalEvent event) { void _onPointerSignal(PointerSignalEvent event) {
@@ -455,6 +529,7 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
widget.onChangePage!.call(event.scrollDelta.dy < 0 ? -1 : 1); widget.onChangePage!.call(event.scrollDelta.dy < 0 ? -1 : 1);
return; return;
} }
_stopFling();
final double scaleChange = math.exp(-event.scrollDelta.dy / _scaleFactor); final double scaleChange = math.exp(-event.scrollDelta.dy / _scaleFactor);
final Offset local = event.localPosition; final Offset local = event.localPosition;
final Offset focalPointScene = _toScene(local); final Offset focalPointScene = _toScene(local);
@@ -471,11 +546,3 @@ class _ViewerState extends State<Viewer> with SingleTickerProviderStateMixin {
} }
enum _GestureType { pan, scale, drag } enum _GestureType { pan, scale, drag }
double _getFinalTime(
double velocity,
double drag, {
double effectivelyMotionless = 10,
}) {
return math.log(effectivelyMotionless / velocity) / math.log(drag / 100);
}

View File

@@ -4,8 +4,6 @@ import 'package:flutter/material.dart';
const Widget m3eLoading = Center(child: M3ELoadingIndicator()); const Widget m3eLoading = Center(child: M3ELoadingIndicator());
const Widget circularLoading = Center(child: CircularProgressIndicator());
const Widget linearLoading = SliverToBoxAdapter( const Widget linearLoading = SliverToBoxAdapter(
child: LinearProgressIndicator(), child: LinearProgressIndicator(),
); );

View File

@@ -17,14 +17,27 @@
import 'dart:math' as math; import 'dart:math' as math;
import 'package:PiliPlus/common/widgets/loading_widget/morphs.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/physics.dart' show SpringSimulation; import 'package:flutter/physics.dart' show SpringSimulation;
import 'package:flutter/semantics.dart';
import 'package:material_new_shapes/material_new_shapes.dart'; import 'package:material_new_shapes/material_new_shapes.dart';
/// reimplement of https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/loading_indicator_m3e /// reimplement of https://github.com/EmilyMoonstone/material_3_expressive/tree/main/packages/loading_indicator_m3e
class M3ELoadingIndicator extends StatefulWidget { class M3ELoadingIndicator extends StatefulWidget {
const M3ELoadingIndicator({super.key}); const M3ELoadingIndicator({
super.key,
// this.childKey,
this.morphs,
this.color,
this.size = const Size.square(40),
});
final List<Morph>? morphs;
final Color? color;
final Size size;
// final Key? childKey;
@override @override
State<M3ELoadingIndicator> createState() => _M3ELoadingIndicatorState(); State<M3ELoadingIndicator> createState() => _M3ELoadingIndicatorState();
@@ -32,42 +45,25 @@ class M3ELoadingIndicator extends StatefulWidget {
class _M3ELoadingIndicatorState extends State<M3ELoadingIndicator> class _M3ELoadingIndicatorState extends State<M3ELoadingIndicator>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
static final List<Morph> _morphs = () {
final List<RoundedPolygon> shapes = [
MaterialShapes.softBurst,
MaterialShapes.cookie9Sided,
MaterialShapes.pentagon,
MaterialShapes.pill,
MaterialShapes.sunny,
MaterialShapes.cookie4Sided,
MaterialShapes.oval,
];
return [
for (var i = 0; i < shapes.length; i++)
Morph(
shapes[i],
shapes[(i + 1) % shapes.length],
),
];
}();
static const int _morphIntervalMs = 650; static const int _morphIntervalMs = 650;
static const double _fullRotation = 360.0; static const double _fullRotation = 2 * math.pi;
static const int _globalRotationDurationMs = 4666; static const int _globalRotationDurationMs = 4666;
static const double _quarterRotation = _fullRotation / 4; static const double _quarterRotation = _fullRotation / 4;
late final List<Morph> _morphs;
late final AnimationController _controller; late final AnimationController _controller;
int _morphIndex = 1; int _morphIndex = 1;
double _morphRotationTargetAngle = _quarterRotation; double _morphRotationTarget = _quarterRotation;
final _morphAnimationSpec = SpringSimulation( static final _morphAnimationSpec = SpringSimulation(
SpringDescription.withDampingRatio(ratio: 0.6, stiffness: 200.0, mass: 1.0), SpringDescription.withDampingRatio(ratio: 0.6, stiffness: 200.0, mass: 1.0),
0.0, 0.0,
1.0, 1.0,
5.0, 5.0,
snapToEnd: true, snapToEnd: true,
// tolerance: const Tolerance(velocity: 0.1, distance: 0.1),
); );
void _statusListener(AnimationStatus status) { void _statusListener(AnimationStatus status) {
@@ -78,23 +74,21 @@ class _M3ELoadingIndicatorState extends State<M3ELoadingIndicator>
void _startAnimation() { void _startAnimation() {
_morphIndex++; _morphIndex++;
_morphRotationTargetAngle = _morphRotationTarget =
(_morphRotationTargetAngle + _quarterRotation) % _fullRotation; (_morphRotationTarget + _quarterRotation) % _fullRotation;
_controller _controller.animateWith(_morphAnimationSpec);
..value = 0.0
..animateWith(_morphAnimationSpec);
} }
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_morphs = widget.morphs ?? Morphs.loadingMorphs;
_controller = _controller =
AnimationController( AnimationController(
vsync: this, vsync: this,
duration: const Duration(milliseconds: _morphIntervalMs), duration: const Duration(milliseconds: _morphIntervalMs),
) )
..addStatusListener(_statusListener) ..addStatusListener(_statusListener)
..value = 0.0
..animateWith(_morphAnimationSpec); ..animateWith(_morphAnimationSpec);
} }
@@ -110,82 +104,86 @@ class _M3ELoadingIndicatorState extends State<M3ELoadingIndicator>
final elapsedInMs = final elapsedInMs =
_morphIntervalMs * (_morphIndex - 1) + _morphIntervalMs * (_morphIndex - 1) +
(_controller.lastElapsedDuration?.inMilliseconds ?? 0); (_controller.lastElapsedDuration?.inMilliseconds ?? 0);
final globalRotationControllerValue = final globalRotation =
(elapsedInMs % _globalRotationDurationMs) / _globalRotationDurationMs; (elapsedInMs % _globalRotationDurationMs) /
final globalRotationDegrees = globalRotationControllerValue * _fullRotation; _globalRotationDurationMs *
final totalRotationDegrees = _fullRotation;
progress * _quarterRotation +
_morphRotationTargetAngle + return progress * _quarterRotation + _morphRotationTarget + globalRotation;
globalRotationDegrees;
return totalRotationDegrees * (math.pi / 180.0);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final color = Theme.of(context).colorScheme.secondaryFixedDim; final color = widget.color ?? ColorScheme.of(context).secondaryFixedDim;
return AnimatedBuilder( return AnimatedBuilder(
animation: _controller, animation: _controller,
builder: (context, child) { builder: (context, child) {
final progress = _controller.value; final progress = _controller.value;
return _M3ELoadingIndicator( return RawM3ELoadingIndicator(
// key: widget.childKey,
morph: _morphs[_morphIndex % _morphs.length], morph: _morphs[_morphIndex % _morphs.length],
progress: progress, progress: progress,
angle: _calcAngle(progress), angle: _calcAngle(progress),
color: color, color: color,
size: widget.size,
); );
}, },
); );
} }
} }
class _M3ELoadingIndicator extends LeafRenderObjectWidget { class RawM3ELoadingIndicator extends LeafRenderObjectWidget {
const _M3ELoadingIndicator({ const RawM3ELoadingIndicator({
super.key,
required this.morph, required this.morph,
required this.progress, required this.progress,
required this.angle, required this.angle,
required this.color, required this.color,
required this.size,
}); });
final Morph morph; final Morph morph;
final double progress; final double progress;
final double angle; final double angle;
final Color color; final Color color;
final Size size;
@override @override
RenderObject createRenderObject(BuildContext context) { RenderObject createRenderObject(BuildContext context) {
return _RenderM3ELoadingIndicator( return RenderM3ELoadingIndicator(
morph: morph, morph: morph,
progress: progress, progress: progress,
angle: angle, angle: angle,
color: color, color: color,
size: size,
); );
} }
@override @override
void updateRenderObject( void updateRenderObject(
BuildContext context, BuildContext context,
_RenderM3ELoadingIndicator renderObject, RenderM3ELoadingIndicator renderObject,
) { ) {
renderObject renderObject
..morph = morph ..morph = morph
..progress = progress ..progress = progress
..angle = angle ..angle = angle
..color = color; ..color = color
..preferredSize = size;
} }
} }
class _RenderM3ELoadingIndicator extends RenderBox { class RenderM3ELoadingIndicator extends RenderBox {
_RenderM3ELoadingIndicator({ RenderM3ELoadingIndicator({
required Morph morph, required Morph morph,
required double progress, required double progress,
required double angle, required double angle,
required Color color, required Color color,
required Size size,
}) : _morph = morph, }) : _morph = morph,
_progress = progress, _progress = progress,
_angle = angle, _angle = angle,
_preferredSize = size,
_color = color, _color = color,
_paint = Paint() _paint = Paint()
..style = PaintingStyle.fill ..style = PaintingStyle.fill
@@ -223,20 +221,38 @@ class _RenderM3ELoadingIndicator extends RenderBox {
markNeedsPaint(); markNeedsPaint();
} }
Size _preferredSize;
set preferredSize(Size value) {
if (_preferredSize == value) return;
_preferredSize = size;
markNeedsLayout();
}
@override
Size computeDryLayout(covariant BoxConstraints constraints) {
return constraints.constrain(_preferredSize);
}
@override @override
void performLayout() { void performLayout() {
size = constraints.constrainDimensions(40, 40); size = computeDryLayout(constraints);
}
@override
void describeSemanticsConfiguration(SemanticsConfiguration config) {
super.describeSemanticsConfiguration(config);
config.role = .loadingSpinner;
} }
@override @override
void paint(PaintingContext context, Offset offset) { void paint(PaintingContext context, Offset offset) {
final width = size.width; final width = size.width;
final value = size.width / 2; final value = size.width / 2;
final matrix = Matrix4.identity() final matrix =
..translateByDouble(offset.dx + value, offset.dy + value, 0.0, 1.0) Matrix4.translationValues(offset.dx + value, offset.dy + value, 0.0)
..rotateZ(angle) ..rotateZ(angle)
..translateByDouble(-value, -value, 0.0, 1.0) ..translateByDouble(-value, -value, 0.0, 1.0)
..scaleByDouble(width, width, width, 1.0); ..scaleByDouble(width, width, width, 1.0);
final path = morph.toPath(progress: progress).transform(matrix.storage); final path = morph.toPath(progress: progress).transform(matrix.storage);
context.canvas.drawPath(path, _paint); context.canvas.drawPath(path, _paint);

View File

@@ -0,0 +1,41 @@
import 'package:material_new_shapes/material_new_shapes.dart';
abstract final class Morphs {
static List<Morph> buildMorph(
List<RoundedPolygon> shapes, {
bool loop = true,
}) {
assert(shapes.length >= 2);
return [
for (var i = 0; i < shapes.length - 1; i++)
Morph(shapes[i], shapes[i + 1]),
if (loop) Morph(shapes[shapes.length - 1], shapes[0]),
];
}
static final loadingMorphs = buildMorph([
MaterialShapes.softBurst,
MaterialShapes.cookie9Sided,
MaterialShapes.pentagon,
MaterialShapes.pill,
MaterialShapes.sunny,
MaterialShapes.cookie4Sided,
MaterialShapes.oval,
]);
// static final refreshMorphs = buildMorph([
// MaterialShapes.softBurst,
// MaterialShapes.cookie9Sided,
// MaterialShapes.gem,
// MaterialShapes.flower,
// MaterialShapes.sunny,
// MaterialShapes.cookie4Sided,
// MaterialShapes.oval,
// MaterialShapes.cookie12Sided,
// ]);
// static final manualMorph = Morph(
// MaterialShapes.circle,
// MaterialShapes.softBurst,
// );
}

View File

@@ -1,168 +1,163 @@
import 'package:PiliPlus/common/assets.dart';
import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/models/common/avatar_badge_type.dart'; import 'package:PiliPlus/models/common/avatar_badge_type.dart';
import 'package:PiliPlus/models/common/image_type.dart'; import 'package:PiliPlus/models/common/image_type.dart';
import 'package:PiliPlus/utils/extension/num_ext.dart'; import 'package:PiliPlus/utils/extension/num_ext.dart';
import 'package:PiliPlus/utils/extension/string_ext.dart';
import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart';
import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class PendantAvatar extends StatelessWidget { class PendantAvatar extends StatelessWidget {
final BadgeType _badgeType; const PendantAvatar(
final String? avatar; this.url, {
final double size;
final double badgeSize;
final String? garbPendantImage;
final int? roomId;
final VoidCallback? onTap;
final bool isMemberAvatar;
const PendantAvatar({
super.key, super.key,
required this.avatar, required double size,
required this.size,
this.isMemberAvatar = false,
double? badgeSize, double? badgeSize,
bool isVip = false, int? vipStatus,
int? officialType, int? officialType,
this.garbPendantImage, this.pendantImage,
this.pendentOffset = 6,
this.roomId, this.roomId,
this.liveBottom,
this.liveFontSize,
this.onTap, this.onTap,
}) : _badgeType = officialType == null || officialType < 0 }) : preferredSize = size,
? isVip badgeSize = badgeSize ?? size / 3,
? BadgeType.vip badgeType = officialType == null || officialType < 0
: BadgeType.none ? vipStatus != null && vipStatus > 0
? .vip
: .none
: officialType == 0 : officialType == 0
? BadgeType.person ? .person
: officialType == 1 : officialType == 1
? BadgeType.institution ? .institution
: BadgeType.none, : .none;
badgeSize = badgeSize ?? size / 3;
static bool showDynDecorate = Pref.showDynDecorate; static bool showDecorate = Pref.showDecorate;
final BadgeType badgeType;
final String? url;
final double preferredSize;
final double badgeSize;
final String? pendantImage;
final double pendentOffset;
final int? roomId;
final double? liveBottom;
final double? liveFontSize;
final VoidCallback? onTap;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme; final colorScheme = Theme.of(context).colorScheme;
final showPendant = showDecorate && pendantImage?.isNotEmpty == true;
final size = showPendant ? preferredSize - pendentOffset : preferredSize;
Widget? pendant; Widget? pendant;
if (showDynDecorate && !garbPendantImage.isNullOrEmpty) { if (showPendant) {
final pendantSize = size * 1.75; final pendantSize = size * 1.75;
pendant = Positioned( pendant = Positioned(
// -(size * 1.75 - size) / 2 // -(size * 1.75 - size) / 2
top: -0.375 * size + (isMemberAvatar ? 2 : 0), top: -0.375 * size + pendentOffset / 2,
child: IgnorePointer( child: IgnorePointer(
child: NetworkImgLayer( child: NetworkImgLayer(
type: .emote, type: .emote,
width: pendantSize, width: pendantSize,
height: pendantSize, height: pendantSize,
src: garbPendantImage, src: pendantImage,
getPlaceHolder: () => const SizedBox.shrink(), getPlaceHolder: () => const SizedBox.shrink(),
), ),
), ),
); );
} }
return Stack( Widget avatar = NetworkImgLayer(
alignment: Alignment.bottomCenter, src: url,
clipBehavior: Clip.none, width: size,
height: size,
type: ImageType.avatar,
);
if (onTap != null) {
avatar = GestureDetector(
behavior: .opaque,
onTap: onTap,
child: avatar,
);
}
Widget child = Stack(
clipBehavior: .none,
alignment: .center,
children: [ children: [
onTap == null avatar,
? _buildAvatar(colorScheme, isMemberAvatar)
: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: _buildAvatar(colorScheme, isMemberAvatar),
),
?pendant, ?pendant,
if (roomId != null) if (roomId != null)
Positioned( _buildLive(colorScheme)
bottom: 0, else if (badgeType != .none)
child: InkWell( _buildBadge(context, colorScheme),
onTap: () => PageUtils.toLiveRoom(roomId), ],
child: Container( );
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 1), if (showPendant) {
decoration: BoxDecoration( return SizedBox.square(
color: colorScheme.secondaryContainer, dimension: preferredSize,
borderRadius: const BorderRadius.all(Radius.circular(36)), child: child,
), );
child: Row( }
mainAxisSize: MainAxisSize.min, return child;
children: [ }
Icon(
size: 16, Widget _buildLive(ColorScheme colorScheme) {
applyTextScaling: true, final fontSize = liveFontSize ?? 13.0;
Icons.equalizer_rounded, return Positioned(
color: colorScheme.onSecondaryContainer, bottom: liveBottom ?? 0.0,
), child: GestureDetector(
Text( onTap: () => PageUtils.toLiveRoom(roomId),
'直播中', child: Container(
style: TextStyle( padding: const .symmetric(horizontal: 5, vertical: 1),
height: 1, decoration: BoxDecoration(
fontSize: 13, color: colorScheme.secondaryContainer,
color: colorScheme.onSecondaryContainer, borderRadius: Style.mdRadius,
), ),
), child: Row(
], mainAxisSize: .min,
children: [
Icon(
size: fontSize + 3,
applyTextScaling: true,
Icons.equalizer_rounded,
color: colorScheme.onSecondaryContainer,
),
Text(
'直播中',
style: TextStyle(
height: 1,
fontSize: fontSize,
color: colorScheme.onSecondaryContainer,
), ),
), ),
), ],
) ),
else if (_badgeType != BadgeType.none) ),
_buildBadge(context, colorScheme, isMemberAvatar), ),
],
); );
} }
Widget _buildAvatar(ColorScheme colorScheme, bool isMemberAvatar) => Widget _buildBadge(BuildContext context, ColorScheme colorScheme) {
isMemberAvatar final child = switch (badgeType) {
? DecoratedBox( .vip => Image.asset(
decoration: BoxDecoration( Assets.vipIcon,
border: Border.all(
width: 2,
color: colorScheme.surface,
),
shape: BoxShape.circle,
),
child: Padding(
padding: const EdgeInsets.all(2),
child: NetworkImgLayer(
src: avatar,
width: size,
height: size,
type: ImageType.avatar,
),
),
)
: NetworkImgLayer(
src: avatar,
width: size,
height: size,
type: ImageType.avatar,
);
Widget _buildBadge(
BuildContext context,
ColorScheme colorScheme,
bool isMemberAvatar,
) {
final child = switch (_badgeType) {
BadgeType.vip => Image.asset(
'assets/images/big-vip.png',
width: badgeSize, width: badgeSize,
height: badgeSize, height: badgeSize,
cacheWidth: badgeSize.cacheSize(context), cacheWidth: badgeSize.cacheSize(context),
semanticLabel: _badgeType.desc, semanticLabel: badgeType.desc,
), ),
_ => Icon( _ => Icon(
Icons.offline_bolt, Icons.offline_bolt,
color: _badgeType.color, color: badgeType.color,
size: badgeSize, size: badgeSize,
semanticLabel: _badgeType.desc, semanticLabel: badgeType.desc,
), ),
}; };
final offset = isMemberAvatar ? 2.0 : 0.0;
return Positioned( return Positioned(
right: offset, right: 0.0,
bottom: offset, bottom: 0.0,
child: IgnorePointer( child: IgnorePointer(
child: DecoratedBox( child: DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(

View File

@@ -23,7 +23,8 @@ import 'package:flutter/rendering.dart'
ContainerRenderObjectMixin, ContainerRenderObjectMixin,
MultiChildLayoutParentData, MultiChildLayoutParentData,
RenderBoxContainerDefaultsMixin, RenderBoxContainerDefaultsMixin,
BoxHitTestResult; BoxHitTestResult,
TransformLayer;
class PlayerBar extends MultiChildRenderObjectWidget { class PlayerBar extends MultiChildRenderObjectWidget {
const PlayerBar({ const PlayerBar({
@@ -92,14 +93,16 @@ class RenderBottomBar extends RenderBox
@override @override
void paint(PaintingContext context, Offset offset) { void paint(PaintingContext context, Offset offset) {
if (_transform != null) { if (_transform != null) {
context.pushTransform( layer = context.pushTransform(
needsCompositing, needsCompositing,
offset, offset,
_transform!, _transform!,
defaultPaint, defaultPaint,
oldLayer: layer as TransformLayer?,
); );
} else { } else {
defaultPaint(context, offset); defaultPaint(context, offset);
layer = null;
} }
} }

View File

@@ -1,10 +1,10 @@
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/style.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
Widget selectMask( Widget selectMask(
ThemeData theme, ThemeData theme,
bool checked, { bool checked, {
BorderRadiusGeometry borderRadius = StyleString.mdRadius, BorderRadiusGeometry borderRadius = Style.mdRadius,
}) { }) {
return AnimatedOpacity( return AnimatedOpacity(
opacity: checked ? 1 : 0, opacity: checked ? 1 : 0,

View File

@@ -1,4 +1,4 @@
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart'; import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
import 'package:PiliPlus/common/widgets/image/image_save.dart'; import 'package:PiliPlus/common/widgets/image/image_save.dart';
@@ -120,14 +120,14 @@ class VideoCardH extends StatelessWidget {
}, },
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: StyleString.safeSpace, horizontal: Style.safeSpace,
vertical: 5, vertical: 5,
), ),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
AspectRatio( AspectRatio(
aspectRatio: StyleString.aspectRatio, aspectRatio: Style.aspectRatio,
child: LayoutBuilder( child: LayoutBuilder(
builder: (context, boxConstraints) { builder: (context, boxConstraints) {
final double maxWidth = boxConstraints.maxWidth; final double maxWidth = boxConstraints.maxWidth;

View File

@@ -1,4 +1,4 @@
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart'; import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
import 'package:PiliPlus/common/widgets/image/image_save.dart'; import 'package:PiliPlus/common/widgets/image/image_save.dart';
@@ -22,7 +22,7 @@ import 'package:intl/intl.dart';
// 视频卡片 - 垂直布局 // 视频卡片 - 垂直布局
class VideoCardV extends StatelessWidget { class VideoCardV extends StatelessWidget {
final BaseRecVideoItemModel videoItem; final BaseRcmdVideoItemModel videoItem;
final VoidCallback? onRemove; final VoidCallback? onRemove;
const VideoCardV({ const VideoCardV({
@@ -87,7 +87,7 @@ class VideoCardV extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
AspectRatio( AspectRatio(
aspectRatio: StyleString.aspectRatio, aspectRatio: Style.aspectRatio,
child: LayoutBuilder( child: LayoutBuilder(
builder: (context, boxConstraints) { builder: (context, boxConstraints) {
double maxWidth = boxConstraints.maxWidth; double maxWidth = boxConstraints.maxWidth;
@@ -229,7 +229,7 @@ class VideoCardV extends StatelessWidget {
value: videoItem.stat.danmu, value: videoItem.stat.danmu,
), ),
], ],
if (videoItem is RecVideoItemModel) ...[ if (videoItem is RcmdVideoItemModel) ...[
const Spacer(), const Spacer(),
Text.rich( Text.rich(
maxLines: 1, maxLines: 1,
@@ -248,7 +248,7 @@ class VideoCardV extends StatelessWidget {
const SizedBox(width: 2), const SizedBox(width: 2),
], ],
// deprecated // deprecated
// else if (videoItem is RecVideoItemAppModel && // else if (videoItem is RcmdVideoItemAppModel &&
// videoItem.desc != null && // videoItem.desc != null &&
// videoItem.desc!.contains(' · ')) ...[ // videoItem.desc!.contains(' · ')) ...[
// const Spacer(), // const Spacer(),

View File

@@ -135,7 +135,7 @@ class VideoPopupMenu extends StatelessWidget {
SmartDialog.showToast("请退出账号后重新登录"); SmartDialog.showToast("请退出账号后重新登录");
return; return;
} }
if (videoItem case final RecVideoItemAppModel item) { if (videoItem case final RcmdVideoItemAppModel item) {
ThreePoint? tp = item.threePoint; ThreePoint? tp = item.threePoint;
if (tp == null) { if (tp == null) {
SmartDialog.showToast("未能获取threePoint"); SmartDialog.showToast("未能获取threePoint");

View File

@@ -649,14 +649,9 @@ abstract final class Api {
/// mid /// mid
static const getMemberViewApi = '/x/space/upstat'; static const getMemberViewApi = '/x/space/upstat';
/// 查询某个专栏 static const seasonArchives = '/x/polymer/web-space/seasons_archives_list';
/// mid
/// season_id static const seriesArchives = '/x/series/archives';
/// sort_reverse
/// page_num
/// page_size
static const getSeasonDetailApi =
'/x/polymer/web-space/seasons_archives_list';
/// 获取未读动态数 /// 获取未读动态数
static const getUnreadDynamic = '/x/web-interface/dynamic/entrance'; static const getUnreadDynamic = '/x/web-interface/dynamic/entrance';
@@ -999,4 +994,7 @@ abstract final class Api {
static const String replySubjectModify = '/x/v2/reply/subject/modify'; static const String replySubjectModify = '/x/v2/reply/subject/modify';
static const String videoshot = '/x/player/videoshot'; static const String videoshot = '/x/player/videoshot';
static const String liveMedalWall =
'${HttpString.liveBaseUrl}/xlive/web-ucenter/user/MedalWall';
} }

3
lib/http/error_msg.dart Normal file
View File

@@ -0,0 +1,3 @@
const errorMsg = {
-352: '风控校验失败,请检查登录状态',
};

View File

@@ -1,4 +1,5 @@
import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/api.dart';
import 'package:PiliPlus/http/error_msg.dart';
import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models_new/follow/data.dart'; import 'package:PiliPlus/models_new/follow/data.dart';
@@ -23,7 +24,7 @@ abstract final class FanHttp {
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
return Success(FollowData.fromJson(res.data['data'])); return Success(FollowData.fromJson(res.data['data']));
} else { } else {
return Error(res.data['message']); return Error(errorMsg[res.data['code']] ?? res.data['message']);
} }
} }
} }

View File

@@ -1,4 +1,5 @@
import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/api.dart';
import 'package:PiliPlus/http/error_msg.dart';
import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models_new/follow/data.dart'; import 'package:PiliPlus/models_new/follow/data.dart';
@@ -23,7 +24,7 @@ abstract final class FollowHttp {
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
return Success(FollowData.fromJson(res.data['data'])); return Success(FollowData.fromJson(res.data['data']));
} else { } else {
return Error(res.data['message']); return Error(errorMsg[res.data['code']] ?? res.data['message']);
} }
} }
} }

View File

@@ -19,6 +19,7 @@ import 'package:PiliPlus/models_new/live/live_emote/data.dart';
import 'package:PiliPlus/models_new/live/live_emote/datum.dart'; import 'package:PiliPlus/models_new/live/live_emote/datum.dart';
import 'package:PiliPlus/models_new/live/live_feed_index/data.dart'; import 'package:PiliPlus/models_new/live/live_feed_index/data.dart';
import 'package:PiliPlus/models_new/live/live_follow/data.dart'; import 'package:PiliPlus/models_new/live/live_follow/data.dart';
import 'package:PiliPlus/models_new/live/live_medal_wall/data.dart';
import 'package:PiliPlus/models_new/live/live_room_info_h5/data.dart'; import 'package:PiliPlus/models_new/live/live_room_info_h5/data.dart';
import 'package:PiliPlus/models_new/live/live_room_play_info/data.dart'; import 'package:PiliPlus/models_new/live/live_room_play_info/data.dart';
import 'package:PiliPlus/models_new/live/live_search/data.dart'; import 'package:PiliPlus/models_new/live/live_search/data.dart';
@@ -742,4 +743,18 @@ abstract final class LiveHttp {
return Error(res.data['message']); return Error(res.data['message']);
} }
} }
static Future<LoadingState<MedalWallData>> liveMedalWall({
required Object mid,
}) async {
final res = await Request().get(
Api.liveMedalWall,
queryParameters: {'target_id': mid},
);
if (res.data['code'] == 0) {
return Success(MedalWallData.fromJson(res.data['data']));
} else {
return Error(res.data['message']);
}
}
} }

View File

@@ -4,9 +4,14 @@ import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/api.dart';
import 'package:PiliPlus/http/browser_ua.dart'; import 'package:PiliPlus/http/browser_ua.dart';
import 'package:PiliPlus/http/constants.dart'; import 'package:PiliPlus/http/constants.dart';
import 'package:PiliPlus/http/error_msg.dart';
import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/common/member/archive_order_type_app.dart';
import 'package:PiliPlus/models/common/member/archive_order_type_web.dart';
import 'package:PiliPlus/models/common/member/archive_sort_type_app.dart';
import 'package:PiliPlus/models/common/member/contribute_type.dart'; import 'package:PiliPlus/models/common/member/contribute_type.dart';
import 'package:PiliPlus/models/common/member/web_ss_type.dart';
import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/models/member/info.dart'; import 'package:PiliPlus/models/member/info.dart';
import 'package:PiliPlus/models/member/tags.dart'; import 'package:PiliPlus/models/member/tags.dart';
@@ -14,6 +19,7 @@ import 'package:PiliPlus/models_new/follow/data.dart';
import 'package:PiliPlus/models_new/follow/list.dart'; import 'package:PiliPlus/models_new/follow/list.dart';
import 'package:PiliPlus/models_new/member/coin_like_arc/data.dart'; import 'package:PiliPlus/models_new/member/coin_like_arc/data.dart';
import 'package:PiliPlus/models_new/member/search_archive/data.dart'; import 'package:PiliPlus/models_new/member/search_archive/data.dart';
import 'package:PiliPlus/models_new/member/season_web/data.dart';
import 'package:PiliPlus/models_new/member_card_info/data.dart'; import 'package:PiliPlus/models_new/member_card_info/data.dart';
import 'package:PiliPlus/models_new/space/space/data.dart'; import 'package:PiliPlus/models_new/space/space/data.dart';
import 'package:PiliPlus/models_new/space/space_archive/data.dart'; import 'package:PiliPlus/models_new/space/space_archive/data.dart';
@@ -113,8 +119,8 @@ abstract final class MemberHttp {
required ContributeType type, required ContributeType type,
required int? mid, required int? mid,
String? aid, String? aid,
String? order, ArchiveOrderTypeApp? order,
String? sort, ArchiveSortTypeApp? sort,
int? pn, int? pn,
int? next, int? next,
int? seasonId, int? seasonId,
@@ -135,22 +141,15 @@ abstract final class MemberHttp {
'next': ?next, 'next': ?next,
'season_id': ?seasonId, 'season_id': ?seasonId,
'series_id': ?seriesId, 'series_id': ?seriesId,
'qn': type == ContributeType.video ? 80 : 32, 'qn': type == .video ? 80 : 32,
'order': ?order, 'order': ?order?.name,
'sort': ?sort, 'sort': ?sort?.name,
'include_cursor': ?includeCursor, 'include_cursor': ?includeCursor,
'statistics': Constants.statisticsApp, 'statistics': Constants.statisticsApp,
'vmid': mid, 'vmid': mid,
}; };
final res = await Request().get( final res = await Request().get(
switch (type) { type.api,
ContributeType.video => Api.spaceArchive,
ContributeType.charging => Api.spaceChargingArchive,
ContributeType.season => Api.spaceSeason,
ContributeType.series => Api.spaceSeries,
ContributeType.bangumi => Api.spaceBangumi,
ContributeType.comic => Api.spaceComic,
},
queryParameters: params, queryParameters: params,
options: Options( options: Options(
headers: { headers: {
@@ -348,12 +347,12 @@ abstract final class MemberHttp {
static Future<LoadingState<SearchArchiveData>> searchArchive({ static Future<LoadingState<SearchArchiveData>> searchArchive({
required Object mid, required Object mid,
int tid = 0, // e.g. pugv: 196
int ps = 30, int ps = 30,
int tid = 0, required int pn,
int? pn,
String? keyword, String? keyword,
String order = 'pubdate', String? specialType, // e.g. 'charging'
bool orderAvoided = true, ArchiveOrderTypeWeb order = .pubdate,
}) async { }) async {
String dmImgStr = Utils.base64EncodeRandomString(16, 64); String dmImgStr = Utils.base64EncodeRandomString(16, 64);
String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128); String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128);
@@ -361,12 +360,13 @@ abstract final class MemberHttp {
'mid': mid, 'mid': mid,
'ps': ps, 'ps': ps,
'tid': tid, 'tid': tid,
'pn': ?pn, 'pn': pn,
'keyword': ?keyword, 'keyword': ?keyword,
'order': order, 'special_type': ?specialType,
'order': order.name,
'platform': 'web', 'platform': 'web',
'web_location': 1550101, 'web_location': 333.1387,
'order_avoided': orderAvoided, 'order_avoided': true,
'dm_img_list': '[]', 'dm_img_list': '[]',
'dm_img_str': dmImgStr, 'dm_img_str': dmImgStr,
'dm_cover_img_str': dmCoverImgStr, 'dm_cover_img_str': dmCoverImgStr,
@@ -386,10 +386,50 @@ abstract final class MemberHttp {
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
return Success(SearchArchiveData.fromJson(res.data['data'])); return Success(SearchArchiveData.fromJson(res.data['data']));
} else { } else {
Map errMap = const { return Error(errorMsg[res.data['code']] ?? res.data['message']);
-352: '风控校验失败,请检查登录状态', }
}; }
return Error(errMap[res.data['code']] ?? res.data['message']);
static Future<LoadingState<SeasonWebData>> seasonSeriesWeb({
required WebSsType type,
required Object mid,
required Object id,
int ps = 30,
required int pn,
ArchiveSortTypeApp sort = .desc,
}) async {
final res = await Request().get(
type.api,
queryParameters: switch (type) {
.season => {
'mid': mid,
'season_id': id,
'sort_reverse': sort == .asc,
'page_size': ps,
'page_num': pn,
'web_location': 333.1387,
},
.series => {
'mid': mid,
'series_id': id,
'sort': sort.name,
'ps': ps,
'pn': pn,
'web_location': 333.1387,
},
},
options: Options(
headers: {
HttpHeaders.userAgentHeader: BrowserUa.pc,
HttpHeaders.refererHeader: '${HttpString.spaceBaseUrl}/$mid',
'origin': HttpString.spaceBaseUrl,
},
),
);
if (res.data['code'] == 0) {
return Success(SeasonWebData.fromJson(res.data['data']));
} else {
return Error(errorMsg[res.data['code']] ?? res.data['message']);
} }
} }
@@ -437,10 +477,7 @@ abstract final class MemberHttp {
return Error('$e\n\n$s'); return Error('$e\n\n$s');
} }
} else { } else {
Map errMap = const { return Error(errorMsg[res.data['code']] ?? res.data['message']);
-352: '风控校验失败,请检查登录状态',
};
return Error(errMap[res.data['code']] ?? res.data['message']);
} }
} }
@@ -551,7 +588,7 @@ abstract final class MemberHttp {
), ),
); );
} else { } else {
return Error(res.data['message']); return Error(errorMsg[res.data['code']] ?? res.data['message']);
} }
} }

View File

@@ -9,6 +9,7 @@ import 'package:PiliPlus/models_new/history/data.dart';
import 'package:PiliPlus/models_new/later/data.dart'; import 'package:PiliPlus/models_new/later/data.dart';
import 'package:PiliPlus/models_new/login_log/data.dart'; import 'package:PiliPlus/models_new/login_log/data.dart';
import 'package:PiliPlus/models_new/media_list/data.dart'; import 'package:PiliPlus/models_new/media_list/data.dart';
import 'package:PiliPlus/models_new/relation/data.dart';
import 'package:PiliPlus/models_new/space_setting/data.dart'; import 'package:PiliPlus/models_new/space_setting/data.dart';
import 'package:PiliPlus/models_new/sub/sub/data.dart'; import 'package:PiliPlus/models_new/sub/sub/data.dart';
import 'package:PiliPlus/models_new/user_real_name/data.dart'; import 'package:PiliPlus/models_new/user_real_name/data.dart';
@@ -269,7 +270,7 @@ abstract final class UserHttp {
} }
} }
static Future<LoadingState<Map>> hasFollow(int mid) async { static Future<LoadingState<RelationData>> userRelation(int mid) async {
final res = await Request().get( final res = await Request().get(
Api.relation, Api.relation,
queryParameters: { queryParameters: {
@@ -277,7 +278,7 @@ abstract final class UserHttp {
}, },
); );
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
return Success(res.data['data']); return Success(RelationData.fromJson(res.data['data']));
} else { } else {
return Error(res.data['message']); return Error(res.data['message']);
} }

View File

@@ -49,7 +49,7 @@ abstract final class VideoHttp {
static bool enableFilter = zoneRegExp.pattern.isNotEmpty; static bool enableFilter = zoneRegExp.pattern.isNotEmpty;
// 首页推荐视频 // 首页推荐视频
static Future<LoadingState<List<RecVideoItemModel>>> rcmdVideoList({ static Future<LoadingState<List<RcmdVideoItemModel>>> rcmdVideoList({
required int ps, required int ps,
required int freshIdx, required int freshIdx,
}) async { }) async {
@@ -66,13 +66,13 @@ abstract final class VideoHttp {
}), }),
); );
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
List<RecVideoItemModel> list = <RecVideoItemModel>[]; List<RcmdVideoItemModel> list = <RcmdVideoItemModel>[];
for (final i in res.data['data']['item']) { for (final i in res.data['data']['item']) {
//过滤掉live与ad以及拉黑用户 //过滤掉live与ad以及拉黑用户
if (i['goto'] == 'av' && if (i['goto'] == 'av' &&
(i['owner'] != null && (i['owner'] != null &&
!GlobalData().blackMids.contains(i['owner']['mid']))) { !GlobalData().blackMids.contains(i['owner']['mid']))) {
RecVideoItemModel videoItem = RecVideoItemModel.fromJson(i); RcmdVideoItemModel videoItem = RcmdVideoItemModel.fromJson(i);
if (!RecommendFilter.filter(videoItem)) { if (!RecommendFilter.filter(videoItem)) {
list.add(videoItem); list.add(videoItem);
} }
@@ -85,7 +85,7 @@ abstract final class VideoHttp {
} }
// 添加额外的loginState变量模拟未登录状态 // 添加额外的loginState变量模拟未登录状态
static Future<LoadingState<List<RecVideoItemAppModel>>> rcmdVideoListApp({ static Future<LoadingState<List<RcmdVideoItemAppModel>>> rcmdVideoListApp({
required int freshIdx, required int freshIdx,
}) async { }) async {
final params = { final params = {
@@ -139,7 +139,7 @@ abstract final class VideoHttp {
), ),
); );
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
List<RecVideoItemAppModel> list = <RecVideoItemAppModel>[]; List<RcmdVideoItemAppModel> list = <RcmdVideoItemAppModel>[];
for (final i in res.data['data']['items']) { for (final i in res.data['data']['items']) {
// 屏蔽推广和拉黑用户 // 屏蔽推广和拉黑用户
if (i['card_goto'] != 'ad_av' && if (i['card_goto'] != 'ad_av' &&
@@ -152,7 +152,7 @@ abstract final class VideoHttp {
zoneRegExp.hasMatch(i['args']['tname'])) { zoneRegExp.hasMatch(i['args']['tname'])) {
continue; continue;
} }
RecVideoItemAppModel videoItem = RecVideoItemAppModel.fromJson(i); RcmdVideoItemAppModel videoItem = RcmdVideoItemAppModel.fromJson(i);
if (!RecommendFilter.filter(videoItem)) { if (!RecommendFilter.filter(videoItem)) {
list.add(videoItem); list.add(videoItem);
} }
@@ -564,7 +564,6 @@ abstract final class VideoHttp {
replyInfo.id.toString(), replyInfo.id.toString(),
(replyInfo.deepCopy() (replyInfo.deepCopy()
..unknownFields.clear() ..unknownFields.clear()
..clearMemberV2()
..clearTrackInfo()) ..clearTrackInfo())
.writeToBuffer(), .writeToBuffer(),
); );

View File

@@ -0,0 +1,11 @@
import 'package:PiliPlus/models/common/enum_with_label.dart';
enum ArchiveOrderTypeApp with EnumWithLabel {
pubdate('最新发布'),
click('最多播放'),
;
@override
final String label;
const ArchiveOrderTypeApp(this.label);
}

View File

@@ -0,0 +1,12 @@
import 'package:PiliPlus/models/common/enum_with_label.dart';
enum ArchiveOrderTypeWeb with EnumWithLabel {
pubdate('最新发布'),
click('最多播放'),
stow('最多收藏'),
;
@override
final String label;
const ArchiveOrderTypeWeb(this.label);
}

View File

@@ -0,0 +1,11 @@
import 'package:PiliPlus/models/common/enum_with_label.dart';
enum ArchiveSortTypeApp with EnumWithLabel {
desc('默认'),
asc('倒序'),
;
@override
final String label;
const ArchiveSortTypeApp(this.label);
}

View File

@@ -1,8 +1,14 @@
import 'package:PiliPlus/http/api.dart';
enum ContributeType { enum ContributeType {
video, video(Api.spaceArchive),
charging, charging(Api.spaceChargingArchive),
season, season(Api.spaceSeason),
series, series(Api.spaceSeries),
bangumi, bangumi(Api.spaceBangumi),
comic, comic(Api.spaceComic)
;
final String api;
const ContributeType(this.api);
} }

View File

@@ -0,0 +1,10 @@
import 'package:PiliPlus/http/api.dart';
enum WebSsType {
season(Api.seasonArchives),
series(Api.seriesArchives),
;
final String api;
const WebSsType(this.api);
}

View File

@@ -1,4 +1,4 @@
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/style.dart' as common_style;
import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/models/dynamics/vote_model.dart'; import 'package:PiliPlus/models/dynamics/vote_model.dart';
@@ -49,7 +49,7 @@ class Pic {
style = json['style']; style = json['style'];
liveUrl = json['live_url']; liveUrl = json['live_url'];
if (width != null && height != null) { if (width != null && height != null) {
isLongPic = (height! / width!) > StyleString.imgMaxRatio; isLongPic = (height! / width!) > common_style.Style.imgMaxRatio;
} }
} }
} }

View File

@@ -424,7 +424,7 @@ class ModuleAuthorModel extends Avatar {
pubTime = json['pub_time']; pubTime = json['pub_time'];
pubTs = json['pub_ts'] == 0 ? null : Utils.safeToInt(json['pub_ts']); pubTs = json['pub_ts'] == 0 ? null : Utils.safeToInt(json['pub_ts']);
type = json['type']; type = json['type'];
if (PendantAvatar.showDynDecorate) { if (PendantAvatar.showDecorate) {
decorate = json['decorate'] == null decorate = json['decorate'] == null
? null ? null
: Decorate.fromJson(json['decorate']); : Decorate.fromJson(json['decorate']);

View File

@@ -1,3 +1,5 @@
import 'package:PiliPlus/utils/utils.dart';
class FollowUpModel { class FollowUpModel {
FollowUpModel({ FollowUpModel({
this.liveUsers, this.liveUsers,
@@ -49,7 +51,7 @@ class LiveUsers {
List<LiveUserItem>? items; List<LiveUserItem>? items;
LiveUsers.fromJson(Map<String, dynamic> json) { LiveUsers.fromJson(Map<String, dynamic> json) {
count = json['count'] ?? 0; count = Utils.safeToInt(json['count']) ?? 0;
group = json['group']; group = json['group'];
items = (json['items'] as List?) items = (json['items'] as List?)
?.map<LiveUserItem>((e) => LiveUserItem.fromJson(e)) ?.map<LiveUserItem>((e) => LiveUserItem.fromJson(e))
@@ -63,14 +65,11 @@ class LiveUserItem extends UpItem {
int? roomId; int? roomId;
String? title; String? title;
LiveUserItem.fromJson(Map<String, dynamic> json) LiveUserItem.fromJson(Map<String, dynamic> json) : super.fromJson(json) {
: super(mid: json['mid'] ?? 0) {
face = json['face'];
isReserveRecall = json['is_reserve_recall']; isReserveRecall = json['is_reserve_recall'];
jumpUrl = json['jump_url']; jumpUrl = json['jump_url'];
roomId = json['room_id']; roomId = Utils.safeToInt(json['room_id']);
title = json['title']; title = json['title'];
uname = json['uname'];
} }
} }
@@ -90,7 +89,7 @@ class UpItem {
UpItem.fromJson(Map<String, dynamic> json) { UpItem.fromJson(Map<String, dynamic> json) {
face = json['face']; face = json['face'];
hasUpdate = json['has_update']; hasUpdate = json['has_update'];
mid = json['mid'] ?? 0; mid = Utils.safeToInt(json['mid']) ?? 0;
uname = json['uname']; uname = json['uname'];
} }

View File

@@ -3,14 +3,14 @@ import 'package:PiliPlus/models/model_video.dart';
import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/id_utils.dart';
import 'package:PiliPlus/utils/num_utils.dart'; import 'package:PiliPlus/utils/num_utils.dart';
class RecVideoItemAppModel extends BaseRecVideoItemModel { class RcmdVideoItemAppModel extends BaseRcmdVideoItemModel {
int? get id => aid; int? get id => aid;
String? talkBack; String? talkBack;
String? cardType; String? cardType;
ThreePoint? threePoint; ThreePoint? threePoint;
RecVideoItemAppModel.fromJson(Map<String, dynamic> json) { RcmdVideoItemAppModel.fromJson(Map<String, dynamic> json) {
aid = json['player_args']?['aid'] ?? int.tryParse(json['param'] ?? '0'); aid = json['player_args']?['aid'] ?? int.tryParse(json['param'] ?? '0');
bvid = json['bvid'] ?? IdUtils.av2bv(aid!); bvid = json['bvid'] ?? IdUtils.av2bv(aid!);
cid = json['player_args']?['cid']; cid = json['player_args']?['cid'];

View File

@@ -5,7 +5,7 @@ import 'package:PiliPlus/models_new/video/video_detail/dimension.dart';
import 'package:PiliPlus/pages/common/multi_select/base.dart'; import 'package:PiliPlus/pages/common/multi_select/base.dart';
// 稍后再看, 排行榜等网页返回也使用该类 // 稍后再看, 排行榜等网页返回也使用该类
class HotVideoItemModel extends BaseRecVideoItemModel with MultiSelectData { class HotVideoItemModel extends BaseRcmdVideoItemModel with MultiSelectData {
int? videos; int? videos;
int? tid; int? tid;
String? tname; String? tname;

View File

@@ -1,7 +1,7 @@
import 'package:PiliPlus/models/model_owner.dart'; import 'package:PiliPlus/models/model_owner.dart';
import 'package:PiliPlus/models/model_video.dart'; import 'package:PiliPlus/models/model_video.dart';
abstract class BaseRecVideoItemModel extends BaseVideoItemModel { abstract class BaseRcmdVideoItemModel extends BaseVideoItemModel {
String? goto; String? goto;
String? uri; String? uri;
String? rcmdReason; String? rcmdReason;
@@ -11,8 +11,8 @@ abstract class BaseRecVideoItemModel extends BaseVideoItemModel {
String? pgcBadge; String? pgcBadge;
} }
class RecVideoItemModel extends BaseRecVideoItemModel { class RcmdVideoItemModel extends BaseRcmdVideoItemModel {
RecVideoItemModel.fromJson(Map<String, dynamic> json) { RcmdVideoItemModel.fromJson(Map<String, dynamic> json) {
aid = json["id"]; aid = json["id"];
bvid = json["bvid"]; bvid = json["bvid"];
cid = json["cid"]; cid = json["cid"];

View File

@@ -377,6 +377,11 @@ class Volume {
i -= measuredI; i -= measuredI;
measuredI = 0; measuredI = 0;
} }
num measuredThreshold = this.measuredThreshold;
if (measuredThreshold > 0) {
measuredThreshold = 0;
}
return 'LRA=$lra:I=$i:TP=$tp:offset=$offset:linear=true:measured_I=$measuredI:measured_LRA=$measuredLra:measured_TP=$measuredTp:measured_thresh=$measuredThreshold'; return 'LRA=$lra:I=$i:TP=$tp:offset=$offset:linear=true:measured_I=$measuredI:measured_LRA=$measuredLra:measured_TP=$measuredTp:measured_thresh=$measuredThreshold';
} }

View File

@@ -1,4 +1,4 @@
import 'package:PiliPlus/models_new/live/live_contribution_rank/medal_info.dart'; import 'package:PiliPlus/models_new/live/live_medal_wall/uinfo_medal.dart';
class LiveContributionRankItem { class LiveContributionRankItem {
int? uid; int? uid;
@@ -6,7 +6,7 @@ class LiveContributionRankItem {
String? face; String? face;
int? rank; int? rank;
int? score; int? score;
MedalInfo? medalInfo; UinfoMedal? uinfoMedal;
LiveContributionRankItem({ LiveContributionRankItem({
this.uid, this.uid,
@@ -14,7 +14,7 @@ class LiveContributionRankItem {
this.face, this.face,
this.rank, this.rank,
this.score, this.score,
this.medalInfo, this.uinfoMedal,
}); });
factory LiveContributionRankItem.fromJson(Map<String, dynamic> json) => factory LiveContributionRankItem.fromJson(Map<String, dynamic> json) =>
@@ -24,8 +24,8 @@ class LiveContributionRankItem {
face: json['face'] as String?, face: json['face'] as String?,
rank: json['rank'] as int?, rank: json['rank'] as int?,
score: json['score'] as int?, score: json['score'] as int?,
medalInfo: json['medal_info'] == null uinfoMedal: json['uinfo']?['medal'] == null
? null ? null
: MedalInfo.fromJson(json['medal_info'] as Map<String, dynamic>), : UinfoMedal.fromJson(json['uinfo']?['medal']),
); );
} }

View File

@@ -1,14 +0,0 @@
class MedalInfo {
String? medalName;
int? level;
MedalInfo({
this.medalName,
this.level,
});
factory MedalInfo.fromJson(Map<String, dynamic> json) => MedalInfo(
medalName: json['medal_name'] as String?,
level: json['level'] as int?,
);
}

View File

@@ -1,6 +1,8 @@
import 'package:PiliPlus/models/model_owner.dart'; import 'package:PiliPlus/models/model_owner.dart';
import 'package:PiliPlus/models_new/live/live_danmaku/live_emote.dart'; import 'package:PiliPlus/models_new/live/live_danmaku/live_emote.dart';
import 'package:PiliPlus/models_new/live/live_medal_wall/uinfo_medal.dart';
import 'package:PiliPlus/pages/danmaku/danmaku_model.dart'; import 'package:PiliPlus/pages/danmaku/danmaku_model.dart';
import 'package:PiliPlus/utils/global_data.dart';
class DanmakuMsg { class DanmakuMsg {
final String name; final String name;
@@ -9,6 +11,7 @@ class DanmakuMsg {
final BaseEmote? uemote; final BaseEmote? uemote;
final Owner? reply; final Owner? reply;
final LiveDanmaku extra; final LiveDanmaku extra;
final UinfoMedal? medalInfo;
const DanmakuMsg({ const DanmakuMsg({
required this.name, required this.name,
@@ -17,6 +20,7 @@ class DanmakuMsg {
this.uemote, this.uemote,
this.reply, this.reply,
required this.extra, required this.extra,
this.medalInfo,
}); });
factory DanmakuMsg.fromPrefetch(Map<String, dynamic> obj) { factory DanmakuMsg.fromPrefetch(Map<String, dynamic> obj) {
@@ -36,6 +40,7 @@ class DanmakuMsg {
); );
} }
} }
final medal = user['medal'];
return DanmakuMsg( return DanmakuMsg(
name: user['base']['name'], name: user['base']['name'],
text: obj['text'], text: obj['text'],
@@ -51,6 +56,9 @@ class DanmakuMsg {
ts: checkInfo['ts'], ts: checkInfo['ts'],
ct: checkInfo['ct'], ct: checkInfo['ct'],
), ),
medalInfo: !GlobalData().showMedal || medal == null
? null
: UinfoMedal.fromJson(medal),
); );
} }
@@ -61,5 +69,6 @@ class DanmakuMsg {
'uemote': ?uemote?.toJson(), 'uemote': ?uemote?.toJson(),
'reply': ?reply?.toJson(), 'reply': ?reply?.toJson(),
'extra': extra.toJson(), 'extra': extra.toJson(),
'medal': ?medalInfo?.toJson(),
}; };
} }

View File

@@ -3,7 +3,7 @@ class BaseEmote {
late String emoticonUnique; late String emoticonUnique;
late double width; late double width;
late double height; late double height;
late final isUpower = emoticonUnique.startsWith('upower_'); late final isOfficial = emoticonUnique.startsWith('official_');
BaseEmote.fromJson(Map<String, dynamic> json) { BaseEmote.fromJson(Map<String, dynamic> json) {
url = json['url']; url = json['url'];

View File

@@ -0,0 +1,30 @@
import 'package:PiliPlus/models_new/live/live_medal_wall/item.dart';
class MedalWallData {
List<MedalWallItem>? list;
int? count;
String? name;
String? icon;
int? uid;
int? level;
MedalWallData({
this.list,
this.count,
this.name,
this.icon,
this.uid,
this.level,
});
factory MedalWallData.fromJson(Map<String, dynamic> json) => MedalWallData(
list: (json['list'] as List<dynamic>?)
?.map((e) => MedalWallItem.fromJson(e as Map<String, dynamic>))
.toList(),
count: json['count'] as int?,
name: json['name'] as String?,
icon: json['icon'] as String?,
uid: json['uid'] as int?,
level: json['level'] as int?,
);
}

View File

@@ -0,0 +1,36 @@
import 'package:PiliPlus/models_new/live/live_medal_wall/medal_info.dart';
import 'package:PiliPlus/models_new/live/live_medal_wall/uinfo_medal.dart';
class MedalWallItem {
MedalInfo? medalInfo;
String? targetName;
String? targetIcon;
String? link;
int? liveStatus;
int? official;
UinfoMedal? uinfoMedal;
MedalWallItem({
this.medalInfo,
this.targetName,
this.targetIcon,
this.link,
this.liveStatus,
this.official,
this.uinfoMedal,
});
factory MedalWallItem.fromJson(Map<String, dynamic> json) => MedalWallItem(
medalInfo: json['medal_info'] == null
? null
: MedalInfo.fromJson(json['medal_info'] as Map<String, dynamic>),
targetName: json['target_name'] as String?,
targetIcon: json['target_icon'] as String?,
link: json['link'] as String?,
liveStatus: json['live_status'] as int?,
official: json['official'] as int?,
uinfoMedal: json['uinfo_medal'] == null
? null
: UinfoMedal.fromJson(json['uinfo_medal'] as Map<String, dynamic>),
);
}

View File

@@ -0,0 +1,11 @@
class MedalInfo {
int? wearingStatus;
MedalInfo({
this.wearingStatus,
});
factory MedalInfo.fromJson(Map<String, dynamic> json) => MedalInfo(
wearingStatus: json['wearing_status'] as int?,
);
}

View File

@@ -0,0 +1,35 @@
class UinfoMedal {
String? name;
int? level;
int? id;
int? ruid;
String? v2MedalColorStart;
String? v2MedalColorText;
UinfoMedal({
this.name,
this.level,
this.id,
this.ruid,
this.v2MedalColorStart,
this.v2MedalColorText,
});
factory UinfoMedal.fromJson(Map<String, dynamic> json) => UinfoMedal(
name: json['name'] as String?,
level: json['level'] as int?,
id: json['id'] as int?,
ruid: json['ruid'] as int?,
v2MedalColorStart: json['v2_medal_color_start'] as String?,
v2MedalColorText: json['v2_medal_color_text'] as String?,
);
Map<String, dynamic> toJson() => {
'name': name,
'level': level,
'id': id,
'ruid': ruid,
'v2_medal_color_start': v2MedalColorStart,
'v2_medal_color_text': v2MedalColorText,
};
}

View File

@@ -1,10 +1,14 @@
import 'package:PiliPlus/models_new/live/live_medal_wall/uinfo_medal.dart';
import 'package:PiliPlus/models_new/live/live_superchat/user_info.dart'; import 'package:PiliPlus/models_new/live/live_superchat/user_info.dart';
import 'package:PiliPlus/utils/global_data.dart';
import 'package:PiliPlus/utils/parse_string.dart';
import 'package:PiliPlus/utils/utils.dart'; import 'package:PiliPlus/utils/utils.dart';
class SuperChatItem { class SuperChatItem {
int id; int id;
int uid; int uid;
int price; int price;
String? backgroundImage;
String backgroundColor; String backgroundColor;
String backgroundBottomColor; String backgroundBottomColor;
String backgroundPriceColor; String backgroundPriceColor;
@@ -16,11 +20,13 @@ class SuperChatItem {
UserInfo userInfo; UserInfo userInfo;
late bool expired = false; late bool expired = false;
late bool deleted = false; late bool deleted = false;
UinfoMedal? medalInfo;
SuperChatItem({ SuperChatItem({
required this.id, required this.id,
required this.uid, required this.uid,
required this.price, required this.price,
this.backgroundImage,
required this.backgroundColor, required this.backgroundColor,
required this.backgroundBottomColor, required this.backgroundBottomColor,
required this.backgroundPriceColor, required this.backgroundPriceColor,
@@ -30,6 +36,7 @@ class SuperChatItem {
required this.token, required this.token,
required this.ts, required this.ts,
required this.userInfo, required this.userInfo,
this.medalInfo,
}); });
static SuperChatItem get random => SuperChatItem.fromJson({ static SuperChatItem get random => SuperChatItem.fromJson({
@@ -44,12 +51,23 @@ class SuperChatItem {
}, },
'token': '', 'token': '',
'ts': 0, 'ts': 0,
'uinfo': {
'medal': {
"name": "Medal",
"level": Utils.random.nextInt(40),
"id": 123,
"ruid": 456,
"v2_medal_color_start": "#4C7DFF99",
"v2_medal_color_text": "#FFFFFF",
},
},
}); });
factory SuperChatItem.fromJson(Map<String, dynamic> json) => SuperChatItem( factory SuperChatItem.fromJson(Map<String, dynamic> json) => SuperChatItem(
id: Utils.safeToInt(json['id']) ?? Utils.random.nextInt(2147483647), id: Utils.safeToInt(json['id']) ?? Utils.random.nextInt(2147483647),
uid: Utils.safeToInt(json['uid'])!, uid: Utils.safeToInt(json['uid'])!,
price: json['price'], price: json['price'],
backgroundImage: noneNullOrEmptyString(json['background_image']),
backgroundColor: json['background_color'] ?? '#EDF5FF', backgroundColor: json['background_color'] ?? '#EDF5FF',
backgroundBottomColor: json['background_bottom_color'] ?? '#2A60B2', backgroundBottomColor: json['background_bottom_color'] ?? '#2A60B2',
backgroundPriceColor: json['background_price_color'] ?? '#7497CD', backgroundPriceColor: json['background_price_color'] ?? '#7497CD',
@@ -59,6 +77,9 @@ class SuperChatItem {
token: json['token'], token: json['token'],
ts: Utils.safeToInt(json['ts'])!, ts: Utils.safeToInt(json['ts'])!,
userInfo: UserInfo.fromJson(json['user_info'] as Map<String, dynamic>), userInfo: UserInfo.fromJson(json['user_info'] as Map<String, dynamic>),
medalInfo: !GlobalData().showMedal || json['uinfo']?['medal'] == null
? null
: UinfoMedal.fromJson(json['uinfo']['medal']),
); );
SuperChatItem copyWith({ SuperChatItem copyWith({
@@ -75,6 +96,7 @@ class SuperChatItem {
int? ts, int? ts,
UserInfo? userInfo, UserInfo? userInfo,
bool? expired, bool? expired,
UinfoMedal? medalInfo,
}) { }) {
return SuperChatItem( return SuperChatItem(
id: id ?? this.id, id: id ?? this.id,
@@ -90,6 +112,7 @@ class SuperChatItem {
token: token ?? this.token, token: token ?? this.token,
ts: ts ?? this.ts, ts: ts ?? this.ts,
userInfo: userInfo ?? this.userInfo, userInfo: userInfo ?? this.userInfo,
medalInfo: medalInfo ?? this.medalInfo,
); );
} }
@@ -97,6 +120,7 @@ class SuperChatItem {
'id': id, 'id': id,
'uid': uid, 'uid': uid,
'price': price, 'price': price,
'background_image': backgroundImage,
'background_color': backgroundColor, 'background_color': backgroundColor,
'background_bottom_color': backgroundBottomColor, 'background_bottom_color': backgroundBottomColor,
'background_price_color': backgroundPriceColor, 'background_price_color': backgroundPriceColor,
@@ -106,5 +130,6 @@ class SuperChatItem {
'token': token, 'token': token,
'ts': ts, 'ts': ts,
'user_info': userInfo.toJson(), 'user_info': userInfo.toJson(),
'medal': ?medalInfo?.toJson(),
}; };
} }

View File

@@ -1,22 +1,13 @@
import 'package:PiliPlus/models_new/member/search_archive/episodic_button.dart';
import 'package:PiliPlus/models_new/member/search_archive/list.dart'; import 'package:PiliPlus/models_new/member/search_archive/list.dart';
import 'package:PiliPlus/models_new/member/search_archive/page.dart'; import 'package:PiliPlus/models_new/member/search_archive/page.dart';
class SearchArchiveData { class SearchArchiveData {
SearchArchiveList? list; SearchArchiveList? list;
Page? page; Page? page;
EpisodicButton? episodicButton;
bool? isRisk;
int? gaiaResType;
dynamic gaiaData;
SearchArchiveData({ SearchArchiveData({
this.list, this.list,
this.page, this.page,
this.episodicButton,
this.isRisk,
this.gaiaResType,
this.gaiaData,
}); });
factory SearchArchiveData.fromJson(Map<String, dynamic> json) => factory SearchArchiveData.fromJson(Map<String, dynamic> json) =>
@@ -27,13 +18,5 @@ class SearchArchiveData {
page: json['page'] == null page: json['page'] == null
? null ? null
: Page.fromJson(json['page'] as Map<String, dynamic>), : Page.fromJson(json['page'] as Map<String, dynamic>),
episodicButton: json['episodic_button'] == null
? null
: EpisodicButton.fromJson(
json['episodic_button'] as Map<String, dynamic>,
),
isRisk: json['is_risk'] as bool?,
gaiaResType: json['gaia_res_type'] as int?,
gaiaData: json['gaia_data'] as dynamic,
); );
} }

View File

@@ -1,13 +0,0 @@
class EpisodicButton {
String? text;
String? uri;
EpisodicButton({this.text, this.uri});
factory EpisodicButton.fromJson(Map<String, dynamic> json) {
return EpisodicButton(
text: json['text'] as String?,
uri: json['uri'] as String?,
);
}
}

View File

@@ -1,14 +1,26 @@
import 'package:PiliPlus/models_new/member/search_archive/slist.dart';
import 'package:PiliPlus/models_new/member/search_archive/vlist.dart'; import 'package:PiliPlus/models_new/member/search_archive/vlist.dart';
class SearchArchiveList { class SearchArchiveList {
List<ListTag>? tags;
List<VListItemModel>? vlist; List<VListItemModel>? vlist;
SearchArchiveList({this.vlist}); SearchArchiveList.fromJson(Map<String, dynamic> json) {
vlist = (json['vlist'] as List<dynamic>?)
factory SearchArchiveList.fromJson(Map<String, dynamic> json) => ?.map((e) => VListItemModel.fromJson(e as Map<String, dynamic>))
SearchArchiveList( .toList();
vlist: (json['vlist'] as List<dynamic>?) tags = (json['slist'] as List<dynamic>?)
?.map((e) => VListItemModel.fromJson(e as Map<String, dynamic>)) ?.map((e) => ListTag.fromJson(e as Map<String, dynamic>))
.toList(), .toList();
); (json['tlist'] as Map<String, dynamic>?)?.forEach((k, v) {
if (k == '196') {
if (tags == null) {
tags = [ListTag.fromJson(v)];
} else {
tags!.add(ListTag.fromJson(v));
}
return;
}
});
}
} }

View File

@@ -0,0 +1,15 @@
class ListTag {
int? tid;
int? count;
String? name;
String? specialType;
ListTag({this.tid, this.count, this.name, this.specialType});
factory ListTag.fromJson(Map<String, dynamic> json) => ListTag(
tid: json['tid'] as int?,
count: json['count'] as int?,
name: json['name'] as String?,
specialType: json['special_type'] as String?,
);
}

View File

@@ -2,31 +2,16 @@ import 'package:PiliPlus/models/model_video.dart';
import 'package:PiliPlus/utils/duration_utils.dart'; import 'package:PiliPlus/utils/duration_utils.dart';
class VListItemModel extends BaseVideoItemModel { class VListItemModel extends BaseVideoItemModel {
int? comment;
int? typeid;
String? subtitle;
String? copyright;
int? review;
bool? hideClick;
bool? isChargingSrc;
VListItemModel.fromJson(Map<String, dynamic> json) { VListItemModel.fromJson(Map<String, dynamic> json) {
comment = json['comment'];
typeid = json['typeid'];
cover = json['pic']; cover = json['pic'];
subtitle = json['subtitle'];
desc = json['description']; desc = json['description'];
copyright = json['copyright'];
title = json['title']; title = json['title'];
review = json['review'];
pubdate = json['created']; pubdate = json['created'];
if (json['length'] != null) { if (json['length'] != null) {
duration = DurationUtils.parseDuration(json['length']); duration = DurationUtils.parseDuration(json['length']);
} }
aid = json['aid']; aid = json['aid'];
bvid = json['bvid']; bvid = json['bvid'];
hideClick = json['hide_click'];
isChargingSrc = json['is_charging_arc'];
stat = VListStat.fromJson(json); stat = VListStat.fromJson(json);
owner = VListOwner.fromJson(json); owner = VListOwner.fromJson(json);
} }

View File

@@ -0,0 +1,28 @@
import 'package:PiliPlus/models/model_video.dart';
class SeasonArchive extends BaseVideoItemModel {
SeasonArchive.fromJson(Map<String, dynamic> json) {
aid = json['aid'];
bvid = json['bvid'];
cover = json['pic'];
title = json['title'];
pubdate = json['pubdate'];
duration = json['duration'];
stat = ArchiveStat.fromJson(json['stat']);
owner = ArchiveOwner.fromJson(json);
}
}
class ArchiveOwner extends BaseOwner {
ArchiveOwner.fromJson(Map<String, dynamic> json) {
mid = json['upMid'];
name = '';
}
}
class ArchiveStat extends BaseStat {
ArchiveStat.fromJson(Map<String, dynamic> json) {
view = json['view'];
danmu = json['danmaku'];
}
}

View File

@@ -0,0 +1,18 @@
import 'package:PiliPlus/models_new/member/season_web/archive.dart';
import 'package:PiliPlus/models_new/member/season_web/page.dart';
class SeasonWebData {
List<SeasonArchive>? archives;
Page? page;
SeasonWebData({this.archives, this.page});
factory SeasonWebData.fromJson(Map<String, dynamic> json) => SeasonWebData(
archives: (json['archives'] as List<dynamic>?)
?.map((e) => SeasonArchive.fromJson(e as Map<String, dynamic>))
.toList(),
page: json['page'] == null
? null
: Page.fromJson(json['page'] as Map<String, dynamic>),
);
}

View File

@@ -0,0 +1,13 @@
class Page {
int? pageNum;
int? pageSize;
int? total;
Page({this.pageNum, this.pageSize, this.total});
factory Page.fromJson(Map<String, dynamic> json) => Page(
pageNum: json['page_num'] ?? json['num'],
pageSize: json['page_size'] ?? json['size'],
total: json['total'] as int?,
);
}

View File

@@ -0,0 +1,25 @@
import 'package:PiliPlus/utils/extension/iterable_ext.dart';
class RelationData {
int? mid;
int? attribute;
int? mtime;
List<int>? tag;
int? special;
RelationData({
this.mid,
this.attribute,
this.mtime,
this.tag,
this.special,
});
factory RelationData.fromJson(Map<String, dynamic> json) => RelationData(
mid: json['mid'] as int?,
attribute: json['attribute'] as int?,
mtime: json['mtime'] as int?,
tag: (json['tag'] as List<dynamic>?)?.fromCast(),
special: json['special'] as int?,
);
}

View File

@@ -1,28 +1,44 @@
class LiveFansWearing { class LiveFansWearing {
int? level; DetailV2? detailV2;
String? medalName;
int? medalColorStart;
int? medalColorEnd;
int? medalColorBorder;
String? medalJumpUrl;
LiveFansWearing({ LiveFansWearing({
this.level, this.detailV2,
this.medalName,
this.medalColorStart,
this.medalColorEnd,
this.medalColorBorder,
this.medalJumpUrl,
}); });
factory LiveFansWearing.fromJson(Map<String, dynamic> json) { factory LiveFansWearing.fromJson(Map<String, dynamic> json) {
return LiveFansWearing( return LiveFansWearing(
level: json['level'] as int?, detailV2: json['detail_v2'] == null
medalName: json['medal_name'] as String?, ? null
medalColorStart: json['medal_color_start'] as int?, : DetailV2.fromJson(json['detail_v2']),
medalColorEnd: json['medal_color_end'] as int?, );
medalColorBorder: json['medal_color_border'] as int?, }
medalJumpUrl: json['medal_jump_url'] as String?, }
class DetailV2 {
int? uid;
int? level;
String? medalColorName;
String? medalName;
int? medalId;
String? medalColor;
DetailV2({
this.uid,
this.level,
this.medalColorName,
this.medalName,
this.medalId,
this.medalColor,
});
factory DetailV2.fromJson(Map<String, dynamic> json) {
return DetailV2(
uid: json["uid"],
level: json["level"],
medalColorName: json["medal_color_name"],
medalName: json["medal_name"],
medalId: json["medal_id"],
medalColor: json["medal_color"],
); );
} }
} }

View File

@@ -1,3 +1,4 @@
import 'package:PiliPlus/utils/extension/iterable_ext.dart';
import 'package:PiliPlus/utils/parse_string.dart'; import 'package:PiliPlus/utils/parse_string.dart';
class Top { class Top {
@@ -20,16 +21,18 @@ class TopImage {
late final String fullCover; late final String fullCover;
String get header => _defaultImage ?? fullCover; String get header => _defaultImage ?? fullCover;
late final double dy; late final double dy;
TopTitle? title;
@pragma('vm:notify-debugger-on-exception') @pragma('vm:notify-debugger-on-exception')
TopImage.fromJson(Map<String, dynamic> json) { TopImage.fromJson(Map<String, dynamic> json) {
_defaultImage = noneNullOrEmptyString( final item = json['item'];
json['item']['image']?['default_image'], final img = item['image'];
); title = json['title'] == null ? null : TopTitle.fromJson(json['title']);
_defaultImage = noneNullOrEmptyString(img?['default_image']);
fullCover = json['cover']; fullCover = json['cover'];
double dy = 0; double dy = 0;
try { try {
final Map image = json['item']['image'] ?? json['item']['animation']; final Map image = img ?? item['animation'];
if (image['location'] case String locStr when (locStr.isNotEmpty)) { if (image['location'] case String locStr when (locStr.isNotEmpty)) {
final location = locStr final location = locStr
.split('-') .split('-')
@@ -48,3 +51,36 @@ class TopImage {
this.dy = dy; this.dy = dy;
} }
} }
class TopTitle {
String? title;
String? subTitle;
SubTitleColorFormat? subTitleColorFormat;
TopTitle({
this.title,
this.subTitle,
this.subTitleColorFormat,
});
factory TopTitle.fromJson(Map<String, dynamic> json) => TopTitle(
title: json["title"],
subTitle: json["sub_title"],
subTitleColorFormat: json["sub_title_color_format"] == null
? null
: SubTitleColorFormat.fromJson(json["sub_title_color_format"]),
);
}
class SubTitleColorFormat {
List<String>? colors;
SubTitleColorFormat({
this.colors,
});
factory SubTitleColorFormat.fromJson(Map<String, dynamic> json) =>
SubTitleColorFormat(
colors: (json["colors"] as List?)?.fromCast(),
);
}

View File

@@ -2,7 +2,9 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:PiliPlus/build_config.dart'; import 'package:PiliPlus/build_config.dart';
import 'package:PiliPlus/common/assets.dart';
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/dialog/dialog.dart'; import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
import 'package:PiliPlus/common/widgets/dialog/export_import.dart'; import 'package:PiliPlus/common/widgets/dialog/export_import.dart';
import 'package:PiliPlus/common/widgets/flutter/list_tile.dart'; import 'package:PiliPlus/common/widgets/flutter/list_tile.dart';
@@ -64,7 +66,7 @@ class _AboutPageState extends State<AboutPage> {
void _showDialog() => showDialog( void _showDialog() => showDialog(
context: context, context: context,
builder: (context) => AlertDialog( builder: (context) => AlertDialog(
constraints: StyleString.dialogFixedConstraints, constraints: Style.dialogFixedConstraints,
content: TextField( content: TextField(
autofocus: true, autofocus: true,
onSubmitted: (value) { onSubmitted: (value) {
@@ -108,7 +110,7 @@ class _AboutPageState extends State<AboutPage> {
height: 150, height: 150,
excludeFromSemantics: true, excludeFromSemantics: true,
cacheWidth: 150.cacheSize(context), cacheWidth: 150.cacheSize(context),
'assets/images/logo/logo.png', Assets.logo,
), ),
), ),
ListTile( ListTile(
@@ -211,8 +213,8 @@ Commit Hash: ${BuildConfig.commitHash}''',
if (cacheSize.value.isNotEmpty) { if (cacheSize.value.isNotEmpty) {
showConfirmDialog( showConfirmDialog(
context: context, context: context,
title: '提示', title: const Text('提示'),
content: '该操作将清除图片及网络请求缓存数据,确认清除?', content: const Text('该操作将清除图片及网络请求缓存数据,确认清除?'),
onConfirm: () async { onConfirm: () async {
SmartDialog.showLoading(msg: '正在清除...'); SmartDialog.showLoading(msg: '正在清除...');
try { try {

View File

@@ -1,6 +1,6 @@
import 'dart:math' as math; import 'dart:math' as math;
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/models_new/article/article_view/ops.dart'; import 'package:PiliPlus/models_new/article/article_view/ops.dart';
import 'package:PiliPlus/pages/dynamics/widgets/vote.dart'; import 'package:PiliPlus/pages/dynamics/widgets/vote.dart';
import 'package:PiliPlus/utils/app_scheme.dart'; import 'package:PiliPlus/utils/app_scheme.dart';
@@ -68,7 +68,7 @@ class ArticleOpus extends StatelessWidget {
} }
}, },
child: ClipRRect( child: ClipRRect(
borderRadius: StyleString.mdRadius, borderRadius: Style.mdRadius,
child: CachedNetworkImage( child: CachedNetworkImage(
width: width, width: width,
height: height, height: height,

View File

@@ -1,3 +1,4 @@
import 'package:PiliPlus/common/assets.dart';
import 'package:PiliPlus/common/widgets/image_viewer/hero.dart'; import 'package:PiliPlus/common/widgets/image_viewer/hero.dart';
import 'package:PiliPlus/models/common/image_preview_type.dart'; import 'package:PiliPlus/models/common/image_preview_type.dart';
import 'package:PiliPlus/utils/extension/num_ext.dart'; import 'package:PiliPlus/utils/extension/num_ext.dart';
@@ -64,8 +65,7 @@ Widget htmlRender({
imageUrl: ImageUtils.thumbnailUrl(imgUrl, 60), imageUrl: ImageUtils.thumbnailUrl(imgUrl, 60),
fadeInDuration: const Duration(milliseconds: 120), fadeInDuration: const Duration(milliseconds: 120),
fadeOutDuration: const Duration(milliseconds: 120), fadeOutDuration: const Duration(milliseconds: 120),
placeholder: (context, url) => placeholder: (context, url) => Image.asset(Assets.loading),
Image.asset('assets/images/loading.png'),
), ),
), ),
); );

View File

@@ -1,5 +1,6 @@
import 'dart:math' as math; import 'dart:math' as math;
import 'package:PiliPlus/common/assets.dart';
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart'; import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart'; import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart';
import 'package:PiliPlus/common/widgets/image/cached_network_svg_image.dart'; import 'package:PiliPlus/common/widgets/image/cached_network_svg_image.dart';
@@ -231,8 +232,7 @@ class OpusContent extends StatelessWidget {
imageUrl: ImageUtils.thumbnailUrl(pic.url!, 60), imageUrl: ImageUtils.thumbnailUrl(pic.url!, 60),
fadeInDuration: const Duration(milliseconds: 120), fadeInDuration: const Duration(milliseconds: 120),
fadeOutDuration: const Duration(milliseconds: 120), fadeOutDuration: const Duration(milliseconds: 120),
placeholder: (_, _) => placeholder: (_, _) => Image.asset(Assets.loading),
Image.asset('assets/images/loading.png'),
); );
if (!(pic.isLongPic ?? false)) { if (!(pic.isLongPic ?? false)) {
child = fromHero( child = fromHero(
@@ -715,18 +715,21 @@ Widget moduleBlockedItem(
) { ) {
late final isDarkMode = theme.brightness.isDark; late final isDarkMode = theme.brightness.isDark;
BoxDecoration? bgImg() { BoxDecoration? bgImg(double width) {
return moduleBlocked.bgImg == null return moduleBlocked.bgImg == null
? null ? null
: BoxDecoration( : BoxDecoration(
image: DecorationImage( image: DecorationImage(
fit: BoxFit.fill, fit: BoxFit.fill,
image: CachedNetworkImageProvider( image: ResizeImage(
ImageUtils.thumbnailUrl( CachedNetworkImageProvider(
isDarkMode ImageUtils.thumbnailUrl(
? moduleBlocked.bgImg!.imgDark isDarkMode
: moduleBlocked.bgImg!.imgDay, ? moduleBlocked.bgImg!.imgDark
: moduleBlocked.bgImg!.imgDay,
),
), ),
width: width.cacheSize(context),
), ),
), ),
); );
@@ -755,9 +758,7 @@ Widget moduleBlockedItem(
padding: padding, padding: padding,
tapTargetSize: MaterialTapTargetSize.shrinkWrap, tapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: visualDensity, visualDensity: visualDensity,
backgroundColor: isDarkMode backgroundColor: theme.colorScheme.btnColor,
? const Color(0xFF8F0030)
: const Color(0xFFFF6699),
foregroundColor: Colors.white, foregroundColor: Colors.white,
shape: shape, shape: shape,
), ),
@@ -793,7 +794,7 @@ Widget moduleBlockedItem(
return Container( return Container(
width: maxWidth, width: maxWidth,
height: maxWidth, height: maxWidth,
decoration: bgImg(), decoration: bgImg(maxWidth),
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@@ -822,43 +823,50 @@ Widget moduleBlockedItem(
), ),
); );
} }
return Container( return LayoutBuilder(
decoration: bgImg(), builder: (context, constraints) {
padding: const EdgeInsets.all(12), return Container(
child: Row( decoration: bgImg(constraints.maxWidth),
spacing: 8, padding: const EdgeInsets.all(12),
children: [ child: Row(
if (moduleBlocked.icon != null) icon(42), spacing: 8,
Expanded( children: [
child: Column( if (moduleBlocked.icon != null) icon(42),
crossAxisAlignment: CrossAxisAlignment.start, Expanded(
mainAxisSize: MainAxisSize.min, child: Column(
spacing: 2, crossAxisAlignment: CrossAxisAlignment.start,
children: [ mainAxisSize: MainAxisSize.min,
if (moduleBlocked.title?.isNotEmpty == true) spacing: 2,
Text(moduleBlocked.title!), children: [
if (moduleBlocked.hintMessage?.isNotEmpty == true) if (moduleBlocked.title?.isNotEmpty == true)
Text( Text(moduleBlocked.title!),
moduleBlocked.hintMessage!, if (moduleBlocked.hintMessage?.isNotEmpty == true)
style: TextStyle( Text(
fontSize: 13, moduleBlocked.hintMessage!,
color: theme.colorScheme.outline, style: TextStyle(
), fontSize: 13,
), color: theme.colorScheme.outline,
], ),
), ),
), ],
if (moduleBlocked.button != null) ),
btn(
context,
visualDensity: const VisualDensity(vertical: -3, horizontal: -4),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(6)),
), ),
padding: const EdgeInsets.symmetric(horizontal: 10), if (moduleBlocked.button != null)
), btn(
], context,
), visualDensity: const VisualDensity(
vertical: -3,
horizontal: -4,
),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(6)),
),
padding: const EdgeInsets.symmetric(horizontal: 10),
),
],
),
);
},
); );
} }

View File

@@ -1,4 +1,4 @@
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart'; import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/common/widgets/stat/stat.dart'; import 'package:PiliPlus/common/widgets/stat/stat.dart';
@@ -33,7 +33,7 @@ class ArticleListItem extends StatelessWidget {
}, },
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: StyleString.safeSpace, horizontal: Style.safeSpace,
vertical: 5, vertical: 5,
), ),
child: Row( child: Row(
@@ -42,7 +42,7 @@ class ArticleListItem extends StatelessWidget {
children: [ children: [
if (item.imageUrls?.isNotEmpty == true) if (item.imageUrls?.isNotEmpty == true)
AspectRatio( AspectRatio(
aspectRatio: StyleString.aspectRatio, aspectRatio: Style.aspectRatio,
child: LayoutBuilder( child: LayoutBuilder(
builder: (context, boxConstraints) { builder: (context, boxConstraints) {
return NetworkImgLayer( return NetworkImgLayer(

View File

@@ -1,6 +1,7 @@
import 'dart:math' show min; import 'dart:math' show min;
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/assets.dart';
import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/button/icon_button.dart'; import 'package:PiliPlus/common/widgets/button/icon_button.dart';
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart'; import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart'; import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart';
@@ -292,7 +293,7 @@ class _AudioPageState extends State<AudioPage> {
WidgetSpan( WidgetSpan(
alignment: .bottom, alignment: .bottom,
child: Image.asset( child: Image.asset(
'assets/images/live.gif', Assets.livingChart,
width: 16, width: 16,
height: 16, height: 16,
cacheWidth: 16.cacheSize( cacheWidth: 16.cacheSize(
@@ -336,7 +337,7 @@ class _AudioPageState extends State<AudioPage> {
WidgetSpan( WidgetSpan(
alignment: .bottom, alignment: .bottom,
child: Image.asset( child: Image.asset(
'assets/images/live.gif', Assets.livingChart,
width: 16, width: 16,
height: 16, height: 16,
cacheWidth: 16.cacheSize( cacheWidth: 16.cacheSize(
@@ -391,7 +392,7 @@ class _AudioPageState extends State<AudioPage> {
children: [ children: [
InkWell( InkWell(
onTap: Get.back, onTap: Get.back,
borderRadius: StyleString.bottomSheetRadius, borderRadius: Style.bottomSheetRadius,
child: SizedBox( child: SizedBox(
height: 35, height: 35,
child: Center( child: Center(
@@ -460,7 +461,7 @@ class _AudioPageState extends State<AudioPage> {
children: [ children: [
InkWell( InkWell(
onTap: Get.back, onTap: Get.back,
borderRadius: StyleString.bottomSheetRadius, borderRadius: Style.bottomSheetRadius,
child: SizedBox( child: SizedBox(
height: 35, height: 35,
child: Center( child: Center(
@@ -595,7 +596,7 @@ class _AudioPageState extends State<AudioPage> {
children: [ children: [
InkWell( InkWell(
onTap: Get.back, onTap: Get.back,
borderRadius: StyleString.bottomSheetRadius, borderRadius: Style.bottomSheetRadius,
child: SizedBox( child: SizedBox(
height: 35, height: 35,
child: Center( child: Center(

View File

@@ -35,7 +35,7 @@ class BlackListController
void onRemove(BuildContext context, int index, name, mid) { void onRemove(BuildContext context, int index, name, mid) {
showConfirmDialog( showConfirmDialog(
context: context, context: context,
title: '确定将 $name 移出黑名单?', title: Text('确定将 $name 移出黑名单?'),
onConfirm: () async { onConfirm: () async {
final result = await VideoHttp.relationMod(mid: mid, act: 6, reSrc: 11); final result = await VideoHttp.relationMod(mid: mid, act: 6, reSrc: 11);
if (result.isSuccess) { if (result.isSuccess) {

View File

@@ -1,4 +1,4 @@
import 'package:PiliPlus/common/constants.dart' show StyleString; import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/pages/home/controller.dart'; import 'package:PiliPlus/pages/home/controller.dart';
import 'package:PiliPlus/pages/main/controller.dart'; import 'package:PiliPlus/pages/main/controller.dart';
import 'package:flutter/foundation.dart' show clampDouble; import 'package:flutter/foundation.dart' show clampDouble;
@@ -58,7 +58,7 @@ abstract class CommonPageState<T extends StatefulWidget> extends State<T> {
_barOffset!.value = clampDouble( _barOffset!.value = clampDouble(
_barOffset!.value + scrollDelta, _barOffset!.value + scrollDelta,
0.0, 0.0,
StyleString.topBarHeight, Style.topBarHeight,
); );
} }
@@ -78,7 +78,7 @@ abstract class CommonPageState<T extends StatefulWidget> extends State<T> {
final newValue = clampDouble( final newValue = clampDouble(
value + scrollDelta, value + scrollDelta,
0.0, 0.0,
StyleString.topBarHeight, Style.topBarHeight,
); );
final offset = value - newValue; final offset = value - newValue;
if (offset != 0) { if (offset != 0) {

View File

@@ -1,7 +1,7 @@
import 'dart:math' show pi; import 'dart:math' show pi;
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/skeleton/video_reply.dart'; import 'package:PiliPlus/common/skeleton/video_reply.dart';
import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_pinned_header.dart'; import 'package:PiliPlus/common/widgets/sliver/sliver_pinned_header.dart';
import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/common/widgets/view_safe_area.dart';
@@ -89,7 +89,7 @@ abstract class CommonDynPageState<T extends StatefulWidget> extends State<T>
}, },
), ),
TextButton.icon( TextButton.icon(
style: StyleString.buttonStyle, style: Style.buttonStyle,
onPressed: controller.queryBySort, onPressed: controller.queryBySort,
icon: Icon(Icons.sort, size: 16, color: secondary), icon: Icon(Icons.sort, size: 16, color: secondary),
label: Obx( label: Obx(
@@ -188,6 +188,7 @@ abstract class CommonDynPageState<T extends StatefulWidget> extends State<T>
isVideoDetail: !showBackBtn, isVideoDetail: !showBackBtn,
replyType: controller.replyType, replyType: controller.replyType,
firstFloor: replyItem, firstFloor: replyItem,
upMid: controller.upMid,
), ),
); );
if (showBackBtn) { if (showBackBtn) {

View File

@@ -97,7 +97,7 @@ class _DanmakuBlockPageState extends State<DanmakuBlockPage> {
icon: const Icon(Icons.delete_outlined), icon: const Icon(Icons.delete_outlined),
onPressed: () => showConfirmDialog( onPressed: () => showConfirmDialog(
context: context, context: context,
title: '确定删除该规则?', title: const Text('确定删除该规则?'),
onConfirm: () => _controller.danmakuFilterDel( onConfirm: () => _controller.danmakuFilterDel(
tabIndex, tabIndex,
itemIndex, itemIndex,

View File

@@ -7,6 +7,7 @@ import 'package:PiliPlus/pages/common/multi_select/base.dart'
import 'package:PiliPlus/services/download/download_service.dart'; import 'package:PiliPlus/services/download/download_service.dart';
import 'package:PiliPlus/utils/extension/iterable_ext.dart'; import 'package:PiliPlus/utils/extension/iterable_ext.dart';
import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/storage.dart';
import 'package:flutter/widgets.dart' show Text;
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@@ -76,7 +77,7 @@ class DownloadPageController extends GetxController
void onRemove() { void onRemove() {
showConfirmDialog( showConfirmDialog(
context: Get.context!, context: Get.context!,
title: '确定删除选中视频?', title: const Text('确定删除选中视频?'),
onConfirm: () async { onConfirm: () async {
SmartDialog.showLoading(); SmartDialog.showLoading();
final watchProgress = GStorage.watchProgress; final watchProgress = GStorage.watchProgress;

View File

@@ -198,7 +198,7 @@ class _DownloadDetailPageState extends State<DownloadDetailPage>
void onRemove() { void onRemove() {
showConfirmDialog( showConfirmDialog(
context: context, context: context,
title: '确定删除选中视频?', title: const Text('确定删除选中视频?'),
onConfirm: () async { onConfirm: () async {
SmartDialog.showLoading(); SmartDialog.showLoading();
final watchProgress = GStorage.watchProgress; final watchProgress = GStorage.watchProgress;

View File

@@ -1,6 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/dialog/dialog.dart'; import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart'; import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
@@ -73,7 +73,7 @@ class DetailItem extends StatelessWidget {
Get.back(); Get.back();
showConfirmDialog( showConfirmDialog(
context: context, context: context,
title: '确定删除该视频?', title: const Text('确定删除该视频?'),
onConfirm: onDelete, onConfirm: onDelete,
); );
}, },
@@ -158,7 +158,7 @@ class DetailItem extends StatelessWidget {
onSecondaryTap: PlatformUtils.isMobile ? null : onLongPress, onSecondaryTap: PlatformUtils.isMobile ? null : onLongPress,
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: StyleString.safeSpace, horizontal: Style.safeSpace,
vertical: 5, vertical: 5,
), ),
child: Row( child: Row(
@@ -168,7 +168,7 @@ class DetailItem extends StatelessWidget {
clipBehavior: Clip.none, clipBehavior: Clip.none,
children: [ children: [
AspectRatio( AspectRatio(
aspectRatio: StyleString.aspectRatio, aspectRatio: Style.aspectRatio,
child: LayoutBuilder( child: LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
final cover = File( final cover = File(
@@ -184,7 +184,7 @@ class DetailItem extends StatelessWidget {
} }
return cover.existsSync() return cover.existsSync()
? ClipRRect( ? ClipRRect(
borderRadius: StyleString.mdRadius, borderRadius: Style.mdRadius,
child: Image.file( child: Image.file(
cover, cover,
width: maxWidth, width: maxWidth,
@@ -443,7 +443,7 @@ class DetailItem extends StatelessWidget {
// ignore: deprecated_member_use // ignore: deprecated_member_use
year2023: true, year2023: true,
minHeight: 2.5, minHeight: 2.5,
borderRadius: StyleString.mdRadius, borderRadius: Style.mdRadius,
color: color, color: color,
backgroundColor: highlightColor, backgroundColor: highlightColor,
value: progress, value: progress,

View File

@@ -109,7 +109,7 @@ class _DownloadingPageState extends State<DownloadingPage>
void onRemove() { void onRemove() {
showConfirmDialog( showConfirmDialog(
context: context, context: context,
title: '确定删除选中视频?', title: const Text('确定删除选中视频?'),
onConfirm: () async { onConfirm: () async {
SmartDialog.showLoading(); SmartDialog.showLoading();
final allChecked = this.allChecked.toSet(); final allChecked = this.allChecked.toSet();

View File

@@ -6,6 +6,7 @@ import 'package:PiliPlus/pages/common/multi_select/base.dart'
import 'package:PiliPlus/pages/common/search/common_search_controller.dart'; import 'package:PiliPlus/pages/common/search/common_search_controller.dart';
import 'package:PiliPlus/services/download/download_service.dart'; import 'package:PiliPlus/services/download/download_service.dart';
import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/storage.dart';
import 'package:flutter/widgets.dart' show Text;
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@@ -52,7 +53,7 @@ class DownloadSearchController
void onRemove() { void onRemove() {
showConfirmDialog( showConfirmDialog(
context: Get.context!, context: Get.context!,
title: '确定删除选中视频?', title: const Text('确定删除选中视频?'),
onConfirm: () async { onConfirm: () async {
SmartDialog.showLoading(); SmartDialog.showLoading();
final allChecked = this.allChecked.toSet(); final allChecked = this.allChecked.toSet();

View File

@@ -1,6 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/appbar/appbar.dart'; import 'package:PiliPlus/common/widgets/appbar/appbar.dart';
import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/dialog/dialog.dart'; import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
@@ -248,7 +248,7 @@ class _DownloadPageState extends State<DownloadPage> {
Get.back(); Get.back();
showConfirmDialog( showConfirmDialog(
context: context, context: context,
title: '确定删除?', title: const Text('确定删除?'),
onConfirm: () async { onConfirm: () async {
await GStorage.watchProgress.deleteAll( await GStorage.watchProgress.deleteAll(
pageInfo.entries.map((e) => e.cid.toString()), pageInfo.entries.map((e) => e.cid.toString()),
@@ -313,7 +313,7 @@ class _DownloadPageState extends State<DownloadPage> {
onSecondaryTap: PlatformUtils.isMobile ? null : onLongPress, onSecondaryTap: PlatformUtils.isMobile ? null : onLongPress,
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: StyleString.safeSpace, horizontal: Style.safeSpace,
vertical: 5, vertical: 5,
), ),
child: Row( child: Row(
@@ -323,7 +323,7 @@ class _DownloadPageState extends State<DownloadPage> {
clipBehavior: Clip.none, clipBehavior: Clip.none,
children: [ children: [
AspectRatio( AspectRatio(
aspectRatio: StyleString.aspectRatio, aspectRatio: Style.aspectRatio,
child: LayoutBuilder( child: LayoutBuilder(
builder: (context, constraints) => NetworkImgLayer( builder: (context, constraints) => NetworkImgLayer(
src: pageInfo.cover, src: pageInfo.cover,

View File

@@ -1,4 +1,4 @@
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart'; import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/http/dynamics.dart'; import 'package:PiliPlus/http/dynamics.dart';
@@ -21,7 +21,7 @@ Widget? addWidget(
late final Color bgColor = floor == 1 late final Color bgColor = floor == 1
? theme.dividerColor.withValues(alpha: 0.08) ? theme.dividerColor.withValues(alpha: 0.08)
: theme.colorScheme.surface; : theme.colorScheme.surface;
late final borderRadius = floor == 1 ? null : StyleString.mdRadius; late final borderRadius = floor == 1 ? null : Style.mdRadius;
Widget? child; Widget? child;
try { try {
switch (type) { switch (type) {

View File

@@ -1,7 +1,9 @@
import 'dart:math'; import 'dart:math';
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/assets.dart';
import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/dialog/report.dart'; import 'package:PiliPlus/common/widgets/dialog/report.dart';
import 'package:PiliPlus/common/widgets/extra_hit_test_widget.dart';
import 'package:PiliPlus/common/widgets/pendant_avatar.dart'; import 'package:PiliPlus/common/widgets/pendant_avatar.dart';
import 'package:PiliPlus/http/constants.dart'; import 'package:PiliPlus/http/constants.dart';
import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/loading_state.dart';
@@ -52,21 +54,6 @@ class AuthorPanel extends StatelessWidget {
this.onSetReplySubject, this.onSetReplySubject,
}); });
Widget _buildAvatar(ModuleAuthorModel moduleAuthor) {
final pendant = moduleAuthor.pendant?.image;
final hasPendant = pendant != null && pendant.isNotEmpty;
Widget avatar = PendantAvatar(
avatar: moduleAuthor.face,
size: hasPendant ? 34 : 40,
officialType: null, // 已被注释
garbPendantImage: pendant,
);
if (hasPendant) {
avatar = Padding(padding: const EdgeInsets.all(3), child: avatar);
}
return avatar;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
@@ -105,147 +92,148 @@ class AuthorPanel extends StatelessWidget {
); );
} }
} }
final moduleTagText = !isDetail ? item.modules.moduleTag?.text : null; Widget header = GestureDetector(
return Stack( onTap: moduleAuthor.type == 'AUTHOR_TYPE_NORMAL'
clipBehavior: Clip.none, ? () {
alignment: Alignment.center, feedBack();
children: [ Get.toNamed('/member?mid=${moduleAuthor.mid}');
Align( }
alignment: Alignment.centerLeft, : null,
child: GestureDetector( child: ExtraHitTestWidget(
behavior: HitTestBehavior.opaque, width: 50,
onTap: moduleAuthor.type == 'AUTHOR_TYPE_NORMAL' child: Row(
? () { spacing: 10,
feedBack(); children: [
Get.toNamed('/member?mid=${moduleAuthor.mid}'); PendantAvatar(
} size: 40,
: null, moduleAuthor.face,
child: Row( pendantImage: moduleAuthor.pendant?.image,
spacing: 10,
mainAxisSize: MainAxisSize.min,
children: [
_buildAvatar(moduleAuthor),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
moduleAuthor.name ?? '',
style: TextStyle(
color:
moduleAuthor.vip != null &&
moduleAuthor.vip!.status > 0 &&
moduleAuthor.vip!.type == 2
? theme.colorScheme.vipColor
: theme.colorScheme.onSurface,
fontSize: theme.textTheme.titleSmall!.fontSize,
),
),
?pubTs,
],
),
],
), ),
), Expanded(
), child: Column(
Align( crossAxisAlignment: CrossAxisAlignment.start,
alignment: Alignment.centerRight, children: [
child: moduleTagText != null Text(
? Row( moduleAuthor.name!,
mainAxisSize: MainAxisSize.min, maxLines: 1,
children: [ overflow: .ellipsis,
Container( style: TextStyle(
padding: const EdgeInsets.symmetric( color:
horizontal: 4, moduleAuthor.vip != null &&
vertical: 2, moduleAuthor.vip!.status > 0 &&
), moduleAuthor.vip!.type == 2
decoration: BoxDecoration( ? theme.colorScheme.vipColor
borderRadius: const BorderRadius.all( : theme.colorScheme.onSurface,
Radius.circular(4), fontSize: theme.textTheme.titleSmall!.fontSize,
),
border: Border.all(
width: 1.25,
color: theme.colorScheme.primary,
),
),
child: Text(
moduleTagText,
style: TextStyle(
height: 1,
fontSize: 12,
color: theme.colorScheme.primary,
),
strutStyle: const StrutStyle(
height: 1,
leading: 0,
fontSize: 12,
),
),
), ),
_moreWidget(context), ),
], ?pubTs,
) ],
: moduleAuthor.decorate != null ),
? Row( ),
mainAxisSize: MainAxisSize.min, ],
children: [
Stack(
clipBehavior: Clip.none,
alignment: Alignment.centerRight,
children: [
CachedNetworkImage(
height: 32,
memCacheHeight: 32.cacheSize(context),
imageUrl: ImageUtils.safeThumbnailUrl(
moduleAuthor.decorate!.cardUrl,
),
placeholder: (_, _) => const SizedBox.shrink(),
),
if (moduleAuthor.decorate!.fan?.numStr?.isNotEmpty ==
true)
Padding(
padding: const EdgeInsets.only(right: 32),
child: Text(
'${moduleAuthor.decorate!.fan!.numStr}',
style: TextStyle(
height: 1,
fontSize: 11,
fontFamily: 'digital_id_num',
color:
moduleAuthor.decorate!.fan?.color
?.startsWith('#') ==
true
? Utils.parseColor(
moduleAuthor.decorate!.fan!.color!,
)
: null,
),
),
),
],
),
_moreWidget(context),
],
)
: _moreWidget(context),
), ),
], ),
); );
} Widget? moreBtn = isSave
? null
Widget _moreWidget(BuildContext context) => isSave : SizedBox(
? const SizedBox.shrink() width: 32,
: SizedBox( height: 32,
width: 32, child: IconButton(
height: 32, tooltip: '更多',
child: IconButton( style: const ButtonStyle(
tooltip: '更多', padding: WidgetStatePropertyAll(EdgeInsets.zero),
style: const ButtonStyle( ),
padding: WidgetStatePropertyAll(EdgeInsets.zero), onPressed: () => morePanel(context),
icon: const Icon(Icons.more_vert_outlined, size: 18),
),
);
final moduleTagText = !isDetail ? item.modules.moduleTag?.text : null;
if (moduleTagText != null) {
header = Row(
children: [
Expanded(child: header),
Container(
padding: const .symmetric(horizontal: 4, vertical: 2),
decoration: BoxDecoration(
borderRadius: const .all(.circular(4)),
border: .all(width: 1.25, color: theme.colorScheme.primary),
),
child: Text(
moduleTagText,
style: TextStyle(
height: 1,
fontSize: 12,
color: theme.colorScheme.primary,
),
strutStyle: const StrutStyle(height: 1, leading: 0, fontSize: 12),
), ),
onPressed: () => morePanel(context),
icon: const Icon(Icons.more_vert_outlined, size: 18),
), ),
?moreBtn,
],
);
} else if (moduleAuthor.decorate != null) {
const height = 32.0;
header = Stack(
clipBehavior: .none,
children: [
Positioned(
top: 0,
right: 0,
height: height,
child: CachedNetworkImage(
height: height,
memCacheHeight: height.cacheSize(context),
imageUrl: ImageUtils.safeThumbnailUrl(
moduleAuthor.decorate!.cardUrl,
),
placeholder: (_, _) => const SizedBox.shrink(),
),
),
if (moduleAuthor.decorate!.fan?.numStr?.isNotEmpty == true)
Positioned(
top: 0,
right: height,
height: height,
child: Center(
child: Text(
moduleAuthor.decorate!.fan!.numStr!.toString(),
style: TextStyle(
height: 1,
fontSize: 11,
fontFamily: Assets.digitalNum,
color: Utils.parseColor(
moduleAuthor.decorate!.fan!.color!,
),
),
),
),
),
Padding(
padding: const .only(right: 80),
child: header,
),
],
);
if (moreBtn != null) {
header = Row(
children: [
Expanded(child: header),
moreBtn,
],
); );
}
} else if (moreBtn != null) {
header = Row(
children: [
Expanded(child: header),
moreBtn,
],
);
}
return header;
}
void morePanel(BuildContext context) { void morePanel(BuildContext context) {
String? bvid; String? bvid;
@@ -283,7 +271,7 @@ class AuthorPanel extends StatelessWidget {
children: [ children: [
InkWell( InkWell(
onTap: Get.back, onTap: Get.back,
borderRadius: StyleString.bottomSheetRadius, borderRadius: Style.bottomSheetRadius,
child: SizedBox( child: SizedBox(
height: 35, height: 35,
child: Center( child: Center(

View File

@@ -1,4 +1,5 @@
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/assets.dart';
import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart'; import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
@@ -42,7 +43,7 @@ Widget livePanelSub(
LayoutBuilder( LayoutBuilder(
builder: (context, constraints) => NetworkImgLayer( builder: (context, constraints) => NetworkImgLayer(
width: constraints.maxWidth, width: constraints.maxWidth,
height: constraints.maxWidth / StyleString.aspectRatio, height: constraints.maxWidth / Style.aspectRatio,
src: live.cover, src: live.cover,
quality: 40, quality: 40,
), ),
@@ -61,7 +62,7 @@ Widget livePanelSub(
child: Image.asset( child: Image.asset(
height: 16, height: 16,
cacheHeight: 16.cacheSize(context), cacheHeight: 16.cacheSize(context),
'assets/images/live/live.gif', Assets.livingRect,
filterQuality: FilterQuality.low, filterQuality: FilterQuality.low,
), ),
) )
@@ -90,7 +91,7 @@ Widget livePanelSub(
], ],
), ),
borderRadius: BorderRadius.vertical( borderRadius: BorderRadius.vertical(
bottom: StyleString.imgRadius, bottom: Style.imgRadius,
), ),
), ),
child: Text( child: Text(

View File

@@ -1,4 +1,5 @@
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/assets.dart';
import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart'; import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
@@ -36,7 +37,7 @@ Widget liveRcmdPanel(
LayoutBuilder( LayoutBuilder(
builder: (context, constraints) => NetworkImgLayer( builder: (context, constraints) => NetworkImgLayer(
width: constraints.maxWidth, width: constraints.maxWidth,
height: constraints.maxWidth / StyleString.aspectRatio, height: constraints.maxWidth / Style.aspectRatio,
src: liveRcmd.cover, src: liveRcmd.cover,
quality: 40, quality: 40,
), ),
@@ -55,7 +56,7 @@ Widget liveRcmdPanel(
child: Image.asset( child: Image.asset(
height: 16, height: 16,
cacheHeight: 16.cacheSize(context), cacheHeight: 16.cacheSize(context),
'assets/images/live/live.gif', Assets.livingRect,
filterQuality: FilterQuality.low, filterQuality: FilterQuality.low,
), ),
) )
@@ -85,7 +86,7 @@ Widget liveRcmdPanel(
], ],
), ),
borderRadius: BorderRadius.vertical( borderRadius: BorderRadius.vertical(
bottom: StyleString.imgRadius, bottom: Style.imgRadius,
), ),
), ),
child: Text( child: Text(

View File

@@ -1,4 +1,4 @@
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/grpc/bilibili/app/listener/v1.pbenum.dart' import 'package:PiliPlus/grpc/bilibili/app/listener/v1.pbenum.dart'
@@ -114,9 +114,9 @@ Widget module(
: theme.colorScheme.surface, : theme.colorScheme.surface,
shape: floor == 1 shape: floor == 1
? null ? null
: const RoundedRectangleBorder(borderRadius: StyleString.mdRadius), : const RoundedRectangleBorder(borderRadius: Style.mdRadius),
child: InkWell( child: InkWell(
borderRadius: floor == 1 ? null : StyleString.mdRadius, borderRadius: floor == 1 ? null : Style.mdRadius,
onTap: () { onTap: () {
try { try {
String url = common.jumpUrl!; String url = common.jumpUrl!;
@@ -179,7 +179,7 @@ Widget module(
); );
case 'DYNAMIC_TYPE_MUSIC': case 'DYNAMIC_TYPE_MUSIC':
final music = major!.music!; final music = major!.music!;
final borderRadius = floor == 1 ? null : StyleString.mdRadius; final borderRadius = floor == 1 ? null : Style.mdRadius;
final Color bgColor = floor == 1 final Color bgColor = floor == 1
? theme.dividerColor.withValues(alpha: 0.08) ? theme.dividerColor.withValues(alpha: 0.08)
: theme.colorScheme.surface; : theme.colorScheme.surface;

View File

@@ -1,3 +1,4 @@
import 'package:PiliPlus/common/assets.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/models/common/dynamic/up_panel_position.dart'; import 'package:PiliPlus/models/common/dynamic/up_panel_position.dart';
import 'package:PiliPlus/models/common/image_type.dart'; import 'package:PiliPlus/models/common/image_type.dart';
@@ -162,7 +163,7 @@ class _UpPanelState extends State<UpPanel> {
width: 38, width: 38,
height: 38, height: 38,
cacheWidth: 38.cacheSize(context), cacheWidth: 38.cacheSize(context),
'assets/images/logo/logo.png', Assets.logo,
), ),
); );
} else { } else {

View File

@@ -1,5 +1,6 @@
// 视频or合集 // 视频or合集
import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/assets.dart';
import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart'; import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
@@ -53,7 +54,7 @@ Widget videoSeasonWidget(
LayoutBuilder( LayoutBuilder(
builder: (context, constraints) => NetworkImgLayer( builder: (context, constraints) => NetworkImgLayer(
width: constraints.maxWidth, width: constraints.maxWidth,
height: constraints.maxWidth / StyleString.aspectRatio, height: constraints.maxWidth / Style.aspectRatio,
src: cover, src: cover,
quality: 40, quality: 40,
), ),
@@ -88,7 +89,7 @@ Widget videoSeasonWidget(
], ],
), ),
borderRadius: BorderRadius.vertical( borderRadius: BorderRadius.vertical(
bottom: StyleString.imgRadius, bottom: Style.imgRadius,
), ),
), ),
child: DefaultTextStyle.merge( child: DefaultTextStyle.merge(
@@ -122,7 +123,7 @@ Widget videoSeasonWidget(
], ],
const Spacer(), const Spacer(),
Image.asset( Image.asset(
'assets/images/play.png', Assets.play,
width: 50, width: 50,
height: 50, height: 50,
cacheHeight: 50.cacheSize(context), cacheHeight: 50.cacheSize(context),

Some files were not shown because too many files have changed in this diff Show More