load illust with id; context menu

This commit is contained in:
wgh19
2024-05-14 22:15:05 +08:00
parent faa857b814
commit a8b0495fc6
6 changed files with 151 additions and 66 deletions

View File

@@ -2,6 +2,8 @@ import 'package:fluent_ui/fluent_ui.dart';
import 'package:pixes/components/animated_image.dart'; import 'package:pixes/components/animated_image.dart';
import 'package:pixes/foundation/app.dart'; import 'package:pixes/foundation/app.dart';
import 'package:pixes/foundation/image_provider.dart'; import 'package:pixes/foundation/image_provider.dart';
import 'package:pixes/network/download.dart';
import 'package:pixes/utils/translation.dart';
import '../network/network.dart'; import '../network/network.dart';
import '../pages/illust_page.dart'; import '../pages/illust_page.dart';
@@ -19,74 +21,118 @@ class IllustWidget extends StatefulWidget {
class _IllustWidgetState extends State<IllustWidget> { class _IllustWidgetState extends State<IllustWidget> {
bool isBookmarking = false; bool isBookmarking = false;
final contextController = FlyoutController();
final contextAttachKey = GlobalKey();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constrains) { return LayoutBuilder(builder: (context, constrains) {
final width = constrains.maxWidth; final width = constrains.maxWidth;
final height = widget.illust.height * width / widget.illust.width; final height = widget.illust.height * width / widget.illust.width;
return SizedBox( return FlyoutTarget(
width: width, controller: contextController,
height: height, child: SizedBox(
child: Stack( key: contextAttachKey,
children: [ width: width,
Positioned.fill(child: Container( height: height,
width: width, child: Stack(
height: height, children: [
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0), Positioned.fill(child: Container(
child: Card( width: width,
padding: EdgeInsets.zero, height: height,
margin: EdgeInsets.zero, padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0),
child: GestureDetector( child: Card(
onTap: (){ padding: EdgeInsets.zero,
context.to(() => IllustPage(widget.illust, favoriteCallback: (v) { margin: EdgeInsets.zero,
setState(() { child: GestureDetector(
widget.illust.isBookmarked = v; onTap: (){
}); context.to(() => IllustPage(widget.illust, favoriteCallback: (v) {
},)); setState(() {
}, widget.illust.isBookmarked = v;
child: ClipRRect( });
borderRadius: BorderRadius.circular(4.0), },));
child: AnimatedImage( },
image: CachedImageProvider(widget.illust.images.first.medium), onSecondaryTapUp: showMenu,
fit: BoxFit.cover, child: ClipRRect(
width: width-16.0, borderRadius: BorderRadius.circular(4.0),
height: height-16.0, child: AnimatedImage(
image: CachedImageProvider(widget.illust.images.first.medium),
fit: BoxFit.cover,
width: width-16.0,
height: height-16.0,
),
), ),
), ),
), ),
), )),
)), Positioned(
Positioned( top: 16,
top: 16, right: 16,
right: 16, child: buildButton(),
child: buildButton(), )
) ],
], ),
), ),
); );
}); });
} }
Widget buildButton() { void showMenu(TapUpDetails details) {
void favorite() async{ // This calculates the position of the flyout according to the parent navigator
if(isBookmarking) return; final targetContext = contextAttachKey.currentContext;
setState(() { if (targetContext == null) return;
isBookmarking = true; final box = targetContext.findRenderObject() as RenderBox;
}); final position = box.localToGlobal(
var method = widget.illust.isBookmarked ? "delete" : "add"; details.localPosition,
var res = await Network().addBookmark(widget.illust.id.toString(), method); ancestor: Navigator.of(context).context.findRenderObject(),
if(res.error) { );
if(mounted) {
context.showToast(message: "Network Error");
}
} else {
widget.illust.isBookmarked = !widget.illust.isBookmarked;
}
setState(() {
isBookmarking = false;
});
}
contextController.showFlyout(
barrierColor: Colors.transparent,
position: position,
builder: (context) {
return MenuFlyout(
items: [
MenuFlyoutItem(text: Text("View".tl), onPressed: (){
context.to(() => IllustPage(widget.illust, favoriteCallback: (v) {
setState(() {
widget.illust.isBookmarked = v;
});
},));
}),
MenuFlyoutItem(text: Text("Private Favorite".tl), onPressed: (){
favorite("private");
}),
MenuFlyoutItem(text: Text("Download".tl), onPressed: (){
context.showToast(message: "Added");
DownloadManager().addDownloadingTask(widget.illust);
}),
],
);
},
);
}
void favorite([String type = "public"]) async{
if(isBookmarking) return;
setState(() {
isBookmarking = true;
});
var method = widget.illust.isBookmarked ? "delete" : "add";
var res = await Network().addBookmark(widget.illust.id.toString(), method, type);
if(res.error) {
if(mounted) {
context.showToast(message: "Network Error");
}
} else {
widget.illust.isBookmarked = !widget.illust.isBookmarked;
}
setState(() {
isBookmarking = false;
});
}
Widget buildButton() {
Widget child; Widget child;
if(isBookmarking) { if(isBookmarking) {
child = const SizedBox( child = const SizedBox(

View File

@@ -233,6 +233,7 @@ class DownloadManager {
} }
void addDownloadingTask(Illust illust) { void addDownloadingTask(Illust illust) {
if(illust.downloaded || illust.downloading) return;
var task = DownloadingTask(illust, receiveBytesCallback: receiveBytes, onCompleted: (task) { var task = DownloadingTask(illust, receiveBytesCallback: receiveBytes, onCompleted: (task) {
saveInfo(illust, task.imagePaths); saveInfo(illust, task.imagePaths);
tasks.remove(task); tasks.remove(task);

View File

@@ -391,4 +391,13 @@ class Network {
return Res.fromErrorRes(res); return Res.fromErrorRes(res);
} }
} }
Future<Res<Illust>> getIllustByID(String id) async {
var res = await apiGet("/v1/illust/detail?illust_id=$id");
if (res.success) {
return Res(Illust.fromJson(res.data["illust"]));
} else {
return Res.error(res.errorMessage);
}
}
} }

View File

@@ -9,6 +9,7 @@ import 'package:pixes/components/page_route.dart';
import 'package:pixes/components/title_bar.dart'; import 'package:pixes/components/title_bar.dart';
import 'package:pixes/foundation/app.dart'; import 'package:pixes/foundation/app.dart';
import 'package:pixes/network/download.dart'; import 'package:pixes/network/download.dart';
import 'package:pixes/pages/illust_page.dart';
import 'package:pixes/utils/translation.dart'; import 'package:pixes/utils/translation.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
@@ -54,7 +55,7 @@ class _DownloadedPageState extends State<DownloadedPage> {
return GridViewWithFixedItemHeight( return GridViewWithFixedItemHeight(
itemCount: illusts.length, itemCount: illusts.length,
itemHeight: 152, itemHeight: 152,
maxCrossAxisExtent: 560, maxCrossAxisExtent: 742,
builder: (context, index) { builder: (context, index) {
return Card( return Card(
margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
@@ -113,14 +114,22 @@ class _DownloadedPageState extends State<DownloadedPage> {
child: Text("View".tl).fixWidth(42), child: Text("View".tl).fixWidth(42),
onPressed: () { onPressed: () {
App.rootNavigatorKey.currentState?.push( App.rootNavigatorKey.currentState?.push(
AppPageRoute(builder: (context) { AppPageRoute(builder: (context) {
return _DownloadedIllustViewPage( return _DownloadedIllustViewPage(
DownloadManager().getImagePaths( DownloadManager().getImagePaths(
illusts[index].illustId)); illusts[index].illustId));
})); }));
}, },
), ),
const SizedBox(width: 8), const SizedBox(width: 6),
Button(
child: Text("Info".tl).fixWidth(42),
onPressed: () {
context.to(() => IllustPageWithId(
illusts[index].illustId.toString()));
},
),
const SizedBox(width: 6),
FlyoutTarget( FlyoutTarget(
controller: flyoutControllers[index], controller: flyoutControllers[index],
child: Button( child: Button(

View File

@@ -887,3 +887,23 @@ class _CommentsPageState extends MultiPageLoadingState<_CommentsPage, Comment> {
} }
class IllustPageWithId extends StatefulWidget {
const IllustPageWithId(this.id, {super.key});
final String id;
@override
State<IllustPageWithId> createState() => _IllustPageWithIdState();
}
class _IllustPageWithIdState extends LoadingState<IllustPageWithId, Illust> {
@override
Widget buildContent(BuildContext context, Illust data) {
return IllustPage(data);
}
@override
Future<Res<Illust>> loadData() {
return Network().getIllustByID(widget.id);
}
}

View File

@@ -2,10 +2,12 @@ import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:pixes/appdata.dart'; import 'package:pixes/appdata.dart';
import 'package:pixes/components/loading.dart'; import 'package:pixes/components/loading.dart';
import 'package:pixes/components/message.dart';
import 'package:pixes/components/page_route.dart'; import 'package:pixes/components/page_route.dart';
import 'package:pixes/components/user_preview.dart'; import 'package:pixes/components/user_preview.dart';
import 'package:pixes/foundation/app.dart'; import 'package:pixes/foundation/app.dart';
import 'package:pixes/network/network.dart'; import 'package:pixes/network/network.dart';
import 'package:pixes/pages/illust_page.dart';
import 'package:pixes/pages/user_info_page.dart'; import 'package:pixes/pages/user_info_page.dart';
import 'package:pixes/utils/translation.dart'; import 'package:pixes/utils/translation.dart';
@@ -41,17 +43,15 @@ class _SearchPageState extends State<SearchPage> {
case 0: case 0:
context.to(() => SearchResultPage(text)); context.to(() => SearchResultPage(text));
case 1: case 1:
// TODO: novel search showToast(context, message: "Not implemented");
case 2: case 2:
context.to(() => SearchUserResultPage(text)); context.to(() => SearchUserResultPage(text));
case 3: case 3:
// TODO: artwork id context.to(() => IllustPageWithId(text));
throw UnimplementedError();
case 4: case 4:
context.to(() => UserInfoPage(text)); context.to(() => UserInfoPage(text));
case 5: case 5:
// TODO: novel id showToast(context, message: "Not implemented");
throw UnimplementedError();
} }
} }