diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index eda8cc3..420c537 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ { context.to(() => IllustPage(widget.illust)); }, onSecondaryTapUp: showMenu, + onLongPress: showMenu, child: ClipRRect( borderRadius: BorderRadius.circular(4.0), child: AnimatedImage( @@ -211,13 +213,13 @@ class _IllustWidgetState extends State { }); } - void showMenu(TapUpDetails details) { + void showMenu([TapUpDetails? details]) { // This calculates the position of the flyout according to the parent navigator final targetContext = contextAttachKey.currentContext; if (targetContext == null) return; final box = targetContext.findRenderObject() as RenderBox; - final position = box.localToGlobal( - details.localPosition, + Offset? position = box.localToGlobal( + details?.localPosition ?? box.size.center(Offset.zero), ancestor: Navigator.of(context).context.findRenderObject(), ); @@ -243,6 +245,12 @@ class _IllustWidgetState extends State { context.showToast(message: "Added"); DownloadManager().addDownloadingTask(widget.illust); }), + MenuFlyoutItem( + text: Text("Related Artworks".tl), + onPressed: () { + context.to( + () => RelatedIllustsPage(widget.illust.id.toString())); + }), ], ); }, @@ -320,150 +328,149 @@ class IllustHistoryWidget extends StatelessWidget { final width = constrains.maxWidth; final height = illust.height * width / illust.width; return SizedBox( - width: width, - height: height, - child: Stack( - children: [ - Positioned.fill( - child: Container( - width: width, - height: height, - padding: - const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0), - child: Card( - padding: EdgeInsets.zero, - margin: EdgeInsets.zero, - child: GestureDetector( - onTap: () { - context.to(() => IllustPageWithId(illust.id.toString())); - }, - child: ClipRRect( - borderRadius: BorderRadius.circular(4.0), - child: AnimatedImage( - image: CachedImageProvider( - illust.imgPath), - fit: BoxFit.cover, - width: width - 16.0, - height: height - 16.0, - ), + width: width, + height: height, + child: Stack( + children: [ + Positioned.fill( + child: Container( + width: width, + height: height, + padding: + const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0), + child: Card( + padding: EdgeInsets.zero, + margin: EdgeInsets.zero, + child: GestureDetector( + onTap: () { + context.to(() => IllustPageWithId(illust.id.toString())); + }, + child: ClipRRect( + borderRadius: BorderRadius.circular(4.0), + child: AnimatedImage( + image: CachedImageProvider(illust.imgPath), + fit: BoxFit.cover, + width: width - 16.0, + height: height - 16.0, ), ), ), - )), - if (illust.imageCount > 1) - Positioned( - top: 12, - left: 12, - child: Container( - width: 28, - height: 20, - decoration: BoxDecoration( - color: FluentTheme.of(context) - .micaBackgroundColor - .withOpacity(0.72), - borderRadius: BorderRadius.circular(4), - border: Border.all( - color: ColorScheme.of(context).outlineVariant, - width: 0.6), + ), + )), + if (illust.imageCount > 1) + Positioned( + top: 12, + left: 12, + child: Container( + width: 28, + height: 20, + decoration: BoxDecoration( + color: FluentTheme.of(context) + .micaBackgroundColor + .withOpacity(0.72), + borderRadius: BorderRadius.circular(4), + border: Border.all( + color: ColorScheme.of(context).outlineVariant, + width: 0.6), + ), + child: Center( + child: Text( + "${illust.imageCount}P", + style: const TextStyle(fontSize: 12), ), - child: Center( - child: Text( - "${illust.imageCount}P", - style: const TextStyle(fontSize: 12), - ), - )), - ), - if (illust.isAi) - Positioned( - bottom: 12, - left: 12, - child: Container( - width: 28, - height: 20, - decoration: BoxDecoration( - color: ColorScheme.of(context) - .errorContainer - .withOpacity(0.8), - borderRadius: BorderRadius.circular(4), - border: Border.all( - color: ColorScheme.of(context).outlineVariant, - width: 0.6), + )), + ), + if (illust.isAi) + Positioned( + bottom: 12, + left: 12, + child: Container( + width: 28, + height: 20, + decoration: BoxDecoration( + color: ColorScheme.of(context) + .errorContainer + .withOpacity(0.8), + borderRadius: BorderRadius.circular(4), + border: Border.all( + color: ColorScheme.of(context).outlineVariant, + width: 0.6), + ), + child: const Center( + child: Text( + "AI", + style: TextStyle(fontSize: 12), ), - child: const Center( - child: Text( - "AI", - style: TextStyle(fontSize: 12), - ), - )), - ), - if (illust.isGif) - Positioned( - bottom: 12, - left: 12, - child: Container( - width: 28, - height: 20, - decoration: BoxDecoration( - color: ColorScheme.of(context) - .primaryContainer - .withOpacity(0.8), - borderRadius: BorderRadius.circular(4), - border: Border.all( - color: ColorScheme.of(context).outlineVariant, - width: 0.6), + )), + ), + if (illust.isGif) + Positioned( + bottom: 12, + left: 12, + child: Container( + width: 28, + height: 20, + decoration: BoxDecoration( + color: ColorScheme.of(context) + .primaryContainer + .withOpacity(0.8), + borderRadius: BorderRadius.circular(4), + border: Border.all( + color: ColorScheme.of(context).outlineVariant, + width: 0.6), + ), + child: const Center( + child: Text( + "GIF", + style: TextStyle(fontSize: 12), ), - child: const Center( - child: Text( - "GIF", - style: TextStyle(fontSize: 12), - ), - )), - ), - if (illust.isR18) - Positioned( - bottom: 12, - right: 12, - child: Container( - width: 28, - height: 20, - decoration: BoxDecoration( - color: ColorScheme.of(context).errorContainer, - borderRadius: BorderRadius.circular(4), - border: Border.all( - color: ColorScheme.of(context).outlineVariant, - width: 0.6), + )), + ), + if (illust.isR18) + Positioned( + bottom: 12, + right: 12, + child: Container( + width: 28, + height: 20, + decoration: BoxDecoration( + color: ColorScheme.of(context).errorContainer, + borderRadius: BorderRadius.circular(4), + border: Border.all( + color: ColorScheme.of(context).outlineVariant, + width: 0.6), + ), + child: const Center( + child: Text( + "R18", + style: TextStyle(fontSize: 12), ), - child: const Center( - child: Text( - "R18", - style: TextStyle(fontSize: 12), - ), - )), - ), - if (illust.isR18G) - Positioned( - bottom: 12, - right: 12, - child: Container( - width: 28, - height: 20, - decoration: BoxDecoration( - color: ColorScheme.of(context).errorContainer, - borderRadius: BorderRadius.circular(4), - border: Border.all( - color: ColorScheme.of(context).outlineVariant, - width: 0.6), + )), + ), + if (illust.isR18G) + Positioned( + bottom: 12, + right: 12, + child: Container( + width: 28, + height: 20, + decoration: BoxDecoration( + color: ColorScheme.of(context).errorContainer, + borderRadius: BorderRadius.circular(4), + border: Border.all( + color: ColorScheme.of(context).outlineVariant, + width: 0.6), + ), + child: const Center( + child: Text( + "R18G", + style: TextStyle(fontSize: 12), ), - child: const Center( - child: Text( - "R18G", - style: TextStyle(fontSize: 12), - ), - )), - ), - ], - ), - ); + )), + ), + ], + ), + ); }); } -} \ No newline at end of file +} diff --git a/lib/foundation/app.dart b/lib/foundation/app.dart index 2da81df..c3ac8a3 100644 --- a/lib/foundation/app.dart +++ b/lib/foundation/app.dart @@ -12,7 +12,7 @@ export "state_controller.dart"; export "navigation.dart"; class _App { - final version = "1.0.7"; + final version = "1.0.8"; bool get isAndroid => Platform.isAndroid; bool get isIOS => Platform.isIOS; diff --git a/lib/pages/illust_page.dart b/lib/pages/illust_page.dart index 34c4b80..5101303 100644 --- a/lib/pages/illust_page.dart +++ b/lib/pages/illust_page.dart @@ -4,7 +4,6 @@ import 'package:fluent_ui/fluent_ui.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart' show Icons; import 'package:flutter/services.dart'; -import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:pixes/appdata.dart'; import 'package:pixes/components/animated_image.dart'; import 'package:pixes/components/keyboard.dart'; @@ -20,6 +19,7 @@ import 'package:pixes/network/download.dart'; import 'package:pixes/network/network.dart'; import 'package:pixes/pages/comments_page.dart'; import 'package:pixes/pages/image_page.dart'; +import 'package:pixes/pages/related_page.dart'; import 'package:pixes/pages/search_page.dart'; import 'package:pixes/pages/user_info_page.dart'; import 'package:pixes/utils/block.dart'; @@ -1140,7 +1140,7 @@ class _BottomBarState extends State<_BottomBar> with TickerProviderStateMixin { ).fixWidth(96), Button( onPressed: () { - context.to(() => _RelatedIllustsPage(widget.illust.id.toString())); + context.to(() => RelatedIllustsPage(widget.illust.id.toString())); }, child: SizedBox( height: 28, @@ -1357,64 +1357,3 @@ class _IllustPageWithIdState extends LoadingState { return Network().getIllustByID(widget.id); } } - -class _RelatedIllustsPage extends StatefulWidget { - const _RelatedIllustsPage(this.id); - - final String id; - - @override - State<_RelatedIllustsPage> createState() => _RelatedIllustsPageState(); -} - -class _RelatedIllustsPageState - extends MultiPageLoadingState<_RelatedIllustsPage, Illust> { - @override - Widget? buildFrame(BuildContext context, Widget child) { - return Column( - children: [ - TitleBar(title: "Related artworks".tl), - Expanded( - child: child, - ) - ], - ); - } - - @override - Widget buildContent(BuildContext context, final List data) { - return LayoutBuilder(builder: (context, constrains) { - return MasonryGridView.builder( - padding: const EdgeInsets.symmetric(horizontal: 8) + - EdgeInsets.only(bottom: context.padding.bottom), - gridDelegate: const SliverSimpleGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 240, - ), - itemCount: data.length, - itemBuilder: (context, index) { - if (index == data.length - 1) { - nextPage(); - } - return IllustWidget(data[index]); - }, - ); - }); - } - - String? nextUrl; - - @override - Future>> loadData(page) async { - if (nextUrl == "end") { - return Res.error("No more data"); - } - var res = nextUrl == null - ? await Network().relatedIllusts(widget.id) - : await Network().getIllustsWithNextUrl(nextUrl!); - if (!res.error) { - nextUrl = res.subData; - nextUrl ??= "end"; - } - return res; - } -} diff --git a/lib/pages/image_page.dart b/lib/pages/image_page.dart index 7d32116..36df683 100644 --- a/lib/pages/image_page.dart +++ b/lib/pages/image_page.dart @@ -188,6 +188,7 @@ class _ImagePageState extends State with WindowListener { var image = widget.urls[index]; return PhotoViewGalleryPageOptions( + filterQuality: FilterQuality.medium, imageProvider: getImageProvider(image), ); }, diff --git a/lib/pages/login_page.dart b/lib/pages/login_page.dart index e16782c..e8c3f0d 100644 --- a/lib/pages/login_page.dart +++ b/lib/pages/login_page.dart @@ -87,7 +87,9 @@ class _LoginPageState extends State { const SizedBox( width: 8, ), - Text("I have read and agree to the Terms of Use".tl) + Expanded( + child: Text("I understand pixes is a free unofficial application.".tl), + ) ], ) ], diff --git a/lib/pages/related_page.dart b/lib/pages/related_page.dart new file mode 100644 index 0000000..e6cdaae --- /dev/null +++ b/lib/pages/related_page.dart @@ -0,0 +1,69 @@ +import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; +import 'package:pixes/components/illust_widget.dart'; +import 'package:pixes/components/loading.dart'; +import 'package:pixes/components/title_bar.dart'; +import 'package:pixes/foundation/app.dart'; +import 'package:pixes/network/network.dart'; +import 'package:pixes/utils/translation.dart'; + +class RelatedIllustsPage extends StatefulWidget { + const RelatedIllustsPage(this.id, {super.key}); + + final String id; + + @override + State createState() => _RelatedIllustsPageState(); +} + +class _RelatedIllustsPageState + extends MultiPageLoadingState { + @override + Widget? buildFrame(BuildContext context, Widget child) { + return Column( + children: [ + TitleBar(title: "Related artworks".tl), + Expanded( + child: child, + ) + ], + ); + } + + @override + Widget buildContent(BuildContext context, final List data) { + return LayoutBuilder(builder: (context, constrains) { + return MasonryGridView.builder( + padding: const EdgeInsets.symmetric(horizontal: 8) + + EdgeInsets.only(bottom: context.padding.bottom), + gridDelegate: const SliverSimpleGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 240, + ), + itemCount: data.length, + itemBuilder: (context, index) { + if (index == data.length - 1) { + nextPage(); + } + return IllustWidget(data[index]); + }, + ); + }); + } + + String? nextUrl; + + @override + Future>> loadData(page) async { + if (nextUrl == "end") { + return Res.error("No more data"); + } + var res = nextUrl == null + ? await Network().relatedIllusts(widget.id) + : await Network().getIllustsWithNextUrl(nextUrl!); + if (!res.error) { + nextUrl = res.subData; + nextUrl ??= "end"; + } + return res; + } +} diff --git a/lib/pages/search_page.dart b/lib/pages/search_page.dart index c51163a..b9e1007 100644 --- a/lib/pages/search_page.dart +++ b/lib/pages/search_page.dart @@ -33,6 +33,8 @@ class _SearchPageState extends State { int searchType = 0; + final focusNode = FocusNode(); + static const searchTypes = [ "Search artwork", "Search novel", @@ -100,6 +102,9 @@ class _SearchPageState extends State { children: [ Expanded( child: TextBox( + focusNode: focusNode, + autofocus: false, + padding: const EdgeInsets.symmetric(horizontal: 12), placeholder: '${searchTypes[searchType].tl} / ${"Open link".tl}', onChanged: (s) => text = s, diff --git a/lib/pages/settings_page.dart b/lib/pages/settings_page.dart index 8fab479..7358775 100644 --- a/lib/pages/settings_page.dart +++ b/lib/pages/settings_page.dart @@ -12,7 +12,6 @@ import 'package:pixes/foundation/app.dart'; import 'package:pixes/pages/main_page.dart'; import 'package:pixes/utils/io.dart'; import 'package:pixes/utils/translation.dart'; -import 'package:pixes/utils/update.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'logs.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 7bf2eed..a9dfebe 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.7+107 +version: 1.0.8+108 environment: sdk: '>=3.3.4 <4.0.0'