mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-08-05 18:20:10 +08:00
@@ -1,4 +1,4 @@
|
|||||||
class BuildConfig {
|
abstract final class BuildConfig {
|
||||||
static const int versionCode = int.fromEnvironment(
|
static const int versionCode = int.fromEnvironment(
|
||||||
'pili.code',
|
'pili.code',
|
||||||
defaultValue: 1,
|
defaultValue: 1,
|
||||||
|
|||||||
42
lib/common/widgets/back_detector.dart
Normal file
42
lib/common/widgets/back_detector.dart
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import 'package:flutter/gestures.dart' show kBackMouseButton;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart' show KeyDownEvent;
|
||||||
|
|
||||||
|
class BackDetector extends StatelessWidget {
|
||||||
|
const BackDetector({
|
||||||
|
super.key,
|
||||||
|
required this.onBack,
|
||||||
|
required this.child,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
final VoidCallback onBack;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Focus(
|
||||||
|
canRequestFocus: false,
|
||||||
|
onKeyEvent: _onKeyEvent,
|
||||||
|
child: Listener(
|
||||||
|
behavior: .translucent,
|
||||||
|
onPointerDown: _onPointerDown,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyEventResult _onKeyEvent(FocusNode node, KeyEvent event) {
|
||||||
|
if (event.logicalKey == .escape && event is KeyDownEvent) {
|
||||||
|
onBack();
|
||||||
|
return .handled;
|
||||||
|
}
|
||||||
|
return .ignored;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onPointerDown(PointerDownEvent event) {
|
||||||
|
if (event.buttons == kBackMouseButton) {
|
||||||
|
onBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,6 +34,9 @@ class _CustomTooltipState extends State<CustomTooltip> {
|
|||||||
final OverlayPortalController _overlayController = OverlayPortalController();
|
final OverlayPortalController _overlayController = OverlayPortalController();
|
||||||
|
|
||||||
LongPressGestureRecognizer? _longPressRecognizer;
|
LongPressGestureRecognizer? _longPressRecognizer;
|
||||||
|
LongPressGestureRecognizer get longPressRecognizer =>
|
||||||
|
_longPressRecognizer ??= LongPressGestureRecognizer()
|
||||||
|
..onLongPress = _scheduleShowTooltip;
|
||||||
|
|
||||||
void _scheduleShowTooltip() {
|
void _scheduleShowTooltip() {
|
||||||
_overlayController.show();
|
_overlayController.show();
|
||||||
@@ -45,9 +48,7 @@ class _CustomTooltipState extends State<CustomTooltip> {
|
|||||||
|
|
||||||
void _handlePointerDown(PointerDownEvent event) {
|
void _handlePointerDown(PointerDownEvent event) {
|
||||||
assert(mounted);
|
assert(mounted);
|
||||||
(_longPressRecognizer ??= LongPressGestureRecognizer(
|
longPressRecognizer.addPointer(event);
|
||||||
debugOwner: this,
|
|
||||||
)..onLongPress = _scheduleShowTooltip).addPointer(event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCustomTooltipOverlay(BuildContext context) {
|
Widget _buildCustomTooltipOverlay(BuildContext context) {
|
||||||
@@ -80,7 +81,7 @@ class _CustomTooltipState extends State<CustomTooltip> {
|
|||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_longPressRecognizer
|
_longPressRecognizer
|
||||||
?..onLongPressCancel = null
|
?..onLongPress = null
|
||||||
..dispose();
|
..dispose();
|
||||||
_longPressRecognizer = null;
|
_longPressRecognizer = null;
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
|||||||
44
lib/common/widgets/flutter/pop_scope.dart
Normal file
44
lib/common/widgets/flutter/pop_scope.dart
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
abstract class PopScopeState<T extends StatefulWidget> extends State<T>
|
||||||
|
implements PopEntry<T> {
|
||||||
|
ModalRoute<dynamic>? _route;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onPopInvoked(bool didPop) {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
late final ValueNotifier<bool> canPopNotifier;
|
||||||
|
|
||||||
|
void initCanPopNotifier() {
|
||||||
|
canPopNotifier = ValueNotifier<bool>(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
initCanPopNotifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
final ModalRoute<dynamic>? nextRoute = ModalRoute.of(context);
|
||||||
|
if (nextRoute != _route) {
|
||||||
|
_route?.unregisterPopEntry(this);
|
||||||
|
_route = nextRoute;
|
||||||
|
_route?.registerPopEntry(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_route?.unregisterPopEntry(this);
|
||||||
|
canPopNotifier.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import 'package:flutter/gestures.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class MouseBackDetector extends StatelessWidget {
|
|
||||||
const MouseBackDetector({
|
|
||||||
super.key,
|
|
||||||
required this.onTapDown,
|
|
||||||
required this.child,
|
|
||||||
});
|
|
||||||
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
final VoidCallback onTapDown;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Listener(
|
|
||||||
onPointerDown: (event) {
|
|
||||||
if (event.buttons == kBackMouseButton) {
|
|
||||||
onTapDown();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
behavior: HitTestBehavior.translucent,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
25
lib/common/widgets/scroll_behavior.dart
Normal file
25
lib/common/widgets/scroll_behavior.dart
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import 'package:flutter/gestures.dart' show PointerDeviceKind;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class CustomScrollBehavior extends MaterialScrollBehavior {
|
||||||
|
const CustomScrollBehavior(this.dragDevices);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildScrollbar(
|
||||||
|
BuildContext context,
|
||||||
|
Widget child,
|
||||||
|
ScrollableDetails details,
|
||||||
|
) => child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final Set<PointerDeviceKind> dragDevices;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Set<PointerDeviceKind> desktopDragDevices = <PointerDeviceKind>{
|
||||||
|
PointerDeviceKind.touch,
|
||||||
|
PointerDeviceKind.stylus,
|
||||||
|
PointerDeviceKind.invertedStylus,
|
||||||
|
PointerDeviceKind.trackpad,
|
||||||
|
PointerDeviceKind.unknown,
|
||||||
|
PointerDeviceKind.mouse,
|
||||||
|
};
|
||||||
140
lib/main.dart
140
lib/main.dart
@@ -2,9 +2,10 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:PiliPlus/build_config.dart';
|
import 'package:PiliPlus/build_config.dart';
|
||||||
import 'package:PiliPlus/common/constants.dart';
|
import 'package:PiliPlus/common/constants.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/back_detector.dart';
|
||||||
import 'package:PiliPlus/common/widgets/custom_toast.dart';
|
import 'package:PiliPlus/common/widgets/custom_toast.dart';
|
||||||
import 'package:PiliPlus/common/widgets/mouse_back.dart';
|
|
||||||
import 'package:PiliPlus/common/widgets/scale_app.dart';
|
import 'package:PiliPlus/common/widgets/scale_app.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/scroll_behavior.dart';
|
||||||
import 'package:PiliPlus/http/init.dart';
|
import 'package:PiliPlus/http/init.dart';
|
||||||
import 'package:PiliPlus/models/common/theme/theme_color_type.dart';
|
import 'package:PiliPlus/models/common/theme/theme_color_type.dart';
|
||||||
import 'package:PiliPlus/router/app_pages.dart';
|
import 'package:PiliPlus/router/app_pages.dart';
|
||||||
@@ -30,7 +31,6 @@ import 'package:PiliPlus/utils/utils.dart';
|
|||||||
import 'package:catcher_2/catcher_2.dart';
|
import 'package:catcher_2/catcher_2.dart';
|
||||||
import 'package:dynamic_color/dynamic_color.dart';
|
import 'package:dynamic_color/dynamic_color.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/gestures.dart' show PointerDeviceKind;
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
||||||
@@ -45,20 +45,7 @@ import 'package:window_manager/window_manager.dart' hide calcWindowPosition;
|
|||||||
|
|
||||||
WebViewEnvironment? webViewEnvironment;
|
WebViewEnvironment? webViewEnvironment;
|
||||||
|
|
||||||
void main() async {
|
Future<void> _initDownPath() async {
|
||||||
ScaledWidgetsFlutterBinding.ensureInitialized();
|
|
||||||
MediaKit.ensureInitialized();
|
|
||||||
tmpDirPath = (await getTemporaryDirectory()).path;
|
|
||||||
appSupportDirPath = (await getApplicationSupportDirectory()).path;
|
|
||||||
try {
|
|
||||||
await GStorage.init();
|
|
||||||
} catch (e) {
|
|
||||||
await Utils.copyText(e.toString());
|
|
||||||
if (kDebugMode) debugPrint('GStorage init error: $e');
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
ScaledWidgetsFlutterBinding.instance.setScaleFactor(Pref.uiScale);
|
|
||||||
|
|
||||||
if (PlatformUtils.isDesktop) {
|
if (PlatformUtils.isDesktop) {
|
||||||
final customDownPath = Pref.downloadPath;
|
final customDownPath = Pref.downloadPath;
|
||||||
if (customDownPath != null && customDownPath.isNotEmpty) {
|
if (customDownPath != null && customDownPath.isNotEmpty) {
|
||||||
@@ -86,6 +73,29 @@ void main() async {
|
|||||||
} else {
|
} else {
|
||||||
downloadPath = defDownloadPath;
|
downloadPath = defDownloadPath;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _initTmpPath() async {
|
||||||
|
tmpDirPath = (await getTemporaryDirectory()).path;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _initAppPath() async {
|
||||||
|
appSupportDirPath = (await getApplicationSupportDirectory()).path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
ScaledWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
MediaKit.ensureInitialized();
|
||||||
|
await _initAppPath();
|
||||||
|
try {
|
||||||
|
await GStorage.init();
|
||||||
|
} catch (e) {
|
||||||
|
await Utils.copyText(e.toString());
|
||||||
|
if (kDebugMode) debugPrint('GStorage init error: $e');
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
ScaledWidgetsFlutterBinding.instance.setScaleFactor(Pref.uiScale);
|
||||||
|
await Future.wait([_initDownPath(), _initTmpPath()]);
|
||||||
Get
|
Get
|
||||||
..lazyPut(AccountService.new)
|
..lazyPut(AccountService.new)
|
||||||
..lazyPut(DownloadService.new);
|
..lazyPut(DownloadService.new);
|
||||||
@@ -106,9 +116,7 @@ void main() async {
|
|||||||
),
|
),
|
||||||
setupServiceLocator(),
|
setupServiceLocator(),
|
||||||
]);
|
]);
|
||||||
}
|
} else if (Platform.isWindows) {
|
||||||
|
|
||||||
if (Platform.isWindows) {
|
|
||||||
if (await WebViewEnvironment.getAvailableVersion() != null) {
|
if (await WebViewEnvironment.getAvailableVersion() != null) {
|
||||||
webViewEnvironment = await WebViewEnvironment.create(
|
webViewEnvironment = await WebViewEnvironment.create(
|
||||||
settings: WebViewEnvironmentSettings(
|
settings: WebViewEnvironmentSettings(
|
||||||
@@ -279,66 +287,49 @@ class MyApp extends StatelessWidget {
|
|||||||
builder: FlutterSmartDialog.init(
|
builder: FlutterSmartDialog.init(
|
||||||
toastBuilder: (msg) => CustomToast(msg: msg),
|
toastBuilder: (msg) => CustomToast(msg: msg),
|
||||||
loadingBuilder: (msg) => LoadingWidget(msg: msg),
|
loadingBuilder: (msg) => LoadingWidget(msg: msg),
|
||||||
builder: (context, child) {
|
builder: _builder,
|
||||||
final uiScale = Pref.uiScale;
|
|
||||||
final mediaQuery = MediaQuery.of(context);
|
|
||||||
final textScaler = TextScaler.linear(Pref.defaultTextScale);
|
|
||||||
if (uiScale != 1.0) {
|
|
||||||
child = MediaQuery(
|
|
||||||
data: mediaQuery.copyWith(
|
|
||||||
textScaler: textScaler,
|
|
||||||
size: mediaQuery.size / uiScale,
|
|
||||||
padding: mediaQuery.padding / uiScale,
|
|
||||||
viewInsets: mediaQuery.viewInsets / uiScale,
|
|
||||||
viewPadding: mediaQuery.viewPadding / uiScale,
|
|
||||||
devicePixelRatio: mediaQuery.devicePixelRatio * uiScale,
|
|
||||||
),
|
|
||||||
child: child!,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
child = MediaQuery(
|
|
||||||
data: mediaQuery.copyWith(textScaler: textScaler),
|
|
||||||
child: child!,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (PlatformUtils.isDesktop) {
|
|
||||||
return Focus(
|
|
||||||
canRequestFocus: false,
|
|
||||||
onKeyEvent: (_, event) {
|
|
||||||
if (event.logicalKey == LogicalKeyboardKey.escape &&
|
|
||||||
event is KeyDownEvent) {
|
|
||||||
_onBack();
|
|
||||||
return KeyEventResult.handled;
|
|
||||||
}
|
|
||||||
return KeyEventResult.ignored;
|
|
||||||
},
|
|
||||||
child: MouseBackDetector(
|
|
||||||
onTapDown: _onBack,
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return child;
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
navigatorObservers: [
|
navigatorObservers: [
|
||||||
PageUtils.routeObserver,
|
PageUtils.routeObserver,
|
||||||
FlutterSmartDialog.observer,
|
FlutterSmartDialog.observer,
|
||||||
],
|
],
|
||||||
scrollBehavior: const MaterialScrollBehavior().copyWith(
|
scrollBehavior: PlatformUtils.isDesktop
|
||||||
scrollbars: false,
|
? const CustomScrollBehavior(desktopDragDevices)
|
||||||
dragDevices: {
|
: null,
|
||||||
PointerDeviceKind.touch,
|
|
||||||
PointerDeviceKind.stylus,
|
|
||||||
PointerDeviceKind.invertedStylus,
|
|
||||||
PointerDeviceKind.trackpad,
|
|
||||||
PointerDeviceKind.unknown,
|
|
||||||
if (PlatformUtils.isDesktop) PointerDeviceKind.mouse,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Widget _builder(BuildContext context, Widget? child) {
|
||||||
|
final uiScale = Pref.uiScale;
|
||||||
|
final mediaQuery = MediaQuery.of(context);
|
||||||
|
final textScaler = TextScaler.linear(Pref.defaultTextScale);
|
||||||
|
if (uiScale != 1.0) {
|
||||||
|
child = MediaQuery(
|
||||||
|
data: mediaQuery.copyWith(
|
||||||
|
textScaler: textScaler,
|
||||||
|
size: mediaQuery.size / uiScale,
|
||||||
|
padding: mediaQuery.padding / uiScale,
|
||||||
|
viewInsets: mediaQuery.viewInsets / uiScale,
|
||||||
|
viewPadding: mediaQuery.viewPadding / uiScale,
|
||||||
|
devicePixelRatio: mediaQuery.devicePixelRatio * uiScale,
|
||||||
|
),
|
||||||
|
child: child!,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
child = MediaQuery(
|
||||||
|
data: mediaQuery.copyWith(textScaler: textScaler),
|
||||||
|
child: child!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (PlatformUtils.isDesktop) {
|
||||||
|
return BackDetector(
|
||||||
|
onBack: _onBack,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
/// from [DynamicColorBuilderState.initPlatformState]
|
/// from [DynamicColorBuilderState.initPlatformState]
|
||||||
static Future<bool> initPlatformState() async {
|
static Future<bool> initPlatformState() async {
|
||||||
if (_light != null || _dark != null) return true;
|
if (_light != null || _dark != null) return true;
|
||||||
@@ -388,9 +379,10 @@ class MyApp extends StatelessWidget {
|
|||||||
class _CustomHttpOverrides extends HttpOverrides {
|
class _CustomHttpOverrides extends HttpOverrides {
|
||||||
@override
|
@override
|
||||||
HttpClient createHttpClient(SecurityContext? context) {
|
HttpClient createHttpClient(SecurityContext? context) {
|
||||||
final client = super.createHttpClient(context)
|
final client = super.createHttpClient(context);
|
||||||
// ..maxConnectionsPerHost = 32
|
// ..maxConnectionsPerHost = 32
|
||||||
..idleTimeout = const Duration(seconds: 15);
|
/// The default value is 15 seconds.
|
||||||
|
// ..idleTimeout = const Duration(seconds: 15);
|
||||||
if (kDebugMode || Pref.badCertificateCallback) {
|
if (kDebugMode || Pref.badCertificateCallback) {
|
||||||
client.badCertificateCallback = (cert, host, port) => true;
|
client.badCertificateCallback = (cert, host, port) => true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:PiliPlus/models/model_owner.dart';
|
import 'package:PiliPlus/models/model_owner.dart';
|
||||||
import 'package:PiliPlus/models/model_rec_video_item.dart';
|
import 'package:PiliPlus/models/model_rec_video_item.dart';
|
||||||
import 'package:PiliPlus/models/model_video.dart';
|
import 'package:PiliPlus/models/model_video.dart';
|
||||||
|
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';
|
||||||
|
|
||||||
// 稍后再看, 排行榜等网页返回也使用该类
|
// 稍后再看, 排行榜等网页返回也使用该类
|
||||||
|
|||||||
@@ -40,15 +40,3 @@ class PlayStat extends BaseStat {
|
|||||||
danmu = json['danmaku'];
|
danmu = json['danmaku'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Dimension {
|
|
||||||
int? width;
|
|
||||||
int? height;
|
|
||||||
int? rotate;
|
|
||||||
|
|
||||||
Dimension.fromJson(Map<String, dynamic> json) {
|
|
||||||
width = json["width"];
|
|
||||||
height = json["height"];
|
|
||||||
rotate = json["rotate"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,12 +17,8 @@ class FavFolderInfo {
|
|||||||
int? favState;
|
int? favState;
|
||||||
int mediaCount;
|
int mediaCount;
|
||||||
int? viewCount;
|
int? viewCount;
|
||||||
int? vt;
|
|
||||||
bool? isTop;
|
bool? isTop;
|
||||||
dynamic recentFav;
|
|
||||||
int? playSwitch;
|
|
||||||
int? type;
|
int? type;
|
||||||
String? link;
|
|
||||||
String? bvid;
|
String? bvid;
|
||||||
|
|
||||||
FavFolderInfo({
|
FavFolderInfo({
|
||||||
@@ -42,12 +38,8 @@ class FavFolderInfo {
|
|||||||
this.favState,
|
this.favState,
|
||||||
this.mediaCount = 0,
|
this.mediaCount = 0,
|
||||||
this.viewCount,
|
this.viewCount,
|
||||||
this.vt,
|
|
||||||
this.isTop,
|
this.isTop,
|
||||||
this.recentFav,
|
|
||||||
this.playSwitch,
|
|
||||||
this.type,
|
this.type,
|
||||||
this.link,
|
|
||||||
this.bvid,
|
this.bvid,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -70,12 +62,8 @@ class FavFolderInfo {
|
|||||||
favState: json['fav_state'] as int?,
|
favState: json['fav_state'] as int?,
|
||||||
mediaCount: json['media_count'] as int? ?? 0,
|
mediaCount: json['media_count'] as int? ?? 0,
|
||||||
viewCount: json['view_count'] as int?,
|
viewCount: json['view_count'] as int?,
|
||||||
vt: json['vt'] as int?,
|
|
||||||
isTop: json['is_top'] as bool?,
|
isTop: json['is_top'] as bool?,
|
||||||
recentFav: json['recent_fav'] as dynamic,
|
|
||||||
playSwitch: json['play_switch'] as int?,
|
|
||||||
type: json['type'] as int?,
|
type: json['type'] as int?,
|
||||||
link: json['link'] as String?,
|
|
||||||
bvid: json['bvid'] as String?,
|
bvid: json['bvid'] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
class Dimension {
|
|
||||||
int? width;
|
|
||||||
int? height;
|
|
||||||
int? rotate;
|
|
||||||
|
|
||||||
Dimension({this.width, this.height, this.rotate});
|
|
||||||
|
|
||||||
factory Dimension.fromJson(Map<String, dynamic> json) => Dimension(
|
|
||||||
width: json['width'] as int?,
|
|
||||||
height: json['height'] as int?,
|
|
||||||
rotate: json['rotate'] as int?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:PiliPlus/models_new/media_list/dimension.dart';
|
import 'package:PiliPlus/models_new/video/video_detail/dimension.dart';
|
||||||
|
|
||||||
class OgvInfo {
|
class OgvInfo {
|
||||||
int? epid;
|
int? epid;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:PiliPlus/models_new/media_list/dimension.dart';
|
import 'package:PiliPlus/models_new/video/video_detail/dimension.dart';
|
||||||
|
|
||||||
class Page {
|
class Page {
|
||||||
int? id;
|
int? id;
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
class Dimension {
|
|
||||||
int? height;
|
|
||||||
int? rotate;
|
|
||||||
int? width;
|
|
||||||
|
|
||||||
Dimension({this.height, this.rotate, this.width});
|
|
||||||
|
|
||||||
factory Dimension.fromJson(Map<String, dynamic> json) => Dimension(
|
|
||||||
height: json['height'] as int?,
|
|
||||||
rotate: json['rotate'] as int?,
|
|
||||||
width: json['width'] as int?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:PiliPlus/models_new/pgc/pgc_info_model/badge_info.dart';
|
import 'package:PiliPlus/models_new/pgc/pgc_info_model/badge_info.dart';
|
||||||
import 'package:PiliPlus/models_new/pgc/pgc_info_model/dimension.dart';
|
|
||||||
import 'package:PiliPlus/models_new/pgc/pgc_info_model/rights.dart';
|
import 'package:PiliPlus/models_new/pgc/pgc_info_model/rights.dart';
|
||||||
import 'package:PiliPlus/models_new/pgc/pgc_info_model/skip.dart';
|
import 'package:PiliPlus/models_new/pgc/pgc_info_model/skip.dart';
|
||||||
|
import 'package:PiliPlus/models_new/video/video_detail/dimension.dart';
|
||||||
import 'package:PiliPlus/models_new/video/video_detail/episode.dart'
|
import 'package:PiliPlus/models_new/video/video_detail/episode.dart'
|
||||||
show BaseEpisodeItem;
|
show BaseEpisodeItem;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
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_new/video/video_detail/dimension.dart';
|
||||||
import 'package:PiliPlus/models_new/video/video_detail/rights.dart';
|
import 'package:PiliPlus/models_new/video/video_detail/rights.dart';
|
||||||
import 'package:PiliPlus/models_new/video/video_detail/stat.dart';
|
import 'package:PiliPlus/models_new/video/video_detail/stat.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
class Dimension {
|
class Dimension {
|
||||||
int? width;
|
int? width;
|
||||||
int? height;
|
int? height;
|
||||||
int? rotate;
|
|
||||||
|
|
||||||
Dimension({this.width, this.height, this.rotate});
|
Dimension({this.width, this.height});
|
||||||
|
|
||||||
factory Dimension.fromJson(Map<String, dynamic> json) => Dimension(
|
factory Dimension.fromJson(Map<String, dynamic> json) => Dimension(
|
||||||
width: json['width'] as int?,
|
width: json['width'] as int?,
|
||||||
height: json['height'] as int?,
|
height: json['height'] as int?,
|
||||||
rotate: json['rotate'] as int?,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ class Part extends BaseEpisodeItem {
|
|||||||
String? part;
|
String? part;
|
||||||
int? duration;
|
int? duration;
|
||||||
String? vid;
|
String? vid;
|
||||||
String? weblink;
|
|
||||||
Dimension? dimension;
|
Dimension? dimension;
|
||||||
int? ctime;
|
int? ctime;
|
||||||
String? firstFrame;
|
String? firstFrame;
|
||||||
@@ -19,7 +18,6 @@ class Part extends BaseEpisodeItem {
|
|||||||
this.part,
|
this.part,
|
||||||
this.duration,
|
this.duration,
|
||||||
this.vid,
|
this.vid,
|
||||||
this.weblink,
|
|
||||||
this.dimension,
|
this.dimension,
|
||||||
this.ctime,
|
this.ctime,
|
||||||
this.firstFrame,
|
this.firstFrame,
|
||||||
@@ -33,7 +31,6 @@ class Part extends BaseEpisodeItem {
|
|||||||
part: json['part'] as String?,
|
part: json['part'] as String?,
|
||||||
duration: json['duration'] as int?,
|
duration: json['duration'] as int?,
|
||||||
vid: json['vid'] as String?,
|
vid: json['vid'] as String?,
|
||||||
weblink: json['weblink'] as String?,
|
|
||||||
dimension: json['dimension'] == null
|
dimension: json['dimension'] == null
|
||||||
? null
|
? null
|
||||||
: Dimension.fromJson(json['dimension'] as Map<String, dynamic>),
|
: Dimension.fromJson(json['dimension'] as Map<String, dynamic>),
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
class Dimension {
|
|
||||||
int? width;
|
|
||||||
int? height;
|
|
||||||
int? rotate;
|
|
||||||
String? sar;
|
|
||||||
|
|
||||||
Dimension({this.width, this.height, this.rotate, this.sar});
|
|
||||||
|
|
||||||
factory Dimension.fromJson(Map<String, dynamic> json) => Dimension(
|
|
||||||
width: json['width'] as int?,
|
|
||||||
height: json['height'] as int?,
|
|
||||||
rotate: json['rotate'] as int?,
|
|
||||||
sar: json['sar'] as String?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:PiliPlus/models_new/video/video_stein_edgeinfo/dimension.dart';
|
import 'package:PiliPlus/models_new/video/video_detail/dimension.dart';
|
||||||
import 'package:PiliPlus/models_new/video/video_stein_edgeinfo/question.dart';
|
import 'package:PiliPlus/models_new/video/video_stein_edgeinfo/question.dart';
|
||||||
import 'package:PiliPlus/models_new/video/video_stein_edgeinfo/skin.dart';
|
import 'package:PiliPlus/models_new/video/video_stein_edgeinfo/skin.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -59,10 +59,12 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> getCacheSize() async {
|
void getCacheSize() {
|
||||||
cacheSize.value = CacheManager.formatSize(
|
CacheManager.loadApplicationCache().then((res) {
|
||||||
await CacheManager.loadApplicationCache(),
|
if (mounted) {
|
||||||
);
|
cacheSize.value = CacheManager.formatSize(res);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showDialog() => showDialog(
|
void _showDialog() => showDialog(
|
||||||
@@ -103,20 +105,18 @@ class _AboutPageState extends State<AboutPage> {
|
|||||||
children: [
|
children: [
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_pressCount++;
|
if (++_pressCount == 5) {
|
||||||
if (_pressCount == 5) {
|
|
||||||
_pressCount = 0;
|
_pressCount = 0;
|
||||||
_showDialog();
|
_showDialog();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSecondaryTap: PlatformUtils.isDesktop ? _showDialog : null,
|
onSecondaryTap: PlatformUtils.isDesktop ? _showDialog : null,
|
||||||
child: ExcludeSemantics(
|
child: Image.asset(
|
||||||
child: Image.asset(
|
width: 150,
|
||||||
width: 150,
|
height: 150,
|
||||||
height: 150,
|
excludeFromSemantics: true,
|
||||||
cacheWidth: 150.cacheSize(context),
|
cacheWidth: 150.cacheSize(context),
|
||||||
'assets/images/logo/logo.png',
|
'assets/images/logo/logo.png',
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
|
|||||||
@@ -57,11 +57,16 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
|
|||||||
int get limit => widget.imageLengthLimit ?? 9;
|
int get limit => widget.imageLengthLimit ?? 9;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
late final RichTextEditingController editController =
|
late final RichTextEditingController editController;
|
||||||
RichTextEditingController(
|
|
||||||
items: widget.items,
|
@override
|
||||||
onMention: onMention,
|
void initState() {
|
||||||
);
|
super.initState();
|
||||||
|
editController = RichTextEditingController(
|
||||||
|
items: widget.items,
|
||||||
|
onMention: onMention,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initPubState() {
|
void initPubState() {
|
||||||
|
|||||||
@@ -127,8 +127,10 @@ class _FavDetailPageState extends State<FavDetailPage> with GridMixin {
|
|||||||
|
|
||||||
Widget _buildHeader(bool enableMultiSelect, ThemeData theme) {
|
Widget _buildHeader(bool enableMultiSelect, ThemeData theme) {
|
||||||
return SliverAppBar.medium(
|
return SliverAppBar.medium(
|
||||||
|
leadingWidth: enableMultiSelect ? 125 : null,
|
||||||
leading: enableMultiSelect
|
leading: enableMultiSelect
|
||||||
? Row(
|
? Row(
|
||||||
|
mainAxisSize: .min,
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: '取消',
|
tooltip: '取消',
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import 'package:PiliPlus/common/constants.dart';
|
import 'package:PiliPlus/common/constants.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/scroll_physics.dart';
|
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
||||||
import 'package:PiliPlus/models/common/dynamic/dynamic_badge_mode.dart';
|
|
||||||
import 'package:PiliPlus/models/common/image_type.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:PiliPlus/pages/mine/controller.dart';
|
import 'package:PiliPlus/pages/mine/controller.dart';
|
||||||
@@ -77,79 +75,18 @@ class _HomePageState extends State<HomePage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget searchBarAndUser(ThemeData theme) {
|
Widget customAppBar(ThemeData theme) {
|
||||||
return Row(
|
const height = 52.0;
|
||||||
|
const padding = EdgeInsets.fromLTRB(14, 6, 14, 0);
|
||||||
|
final child = Row(
|
||||||
children: [
|
children: [
|
||||||
searchBar(theme),
|
searchBar(theme),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Obx(
|
msgBadge(_mainController),
|
||||||
() => _homeController.accountService.isLogin.value
|
|
||||||
? msgBadge(_mainController)
|
|
||||||
: const SizedBox.shrink(),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Semantics(
|
userAvatar(theme: theme, mainController: _mainController),
|
||||||
label: "我的",
|
|
||||||
child: Obx(
|
|
||||||
() => _homeController.accountService.isLogin.value
|
|
||||||
? Stack(
|
|
||||||
clipBehavior: Clip.none,
|
|
||||||
children: [
|
|
||||||
NetworkImgLayer(
|
|
||||||
type: ImageType.avatar,
|
|
||||||
width: 34,
|
|
||||||
height: 34,
|
|
||||||
src: _homeController.accountService.face.value,
|
|
||||||
),
|
|
||||||
Positioned.fill(
|
|
||||||
child: Material(
|
|
||||||
type: MaterialType.transparency,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: _mainController.toMinePage,
|
|
||||||
splashColor: theme.colorScheme.primaryContainer
|
|
||||||
.withValues(alpha: 0.3),
|
|
||||||
customBorder: const CircleBorder(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
right: -6,
|
|
||||||
bottom: -6,
|
|
||||||
child: Obx(
|
|
||||||
() => MineController.anonymity.value
|
|
||||||
? IgnorePointer(
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(2),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color:
|
|
||||||
theme.colorScheme.secondaryContainer,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
size: 16,
|
|
||||||
MdiIcons.incognito,
|
|
||||||
color: theme
|
|
||||||
.colorScheme
|
|
||||||
.onSecondaryContainer,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: const SizedBox.shrink(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: defaultUser(
|
|
||||||
theme: theme,
|
|
||||||
onPressed: _mainController.toMinePage,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
Widget customAppBar(ThemeData theme) {
|
|
||||||
if (_homeController.searchBar case final searchBar?) {
|
if (_homeController.searchBar case final searchBar?) {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
final showSearchBar = searchBar.value;
|
final showSearchBar = searchBar.value;
|
||||||
@@ -159,39 +96,39 @@ class _HomePageState extends State<HomePage>
|
|||||||
child: AnimatedContainer(
|
child: AnimatedContainer(
|
||||||
curve: Curves.easeInOutCubicEmphasized,
|
curve: Curves.easeInOutCubicEmphasized,
|
||||||
duration: const Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
height: showSearchBar ? 52 : 0,
|
height: showSearchBar ? height : 0,
|
||||||
padding: const EdgeInsets.fromLTRB(14, 6, 14, 0),
|
padding: padding,
|
||||||
child: searchBarAndUser(theme),
|
child: child,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return Container(
|
return Container(
|
||||||
height: 52,
|
height: height,
|
||||||
padding: const EdgeInsets.fromLTRB(14, 6, 14, 0),
|
padding: padding,
|
||||||
child: searchBarAndUser(theme),
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget searchBar(ThemeData theme) {
|
Widget searchBar(ThemeData theme) {
|
||||||
|
const borderRadius = BorderRadius.all(Radius.circular(25));
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 44,
|
height: 44,
|
||||||
child: Material(
|
child: Material(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(25)),
|
borderRadius: borderRadius,
|
||||||
color: theme.colorScheme.onSecondaryContainer.withValues(alpha: 0.05),
|
color: theme.colorScheme.onSecondaryContainer.withValues(alpha: 0.05),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(25)),
|
borderRadius: borderRadius,
|
||||||
splashColor: theme.colorScheme.primaryContainer.withValues(
|
splashColor: theme.colorScheme.primaryContainer.withValues(
|
||||||
alpha: 0.3,
|
alpha: 0.3,
|
||||||
),
|
),
|
||||||
onTap: () => Get.toNamed(
|
onTap: () => Get.toNamed(
|
||||||
'/search',
|
'/search',
|
||||||
parameters: {
|
parameters: _homeController.enableSearchWord
|
||||||
if (_homeController.enableSearchWord)
|
? {'hintText': _homeController.defaultSearch.value}
|
||||||
'hintText': _homeController.defaultSearch.value,
|
: null,
|
||||||
},
|
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -222,60 +159,109 @@ class _HomePageState extends State<HomePage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget defaultUser({
|
Widget userAvatar({
|
||||||
required ThemeData theme,
|
required ThemeData theme,
|
||||||
required VoidCallback onPressed,
|
required MainController mainController,
|
||||||
}) {
|
}) {
|
||||||
return SizedBox(
|
return Semantics(
|
||||||
width: 38,
|
label: "我的",
|
||||||
height: 38,
|
child: Obx(
|
||||||
child: IconButton(
|
() {
|
||||||
tooltip: '点击登录',
|
if (mainController.accountService.isLogin.value) {
|
||||||
style: ButtonStyle(
|
return Stack(
|
||||||
padding: const WidgetStatePropertyAll(EdgeInsets.zero),
|
clipBehavior: .none,
|
||||||
backgroundColor: WidgetStatePropertyAll(
|
children: [
|
||||||
theme.colorScheme.onInverseSurface,
|
NetworkImgLayer(
|
||||||
),
|
type: .avatar,
|
||||||
),
|
width: 34,
|
||||||
onPressed: onPressed,
|
height: 34,
|
||||||
icon: Icon(
|
src: mainController.accountService.face.value,
|
||||||
Icons.person_rounded,
|
),
|
||||||
size: 22,
|
Positioned.fill(
|
||||||
color: theme.colorScheme.primary,
|
child: Material(
|
||||||
),
|
type: .transparency,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: mainController.toMinePage,
|
||||||
|
splashColor: theme.colorScheme.primaryContainer.withValues(
|
||||||
|
alpha: 0.3,
|
||||||
|
),
|
||||||
|
customBorder: const CircleBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
right: -4,
|
||||||
|
bottom: -4,
|
||||||
|
child: Obx(
|
||||||
|
() => MineController.anonymity.value
|
||||||
|
? IgnorePointer(
|
||||||
|
child: Container(
|
||||||
|
padding: const .all(2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: .circle,
|
||||||
|
color: theme.colorScheme.secondaryContainer,
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
size: 14,
|
||||||
|
MdiIcons.incognito,
|
||||||
|
color: theme.colorScheme.onSecondaryContainer,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return SizedBox(
|
||||||
|
width: 38,
|
||||||
|
height: 38,
|
||||||
|
child: IconButton(
|
||||||
|
tooltip: '点击登录',
|
||||||
|
style: IconButton.styleFrom(
|
||||||
|
padding: .zero,
|
||||||
|
backgroundColor: theme.colorScheme.onInverseSurface,
|
||||||
|
),
|
||||||
|
onPressed: mainController.toMinePage,
|
||||||
|
icon: Icon(
|
||||||
|
Icons.person_rounded,
|
||||||
|
size: 22,
|
||||||
|
color: theme.colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget msgBadge(MainController mainController) {
|
Widget msgBadge(MainController mainController) {
|
||||||
void toWhisper() {
|
return Obx(
|
||||||
mainController.msgUnReadCount.value = '';
|
() {
|
||||||
mainController.lastCheckUnreadAt = DateTime.now().millisecondsSinceEpoch;
|
if (mainController.accountService.isLogin.value) {
|
||||||
Get.toNamed('/whisper');
|
final count = mainController.msgUnReadCount.value;
|
||||||
}
|
final isNumBadge = mainController.msgBadgeMode == .number;
|
||||||
|
return IconButton(
|
||||||
final msgUnReadCount = mainController.msgUnReadCount.value;
|
tooltip: '消息',
|
||||||
return GestureDetector(
|
onPressed: () {
|
||||||
onTap: toWhisper,
|
mainController
|
||||||
child: Badge(
|
..msgUnReadCount.value = ''
|
||||||
isLabelVisible:
|
..lastCheckUnreadAt = DateTime.now().millisecondsSinceEpoch;
|
||||||
mainController.msgBadgeMode != DynamicBadgeMode.hidden &&
|
Get.toNamed('/whisper');
|
||||||
msgUnReadCount.isNotEmpty,
|
},
|
||||||
alignment: mainController.msgBadgeMode == DynamicBadgeMode.number
|
icon: Badge(
|
||||||
? const Alignment(0, -0.5)
|
isLabelVisible:
|
||||||
: const Alignment(0.5, -0.5),
|
mainController.msgBadgeMode != .hidden && count.isNotEmpty,
|
||||||
label:
|
alignment: isNumBadge
|
||||||
mainController.msgBadgeMode == DynamicBadgeMode.number &&
|
? const Alignment(0.0, -0.85)
|
||||||
msgUnReadCount.isNotEmpty
|
: const Alignment(1.0, -0.85),
|
||||||
? Text(msgUnReadCount)
|
label: isNumBadge && count.isNotEmpty ? Text(count) : null,
|
||||||
: null,
|
child: const Icon(Icons.notifications_none),
|
||||||
child: IconButton(
|
),
|
||||||
tooltip: '消息',
|
);
|
||||||
onPressed: toWhisper,
|
}
|
||||||
icon: const Icon(
|
return const SizedBox.shrink();
|
||||||
Icons.notifications_none,
|
},
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -288,9 +288,14 @@ class LiveRoomController extends GetxController {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Get
|
onPressed: () {
|
||||||
..back()
|
if (plPlayerController.isDesktopPip) {
|
||||||
..back(),
|
plPlayerController.exitDesktopPip();
|
||||||
|
}
|
||||||
|
Get
|
||||||
|
..back()
|
||||||
|
..back();
|
||||||
|
},
|
||||||
child: const Text('退出'),
|
child: const Text('退出'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:PiliPlus/common/constants.dart';
|
import 'package:PiliPlus/common/constants.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/flutter/pop_scope.dart';
|
||||||
import 'package:PiliPlus/common/widgets/flutter/tabs.dart';
|
import 'package:PiliPlus/common/widgets/flutter/tabs.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/dynamic_badge_mode.dart';
|
|
||||||
import 'package:PiliPlus/models/common/image_type.dart';
|
|
||||||
import 'package:PiliPlus/models/common/nav_bar_config.dart';
|
import 'package:PiliPlus/models/common/nav_bar_config.dart';
|
||||||
import 'package:PiliPlus/pages/home/view.dart';
|
import 'package:PiliPlus/pages/home/view.dart';
|
||||||
import 'package:PiliPlus/pages/main/controller.dart';
|
import 'package:PiliPlus/pages/main/controller.dart';
|
||||||
import 'package:PiliPlus/pages/mine/controller.dart';
|
|
||||||
import 'package:PiliPlus/plugin/pl_player/controller.dart';
|
import 'package:PiliPlus/plugin/pl_player/controller.dart';
|
||||||
import 'package:PiliPlus/plugin/pl_player/models/play_status.dart';
|
import 'package:PiliPlus/plugin/pl_player/models/play_status.dart';
|
||||||
import 'package:PiliPlus/utils/app_scheme.dart';
|
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||||
@@ -23,7 +21,6 @@ import 'package:PiliPlus/utils/utils.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
|
||||||
import 'package:tray_manager/tray_manager.dart';
|
import 'package:tray_manager/tray_manager.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
@@ -34,7 +31,7 @@ class MainApp extends StatefulWidget {
|
|||||||
State<MainApp> createState() => _MainAppState();
|
State<MainApp> createState() => _MainAppState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MainAppState extends State<MainApp>
|
class _MainAppState extends PopScopeState<MainApp>
|
||||||
with RouteAware, WidgetsBindingObserver, WindowListener, TrayListener {
|
with RouteAware, WidgetsBindingObserver, WindowListener, TrayListener {
|
||||||
final _mainController = Get.put(MainController());
|
final _mainController = Get.put(MainController());
|
||||||
late final _setting = GStorage.setting;
|
late final _setting = GStorage.setting;
|
||||||
@@ -234,7 +231,7 @@ class _MainAppState extends State<MainApp>
|
|||||||
await trayManager.setContextMenu(trayMenu);
|
await trayManager.setContextMenu(trayMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onBack() {
|
static void _onBack() {
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
Utils.channel.invokeMethod('back');
|
Utils.channel.invokeMethod('back');
|
||||||
} else {
|
} else {
|
||||||
@@ -244,13 +241,8 @@ class _MainAppState extends State<MainApp>
|
|||||||
|
|
||||||
late bool useBottomNav;
|
late bool useBottomNav;
|
||||||
|
|
||||||
@override
|
Widget? get _bottomNav {
|
||||||
Widget build(BuildContext context) {
|
return useBottomNav
|
||||||
final theme = Theme.of(context);
|
|
||||||
final padding = MediaQuery.viewPaddingOf(context);
|
|
||||||
useBottomNav =
|
|
||||||
!_mainController.useSideBar && MediaQuery.sizeOf(context).isPortrait;
|
|
||||||
Widget? bottomNav = useBottomNav
|
|
||||||
? _mainController.navigationBars.length > 1
|
? _mainController.navigationBars.length > 1
|
||||||
? _mainController.enableMYBar
|
? _mainController.enableMYBar
|
||||||
? Obx(
|
? Obx(
|
||||||
@@ -279,7 +271,7 @@ class _MainAppState extends State<MainApp>
|
|||||||
iconSize: 16,
|
iconSize: 16,
|
||||||
selectedFontSize: 12,
|
selectedFontSize: 12,
|
||||||
unselectedFontSize: 12,
|
unselectedFontSize: 12,
|
||||||
type: BottomNavigationBarType.fixed,
|
type: .fixed,
|
||||||
items: _mainController.navigationBars
|
items: _mainController.navigationBars
|
||||||
.map(
|
.map(
|
||||||
(e) => BottomNavigationBarItem(
|
(e) => BottomNavigationBarItem(
|
||||||
@@ -296,151 +288,151 @@ class _MainAppState extends State<MainApp>
|
|||||||
)
|
)
|
||||||
: null
|
: null
|
||||||
: null;
|
: null;
|
||||||
return PopScope(
|
}
|
||||||
canPop: false,
|
|
||||||
onPopInvokedWithResult: (bool didPop, Object? result) {
|
@override
|
||||||
if (_mainController.directExitOnBack) {
|
void onPopInvokedWithResult(bool didPop, Object? result) {
|
||||||
onBack();
|
if (_mainController.directExitOnBack) {
|
||||||
} else {
|
_onBack();
|
||||||
if (_mainController.selectedIndex.value != 0) {
|
} else {
|
||||||
_mainController
|
if (_mainController.selectedIndex.value != 0) {
|
||||||
..setIndex(0)
|
_mainController
|
||||||
..bottomBar?.value = true
|
..setIndex(0)
|
||||||
..setSearchBar();
|
..bottomBar?.value = true
|
||||||
} else {
|
..setSearchBar();
|
||||||
onBack();
|
} else {
|
||||||
}
|
_onBack();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
child: AnnotatedRegion<SystemUiOverlayStyle>(
|
}
|
||||||
value: SystemUiOverlayStyle(
|
|
||||||
systemNavigationBarColor: Colors.transparent,
|
@override
|
||||||
systemNavigationBarIconBrightness: theme.brightness.reverse,
|
Widget build(BuildContext context) {
|
||||||
),
|
final theme = Theme.of(context);
|
||||||
child: Scaffold(
|
final padding = MediaQuery.viewPaddingOf(context);
|
||||||
extendBody: true,
|
useBottomNav =
|
||||||
resizeToAvoidBottomInset: false,
|
!_mainController.useSideBar && MediaQuery.sizeOf(context).isPortrait;
|
||||||
appBar: AppBar(toolbarHeight: 0),
|
final bottomNav = _bottomNav;
|
||||||
body: Padding(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
padding: EdgeInsets.only(
|
value: SystemUiOverlayStyle(
|
||||||
left: useBottomNav ? padding.left : 0.0,
|
systemNavigationBarColor: Colors.transparent,
|
||||||
right: padding.right,
|
systemNavigationBarIconBrightness: theme.brightness.reverse,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Scaffold(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
extendBody: true,
|
||||||
children: [
|
resizeToAvoidBottomInset: false,
|
||||||
if (!useBottomNav) ...[
|
appBar: AppBar(toolbarHeight: 0),
|
||||||
_mainController.navigationBars.length > 1
|
body: Padding(
|
||||||
? context.isTablet && _mainController.optTabletNav
|
padding: EdgeInsets.only(
|
||||||
? Column(
|
left: useBottomNav ? padding.left : 0.0,
|
||||||
children: [
|
right: padding.right,
|
||||||
const SizedBox(height: 25),
|
),
|
||||||
userAndSearchVertical(theme),
|
child: Row(
|
||||||
const Spacer(flex: 2),
|
mainAxisAlignment: .center,
|
||||||
Expanded(
|
children: [
|
||||||
flex: 5,
|
if (!useBottomNav) ...[
|
||||||
child: SizedBox(
|
_mainController.navigationBars.length > 1
|
||||||
width: 130,
|
? context.isTablet && _mainController.optTabletNav
|
||||||
child: Obx(
|
? Column(
|
||||||
() => NavigationDrawer(
|
children: [
|
||||||
backgroundColor: Colors.transparent,
|
const SizedBox(height: 25),
|
||||||
tilePadding:
|
userAndSearchVertical(theme),
|
||||||
const EdgeInsets.symmetric(
|
const Spacer(flex: 2),
|
||||||
vertical: 5,
|
Expanded(
|
||||||
horizontal: 12,
|
flex: 5,
|
||||||
),
|
child: SizedBox(
|
||||||
indicatorShape:
|
width: 130,
|
||||||
const RoundedRectangleBorder(
|
child: Obx(
|
||||||
borderRadius: BorderRadius.all(
|
() => NavigationDrawer(
|
||||||
Radius.circular(16),
|
backgroundColor: Colors.transparent,
|
||||||
),
|
tilePadding: const .symmetric(
|
||||||
),
|
vertical: 5,
|
||||||
onDestinationSelected:
|
horizontal: 12,
|
||||||
_mainController.setIndex,
|
|
||||||
selectedIndex: _mainController
|
|
||||||
.selectedIndex
|
|
||||||
.value,
|
|
||||||
children: _mainController
|
|
||||||
.navigationBars
|
|
||||||
.map(
|
|
||||||
(e) =>
|
|
||||||
NavigationDrawerDestination(
|
|
||||||
label: Text(e.label),
|
|
||||||
icon: _buildIcon(
|
|
||||||
type: e,
|
|
||||||
),
|
|
||||||
selectedIcon: _buildIcon(
|
|
||||||
type: e,
|
|
||||||
selected: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
),
|
),
|
||||||
|
indicatorShape:
|
||||||
|
const RoundedRectangleBorder(
|
||||||
|
borderRadius: .all(
|
||||||
|
.circular(16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onDestinationSelected:
|
||||||
|
_mainController.setIndex,
|
||||||
|
selectedIndex:
|
||||||
|
_mainController.selectedIndex.value,
|
||||||
|
children: _mainController.navigationBars
|
||||||
|
.map(
|
||||||
|
(e) =>
|
||||||
|
NavigationDrawerDestination(
|
||||||
|
label: Text(e.label),
|
||||||
|
icon: _buildIcon(type: e),
|
||||||
|
selectedIcon: _buildIcon(
|
||||||
|
type: e,
|
||||||
|
selected: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
)
|
|
||||||
: Obx(
|
|
||||||
() => NavigationRail(
|
|
||||||
groupAlignment: 0.5,
|
|
||||||
selectedIndex:
|
|
||||||
_mainController.selectedIndex.value,
|
|
||||||
onDestinationSelected:
|
|
||||||
_mainController.setIndex,
|
|
||||||
labelType: NavigationRailLabelType.selected,
|
|
||||||
leading: userAndSearchVertical(theme),
|
|
||||||
destinations: _mainController.navigationBars
|
|
||||||
.map(
|
|
||||||
(e) => NavigationRailDestination(
|
|
||||||
label: Text(e.label),
|
|
||||||
icon: _buildIcon(type: e),
|
|
||||||
selectedIcon: _buildIcon(
|
|
||||||
type: e,
|
|
||||||
selected: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
),
|
),
|
||||||
)
|
],
|
||||||
: Container(
|
)
|
||||||
padding: const EdgeInsets.only(top: 10),
|
: Obx(
|
||||||
width: 80,
|
() => NavigationRail(
|
||||||
child: userAndSearchVertical(theme),
|
groupAlignment: 0.5,
|
||||||
),
|
selectedIndex:
|
||||||
VerticalDivider(
|
_mainController.selectedIndex.value,
|
||||||
width: 1,
|
onDestinationSelected: _mainController.setIndex,
|
||||||
endIndent: padding.bottom,
|
labelType: .selected,
|
||||||
color: theme.colorScheme.outline.withValues(alpha: 0.06),
|
leading: userAndSearchVertical(theme),
|
||||||
),
|
destinations: _mainController.navigationBars
|
||||||
],
|
.map(
|
||||||
Expanded(
|
(e) => NavigationRailDestination(
|
||||||
child: _mainController.mainTabBarView
|
label: Text(e.label),
|
||||||
? CustomTabBarView(
|
icon: _buildIcon(type: e),
|
||||||
scrollDirection: useBottomNav
|
selectedIcon: _buildIcon(
|
||||||
? Axis.horizontal
|
type: e,
|
||||||
: Axis.vertical,
|
selected: true,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
),
|
||||||
controller: _mainController.controller,
|
),
|
||||||
children: _mainController.navigationBars
|
)
|
||||||
.map((i) => i.page)
|
.toList(),
|
||||||
.toList(),
|
),
|
||||||
)
|
)
|
||||||
: PageView(
|
: Container(
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
width: 80,
|
||||||
controller: _mainController.controller,
|
padding: const .only(top: 10),
|
||||||
children: _mainController.navigationBars
|
child: userAndSearchVertical(theme),
|
||||||
.map((i) => i.page)
|
),
|
||||||
.toList(),
|
VerticalDivider(
|
||||||
),
|
width: 1,
|
||||||
|
endIndent: padding.bottom,
|
||||||
|
color: theme.colorScheme.outline.withValues(alpha: 0.06),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
Expanded(
|
||||||
|
child: _mainController.mainTabBarView
|
||||||
|
? CustomTabBarView(
|
||||||
|
scrollDirection: useBottomNav ? .horizontal : .vertical,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
controller: _mainController.controller,
|
||||||
|
children: _mainController.navigationBars
|
||||||
|
.map((i) => i.page)
|
||||||
|
.toList(),
|
||||||
|
)
|
||||||
|
: PageView(
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
controller: _mainController.controller,
|
||||||
|
children: _mainController.navigationBars
|
||||||
|
.map((i) => i.page)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
bottomNavigationBar: _buildBottom(bottomNav),
|
|
||||||
),
|
),
|
||||||
|
bottomNavigationBar: _buildBottom(bottomNav),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -461,22 +453,18 @@ class _MainAppState extends State<MainApp>
|
|||||||
return bottomNav;
|
return bottomNav;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildIcon({
|
Widget _buildIcon({required NavigationBarType type, bool selected = false}) {
|
||||||
required NavigationBarType type,
|
|
||||||
bool selected = false,
|
|
||||||
}) {
|
|
||||||
final icon = selected ? type.selectIcon : type.icon;
|
final icon = selected ? type.selectIcon : type.icon;
|
||||||
return type == NavigationBarType.dynamics
|
return type == .dynamics
|
||||||
? Obx(
|
? Obx(
|
||||||
() {
|
() {
|
||||||
final dynCount = _mainController.dynCount.value;
|
final dynCount = _mainController.dynCount.value;
|
||||||
return Badge(
|
return Badge(
|
||||||
isLabelVisible: dynCount > 0,
|
isLabelVisible: dynCount > 0,
|
||||||
label:
|
label: _mainController.dynamicBadgeMode == .number
|
||||||
_mainController.dynamicBadgeMode == DynamicBadgeMode.number
|
|
||||||
? Text(dynCount.toString())
|
? Text(dynCount.toString())
|
||||||
: null,
|
: null,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6),
|
padding: const .symmetric(horizontal: 6),
|
||||||
child: icon,
|
child: icon,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -487,69 +475,9 @@ class _MainAppState extends State<MainApp>
|
|||||||
Widget userAndSearchVertical(ThemeData theme) {
|
Widget userAndSearchVertical(ThemeData theme) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Semantics(
|
userAvatar(theme: theme, mainController: _mainController),
|
||||||
label: "我的",
|
|
||||||
child: Obx(
|
|
||||||
() => _mainController.accountService.isLogin.value
|
|
||||||
? Stack(
|
|
||||||
clipBehavior: Clip.none,
|
|
||||||
children: [
|
|
||||||
NetworkImgLayer(
|
|
||||||
type: ImageType.avatar,
|
|
||||||
width: 34,
|
|
||||||
height: 34,
|
|
||||||
src: _mainController.accountService.face.value,
|
|
||||||
),
|
|
||||||
Positioned.fill(
|
|
||||||
child: Material(
|
|
||||||
type: MaterialType.transparency,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: _mainController.toMinePage,
|
|
||||||
splashColor: theme.colorScheme.primaryContainer
|
|
||||||
.withValues(alpha: 0.3),
|
|
||||||
customBorder: const CircleBorder(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
right: -6,
|
|
||||||
bottom: -6,
|
|
||||||
child: Obx(
|
|
||||||
() => MineController.anonymity.value
|
|
||||||
? IgnorePointer(
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(2),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color:
|
|
||||||
theme.colorScheme.secondaryContainer,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
size: 16,
|
|
||||||
MdiIcons.incognito,
|
|
||||||
color: theme
|
|
||||||
.colorScheme
|
|
||||||
.onSecondaryContainer,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: const SizedBox.shrink(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: defaultUser(
|
|
||||||
theme: theme,
|
|
||||||
onPressed: _mainController.toMinePage,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Obx(
|
msgBadge(_mainController),
|
||||||
() => _mainController.accountService.isLogin.value
|
|
||||||
? msgBadge(_mainController)
|
|
||||||
: const SizedBox.shrink(),
|
|
||||||
),
|
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: '搜索',
|
tooltip: '搜索',
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ import 'package:PiliPlus/common/widgets/flutter/list_tile.dart';
|
|||||||
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
|
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.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/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/models/common/image_type.dart';
|
|
||||||
import 'package:PiliPlus/models/common/nav_bar_config.dart';
|
import 'package:PiliPlus/models/common/nav_bar_config.dart';
|
||||||
import 'package:PiliPlus/models/user/info.dart';
|
|
||||||
import 'package:PiliPlus/models_new/fav/fav_folder/list.dart';
|
import 'package:PiliPlus/models_new/fav/fav_folder/list.dart';
|
||||||
import 'package:PiliPlus/pages/common/common_page.dart';
|
import 'package:PiliPlus/pages/common/common_page.dart';
|
||||||
import 'package:PiliPlus/pages/home/view.dart';
|
import 'package:PiliPlus/pages/home/view.dart';
|
||||||
@@ -70,16 +68,17 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
return onBuild(
|
return onBuild(
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 10),
|
Padding(
|
||||||
_buildHeaderActions,
|
padding: const .symmetric(vertical: 10),
|
||||||
const SizedBox(height: 10),
|
child: _buildHeaderActions,
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Material(
|
child: Material(
|
||||||
type: MaterialType.transparency,
|
type: .transparency,
|
||||||
child: refreshIndicator(
|
child: refreshIndicator(
|
||||||
onRefresh: controller.onRefresh,
|
onRefresh: controller.onRefresh,
|
||||||
child: ListView(
|
child: ListView(
|
||||||
padding: const EdgeInsets.only(bottom: 100),
|
padding: const .only(bottom: 100),
|
||||||
controller: controller.scrollController,
|
controller: controller.scrollController,
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
children: [
|
children: [
|
||||||
@@ -102,7 +101,7 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
|
|
||||||
Widget _buildActions(Color primary) {
|
Widget _buildActions(Color primary) {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: .spaceEvenly,
|
||||||
children: controller.list
|
children: controller.list
|
||||||
.map(
|
.map(
|
||||||
(e) => Flexible(
|
(e) => Flexible(
|
||||||
@@ -115,14 +114,10 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
child: Column(
|
child: Column(
|
||||||
spacing: 6,
|
spacing: 6,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: .min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: .center,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(size: e.size, e.icon, color: primary),
|
||||||
size: e.size,
|
|
||||||
e.icon,
|
|
||||||
color: primary,
|
|
||||||
),
|
|
||||||
Text(
|
Text(
|
||||||
e.title,
|
e.title,
|
||||||
style: const TextStyle(fontSize: 13),
|
style: const TextStyle(fontSize: 13),
|
||||||
@@ -139,9 +134,12 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget get _buildHeaderActions {
|
Widget get _buildHeaderActions {
|
||||||
|
const iconSize = 22.0;
|
||||||
|
const padding = EdgeInsets.all(8);
|
||||||
|
const style = ButtonStyle(tapTargetSize: .shrinkWrap);
|
||||||
return Row(
|
return Row(
|
||||||
spacing: 5,
|
spacing: 5,
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: .end,
|
||||||
children: [
|
children: [
|
||||||
if (widget.showBackBtn)
|
if (widget.showBackBtn)
|
||||||
const Expanded(
|
const Expanded(
|
||||||
@@ -155,11 +153,9 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
),
|
),
|
||||||
if (!_mainController.hasHome) ...[
|
if (!_mainController.hasHome) ...[
|
||||||
IconButton(
|
IconButton(
|
||||||
iconSize: 22,
|
iconSize: iconSize,
|
||||||
padding: const EdgeInsets.all(8),
|
padding: padding,
|
||||||
style: const ButtonStyle(
|
style: style,
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
||||||
),
|
|
||||||
tooltip: '搜索',
|
tooltip: '搜索',
|
||||||
onPressed: () => Get.toNamed('/search'),
|
onPressed: () => Get.toNamed('/search'),
|
||||||
icon: const Icon(Icons.search),
|
icon: const Icon(Icons.search),
|
||||||
@@ -170,11 +166,9 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
() {
|
() {
|
||||||
final anonymity = MineController.anonymity.value;
|
final anonymity = MineController.anonymity.value;
|
||||||
return IconButton(
|
return IconButton(
|
||||||
iconSize: 22,
|
iconSize: iconSize,
|
||||||
padding: const EdgeInsets.all(8),
|
padding: padding,
|
||||||
style: const ButtonStyle(
|
style: style,
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
||||||
),
|
|
||||||
tooltip: "${anonymity ? '退出' : '进入'}无痕模式",
|
tooltip: "${anonymity ? '退出' : '进入'}无痕模式",
|
||||||
onPressed: MineController.onChangeAnonymity,
|
onPressed: MineController.onChangeAnonymity,
|
||||||
icon: anonymity
|
icon: anonymity
|
||||||
@@ -184,11 +178,9 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
iconSize: 22,
|
iconSize: iconSize,
|
||||||
padding: const EdgeInsets.all(8),
|
padding: padding,
|
||||||
style: const ButtonStyle(
|
style: style,
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
||||||
),
|
|
||||||
tooltip: '设置账号模式',
|
tooltip: '设置账号模式',
|
||||||
onPressed: () => LoginPageController.switchAccountDialog(context),
|
onPressed: () => LoginPageController.switchAccountDialog(context),
|
||||||
icon: const Icon(Icons.switch_account_outlined),
|
icon: const Icon(Icons.switch_account_outlined),
|
||||||
@@ -196,11 +188,9 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
Obx(
|
Obx(
|
||||||
() {
|
() {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
iconSize: 22,
|
iconSize: iconSize,
|
||||||
padding: const EdgeInsets.all(8),
|
padding: padding,
|
||||||
style: const ButtonStyle(
|
style: style,
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
||||||
),
|
|
||||||
tooltip: '切换至${controller.nextThemeType.desc}主题',
|
tooltip: '切换至${controller.nextThemeType.desc}主题',
|
||||||
onPressed: controller.onChangeTheme,
|
onPressed: controller.onChangeTheme,
|
||||||
icon: controller.themeType.value.icon,
|
icon: controller.themeType.value.icon,
|
||||||
@@ -208,11 +198,9 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
iconSize: 22,
|
iconSize: iconSize,
|
||||||
padding: const EdgeInsets.all(8),
|
padding: padding,
|
||||||
style: const ButtonStyle(
|
style: style,
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
||||||
),
|
|
||||||
tooltip: '设置',
|
tooltip: '设置',
|
||||||
onPressed: () => Get.toNamed('/setting', preventDuplicates: false),
|
onPressed: () => Get.toNamed('/setting', preventDuplicates: false),
|
||||||
icon: const Icon(Icons.settings_outlined),
|
icon: const Icon(Icons.settings_outlined),
|
||||||
@@ -240,8 +228,8 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
color: secondary,
|
color: secondary,
|
||||||
);
|
);
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
final UserInfoData userInfo = controller.userInfo.value;
|
final userInfo = controller.userInfo.value;
|
||||||
final LevelInfo? levelInfo = userInfo.levelInfo;
|
final levelInfo = userInfo.levelInfo;
|
||||||
final hasLevel = levelInfo != null;
|
final hasLevel = levelInfo != null;
|
||||||
final isVip = userInfo.vipStatus != null && userInfo.vipStatus! > 0;
|
final isVip = userInfo.vipStatus != null && userInfo.vipStatus! > 0;
|
||||||
final userStat = controller.userStat.value;
|
final userStat = controller.userStat.value;
|
||||||
@@ -249,7 +237,7 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: .opaque,
|
||||||
onTap: controller.onLogin,
|
onTap: controller.onLogin,
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
Feedback.forLongPress(context);
|
Feedback.forLongPress(context);
|
||||||
@@ -259,16 +247,16 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
? null
|
? null
|
||||||
: () => controller.onLogin(true),
|
: () => controller.onLogin(true),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: .min,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(width: 20),
|
const SizedBox(width: 20),
|
||||||
userInfo.face != null
|
userInfo.face != null
|
||||||
? Stack(
|
? Stack(
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: .none,
|
||||||
children: [
|
children: [
|
||||||
NetworkImgLayer(
|
NetworkImgLayer(
|
||||||
src: userInfo.face,
|
src: userInfo.face,
|
||||||
type: ImageType.avatar,
|
type: .avatar,
|
||||||
width: 55,
|
width: 55,
|
||||||
height: 55,
|
height: 55,
|
||||||
),
|
),
|
||||||
@@ -297,9 +285,9 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: .min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: .center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: .start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
spacing: 6,
|
spacing: 6,
|
||||||
@@ -313,6 +301,8 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
? theme.colorScheme.vipColor
|
? theme.colorScheme.vipColor
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: .ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Image.asset(
|
Image.asset(
|
||||||
@@ -376,7 +366,7 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: .spaceEvenly,
|
||||||
children: [
|
children: [
|
||||||
_btn(
|
_btn(
|
||||||
count: userStat.dynamicCount,
|
count: userStat.dynamicCount,
|
||||||
@@ -422,14 +412,14 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
spacing: 4,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisSize: .min,
|
||||||
|
mainAxisAlignment: .center,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
count?.toString() ?? '-',
|
count?.toString() ?? '-',
|
||||||
style: countStyle,
|
style: countStyle,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
Text(
|
||||||
name,
|
name,
|
||||||
style: labelStyle,
|
style: labelStyle,
|
||||||
@@ -466,7 +456,7 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
text: '我的收藏 ',
|
text: '我的收藏 ',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: theme.textTheme.titleMedium!.fontSize,
|
fontSize: theme.textTheme.titleMedium!.fontSize,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: .bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (controller.favFolderCount != null)
|
if (controller.favFolderCount != null)
|
||||||
@@ -516,19 +506,17 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 200,
|
height: 200,
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
padding: const EdgeInsets.only(left: 20, top: 12, right: 20),
|
padding: const .only(left: 20, top: 10, right: 20),
|
||||||
itemCount: response.list.length + (flag ? 1 : 0),
|
itemCount: response.list.length + (flag ? 1 : 0),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
if (flag && index == favFolderList.length) {
|
if (flag && index == favFolderList.length) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 35),
|
padding: const .only(bottom: 35),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
tooltip: '查看更多',
|
tooltip: '查看更多',
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: const WidgetStatePropertyAll(
|
padding: const WidgetStatePropertyAll(.zero),
|
||||||
EdgeInsets.zero,
|
|
||||||
),
|
|
||||||
backgroundColor: WidgetStatePropertyAll(
|
backgroundColor: WidgetStatePropertyAll(
|
||||||
theme.colorScheme.secondaryContainer.withValues(
|
theme.colorScheme.secondaryContainer.withValues(
|
||||||
alpha: 0.5,
|
alpha: 0.5,
|
||||||
@@ -560,8 +548,8 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: .horizontal,
|
||||||
separatorBuilder: (context, index) => const SizedBox(width: 14),
|
separatorBuilder: (_, _) => const SizedBox(width: 14),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -571,7 +559,7 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
|||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
errMsg ?? '',
|
errMsg ?? '',
|
||||||
textAlign: TextAlign.center,
|
textAlign: .center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class FavFolderItem extends StatelessWidget {
|
|||||||
color: theme.colorScheme.onInverseSurface.withValues(
|
color: theme.colorScheme.onInverseSurface.withValues(
|
||||||
alpha: 0.4,
|
alpha: 0.4,
|
||||||
),
|
),
|
||||||
offset: const Offset(4, -12),
|
offset: const Offset(6, -8),
|
||||||
blurRadius: 0.0,
|
blurRadius: 0.0,
|
||||||
spreadRadius: 0.0,
|
spreadRadius: 0.0,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ class _RcmdPageState extends CommonPageState<RcmdPage, RcmdController>
|
|||||||
super.build(context);
|
super.build(context);
|
||||||
return onBuild(
|
return onBuild(
|
||||||
Container(
|
Container(
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: .hardEdge,
|
||||||
margin: const EdgeInsets.symmetric(horizontal: StyleString.safeSpace),
|
margin: const .symmetric(horizontal: StyleString.safeSpace),
|
||||||
decoration: const BoxDecoration(borderRadius: StyleString.mdRadius),
|
decoration: const BoxDecoration(borderRadius: StyleString.mdRadius),
|
||||||
child: refreshIndicator(
|
child: refreshIndicator(
|
||||||
onRefresh: controller.onRefresh,
|
onRefresh: controller.onRefresh,
|
||||||
@@ -41,10 +41,7 @@ class _RcmdPageState extends CommonPageState<RcmdPage, RcmdController>
|
|||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const .only(top: StyleString.cardSpace, bottom: 100),
|
||||||
top: StyleString.cardSpace,
|
|
||||||
bottom: 100,
|
|
||||||
),
|
|
||||||
sliver: Obx(() => _buildBody(controller.loadingState.value)),
|
sliver: Obx(() => _buildBody(controller.loadingState.value)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -87,7 +84,7 @@ class _RcmdPageState extends CommonPageState<RcmdPage, RcmdController>
|
|||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'上次看到这里\n点击刷新',
|
'上次看到这里\n点击刷新',
|
||||||
textAlign: TextAlign.center,
|
textAlign: .center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(
|
color: Theme.of(
|
||||||
context,
|
context,
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
final RxBool _noAccount = Accounts.account.isEmpty.obs;
|
final RxBool _noAccount = Accounts.account.isEmpty.obs;
|
||||||
late bool _isPortrait;
|
late bool _isPortrait;
|
||||||
|
|
||||||
final List<_SettingsModel> _items = const [
|
static const List<_SettingsModel> _items = [
|
||||||
_SettingsModel(
|
_SettingsModel(
|
||||||
type: SettingType.privacySetting,
|
type: SettingType.privacySetting,
|
||||||
subtitle: '黑名单、无痕模式',
|
subtitle: '黑名单、无痕模式',
|
||||||
|
|||||||
@@ -91,6 +91,17 @@ class _CdnSelectDialogState extends State<CdnSelectDialog> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
_cdnSpeedTest = Pref.cdnSpeedTest;
|
_cdnSpeedTest = Pref.cdnSpeedTest;
|
||||||
if (_cdnSpeedTest) {
|
if (_cdnSpeedTest) {
|
||||||
|
_dio =
|
||||||
|
Dio(
|
||||||
|
BaseOptions(
|
||||||
|
connectTimeout: const Duration(seconds: 15),
|
||||||
|
receiveTimeout: const Duration(seconds: 15),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
..options.headers = {
|
||||||
|
'user-agent': UaType.pc.ua,
|
||||||
|
'referer': HttpString.baseUrl,
|
||||||
|
};
|
||||||
final length = CDNService.values.length;
|
final length = CDNService.values.length;
|
||||||
_cdnResList = List.generate(
|
_cdnResList = List.generate(
|
||||||
length,
|
length,
|
||||||
@@ -156,11 +167,7 @@ class _CdnSelectDialogState extends State<CdnSelectDialog> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
late final _dio = Dio()
|
late final Dio _dio;
|
||||||
..options.headers = {
|
|
||||||
'user-agent': UaType.pc.ua,
|
|
||||||
'referer': HttpString.baseUrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
Future<void> _measureDownloadSpeed(String url, int index) async {
|
Future<void> _measureDownloadSpeed(String url, int index) async {
|
||||||
const maxSize = 8 * 1024 * 1024;
|
const maxSize = 8 * 1024 * 1024;
|
||||||
|
|||||||
@@ -1456,7 +1456,7 @@ class VideoDetailController extends GetxController
|
|||||||
}
|
}
|
||||||
|
|
||||||
RxList<Subtitle> subtitles = RxList<Subtitle>();
|
RxList<Subtitle> subtitles = RxList<Subtitle>();
|
||||||
late final Map<int, String> vttSubtitles = {};
|
final Map<int, ({bool isData, String id})> vttSubtitles = {};
|
||||||
late final RxInt vttSubtitlesIndex = (-1).obs;
|
late final RxInt vttSubtitlesIndex = (-1).obs;
|
||||||
late final RxBool showVP = true.obs;
|
late final RxBool showVP = true.obs;
|
||||||
late final RxList<ViewPointSegment> viewPointList = <ViewPointSegment>[].obs;
|
late final RxList<ViewPointSegment> viewPointList = <ViewPointSegment>[].obs;
|
||||||
@@ -1471,19 +1471,21 @@ class VideoDetailController extends GetxController
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setSub(String subtitle) async {
|
Future<void> setSub(({bool isData, String id}) subtitle) async {
|
||||||
final sub = subtitles[index - 1];
|
final sub = subtitles[index - 1];
|
||||||
await plPlayerController.videoPlayerController?.setSubtitleTrack(
|
await plPlayerController.videoPlayerController?.setSubtitleTrack(
|
||||||
SubtitleTrack.data(
|
SubtitleTrack(
|
||||||
subtitle,
|
subtitle.id,
|
||||||
title: sub.lanDoc,
|
sub.lanDoc,
|
||||||
language: sub.lan,
|
sub.lan,
|
||||||
|
uri: !subtitle.isData,
|
||||||
|
data: subtitle.isData,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
vttSubtitlesIndex.value = index;
|
vttSubtitlesIndex.value = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
String? subtitle = vttSubtitles[index - 1];
|
({bool isData, String id})? subtitle = vttSubtitles[index - 1];
|
||||||
if (subtitle != null) {
|
if (subtitle != null) {
|
||||||
await setSub(subtitle);
|
await setSub(subtitle);
|
||||||
} else {
|
} else {
|
||||||
@@ -1491,8 +1493,9 @@ class VideoDetailController extends GetxController
|
|||||||
subtitles[index - 1].subtitleUrl!,
|
subtitles[index - 1].subtitleUrl!,
|
||||||
);
|
);
|
||||||
if (!isClosed && result != null) {
|
if (!isClosed && result != null) {
|
||||||
vttSubtitles[index - 1] = result;
|
final subtitle = (isData: true, id: result);
|
||||||
await setSub(result);
|
vttSubtitles[index - 1] = subtitle;
|
||||||
|
await setSub(subtitle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1665,6 +1668,8 @@ class VideoDetailController extends GetxController
|
|||||||
?..removeListener(scrollListener)
|
?..removeListener(scrollListener)
|
||||||
..dispose();
|
..dispose();
|
||||||
animController?.dispose();
|
animController?.dispose();
|
||||||
|
subtitles.clear();
|
||||||
|
vttSubtitles.clear();
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -324,7 +324,6 @@ class _DownloadPanelState extends State<DownloadPanel> {
|
|||||||
required ugc.BaseEpisodeItem episode,
|
required ugc.BaseEpisodeItem episode,
|
||||||
}) {
|
}) {
|
||||||
late String title;
|
late String title;
|
||||||
String? cover;
|
|
||||||
num? duration;
|
num? duration;
|
||||||
int? pubdate;
|
int? pubdate;
|
||||||
int? view;
|
int? view;
|
||||||
@@ -332,6 +331,11 @@ class _DownloadPanelState extends State<DownloadPanel> {
|
|||||||
bool? isCharging;
|
bool? isCharging;
|
||||||
int? cid;
|
int? cid;
|
||||||
|
|
||||||
|
String? cover;
|
||||||
|
int? width;
|
||||||
|
int? height;
|
||||||
|
bool cacheWidth = false;
|
||||||
|
|
||||||
switch (episode) {
|
switch (episode) {
|
||||||
case Part part:
|
case Part part:
|
||||||
cid = part.cid;
|
cid = part.cid;
|
||||||
@@ -339,15 +343,27 @@ class _DownloadPanelState extends State<DownloadPanel> {
|
|||||||
title = part.part ?? widget.videoDetail!.title!;
|
title = part.part ?? widget.videoDetail!.title!;
|
||||||
duration = part.duration;
|
duration = part.duration;
|
||||||
pubdate = part.ctime;
|
pubdate = part.ctime;
|
||||||
|
if (part.dimension case final dimension?) {
|
||||||
|
width = dimension.width;
|
||||||
|
height = dimension.height;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ugc.EpisodeItem item:
|
case ugc.EpisodeItem item:
|
||||||
cid = item.cid;
|
cid = item.cid;
|
||||||
title = item.title!;
|
title = item.title!;
|
||||||
cover = item.arc?.pic;
|
if (item.arc case final arc?) {
|
||||||
duration = item.arc?.duration;
|
cover = arc.pic;
|
||||||
pubdate = item.arc?.pubdate;
|
duration = arc.duration;
|
||||||
view = item.arc?.stat?.view;
|
pubdate = arc.pubdate;
|
||||||
danmaku = item.arc?.stat?.danmaku;
|
if (arc.stat case final stat?) {
|
||||||
|
view = stat.view;
|
||||||
|
danmaku = stat.danmaku;
|
||||||
|
}
|
||||||
|
if (arc.dimension case final dimension?) {
|
||||||
|
width = dimension.width;
|
||||||
|
height = dimension.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (item.attribute == 8) {
|
if (item.attribute == 8) {
|
||||||
isCharging = true;
|
isCharging = true;
|
||||||
}
|
}
|
||||||
@@ -363,8 +379,15 @@ class _DownloadPanelState extends State<DownloadPanel> {
|
|||||||
duration = item.duration == null ? null : item.duration! ~/ 1000;
|
duration = item.duration == null ? null : item.duration! ~/ 1000;
|
||||||
}
|
}
|
||||||
pubdate = item.pubTime;
|
pubdate = item.pubTime;
|
||||||
|
if (item.dimension case final dimension?) {
|
||||||
|
width = dimension.width;
|
||||||
|
height = dimension.height;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (width != null && height != null) {
|
||||||
|
cacheWidth = width <= height;
|
||||||
|
}
|
||||||
late final primary = theme.colorScheme.primary;
|
late final primary = theme.colorScheme.primary;
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
@@ -401,6 +424,7 @@ class _DownloadPanelState extends State<DownloadPanel> {
|
|||||||
src: cover,
|
src: cover,
|
||||||
width: 140.8,
|
width: 140.8,
|
||||||
height: 88,
|
height: 88,
|
||||||
|
cacheWidth: cacheWidth,
|
||||||
),
|
),
|
||||||
if (duration != null && duration > 0)
|
if (duration != null && duration > 0)
|
||||||
PBadge(
|
PBadge(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert' show jsonDecode, utf8;
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
@@ -717,34 +717,41 @@ class HeaderControlState extends State<HeaderControl>
|
|||||||
final first = file.files.first;
|
final first = file.files.first;
|
||||||
final path = first.path;
|
final path = first.path;
|
||||||
if (path != null) {
|
if (path != null) {
|
||||||
final file = File(path);
|
|
||||||
final stream = file.openRead().transform(
|
|
||||||
utf8.decoder,
|
|
||||||
);
|
|
||||||
final buffer = StringBuffer();
|
|
||||||
await for (final chunk in stream) {
|
|
||||||
if (!mounted) return;
|
|
||||||
buffer.write(chunk);
|
|
||||||
}
|
|
||||||
if (!mounted) return;
|
|
||||||
String sub = buffer.toString();
|
|
||||||
final name = first.name;
|
final name = first.name;
|
||||||
|
final length = videoDetailCtr.subtitles.length;
|
||||||
if (name.endsWith('.json')) {
|
if (name.endsWith('.json')) {
|
||||||
|
final file = File(path);
|
||||||
|
final stream = file.openRead().transform(
|
||||||
|
utf8.decoder,
|
||||||
|
);
|
||||||
|
final buffer = StringBuffer();
|
||||||
|
await for (final chunk in stream) {
|
||||||
|
if (!mounted) return;
|
||||||
|
buffer.write(chunk);
|
||||||
|
}
|
||||||
|
if (!mounted) return;
|
||||||
|
String sub = buffer.toString();
|
||||||
sub = await compute<List, String>(
|
sub = await compute<List, String>(
|
||||||
VideoHttp.processList,
|
VideoHttp.processList,
|
||||||
jsonDecode(sub)['body'],
|
jsonDecode(sub)['body'],
|
||||||
);
|
);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
videoDetailCtr.vttSubtitles[length] = (
|
||||||
|
isData: true,
|
||||||
|
id: sub,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
videoDetailCtr.vttSubtitles[length] = (
|
||||||
|
isData: false,
|
||||||
|
id: path,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
final length = videoDetailCtr.subtitles.length;
|
videoDetailCtr.subtitles.add(
|
||||||
videoDetailCtr
|
Subtitle(
|
||||||
..subtitles.add(
|
lan: '',
|
||||||
Subtitle(
|
lanDoc: name.split('.').firstOrNull ?? name,
|
||||||
lan: '',
|
),
|
||||||
lanDoc: name.split('.').firstOrNull ?? name,
|
);
|
||||||
),
|
|
||||||
)
|
|
||||||
..vttSubtitles[length] = sub;
|
|
||||||
await videoDetailCtr.setSubtitle(length + 1);
|
await videoDetailCtr.setSubtitle(length + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user