mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-07-26 21:40:11 +08:00
@@ -59,10 +59,12 @@ class _AboutPageState extends State<AboutPage> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> getCacheSize() async {
|
||||
cacheSize.value = CacheManager.formatSize(
|
||||
await CacheManager.loadApplicationCache(),
|
||||
);
|
||||
void getCacheSize() {
|
||||
CacheManager.loadApplicationCache().then((res) {
|
||||
if (mounted) {
|
||||
cacheSize.value = CacheManager.formatSize(res);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _showDialog() => showDialog(
|
||||
@@ -103,20 +105,18 @@ class _AboutPageState extends State<AboutPage> {
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
_pressCount++;
|
||||
if (_pressCount == 5) {
|
||||
if (++_pressCount == 5) {
|
||||
_pressCount = 0;
|
||||
_showDialog();
|
||||
}
|
||||
},
|
||||
onSecondaryTap: PlatformUtils.isDesktop ? _showDialog : null,
|
||||
child: ExcludeSemantics(
|
||||
child: Image.asset(
|
||||
width: 150,
|
||||
height: 150,
|
||||
cacheWidth: 150.cacheSize(context),
|
||||
'assets/images/logo/logo.png',
|
||||
),
|
||||
child: Image.asset(
|
||||
width: 150,
|
||||
height: 150,
|
||||
excludeFromSemantics: true,
|
||||
cacheWidth: 150.cacheSize(context),
|
||||
'assets/images/logo/logo.png',
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
|
||||
@@ -57,11 +57,16 @@ abstract class CommonRichTextPubPageState<T extends CommonRichTextPubPage>
|
||||
int get limit => widget.imageLengthLimit ?? 9;
|
||||
|
||||
@override
|
||||
late final RichTextEditingController editController =
|
||||
RichTextEditingController(
|
||||
items: widget.items,
|
||||
onMention: onMention,
|
||||
);
|
||||
late final RichTextEditingController editController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
editController = RichTextEditingController(
|
||||
items: widget.items,
|
||||
onMention: onMention,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void initPubState() {
|
||||
|
||||
@@ -127,8 +127,10 @@ class _FavDetailPageState extends State<FavDetailPage> with GridMixin {
|
||||
|
||||
Widget _buildHeader(bool enableMultiSelect, ThemeData theme) {
|
||||
return SliverAppBar.medium(
|
||||
leadingWidth: enableMultiSelect ? 125 : null,
|
||||
leading: enableMultiSelect
|
||||
? Row(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
IconButton(
|
||||
tooltip: '取消',
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.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/main/controller.dart';
|
||||
import 'package:PiliPlus/pages/mine/controller.dart';
|
||||
@@ -77,79 +75,18 @@ class _HomePageState extends State<HomePage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget searchBarAndUser(ThemeData theme) {
|
||||
return Row(
|
||||
Widget customAppBar(ThemeData theme) {
|
||||
const height = 52.0;
|
||||
const padding = EdgeInsets.fromLTRB(14, 6, 14, 0);
|
||||
final child = Row(
|
||||
children: [
|
||||
searchBar(theme),
|
||||
const SizedBox(width: 4),
|
||||
Obx(
|
||||
() => _homeController.accountService.isLogin.value
|
||||
? msgBadge(_mainController)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
msgBadge(_mainController),
|
||||
const SizedBox(width: 8),
|
||||
Semantics(
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
userAvatar(theme: theme, mainController: _mainController),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget customAppBar(ThemeData theme) {
|
||||
if (_homeController.searchBar case final searchBar?) {
|
||||
return Obx(() {
|
||||
final showSearchBar = searchBar.value;
|
||||
@@ -159,39 +96,39 @@ class _HomePageState extends State<HomePage>
|
||||
child: AnimatedContainer(
|
||||
curve: Curves.easeInOutCubicEmphasized,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
height: showSearchBar ? 52 : 0,
|
||||
padding: const EdgeInsets.fromLTRB(14, 6, 14, 0),
|
||||
child: searchBarAndUser(theme),
|
||||
height: showSearchBar ? height : 0,
|
||||
padding: padding,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
return Container(
|
||||
height: 52,
|
||||
padding: const EdgeInsets.fromLTRB(14, 6, 14, 0),
|
||||
child: searchBarAndUser(theme),
|
||||
height: height,
|
||||
padding: padding,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget searchBar(ThemeData theme) {
|
||||
const borderRadius = BorderRadius.all(Radius.circular(25));
|
||||
return Expanded(
|
||||
child: SizedBox(
|
||||
height: 44,
|
||||
child: Material(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(25)),
|
||||
borderRadius: borderRadius,
|
||||
color: theme.colorScheme.onSecondaryContainer.withValues(alpha: 0.05),
|
||||
child: InkWell(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(25)),
|
||||
borderRadius: borderRadius,
|
||||
splashColor: theme.colorScheme.primaryContainer.withValues(
|
||||
alpha: 0.3,
|
||||
),
|
||||
onTap: () => Get.toNamed(
|
||||
'/search',
|
||||
parameters: {
|
||||
if (_homeController.enableSearchWord)
|
||||
'hintText': _homeController.defaultSearch.value,
|
||||
},
|
||||
parameters: _homeController.enableSearchWord
|
||||
? {'hintText': _homeController.defaultSearch.value}
|
||||
: null,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
@@ -222,60 +159,109 @@ class _HomePageState extends State<HomePage>
|
||||
}
|
||||
}
|
||||
|
||||
Widget defaultUser({
|
||||
Widget userAvatar({
|
||||
required ThemeData theme,
|
||||
required VoidCallback onPressed,
|
||||
required MainController mainController,
|
||||
}) {
|
||||
return SizedBox(
|
||||
width: 38,
|
||||
height: 38,
|
||||
child: IconButton(
|
||||
tooltip: '点击登录',
|
||||
style: ButtonStyle(
|
||||
padding: const WidgetStatePropertyAll(EdgeInsets.zero),
|
||||
backgroundColor: WidgetStatePropertyAll(
|
||||
theme.colorScheme.onInverseSurface,
|
||||
),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
icon: Icon(
|
||||
Icons.person_rounded,
|
||||
size: 22,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
return Semantics(
|
||||
label: "我的",
|
||||
child: Obx(
|
||||
() {
|
||||
if (mainController.accountService.isLogin.value) {
|
||||
return Stack(
|
||||
clipBehavior: .none,
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
type: .avatar,
|
||||
width: 34,
|
||||
height: 34,
|
||||
src: mainController.accountService.face.value,
|
||||
),
|
||||
Positioned.fill(
|
||||
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) {
|
||||
void toWhisper() {
|
||||
mainController.msgUnReadCount.value = '';
|
||||
mainController.lastCheckUnreadAt = DateTime.now().millisecondsSinceEpoch;
|
||||
Get.toNamed('/whisper');
|
||||
}
|
||||
|
||||
final msgUnReadCount = mainController.msgUnReadCount.value;
|
||||
return GestureDetector(
|
||||
onTap: toWhisper,
|
||||
child: Badge(
|
||||
isLabelVisible:
|
||||
mainController.msgBadgeMode != DynamicBadgeMode.hidden &&
|
||||
msgUnReadCount.isNotEmpty,
|
||||
alignment: mainController.msgBadgeMode == DynamicBadgeMode.number
|
||||
? const Alignment(0, -0.5)
|
||||
: const Alignment(0.5, -0.5),
|
||||
label:
|
||||
mainController.msgBadgeMode == DynamicBadgeMode.number &&
|
||||
msgUnReadCount.isNotEmpty
|
||||
? Text(msgUnReadCount)
|
||||
: null,
|
||||
child: IconButton(
|
||||
tooltip: '消息',
|
||||
onPressed: toWhisper,
|
||||
icon: const Icon(
|
||||
Icons.notifications_none,
|
||||
),
|
||||
),
|
||||
),
|
||||
return Obx(
|
||||
() {
|
||||
if (mainController.accountService.isLogin.value) {
|
||||
final count = mainController.msgUnReadCount.value;
|
||||
final isNumBadge = mainController.msgBadgeMode == .number;
|
||||
return IconButton(
|
||||
tooltip: '消息',
|
||||
onPressed: () {
|
||||
mainController
|
||||
..msgUnReadCount.value = ''
|
||||
..lastCheckUnreadAt = DateTime.now().millisecondsSinceEpoch;
|
||||
Get.toNamed('/whisper');
|
||||
},
|
||||
icon: Badge(
|
||||
isLabelVisible:
|
||||
mainController.msgBadgeMode != .hidden && count.isNotEmpty,
|
||||
alignment: isNumBadge
|
||||
? const Alignment(0.0, -0.85)
|
||||
: const Alignment(1.0, -0.85),
|
||||
label: isNumBadge && count.isNotEmpty ? Text(count) : null,
|
||||
child: const Icon(Icons.notifications_none),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -288,9 +288,14 @@ class LiveRoomController extends GetxController {
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Get
|
||||
..back()
|
||||
..back(),
|
||||
onPressed: () {
|
||||
if (plPlayerController.isDesktopPip) {
|
||||
plPlayerController.exitDesktopPip();
|
||||
}
|
||||
Get
|
||||
..back()
|
||||
..back();
|
||||
},
|
||||
child: const Text('退出'),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import 'dart:io';
|
||||
|
||||
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/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/pages/home/view.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/models/play_status.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/services.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:window_manager/window_manager.dart';
|
||||
|
||||
@@ -34,7 +31,7 @@ class MainApp extends StatefulWidget {
|
||||
State<MainApp> createState() => _MainAppState();
|
||||
}
|
||||
|
||||
class _MainAppState extends State<MainApp>
|
||||
class _MainAppState extends PopScopeState<MainApp>
|
||||
with RouteAware, WidgetsBindingObserver, WindowListener, TrayListener {
|
||||
final _mainController = Get.put(MainController());
|
||||
late final _setting = GStorage.setting;
|
||||
@@ -234,7 +231,7 @@ class _MainAppState extends State<MainApp>
|
||||
await trayManager.setContextMenu(trayMenu);
|
||||
}
|
||||
|
||||
void onBack() {
|
||||
static void _onBack() {
|
||||
if (Platform.isAndroid) {
|
||||
Utils.channel.invokeMethod('back');
|
||||
} else {
|
||||
@@ -244,13 +241,8 @@ class _MainAppState extends State<MainApp>
|
||||
|
||||
late bool useBottomNav;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final padding = MediaQuery.viewPaddingOf(context);
|
||||
useBottomNav =
|
||||
!_mainController.useSideBar && MediaQuery.sizeOf(context).isPortrait;
|
||||
Widget? bottomNav = useBottomNav
|
||||
Widget? get _bottomNav {
|
||||
return useBottomNav
|
||||
? _mainController.navigationBars.length > 1
|
||||
? _mainController.enableMYBar
|
||||
? Obx(
|
||||
@@ -279,7 +271,7 @@ class _MainAppState extends State<MainApp>
|
||||
iconSize: 16,
|
||||
selectedFontSize: 12,
|
||||
unselectedFontSize: 12,
|
||||
type: BottomNavigationBarType.fixed,
|
||||
type: .fixed,
|
||||
items: _mainController.navigationBars
|
||||
.map(
|
||||
(e) => BottomNavigationBarItem(
|
||||
@@ -296,151 +288,151 @@ class _MainAppState extends State<MainApp>
|
||||
)
|
||||
: null
|
||||
: null;
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvokedWithResult: (bool didPop, Object? result) {
|
||||
if (_mainController.directExitOnBack) {
|
||||
onBack();
|
||||
} else {
|
||||
if (_mainController.selectedIndex.value != 0) {
|
||||
_mainController
|
||||
..setIndex(0)
|
||||
..bottomBar?.value = true
|
||||
..setSearchBar();
|
||||
} else {
|
||||
onBack();
|
||||
}
|
||||
}
|
||||
},
|
||||
child: AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
systemNavigationBarColor: Colors.transparent,
|
||||
systemNavigationBarIconBrightness: theme.brightness.reverse,
|
||||
),
|
||||
child: Scaffold(
|
||||
extendBody: true,
|
||||
resizeToAvoidBottomInset: false,
|
||||
appBar: AppBar(toolbarHeight: 0),
|
||||
body: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: useBottomNav ? padding.left : 0.0,
|
||||
right: padding.right,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (!useBottomNav) ...[
|
||||
_mainController.navigationBars.length > 1
|
||||
? context.isTablet && _mainController.optTabletNav
|
||||
? Column(
|
||||
children: [
|
||||
const SizedBox(height: 25),
|
||||
userAndSearchVertical(theme),
|
||||
const Spacer(flex: 2),
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: SizedBox(
|
||||
width: 130,
|
||||
child: Obx(
|
||||
() => NavigationDrawer(
|
||||
backgroundColor: Colors.transparent,
|
||||
tilePadding:
|
||||
const EdgeInsets.symmetric(
|
||||
vertical: 5,
|
||||
horizontal: 12,
|
||||
),
|
||||
indicatorShape:
|
||||
const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.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(),
|
||||
}
|
||||
|
||||
@override
|
||||
void onPopInvokedWithResult(bool didPop, Object? result) {
|
||||
if (_mainController.directExitOnBack) {
|
||||
_onBack();
|
||||
} else {
|
||||
if (_mainController.selectedIndex.value != 0) {
|
||||
_mainController
|
||||
..setIndex(0)
|
||||
..bottomBar?.value = true
|
||||
..setSearchBar();
|
||||
} else {
|
||||
_onBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final padding = MediaQuery.viewPaddingOf(context);
|
||||
useBottomNav =
|
||||
!_mainController.useSideBar && MediaQuery.sizeOf(context).isPortrait;
|
||||
final bottomNav = _bottomNav;
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
systemNavigationBarColor: Colors.transparent,
|
||||
systemNavigationBarIconBrightness: theme.brightness.reverse,
|
||||
),
|
||||
child: Scaffold(
|
||||
extendBody: true,
|
||||
resizeToAvoidBottomInset: false,
|
||||
appBar: AppBar(toolbarHeight: 0),
|
||||
body: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: useBottomNav ? padding.left : 0.0,
|
||||
right: padding.right,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: .center,
|
||||
children: [
|
||||
if (!useBottomNav) ...[
|
||||
_mainController.navigationBars.length > 1
|
||||
? context.isTablet && _mainController.optTabletNav
|
||||
? Column(
|
||||
children: [
|
||||
const SizedBox(height: 25),
|
||||
userAndSearchVertical(theme),
|
||||
const Spacer(flex: 2),
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: SizedBox(
|
||||
width: 130,
|
||||
child: Obx(
|
||||
() => NavigationDrawer(
|
||||
backgroundColor: Colors.transparent,
|
||||
tilePadding: const .symmetric(
|
||||
vertical: 5,
|
||||
horizontal: 12,
|
||||
),
|
||||
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),
|
||||
width: 80,
|
||||
child: userAndSearchVertical(theme),
|
||||
),
|
||||
VerticalDivider(
|
||||
width: 1,
|
||||
endIndent: padding.bottom,
|
||||
color: theme.colorScheme.outline.withValues(alpha: 0.06),
|
||||
),
|
||||
],
|
||||
Expanded(
|
||||
child: _mainController.mainTabBarView
|
||||
? CustomTabBarView(
|
||||
scrollDirection: useBottomNav
|
||||
? Axis.horizontal
|
||||
: Axis.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(),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Obx(
|
||||
() => NavigationRail(
|
||||
groupAlignment: 0.5,
|
||||
selectedIndex:
|
||||
_mainController.selectedIndex.value,
|
||||
onDestinationSelected: _mainController.setIndex,
|
||||
labelType: .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(
|
||||
width: 80,
|
||||
padding: const .only(top: 10),
|
||||
child: userAndSearchVertical(theme),
|
||||
),
|
||||
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;
|
||||
}
|
||||
|
||||
Widget _buildIcon({
|
||||
required NavigationBarType type,
|
||||
bool selected = false,
|
||||
}) {
|
||||
Widget _buildIcon({required NavigationBarType type, bool selected = false}) {
|
||||
final icon = selected ? type.selectIcon : type.icon;
|
||||
return type == NavigationBarType.dynamics
|
||||
return type == .dynamics
|
||||
? Obx(
|
||||
() {
|
||||
final dynCount = _mainController.dynCount.value;
|
||||
return Badge(
|
||||
isLabelVisible: dynCount > 0,
|
||||
label:
|
||||
_mainController.dynamicBadgeMode == DynamicBadgeMode.number
|
||||
label: _mainController.dynamicBadgeMode == .number
|
||||
? Text(dynCount.toString())
|
||||
: null,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6),
|
||||
padding: const .symmetric(horizontal: 6),
|
||||
child: icon,
|
||||
);
|
||||
},
|
||||
@@ -487,69 +475,9 @@ class _MainAppState extends State<MainApp>
|
||||
Widget userAndSearchVertical(ThemeData theme) {
|
||||
return Column(
|
||||
children: [
|
||||
Semantics(
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
userAvatar(theme: theme, mainController: _mainController),
|
||||
const SizedBox(height: 8),
|
||||
Obx(
|
||||
() => _mainController.accountService.isLogin.value
|
||||
? msgBadge(_mainController)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
msgBadge(_mainController),
|
||||
IconButton(
|
||||
tooltip: '搜索',
|
||||
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/image/network_img_layer.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/user/info.dart';
|
||||
import 'package:PiliPlus/models_new/fav/fav_folder/list.dart';
|
||||
import 'package:PiliPlus/pages/common/common_page.dart';
|
||||
import 'package:PiliPlus/pages/home/view.dart';
|
||||
@@ -70,16 +68,17 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
return onBuild(
|
||||
Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
_buildHeaderActions,
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: const .symmetric(vertical: 10),
|
||||
child: _buildHeaderActions,
|
||||
),
|
||||
Expanded(
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
type: .transparency,
|
||||
child: refreshIndicator(
|
||||
onRefresh: controller.onRefresh,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.only(bottom: 100),
|
||||
padding: const .only(bottom: 100),
|
||||
controller: controller.scrollController,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
children: [
|
||||
@@ -102,7 +101,7 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
|
||||
Widget _buildActions(Color primary) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
mainAxisAlignment: .spaceEvenly,
|
||||
children: controller.list
|
||||
.map(
|
||||
(e) => Flexible(
|
||||
@@ -115,14 +114,10 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
aspectRatio: 1,
|
||||
child: Column(
|
||||
spacing: 6,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: .min,
|
||||
mainAxisAlignment: .center,
|
||||
children: [
|
||||
Icon(
|
||||
size: e.size,
|
||||
e.icon,
|
||||
color: primary,
|
||||
),
|
||||
Icon(size: e.size, e.icon, color: primary),
|
||||
Text(
|
||||
e.title,
|
||||
style: const TextStyle(fontSize: 13),
|
||||
@@ -139,9 +134,12 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
}
|
||||
|
||||
Widget get _buildHeaderActions {
|
||||
const iconSize = 22.0;
|
||||
const padding = EdgeInsets.all(8);
|
||||
const style = ButtonStyle(tapTargetSize: .shrinkWrap);
|
||||
return Row(
|
||||
spacing: 5,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
mainAxisAlignment: .end,
|
||||
children: [
|
||||
if (widget.showBackBtn)
|
||||
const Expanded(
|
||||
@@ -155,11 +153,9 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
),
|
||||
if (!_mainController.hasHome) ...[
|
||||
IconButton(
|
||||
iconSize: 22,
|
||||
padding: const EdgeInsets.all(8),
|
||||
style: const ButtonStyle(
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
iconSize: iconSize,
|
||||
padding: padding,
|
||||
style: style,
|
||||
tooltip: '搜索',
|
||||
onPressed: () => Get.toNamed('/search'),
|
||||
icon: const Icon(Icons.search),
|
||||
@@ -170,11 +166,9 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
() {
|
||||
final anonymity = MineController.anonymity.value;
|
||||
return IconButton(
|
||||
iconSize: 22,
|
||||
padding: const EdgeInsets.all(8),
|
||||
style: const ButtonStyle(
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
iconSize: iconSize,
|
||||
padding: padding,
|
||||
style: style,
|
||||
tooltip: "${anonymity ? '退出' : '进入'}无痕模式",
|
||||
onPressed: MineController.onChangeAnonymity,
|
||||
icon: anonymity
|
||||
@@ -184,11 +178,9 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 22,
|
||||
padding: const EdgeInsets.all(8),
|
||||
style: const ButtonStyle(
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
iconSize: iconSize,
|
||||
padding: padding,
|
||||
style: style,
|
||||
tooltip: '设置账号模式',
|
||||
onPressed: () => LoginPageController.switchAccountDialog(context),
|
||||
icon: const Icon(Icons.switch_account_outlined),
|
||||
@@ -196,11 +188,9 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
Obx(
|
||||
() {
|
||||
return IconButton(
|
||||
iconSize: 22,
|
||||
padding: const EdgeInsets.all(8),
|
||||
style: const ButtonStyle(
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
iconSize: iconSize,
|
||||
padding: padding,
|
||||
style: style,
|
||||
tooltip: '切换至${controller.nextThemeType.desc}主题',
|
||||
onPressed: controller.onChangeTheme,
|
||||
icon: controller.themeType.value.icon,
|
||||
@@ -208,11 +198,9 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 22,
|
||||
padding: const EdgeInsets.all(8),
|
||||
style: const ButtonStyle(
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
iconSize: iconSize,
|
||||
padding: padding,
|
||||
style: style,
|
||||
tooltip: '设置',
|
||||
onPressed: () => Get.toNamed('/setting', preventDuplicates: false),
|
||||
icon: const Icon(Icons.settings_outlined),
|
||||
@@ -240,8 +228,8 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
color: secondary,
|
||||
);
|
||||
return Obx(() {
|
||||
final UserInfoData userInfo = controller.userInfo.value;
|
||||
final LevelInfo? levelInfo = userInfo.levelInfo;
|
||||
final userInfo = controller.userInfo.value;
|
||||
final levelInfo = userInfo.levelInfo;
|
||||
final hasLevel = levelInfo != null;
|
||||
final isVip = userInfo.vipStatus != null && userInfo.vipStatus! > 0;
|
||||
final userStat = controller.userStat.value;
|
||||
@@ -249,7 +237,7 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
behavior: .opaque,
|
||||
onTap: controller.onLogin,
|
||||
onLongPress: () {
|
||||
Feedback.forLongPress(context);
|
||||
@@ -259,16 +247,16 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
? null
|
||||
: () => controller.onLogin(true),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const SizedBox(width: 20),
|
||||
userInfo.face != null
|
||||
? Stack(
|
||||
clipBehavior: Clip.none,
|
||||
clipBehavior: .none,
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
src: userInfo.face,
|
||||
type: ImageType.avatar,
|
||||
type: .avatar,
|
||||
width: 55,
|
||||
height: 55,
|
||||
),
|
||||
@@ -297,9 +285,9 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: .min,
|
||||
mainAxisAlignment: .center,
|
||||
crossAxisAlignment: .start,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 6,
|
||||
@@ -313,6 +301,8 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
? theme.colorScheme.vipColor
|
||||
: null,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: .ellipsis,
|
||||
),
|
||||
),
|
||||
Image.asset(
|
||||
@@ -376,7 +366,7 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
mainAxisAlignment: .spaceEvenly,
|
||||
children: [
|
||||
_btn(
|
||||
count: userStat.dynamicCount,
|
||||
@@ -422,14 +412,14 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 4,
|
||||
mainAxisSize: .min,
|
||||
mainAxisAlignment: .center,
|
||||
children: [
|
||||
Text(
|
||||
count?.toString() ?? '-',
|
||||
style: countStyle,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
name,
|
||||
style: labelStyle,
|
||||
@@ -466,7 +456,7 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
text: '我的收藏 ',
|
||||
style: TextStyle(
|
||||
fontSize: theme.textTheme.titleMedium!.fontSize,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontWeight: .bold,
|
||||
),
|
||||
),
|
||||
if (controller.favFolderCount != null)
|
||||
@@ -516,19 +506,17 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
return SizedBox(
|
||||
height: 200,
|
||||
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),
|
||||
itemBuilder: (context, index) {
|
||||
if (flag && index == favFolderList.length) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 35),
|
||||
padding: const .only(bottom: 35),
|
||||
child: Center(
|
||||
child: IconButton(
|
||||
tooltip: '查看更多',
|
||||
style: ButtonStyle(
|
||||
padding: const WidgetStatePropertyAll(
|
||||
EdgeInsets.zero,
|
||||
),
|
||||
padding: const WidgetStatePropertyAll(.zero),
|
||||
backgroundColor: WidgetStatePropertyAll(
|
||||
theme.colorScheme.secondaryContainer.withValues(
|
||||
alpha: 0.5,
|
||||
@@ -560,8 +548,8 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
);
|
||||
}
|
||||
},
|
||||
scrollDirection: Axis.horizontal,
|
||||
separatorBuilder: (context, index) => const SizedBox(width: 14),
|
||||
scrollDirection: .horizontal,
|
||||
separatorBuilder: (_, _) => const SizedBox(width: 14),
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -571,7 +559,7 @@ class _MediaPageState extends CommonPageState<MinePage, MineController>
|
||||
child: Center(
|
||||
child: Text(
|
||||
errMsg ?? '',
|
||||
textAlign: TextAlign.center,
|
||||
textAlign: .center,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -42,7 +42,7 @@ class FavFolderItem extends StatelessWidget {
|
||||
color: theme.colorScheme.onInverseSurface.withValues(
|
||||
alpha: 0.4,
|
||||
),
|
||||
offset: const Offset(4, -12),
|
||||
offset: const Offset(6, -8),
|
||||
blurRadius: 0.0,
|
||||
spreadRadius: 0.0,
|
||||
),
|
||||
|
||||
@@ -31,8 +31,8 @@ class _RcmdPageState extends CommonPageState<RcmdPage, RcmdController>
|
||||
super.build(context);
|
||||
return onBuild(
|
||||
Container(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
margin: const EdgeInsets.symmetric(horizontal: StyleString.safeSpace),
|
||||
clipBehavior: .hardEdge,
|
||||
margin: const .symmetric(horizontal: StyleString.safeSpace),
|
||||
decoration: const BoxDecoration(borderRadius: StyleString.mdRadius),
|
||||
child: refreshIndicator(
|
||||
onRefresh: controller.onRefresh,
|
||||
@@ -41,10 +41,7 @@ class _RcmdPageState extends CommonPageState<RcmdPage, RcmdController>
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: StyleString.cardSpace,
|
||||
bottom: 100,
|
||||
),
|
||||
padding: const .only(top: StyleString.cardSpace, bottom: 100),
|
||||
sliver: Obx(() => _buildBody(controller.loadingState.value)),
|
||||
),
|
||||
],
|
||||
@@ -87,7 +84,7 @@ class _RcmdPageState extends CommonPageState<RcmdPage, RcmdController>
|
||||
),
|
||||
child: Text(
|
||||
'上次看到这里\n点击刷新',
|
||||
textAlign: TextAlign.center,
|
||||
textAlign: .center,
|
||||
style: TextStyle(
|
||||
color: Theme.of(
|
||||
context,
|
||||
|
||||
@@ -44,7 +44,7 @@ class _SettingPageState extends State<SettingPage> {
|
||||
final RxBool _noAccount = Accounts.account.isEmpty.obs;
|
||||
late bool _isPortrait;
|
||||
|
||||
final List<_SettingsModel> _items = const [
|
||||
static const List<_SettingsModel> _items = [
|
||||
_SettingsModel(
|
||||
type: SettingType.privacySetting,
|
||||
subtitle: '黑名单、无痕模式',
|
||||
|
||||
@@ -91,6 +91,17 @@ class _CdnSelectDialogState extends State<CdnSelectDialog> {
|
||||
void initState() {
|
||||
_cdnSpeedTest = Pref.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;
|
||||
_cdnResList = List.generate(
|
||||
length,
|
||||
@@ -156,11 +167,7 @@ class _CdnSelectDialogState extends State<CdnSelectDialog> {
|
||||
}
|
||||
}
|
||||
|
||||
late final _dio = Dio()
|
||||
..options.headers = {
|
||||
'user-agent': UaType.pc.ua,
|
||||
'referer': HttpString.baseUrl,
|
||||
};
|
||||
late final Dio _dio;
|
||||
|
||||
Future<void> _measureDownloadSpeed(String url, int index) async {
|
||||
const maxSize = 8 * 1024 * 1024;
|
||||
|
||||
@@ -1456,7 +1456,7 @@ class VideoDetailController extends GetxController
|
||||
}
|
||||
|
||||
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 RxBool showVP = true.obs;
|
||||
late final RxList<ViewPointSegment> viewPointList = <ViewPointSegment>[].obs;
|
||||
@@ -1471,19 +1471,21 @@ class VideoDetailController extends GetxController
|
||||
return;
|
||||
}
|
||||
|
||||
Future<void> setSub(String subtitle) async {
|
||||
Future<void> setSub(({bool isData, String id}) subtitle) async {
|
||||
final sub = subtitles[index - 1];
|
||||
await plPlayerController.videoPlayerController?.setSubtitleTrack(
|
||||
SubtitleTrack.data(
|
||||
subtitle,
|
||||
title: sub.lanDoc,
|
||||
language: sub.lan,
|
||||
SubtitleTrack(
|
||||
subtitle.id,
|
||||
sub.lanDoc,
|
||||
sub.lan,
|
||||
uri: !subtitle.isData,
|
||||
data: subtitle.isData,
|
||||
),
|
||||
);
|
||||
vttSubtitlesIndex.value = index;
|
||||
}
|
||||
|
||||
String? subtitle = vttSubtitles[index - 1];
|
||||
({bool isData, String id})? subtitle = vttSubtitles[index - 1];
|
||||
if (subtitle != null) {
|
||||
await setSub(subtitle);
|
||||
} else {
|
||||
@@ -1491,8 +1493,9 @@ class VideoDetailController extends GetxController
|
||||
subtitles[index - 1].subtitleUrl!,
|
||||
);
|
||||
if (!isClosed && result != null) {
|
||||
vttSubtitles[index - 1] = result;
|
||||
await setSub(result);
|
||||
final subtitle = (isData: true, id: result);
|
||||
vttSubtitles[index - 1] = subtitle;
|
||||
await setSub(subtitle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1665,6 +1668,8 @@ class VideoDetailController extends GetxController
|
||||
?..removeListener(scrollListener)
|
||||
..dispose();
|
||||
animController?.dispose();
|
||||
subtitles.clear();
|
||||
vttSubtitles.clear();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
|
||||
@@ -324,7 +324,6 @@ class _DownloadPanelState extends State<DownloadPanel> {
|
||||
required ugc.BaseEpisodeItem episode,
|
||||
}) {
|
||||
late String title;
|
||||
String? cover;
|
||||
num? duration;
|
||||
int? pubdate;
|
||||
int? view;
|
||||
@@ -332,6 +331,11 @@ class _DownloadPanelState extends State<DownloadPanel> {
|
||||
bool? isCharging;
|
||||
int? cid;
|
||||
|
||||
String? cover;
|
||||
int? width;
|
||||
int? height;
|
||||
bool cacheWidth = false;
|
||||
|
||||
switch (episode) {
|
||||
case Part part:
|
||||
cid = part.cid;
|
||||
@@ -339,15 +343,27 @@ class _DownloadPanelState extends State<DownloadPanel> {
|
||||
title = part.part ?? widget.videoDetail!.title!;
|
||||
duration = part.duration;
|
||||
pubdate = part.ctime;
|
||||
if (part.dimension case final dimension?) {
|
||||
width = dimension.width;
|
||||
height = dimension.height;
|
||||
}
|
||||
break;
|
||||
case ugc.EpisodeItem item:
|
||||
cid = item.cid;
|
||||
title = item.title!;
|
||||
cover = item.arc?.pic;
|
||||
duration = item.arc?.duration;
|
||||
pubdate = item.arc?.pubdate;
|
||||
view = item.arc?.stat?.view;
|
||||
danmaku = item.arc?.stat?.danmaku;
|
||||
if (item.arc case final arc?) {
|
||||
cover = arc.pic;
|
||||
duration = arc.duration;
|
||||
pubdate = arc.pubdate;
|
||||
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) {
|
||||
isCharging = true;
|
||||
}
|
||||
@@ -363,8 +379,15 @@ class _DownloadPanelState extends State<DownloadPanel> {
|
||||
duration = item.duration == null ? null : item.duration! ~/ 1000;
|
||||
}
|
||||
pubdate = item.pubTime;
|
||||
if (item.dimension case final dimension?) {
|
||||
width = dimension.width;
|
||||
height = dimension.height;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (width != null && height != null) {
|
||||
cacheWidth = width <= height;
|
||||
}
|
||||
late final primary = theme.colorScheme.primary;
|
||||
|
||||
return Padding(
|
||||
@@ -401,6 +424,7 @@ class _DownloadPanelState extends State<DownloadPanel> {
|
||||
src: cover,
|
||||
width: 140.8,
|
||||
height: 88,
|
||||
cacheWidth: cacheWidth,
|
||||
),
|
||||
if (duration != null && duration > 0)
|
||||
PBadge(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:convert' show jsonDecode, utf8;
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
@@ -717,34 +717,41 @@ class HeaderControlState extends State<HeaderControl>
|
||||
final first = file.files.first;
|
||||
final path = first.path;
|
||||
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 length = videoDetailCtr.subtitles.length;
|
||||
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>(
|
||||
VideoHttp.processList,
|
||||
jsonDecode(sub)['body'],
|
||||
);
|
||||
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(
|
||||
Subtitle(
|
||||
lan: '',
|
||||
lanDoc: name.split('.').firstOrNull ?? name,
|
||||
),
|
||||
)
|
||||
..vttSubtitles[length] = sub;
|
||||
videoDetailCtr.subtitles.add(
|
||||
Subtitle(
|
||||
lan: '',
|
||||
lanDoc: name.split('.').firstOrNull ?? name,
|
||||
),
|
||||
);
|
||||
await videoDetailCtr.setSubtitle(length + 1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user