mirror of
https://github.com/venera-app/venera.git
synced 2025-12-15 14:41:15 +00:00
Merge pull request #594 from lings03/favorite
Optimize favorite page and home page.
This commit is contained in:
@@ -753,9 +753,9 @@ class SliverGridComics extends StatefulWidget {
|
||||
|
||||
final List<MenuEntry> Function(Comic)? menuBuilder;
|
||||
|
||||
final void Function(Comic)? onTap;
|
||||
final void Function(Comic, int heroID)? onTap;
|
||||
|
||||
final void Function(Comic)? onLongPressed;
|
||||
final void Function(Comic, int heroID)? onLongPressed;
|
||||
|
||||
@override
|
||||
State<SliverGridComics> createState() => _SliverGridComicsState();
|
||||
@@ -856,52 +856,51 @@ class _SliverGridComics extends StatelessWidget {
|
||||
|
||||
final List<MenuEntry> Function(Comic)? menuBuilder;
|
||||
|
||||
final void Function(Comic)? onTap;
|
||||
final void Function(Comic, int heroID)? onTap;
|
||||
|
||||
final void Function(Comic)? onLongPressed;
|
||||
final void Function(Comic, int heroID)? onLongPressed;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverGrid(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == comics.length - 1) {
|
||||
onLastItemBuild?.call();
|
||||
}
|
||||
var badge = badgeBuilder?.call(comics[index]);
|
||||
var isSelected =
|
||||
selection == null ? false : selection![comics[index]] ?? false;
|
||||
var comic = ComicTile(
|
||||
comic: comics[index],
|
||||
badge: badge,
|
||||
menuOptions: menuBuilder?.call(comics[index]),
|
||||
onTap: onTap != null ? () => onTap!(comics[index]) : null,
|
||||
onLongPressed: onLongPressed != null
|
||||
? () => onLongPressed!(comics[index])
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
if (index == comics.length - 1) {
|
||||
onLastItemBuild?.call();
|
||||
}
|
||||
var badge = badgeBuilder?.call(comics[index]);
|
||||
var isSelected = selection == null
|
||||
? false
|
||||
: selection![comics[index]] ?? false;
|
||||
var comic = ComicTile(
|
||||
comic: comics[index],
|
||||
badge: badge,
|
||||
menuOptions: menuBuilder?.call(comics[index]),
|
||||
onTap: onTap != null
|
||||
? () => onTap!(comics[index], heroIDs[index])
|
||||
: null,
|
||||
onLongPressed: onLongPressed != null
|
||||
? () => onLongPressed!(comics[index], heroIDs[index])
|
||||
: null,
|
||||
heroID: heroIDs[index],
|
||||
);
|
||||
if (selection == null) {
|
||||
return comic;
|
||||
}
|
||||
return AnimatedContainer(
|
||||
key: ValueKey(comics[index].id),
|
||||
duration: const Duration(milliseconds: 150),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? Theme.of(
|
||||
context,
|
||||
).colorScheme.secondaryContainer.toOpacity(0.72)
|
||||
: null,
|
||||
heroID: heroIDs[index],
|
||||
);
|
||||
if (selection == null) {
|
||||
return comic;
|
||||
}
|
||||
return AnimatedContainer(
|
||||
key: ValueKey(comics[index].id),
|
||||
duration: const Duration(milliseconds: 150),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.secondaryContainer
|
||||
.toOpacity(0.72)
|
||||
: null,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
margin: const EdgeInsets.all(4),
|
||||
child: comic,
|
||||
);
|
||||
},
|
||||
childCount: comics.length,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
margin: const EdgeInsets.all(4),
|
||||
child: comic,
|
||||
);
|
||||
}, childCount: comics.length),
|
||||
gridDelegate: SliverGridDelegateWithComics(),
|
||||
);
|
||||
}
|
||||
@@ -1627,7 +1626,7 @@ class _SMClipper extends CustomClipper<Rect> {
|
||||
|
||||
class SimpleComicTile extends StatelessWidget {
|
||||
const SimpleComicTile(
|
||||
{super.key, required this.comic, this.onTap, this.withTitle = false});
|
||||
{super.key, required this.comic, this.onTap, this.withTitle = false, this.heroID});
|
||||
|
||||
final Comic comic;
|
||||
|
||||
@@ -1635,6 +1634,8 @@ class SimpleComicTile extends StatelessWidget {
|
||||
|
||||
final bool withTitle;
|
||||
|
||||
final int? heroID;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var image = _findImageProvider(comic);
|
||||
@@ -1660,6 +1661,13 @@ class SimpleComicTile extends StatelessWidget {
|
||||
child: child,
|
||||
);
|
||||
|
||||
if (heroID != null) {
|
||||
child = Hero(
|
||||
tag: "cover$heroID",
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
child = AnimatedTapRegion(
|
||||
borderRadius: 8,
|
||||
onTap: onTap ??
|
||||
@@ -1668,6 +1676,9 @@ class SimpleComicTile extends StatelessWidget {
|
||||
() => ComicPage(
|
||||
id: comic.id,
|
||||
sourceKey: comic.sourceKey,
|
||||
cover: comic.cover,
|
||||
title: comic.title,
|
||||
heroID: heroID,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -241,6 +241,10 @@ class _AppScrollBarState extends State<AppScrollBar> {
|
||||
|
||||
late final VerticalDragGestureRecognizer _dragGestureRecognizer;
|
||||
|
||||
bool _isVisible = false;
|
||||
Timer? _hideTimer;
|
||||
static const _hideDuration = Duration(seconds: 2);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -248,7 +252,41 @@ class _AppScrollBarState extends State<AppScrollBar> {
|
||||
_scrollController.addListener(onChanged);
|
||||
Future.microtask(onChanged);
|
||||
_dragGestureRecognizer = VerticalDragGestureRecognizer()
|
||||
..onUpdate = onUpdate;
|
||||
..onUpdate = onUpdate
|
||||
..onStart = (_) {
|
||||
_showScrollbar();
|
||||
}
|
||||
..onEnd = (_) {
|
||||
_scheduleHide();
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_hideTimer?.cancel();
|
||||
_scrollController.removeListener(onChanged);
|
||||
_dragGestureRecognizer.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _showScrollbar() {
|
||||
if (!_isVisible && mounted) {
|
||||
setState(() {
|
||||
_isVisible = true;
|
||||
});
|
||||
}
|
||||
_hideTimer?.cancel();
|
||||
}
|
||||
|
||||
void _scheduleHide() {
|
||||
_hideTimer?.cancel();
|
||||
_hideTimer = Timer(_hideDuration, () {
|
||||
if (mounted && _isVisible) {
|
||||
setState(() {
|
||||
_isVisible = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void onUpdate(DragUpdateDetails details) {
|
||||
@@ -269,14 +307,24 @@ class _AppScrollBarState extends State<AppScrollBar> {
|
||||
void onChanged() {
|
||||
if (_scrollController.positions.isEmpty) return;
|
||||
var position = _scrollController.position;
|
||||
|
||||
bool hasChanged = false;
|
||||
if (position.minScrollExtent != minExtent ||
|
||||
position.maxScrollExtent != maxExtent ||
|
||||
position.pixels != this.position) {
|
||||
setState(() {
|
||||
minExtent = position.minScrollExtent;
|
||||
maxExtent = position.maxScrollExtent;
|
||||
this.position = position.pixels;
|
||||
});
|
||||
hasChanged = true;
|
||||
minExtent = position.minScrollExtent;
|
||||
maxExtent = position.maxScrollExtent;
|
||||
this.position = position.pixels;
|
||||
}
|
||||
|
||||
if (hasChanged) {
|
||||
_showScrollbar();
|
||||
_scheduleHide();
|
||||
}
|
||||
|
||||
if (hasChanged && mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,29 +348,35 @@ class _AppScrollBarState extends State<AppScrollBar> {
|
||||
Positioned(
|
||||
top: top + widget.topPadding,
|
||||
right: 0,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: Listener(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onPointerDown: (event) {
|
||||
_dragGestureRecognizer.addPointer(event);
|
||||
},
|
||||
child: SizedBox(
|
||||
width: _scrollIndicatorSize/2,
|
||||
height: _scrollIndicatorSize,
|
||||
child: CustomPaint(
|
||||
painter: _ScrollIndicatorPainter(
|
||||
backgroundColor: context.colorScheme.surface,
|
||||
shadowColor: context.colorScheme.shadow,
|
||||
child: AnimatedOpacity(
|
||||
opacity: _isVisible ? 1.0 : 0.0,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
onEnter: (_) => _showScrollbar(),
|
||||
onExit: (_) => _scheduleHide(),
|
||||
child: Listener(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onPointerDown: (event) {
|
||||
_dragGestureRecognizer.addPointer(event);
|
||||
},
|
||||
child: SizedBox(
|
||||
width: _scrollIndicatorSize / 2,
|
||||
height: _scrollIndicatorSize,
|
||||
child: CustomPaint(
|
||||
painter: _ScrollIndicatorPainter(
|
||||
backgroundColor: context.colorScheme.surface,
|
||||
shadowColor: context.colorScheme.shadow,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
const Spacer(),
|
||||
Icon(Icons.arrow_drop_up, size: 18),
|
||||
Icon(Icons.arrow_drop_down, size: 18),
|
||||
const Spacer(),
|
||||
],
|
||||
).paddingLeft(4),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
const Spacer(),
|
||||
Icon(Icons.arrow_drop_up, size: 18),
|
||||
Icon(Icons.arrow_drop_down, size: 18),
|
||||
const Spacer(),
|
||||
],
|
||||
).paddingLeft(4),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -689,7 +689,7 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
|
||||
),
|
||||
];
|
||||
},
|
||||
onTap: (c) {
|
||||
onTap: (c, heroID) {
|
||||
if (multiSelectMode) {
|
||||
setState(() {
|
||||
if (selectedComics.containsKey(c as FavoriteItem)) {
|
||||
@@ -701,20 +701,24 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
|
||||
lastSelectedIndex = comics.indexOf(c);
|
||||
});
|
||||
} else if (appdata.settings["onClickFavorite"] == "viewDetail") {
|
||||
App.mainNavigatorKey?.currentContext
|
||||
?.to(() => ComicPage(id: c.id, sourceKey: c.sourceKey));
|
||||
} else {
|
||||
App.mainNavigatorKey?.currentContext?.to(
|
||||
() => ReaderWithLoading(
|
||||
() => ComicPage(
|
||||
id: c.id,
|
||||
sourceKey: c.sourceKey,
|
||||
cover: c.cover,
|
||||
title: c.title,
|
||||
heroID: heroID,
|
||||
),
|
||||
enableIOSGesture: false,
|
||||
iosFullScreenGesture: false,
|
||||
);
|
||||
} else {
|
||||
App.mainNavigatorKey?.currentContext?.to(
|
||||
() => ReaderWithLoading(id: c.id, sourceKey: c.sourceKey),
|
||||
);
|
||||
}
|
||||
},
|
||||
onLongPressed: (c) {
|
||||
onLongPressed: (c, heroID) {
|
||||
setState(() {
|
||||
if (!multiSelectMode) {
|
||||
multiSelectMode = true;
|
||||
|
||||
@@ -211,7 +211,7 @@ class _HistoryPageState extends State<HistoryPage> {
|
||||
selections: selectedComics,
|
||||
onLongPressed: null,
|
||||
onTap: multiSelectMode
|
||||
? (c) {
|
||||
? (c, heroID) {
|
||||
setState(() {
|
||||
if (selectedComics.containsKey(c as History)) {
|
||||
selectedComics.remove(c);
|
||||
|
||||
@@ -302,13 +302,18 @@ class _HistoryState extends State<_History> {
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: history.length,
|
||||
itemBuilder: (context, index) {
|
||||
final heroID = history[index].id.hashCode;
|
||||
return SimpleComicTile(
|
||||
comic: history[index],
|
||||
heroID: heroID,
|
||||
onTap: () {
|
||||
context.to(
|
||||
() => ComicPage(
|
||||
id: history[index].id,
|
||||
sourceKey: history[index].type.sourceKey,
|
||||
cover: history[index].cover,
|
||||
title: history[index].title,
|
||||
heroID: heroID,
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -386,7 +391,9 @@ class _LocalState extends State<_Local> {
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8, vertical: 2),
|
||||
horizontal: 8,
|
||||
vertical: 2,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
@@ -405,9 +412,22 @@ class _LocalState extends State<_Local> {
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: local.length,
|
||||
itemBuilder: (context, index) {
|
||||
return SimpleComicTile(comic: local[index])
|
||||
.paddingHorizontal(8)
|
||||
.paddingVertical(2);
|
||||
final heroID = local[index].id.hashCode;
|
||||
return SimpleComicTile(
|
||||
comic: local[index],
|
||||
heroID: heroID,
|
||||
onTap: () {
|
||||
context.to(
|
||||
() => ComicPage(
|
||||
id: local[index].id,
|
||||
sourceKey: local[index].sourceKey,
|
||||
cover: local[index].cover,
|
||||
title: local[index].title,
|
||||
heroID: heroID,
|
||||
),
|
||||
);
|
||||
},
|
||||
).paddingHorizontal(8).paddingVertical(2);
|
||||
},
|
||||
),
|
||||
).paddingHorizontal(8),
|
||||
|
||||
@@ -285,13 +285,13 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
|
||||
SliverGridComics(
|
||||
comics: comics,
|
||||
selections: selectedComics,
|
||||
onLongPressed: (c) {
|
||||
onLongPressed: (c, heroID) {
|
||||
setState(() {
|
||||
multiSelectMode = true;
|
||||
selectedComics[c as LocalComic] = true;
|
||||
});
|
||||
},
|
||||
onTap: (c) {
|
||||
onTap: (c, heroID) {
|
||||
if (multiSelectMode) {
|
||||
setState(() {
|
||||
if (selectedComics.containsKey(c as LocalComic)) {
|
||||
|
||||
Reference in New Issue
Block a user