mirror of
https://github.com/venera-app/venera.git
synced 2025-12-16 07:01:16 +00:00
Merge branch 'master' into feat-add-filter-local-favorites
This commit is contained in:
@@ -2,6 +2,7 @@ import 'dart:math';
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:venera/foundation/app.dart';
|
import 'package:venera/foundation/app.dart';
|
||||||
|
|
||||||
const double _kBackGestureWidth = 20.0;
|
const double _kBackGestureWidth = 20.0;
|
||||||
@@ -19,6 +20,7 @@ class AppPageRoute<T> extends PageRoute<T> with _AppRouteTransitionMixin{
|
|||||||
super.allowSnapshotting = true,
|
super.allowSnapshotting = true,
|
||||||
super.barrierDismissible = false,
|
super.barrierDismissible = false,
|
||||||
this.enableIOSGesture = true,
|
this.enableIOSGesture = true,
|
||||||
|
this.iosFullScreenPopGesture = true,
|
||||||
this.preventRebuild = true,
|
this.preventRebuild = true,
|
||||||
}) {
|
}) {
|
||||||
assert(opaque);
|
assert(opaque);
|
||||||
@@ -48,6 +50,9 @@ class AppPageRoute<T> extends PageRoute<T> with _AppRouteTransitionMixin{
|
|||||||
@override
|
@override
|
||||||
final bool enableIOSGesture;
|
final bool enableIOSGesture;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final bool iosFullScreenPopGesture;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final bool preventRebuild;
|
final bool preventRebuild;
|
||||||
}
|
}
|
||||||
@@ -74,6 +79,8 @@ mixin _AppRouteTransitionMixin<T> on PageRoute<T> {
|
|||||||
|
|
||||||
bool get enableIOSGesture;
|
bool get enableIOSGesture;
|
||||||
|
|
||||||
|
bool get iosFullScreenPopGesture;
|
||||||
|
|
||||||
bool get preventRebuild;
|
bool get preventRebuild;
|
||||||
|
|
||||||
Widget? _child;
|
Widget? _child;
|
||||||
@@ -133,7 +140,9 @@ mixin _AppRouteTransitionMixin<T> on PageRoute<T> {
|
|||||||
gestureWidth: _kBackGestureWidth,
|
gestureWidth: _kBackGestureWidth,
|
||||||
enabledCallback: () => _isPopGestureEnabled<T>(this),
|
enabledCallback: () => _isPopGestureEnabled<T>(this),
|
||||||
onStartPopGesture: () => _startPopGesture(this),
|
onStartPopGesture: () => _startPopGesture(this),
|
||||||
child: child)
|
fullScreen: iosFullScreenPopGesture,
|
||||||
|
child: child,
|
||||||
|
)
|
||||||
: child);
|
: child);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,6 +215,7 @@ class IOSBackGestureDetector extends StatefulWidget {
|
|||||||
required this.child,
|
required this.child,
|
||||||
required this.gestureWidth,
|
required this.gestureWidth,
|
||||||
required this.onStartPopGesture,
|
required this.onStartPopGesture,
|
||||||
|
this.fullScreen = false,
|
||||||
super.key});
|
super.key});
|
||||||
|
|
||||||
final double gestureWidth;
|
final double gestureWidth;
|
||||||
@@ -216,6 +226,8 @@ class IOSBackGestureDetector extends StatefulWidget {
|
|||||||
|
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
|
final bool fullScreen;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<IOSBackGestureDetector> createState() => _IOSBackGestureDetectorState();
|
State<IOSBackGestureDetector> createState() => _IOSBackGestureDetectorState();
|
||||||
}
|
}
|
||||||
@@ -247,26 +259,40 @@ class _IOSBackGestureDetectorState extends State<IOSBackGestureDetector> {
|
|||||||
? MediaQuery.of(context).padding.left
|
? MediaQuery.of(context).padding.left
|
||||||
: MediaQuery.of(context).padding.right;
|
: MediaQuery.of(context).padding.right;
|
||||||
dragAreaWidth = max(dragAreaWidth, widget.gestureWidth);
|
dragAreaWidth = max(dragAreaWidth, widget.gestureWidth);
|
||||||
return Stack(
|
final Widget gestureListener = widget.fullScreen
|
||||||
fit: StackFit.passthrough,
|
? Positioned.fill(
|
||||||
children: <Widget>[
|
|
||||||
widget.child,
|
|
||||||
Positioned(
|
|
||||||
width: dragAreaWidth,
|
|
||||||
top: 0.0,
|
|
||||||
bottom: 0.0,
|
|
||||||
left: 0,
|
|
||||||
child: Listener(
|
child: Listener(
|
||||||
onPointerDown: _handlePointerDown,
|
onPointerDown: _handlePointerDown,
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
: Positioned(
|
||||||
|
width: dragAreaWidth,
|
||||||
|
top: 0.0,
|
||||||
|
bottom: 0.0,
|
||||||
|
left: Directionality.of(context) == TextDirection.ltr ? 0.0 : null,
|
||||||
|
right: Directionality.of(context) == TextDirection.rtl ? 0.0 : null,
|
||||||
|
child: Listener(
|
||||||
|
onPointerDown: _handlePointerDown,
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Stack(
|
||||||
|
fit: StackFit.passthrough,
|
||||||
|
children: <Widget>[
|
||||||
|
widget.child,
|
||||||
|
gestureListener,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handlePointerDown(PointerDownEvent event) {
|
void _handlePointerDown(PointerDownEvent event) {
|
||||||
if (widget.enabledCallback()) _recognizer.addPointer(event);
|
if (!widget.enabledCallback()) return;
|
||||||
|
if (widget.fullScreen && _isPointerOverHorizontalScrollable(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_recognizer.addPointer(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleDragCancel() {
|
void _handleDragCancel() {
|
||||||
@@ -304,6 +330,28 @@ class _IOSBackGestureDetectorState extends State<IOSBackGestureDetector> {
|
|||||||
_backGestureController!.dragUpdate(
|
_backGestureController!.dragUpdate(
|
||||||
_convertToLogical(details.primaryDelta! / context.size!.width));
|
_convertToLogical(details.primaryDelta! / context.size!.width));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isPointerOverHorizontalScrollable(PointerDownEvent event) {
|
||||||
|
final HitTestResult result = HitTestResult();
|
||||||
|
WidgetsBinding.instance.hitTest(result, event.position);
|
||||||
|
for (final entry in result.path) {
|
||||||
|
final target = entry.target;
|
||||||
|
if (target is RenderViewport) {
|
||||||
|
if (_isAxisHorizontal(target.axisDirection)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (target is RenderSliver) {
|
||||||
|
if (_isAxisHorizontal(target.constraints.axisDirection)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isAxisHorizontal(AxisDirection direction) {
|
||||||
|
return direction == AxisDirection.left || direction == AxisDirection.right;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SlidePageTransitionBuilder extends PageTransitionsBuilder {
|
class SlidePageTransitionBuilder extends PageTransitionsBuilder {
|
||||||
@@ -314,30 +362,31 @@ class SlidePageTransitionBuilder extends PageTransitionsBuilder {
|
|||||||
Animation<double> animation,
|
Animation<double> animation,
|
||||||
Animation<double> secondaryAnimation,
|
Animation<double> secondaryAnimation,
|
||||||
Widget child) {
|
Widget child) {
|
||||||
|
final Animation<double> primaryAnimation = App.isIOS
|
||||||
|
? animation
|
||||||
|
: CurvedAnimation(parent: animation, curve: Curves.ease);
|
||||||
|
final Animation<double> secondaryCurve = App.isIOS
|
||||||
|
? secondaryAnimation
|
||||||
|
: CurvedAnimation(parent: secondaryAnimation, curve: Curves.ease);
|
||||||
|
|
||||||
return SlideTransition(
|
return SlideTransition(
|
||||||
position: Tween<Offset>(
|
position: Tween<Offset>(
|
||||||
begin: const Offset(1, 0),
|
begin: const Offset(1, 0),
|
||||||
end: Offset.zero,
|
end: Offset.zero,
|
||||||
).animate(CurvedAnimation(
|
).animate(primaryAnimation),
|
||||||
parent: animation,
|
|
||||||
curve: Curves.ease,
|
|
||||||
)),
|
|
||||||
child: SlideTransition(
|
child: SlideTransition(
|
||||||
position: Tween<Offset>(
|
position: Tween<Offset>(
|
||||||
begin: Offset.zero,
|
begin: Offset.zero,
|
||||||
end: const Offset(-0.4, 0),
|
end: const Offset(-0.4, 0),
|
||||||
).animate(CurvedAnimation(
|
).animate(secondaryCurve),
|
||||||
parent: secondaryAnimation,
|
|
||||||
curve: Curves.ease,
|
|
||||||
)),
|
|
||||||
child: PhysicalModel(
|
child: PhysicalModel(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.zero,
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
elevation: 6,
|
elevation: 6,
|
||||||
child: Material(child: child,),
|
child: Material(child: child),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -542,6 +542,7 @@ class PageJumpTarget {
|
|||||||
sourceKey: sourceKey,
|
sourceKey: sourceKey,
|
||||||
options: List.from(attributes?["options"] ?? []),
|
options: List.from(attributes?["options"] ?? []),
|
||||||
),
|
),
|
||||||
|
iosFullScreenGesture: false,
|
||||||
);
|
);
|
||||||
} else if (page == "category") {
|
} else if (page == "category") {
|
||||||
var key = ComicSource.find(sourceKey)!.categoryData!.key;
|
var key = ComicSource.find(sourceKey)!.categoryData!.key;
|
||||||
|
|||||||
@@ -14,14 +14,20 @@ extension Navigation on BuildContext {
|
|||||||
return Navigator.of(this).canPop();
|
return Navigator.of(this).canPop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<T?> to<T>(Widget Function() builder) {
|
Future<T?> to<T>(Widget Function() builder,
|
||||||
return Navigator.of(this)
|
{bool enableIOSGesture = true, bool iosFullScreenGesture = true}) {
|
||||||
.push<T>(AppPageRoute(builder: (context) => builder()));
|
return Navigator.of(this).push<T>(AppPageRoute(
|
||||||
|
builder: (context) => builder(),
|
||||||
|
enableIOSGesture: enableIOSGesture,
|
||||||
|
iosFullScreenPopGesture: iosFullScreenGesture));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> toReplacement<T>(Widget Function() builder) {
|
Future<void> toReplacement<T>(Widget Function() builder,
|
||||||
return Navigator.of(this)
|
{bool enableIOSGesture = true, bool iosFullScreenGesture = true}) {
|
||||||
.pushReplacement(AppPageRoute(builder: (context) => builder()));
|
return Navigator.of(this).pushReplacement(AppPageRoute(
|
||||||
|
builder: (context) => builder(),
|
||||||
|
enableIOSGesture: enableIOSGesture,
|
||||||
|
iosFullScreenPopGesture: iosFullScreenGesture));
|
||||||
}
|
}
|
||||||
|
|
||||||
double get width => MediaQuery.of(this).size.width;
|
double get width => MediaQuery.of(this).size.width;
|
||||||
|
|||||||
@@ -154,6 +154,8 @@ class LocalComic with HistoryMixin implements Comic {
|
|||||||
author: subtitle,
|
author: subtitle,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
),
|
),
|
||||||
|
enableIOSGesture: false,
|
||||||
|
iosFullScreenGesture: false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,11 @@ abstract class ImageDownloader {
|
|||||||
responseType: ResponseType.stream,
|
responseType: ResponseType.stream,
|
||||||
));
|
));
|
||||||
|
|
||||||
var req = await dio.request<ResponseBody>(configs['url'] ?? url,
|
String requestUrl = configs['url'] ?? url;
|
||||||
|
if (requestUrl.startsWith('//')) {
|
||||||
|
requestUrl = 'https:$requestUrl';
|
||||||
|
}
|
||||||
|
var req = await dio.request<ResponseBody>(requestUrl,
|
||||||
data: configs['data']);
|
data: configs['data']);
|
||||||
var stream = req.data?.stream ?? (throw "Error: Empty response body.");
|
var stream = req.data?.stream ?? (throw "Error: Empty response body.");
|
||||||
int? expectedBytes = req.data!.contentLength;
|
int? expectedBytes = req.data!.contentLength;
|
||||||
|
|||||||
@@ -170,6 +170,7 @@ class _SliverSearchResultState extends State<_SliverSearchResult>
|
|||||||
text: widget.keyword,
|
text: widget.keyword,
|
||||||
sourceKey: widget.source.key,
|
sourceKey: widget.source.key,
|
||||||
),
|
),
|
||||||
|
iosFullScreenGesture: false,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|||||||
@@ -116,6 +116,8 @@ abstract mixin class _ComicPageActions {
|
|||||||
author: comic.findAuthor() ?? '',
|
author: comic.findAuthor() ?? '',
|
||||||
tags: comic.plainTags,
|
tags: comic.plainTags,
|
||||||
),
|
),
|
||||||
|
enableIOSGesture: false,
|
||||||
|
iosFullScreenGesture: false,
|
||||||
)
|
)
|
||||||
.then((_) {
|
.then((_) {
|
||||||
onReadEnd();
|
onReadEnd();
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ class _ComicPageState extends LoadingState<ComicPage, ComicDetails>
|
|||||||
author: localComic.subTitle ?? '',
|
author: localComic.subTitle ?? '',
|
||||||
tags: localComic.tags,
|
tags: localComic.tags,
|
||||||
);
|
);
|
||||||
});
|
}, enableIOSGesture: false, iosFullScreenGesture: false);
|
||||||
App.mainNavigatorKey!.currentContext!.pop();
|
App.mainNavigatorKey!.currentContext!.pop();
|
||||||
});
|
});
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
|
|||||||
@@ -563,7 +563,10 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
|
|||||||
App.rootContext.to(() => ReaderWithLoading(
|
App.rootContext.to(() => ReaderWithLoading(
|
||||||
id: c.id,
|
id: c.id,
|
||||||
sourceKey: c.sourceKey,
|
sourceKey: c.sourceKey,
|
||||||
));
|
),
|
||||||
|
enableIOSGesture: false,
|
||||||
|
iosFullScreenGesture: false,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (selectedComics.length == 1)
|
if (selectedComics.length == 1)
|
||||||
@@ -575,7 +578,10 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
|
|||||||
App.mainNavigatorKey?.currentContext?.to(() => ComicPage(
|
App.mainNavigatorKey?.currentContext?.to(() => ComicPage(
|
||||||
id: c.id,
|
id: c.id,
|
||||||
sourceKey: c.sourceKey,
|
sourceKey: c.sourceKey,
|
||||||
));
|
),
|
||||||
|
enableIOSGesture: false,
|
||||||
|
iosFullScreenGesture: false,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
@@ -676,6 +682,8 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
|
|||||||
id: c.id,
|
id: c.id,
|
||||||
sourceKey: c.sourceKey,
|
sourceKey: c.sourceKey,
|
||||||
),
|
),
|
||||||
|
enableIOSGesture: false,
|
||||||
|
iosFullScreenGesture: false,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -701,6 +709,8 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
|
|||||||
id: c.id,
|
id: c.id,
|
||||||
sourceKey: c.sourceKey,
|
sourceKey: c.sourceKey,
|
||||||
),
|
),
|
||||||
|
enableIOSGesture: false,
|
||||||
|
iosFullScreenGesture: false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -874,7 +874,10 @@ class _ImageFavoritesState extends State<ImageFavorites> {
|
|||||||
child: InkWell(
|
child: InkWell(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.to(() => const ImageFavoritesPage());
|
context.to(
|
||||||
|
() => const ImageFavoritesPage(),
|
||||||
|
iosFullScreenGesture: false,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -993,7 +996,10 @@ class _ImageFavoritesState extends State<ImageFavorites> {
|
|||||||
maxCount: maxCount,
|
maxCount: maxCount,
|
||||||
enableTranslation: displayType != 2,
|
enableTranslation: displayType != 2,
|
||||||
onTap: (text) {
|
onTap: (text) {
|
||||||
context.to(() => ImageFavoritesPage(initialKeyword: text));
|
context.to(
|
||||||
|
() => ImageFavoritesPage(initialKeyword: text),
|
||||||
|
iosFullScreenGesture: false,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ class _ImageFavoritesItemState extends State<_ImageFavoritesItem> {
|
|||||||
initialEp: ep,
|
initialEp: ep,
|
||||||
initialPage: page,
|
initialPage: page,
|
||||||
),
|
),
|
||||||
|
enableIOSGesture: false,
|
||||||
|
iosFullScreenGesture: false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -244,6 +244,8 @@ class _ImageFavoritesPhotoViewState extends State<ImageFavoritesPhotoView> {
|
|||||||
initialEp: ep,
|
initialEp: ep,
|
||||||
initialPage: page,
|
initialPage: page,
|
||||||
),
|
),
|
||||||
|
enableIOSGesture: false,
|
||||||
|
iosFullScreenGesture: false,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -49,7 +49,10 @@ class _SearchPageState extends State<SearchPage> {
|
|||||||
void search([String? text]) {
|
void search([String? text]) {
|
||||||
if (aggregatedSearch) {
|
if (aggregatedSearch) {
|
||||||
context
|
context
|
||||||
.to(() => AggregatedSearchPage(keyword: text ?? controller.text))
|
.to(
|
||||||
|
() => AggregatedSearchPage(keyword: text ?? controller.text),
|
||||||
|
iosFullScreenGesture: false,
|
||||||
|
)
|
||||||
.then((_) => update());
|
.then((_) => update());
|
||||||
} else {
|
} else {
|
||||||
context
|
context
|
||||||
@@ -59,6 +62,7 @@ class _SearchPageState extends State<SearchPage> {
|
|||||||
sourceKey: searchTarget,
|
sourceKey: searchTarget,
|
||||||
options: options,
|
options: options,
|
||||||
),
|
),
|
||||||
|
iosFullScreenGesture: false,
|
||||||
)
|
)
|
||||||
.then((_) => update());
|
.then((_) => update());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -252,9 +252,10 @@ class _SettingsPageState extends State<SettingsPage> implements PopEntry {
|
|||||||
if (!App.isIOS) {
|
if (!App.isIOS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.position.dx < 20) {
|
if (currentPage == -1) {
|
||||||
gestureRecognizer.addPointer(event);
|
return;
|
||||||
}
|
}
|
||||||
|
gestureRecognizer.addPointer(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildLeft() {
|
Widget buildLeft() {
|
||||||
|
|||||||
Reference in New Issue
Block a user