mirror of
https://github.com/wgh136/pixes.git
synced 2025-09-27 12:57:24 +00:00
add actions
This commit is contained in:
@@ -28,7 +28,7 @@ class ToastOverlay extends StatelessWidget {
|
|||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
child: PhysicalModel(
|
child: PhysicalModel(
|
||||||
color: ColorScheme.of(context).surface,
|
color: ColorScheme.of(context).surface.withOpacity(1),
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
child: Container(
|
child: Container(
|
||||||
|
@@ -10,8 +10,10 @@ 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/utils/translation.dart';
|
import 'package:pixes/utils/translation.dart';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
|
import '../utils/io.dart';
|
||||||
import 'main_page.dart';
|
import 'main_page.dart';
|
||||||
|
|
||||||
class DownloadedPage extends StatefulWidget {
|
class DownloadedPage extends StatefulWidget {
|
||||||
@@ -208,6 +210,46 @@ class _DownloadedIllustViewPageState extends State<_DownloadedIllustViewPage> wi
|
|||||||
|
|
||||||
var controller = PageController();
|
var controller = PageController();
|
||||||
|
|
||||||
|
int currentPage = 0;
|
||||||
|
|
||||||
|
var menuController = FlyoutController();
|
||||||
|
|
||||||
|
Future<File?> getFile() async {
|
||||||
|
var file = File(widget.imagePaths[currentPage]);
|
||||||
|
if(file.existsSync()) {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void showMenu() {
|
||||||
|
menuController.showFlyout(builder: (context) => MenuFlyout(
|
||||||
|
items: [
|
||||||
|
MenuFlyoutItem(text: Text("Save to".tl), onPressed: () async{
|
||||||
|
var file = await getFile();
|
||||||
|
if(file != null){
|
||||||
|
saveFile(file);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
MenuFlyoutItem(text: Text("Share".tl), onPressed: () async{
|
||||||
|
var file = await getFile();
|
||||||
|
if(file != null){
|
||||||
|
var ext = file.path.split('.').last;
|
||||||
|
var mediaType = switch(ext){
|
||||||
|
'jpg' => 'image/jpeg',
|
||||||
|
'jpeg' => 'image/jpeg',
|
||||||
|
'png' => 'image/png',
|
||||||
|
'gif' => 'image/gif',
|
||||||
|
'webp' => 'image/webp',
|
||||||
|
_ => 'application/octet-stream'
|
||||||
|
};
|
||||||
|
Share.shareXFiles([XFile(file.path, mimeType: mediaType, name: file.path.split('/').last)]);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ScaffoldPage(
|
return ScaffoldPage(
|
||||||
@@ -240,7 +282,9 @@ class _DownloadedIllustViewPageState extends State<_DownloadedIllustViewPage> wi
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
onPageChanged: (index) {
|
onPageChanged: (index) {
|
||||||
setState(() {});
|
setState(() {
|
||||||
|
currentPage = index;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
Positioned(
|
Positioned(
|
||||||
@@ -259,6 +303,7 @@ class _DownloadedIllustViewPageState extends State<_DownloadedIllustViewPage> wi
|
|||||||
const Expanded(
|
const Expanded(
|
||||||
child: DragToMoveArea(child: SizedBox.expand(),),
|
child: DragToMoveArea(child: SizedBox.expand(),),
|
||||||
),
|
),
|
||||||
|
buildActions(),
|
||||||
if(App.isDesktop)
|
if(App.isDesktop)
|
||||||
WindowButtons(key: ValueKey(windowButtonKey),),
|
WindowButtons(key: ValueKey(windowButtonKey),),
|
||||||
],
|
],
|
||||||
@@ -295,7 +340,7 @@ class _DownloadedIllustViewPageState extends State<_DownloadedIllustViewPage> wi
|
|||||||
left: 12,
|
left: 12,
|
||||||
bottom: 8,
|
bottom: 8,
|
||||||
child: Text(
|
child: Text(
|
||||||
"${controller.page!.toInt() + 1}/${widget.imagePaths.length}",
|
"${currentPage + 1}/${widget.imagePaths.length}",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@@ -305,5 +350,33 @@ class _DownloadedIllustViewPageState extends State<_DownloadedIllustViewPage> wi
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget buildActions() {
|
||||||
|
var width = MediaQuery.of(context).size.width;
|
||||||
|
return FlyoutTarget(
|
||||||
|
controller: menuController,
|
||||||
|
child: width > 600
|
||||||
|
? Button(
|
||||||
|
onPressed: showMenu,
|
||||||
|
child: const Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
MdIcons.menu,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
Text('Actions'),
|
||||||
|
],
|
||||||
|
))
|
||||||
|
: IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
MdIcons.more_horiz,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
onPressed: showMenu),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,6 +3,7 @@ import 'dart:io';
|
|||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart' show Icons;
|
import 'package:flutter/material.dart' show Icons;
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:pixes/components/animated_image.dart';
|
import 'package:pixes/components/animated_image.dart';
|
||||||
import 'package:pixes/components/loading.dart';
|
import 'package:pixes/components/loading.dart';
|
||||||
import 'package:pixes/components/message.dart';
|
import 'package:pixes/components/message.dart';
|
||||||
@@ -15,6 +16,7 @@ import 'package:pixes/pages/image_page.dart';
|
|||||||
import 'package:pixes/pages/search_page.dart';
|
import 'package:pixes/pages/search_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';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
import '../components/md.dart';
|
import '../components/md.dart';
|
||||||
|
|
||||||
@@ -203,18 +205,21 @@ class _BottomBarState extends State<_BottomBar> with TickerProviderStateMixin{
|
|||||||
top = (top + offset).clamp(minValue, maxValue);
|
top = (top + offset).clamp(minValue, maxValue);
|
||||||
animationController.value = (top - minValue) / (maxValue - minValue);
|
animationController.value = (top - minValue) / (maxValue - minValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handlePointerUp(DragEndDetails details) {
|
void _handlePointerUp(DragEndDetails details) {
|
||||||
var speed = details.primaryVelocity ?? 0;
|
var speed = details.primaryVelocity ?? 0;
|
||||||
const minShouldTransitionSpeed = 1000;
|
const minShouldTransitionSpeed = 1000;
|
||||||
if(speed > minShouldTransitionSpeed) {
|
if(speed > minShouldTransitionSpeed) {
|
||||||
animationController.forward();
|
animationController.forward();
|
||||||
} else if(speed < minShouldTransitionSpeed) {
|
} else if(speed < 0 - minShouldTransitionSpeed) {
|
||||||
animationController.reverse();
|
animationController.reverse();
|
||||||
} else {
|
} else {
|
||||||
_handlePointerCancel();
|
_handlePointerCancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handlePointerCancel() {
|
void _handlePointerCancel() {
|
||||||
|
if(animationController.value == 1 || animationController.value == 0) return;
|
||||||
if(animationController.value >= 0.5 ) {
|
if(animationController.value >= 0.5 ) {
|
||||||
animationController.forward();
|
animationController.forward();
|
||||||
} else {
|
} else {
|
||||||
@@ -261,6 +266,16 @@ class _BottomBarState extends State<_BottomBar> with TickerProviderStateMixin{
|
|||||||
onPointerDown: (event) {
|
onPointerDown: (event) {
|
||||||
_recognizer.addPointer(event);
|
_recognizer.addPointer(event);
|
||||||
},
|
},
|
||||||
|
onPointerSignal: (event) {
|
||||||
|
if(event is PointerScrollEvent) {
|
||||||
|
var offset = (event).scrollDelta.dy;
|
||||||
|
if(offset < 0) {
|
||||||
|
animationController.reverse();
|
||||||
|
} else {
|
||||||
|
animationController.forward();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
child: Card(
|
child: Card(
|
||||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(8)),
|
borderRadius: const BorderRadius.vertical(top: Radius.circular(8)),
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
@@ -275,6 +290,7 @@ class _BottomBarState extends State<_BottomBar> with TickerProviderStateMixin{
|
|||||||
buildTop(),
|
buildTop(),
|
||||||
buildStats(),
|
buildStats(),
|
||||||
buildTags(),
|
buildTags(),
|
||||||
|
buildMoreActions(),
|
||||||
SelectableText("${"Artwork ID".tl}: ${widget.illust.id}\n${"Artist ID".tl}: ${widget.illust.author.id}", style: TextStyle(color: ColorScheme.of(context).outline),).paddingLeft(4),
|
SelectableText("${"Artwork ID".tl}: ${widget.illust.id}\n${"Artist ID".tl}: ${widget.illust.author.id}", style: TextStyle(color: ColorScheme.of(context).outline),).paddingLeft(4),
|
||||||
const SizedBox(height: 8,)
|
const SizedBox(height: 8,)
|
||||||
],
|
],
|
||||||
@@ -395,14 +411,14 @@ class _BottomBarState extends State<_BottomBar> with TickerProviderStateMixin{
|
|||||||
),
|
),
|
||||||
))
|
))
|
||||||
else if (!widget.illust.author.isFollowed)
|
else if (!widget.illust.author.isFollowed)
|
||||||
Button(onPressed: follow, child: Text("Follow".tl).fixWidth(56))
|
Button(onPressed: follow, child: Text("Follow".tl).fixWidth(62))
|
||||||
else
|
else
|
||||||
Button(
|
Button(
|
||||||
onPressed: follow,
|
onPressed: follow,
|
||||||
child: Text(
|
child: Text(
|
||||||
"Unfollow".tl,
|
"Unfollow".tl,
|
||||||
style: TextStyle(color: ColorScheme.of(context).error),
|
style: TextStyle(color: ColorScheme.of(context).error),
|
||||||
).fixWidth(56),
|
).fixWidth(62),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -412,29 +428,29 @@ class _BottomBarState extends State<_BottomBar> with TickerProviderStateMixin{
|
|||||||
|
|
||||||
bool isBookmarking = false;
|
bool isBookmarking = false;
|
||||||
|
|
||||||
|
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;
|
||||||
|
widget.favoriteCallback?.call(widget.illust.isBookmarked);
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
isBookmarking = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Iterable<Widget> buildActions(double width) sync* {
|
Iterable<Widget> buildActions(double width) sync* {
|
||||||
yield const SizedBox(width: 8,);
|
yield const SizedBox(width: 8,);
|
||||||
|
|
||||||
void favorite() async{
|
|
||||||
if(isBookmarking) return;
|
|
||||||
setState(() {
|
|
||||||
isBookmarking = true;
|
|
||||||
});
|
|
||||||
var method = widget.illust.isBookmarked ? "delete" : "add";
|
|
||||||
var res = await Network().addBookmark(widget.illust.id.toString(), method);
|
|
||||||
if(res.error) {
|
|
||||||
if(mounted) {
|
|
||||||
context.showToast(message: "Network Error");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
widget.illust.isBookmarked = !widget.illust.isBookmarked;
|
|
||||||
widget.favoriteCallback?.call(widget.illust.isBookmarked);
|
|
||||||
}
|
|
||||||
setState(() {
|
|
||||||
isBookmarking = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void download() {
|
void download() {
|
||||||
DownloadManager().addDownloadingTask(widget.illust);
|
DownloadManager().addDownloadingTask(widget.illust);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
@@ -629,6 +645,87 @@ class _BottomBarState extends State<_BottomBar> with TickerProviderStateMixin{
|
|||||||
),
|
),
|
||||||
).paddingVertical(8).paddingHorizontal(2);
|
).paddingVertical(8).paddingHorizontal(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget buildMoreActions() {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Button(
|
||||||
|
onPressed: () => favorite("private"),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 28,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
if(isBookmarking)
|
||||||
|
const SizedBox(
|
||||||
|
width: 18,
|
||||||
|
height: 18,
|
||||||
|
child: ProgressRing(strokeWidth: 2,),
|
||||||
|
)
|
||||||
|
else if(widget.illust.isBookmarked)
|
||||||
|
Icon(
|
||||||
|
Icons.favorite,
|
||||||
|
color: ColorScheme.of(context).error,
|
||||||
|
size: 18,
|
||||||
|
)
|
||||||
|
else
|
||||||
|
const Icon(
|
||||||
|
Icons.favorite_border,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8,),
|
||||||
|
if(widget.illust.isBookmarked)
|
||||||
|
Text("Cancel".tl)
|
||||||
|
else
|
||||||
|
Text("Private".tl)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8,),
|
||||||
|
Button(
|
||||||
|
onPressed: () {
|
||||||
|
Share.share("${widget.illust.title}\nhttps://pixiv.net/artworks/${widget.illust.id}");
|
||||||
|
},
|
||||||
|
child: SizedBox(
|
||||||
|
height: 28,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.share,
|
||||||
|
color: ColorScheme.of(context).error,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8,),
|
||||||
|
Text("Share".tl)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8,),
|
||||||
|
Button(
|
||||||
|
onPressed: () {
|
||||||
|
var text = "https://pixiv.net/artworks/${widget.illust.id}";
|
||||||
|
Clipboard.setData(ClipboardData(text: text));
|
||||||
|
showToast(context, message: "Copied".tl);
|
||||||
|
},
|
||||||
|
child: SizedBox(
|
||||||
|
height: 28,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.copy,
|
||||||
|
color: ColorScheme.of(context).error,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8,),
|
||||||
|
Text("Link".tl)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingHorizontal(4).paddingBottom(4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CommentsPage extends StatefulWidget {
|
class _CommentsPage extends StatefulWidget {
|
||||||
|
@@ -2,10 +2,15 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:photo_view/photo_view.dart';
|
import 'package:photo_view/photo_view.dart';
|
||||||
|
import 'package:pixes/components/md.dart';
|
||||||
import 'package:pixes/components/page_route.dart';
|
import 'package:pixes/components/page_route.dart';
|
||||||
import 'package:pixes/foundation/app.dart';
|
import 'package:pixes/foundation/app.dart';
|
||||||
|
import 'package:pixes/foundation/cache_manager.dart';
|
||||||
import 'package:pixes/foundation/image_provider.dart';
|
import 'package:pixes/foundation/image_provider.dart';
|
||||||
import 'package:pixes/pages/main_page.dart';
|
import 'package:pixes/pages/main_page.dart';
|
||||||
|
import 'package:pixes/utils/io.dart';
|
||||||
|
import 'package:pixes/utils/translation.dart';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
class ImagePage extends StatefulWidget {
|
class ImagePage extends StatefulWidget {
|
||||||
@@ -14,15 +19,15 @@ class ImagePage extends StatefulWidget {
|
|||||||
final String url;
|
final String url;
|
||||||
|
|
||||||
static show(String url) {
|
static show(String url) {
|
||||||
App.rootNavigatorKey.currentState?.push(
|
App.rootNavigatorKey.currentState
|
||||||
AppPageRoute(builder: (context) => ImagePage(url)));
|
?.push(AppPageRoute(builder: (context) => ImagePage(url)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ImagePage> createState() => _ImagePageState();
|
State<ImagePage> createState() => _ImagePageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ImagePageState extends State<ImagePage> with WindowListener{
|
class _ImagePageState extends State<ImagePage> with WindowListener {
|
||||||
int windowButtonKey = 0;
|
int windowButtonKey = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -53,14 +58,15 @@ class _ImagePageState extends State<ImagePage> with WindowListener{
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ScaffoldPage(
|
return Container(
|
||||||
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
|
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
|
||||||
content: Stack(
|
color: FluentTheme.of(context).micaBackgroundColor,
|
||||||
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
Positioned.fill(child: PhotoView(
|
Positioned.fill(
|
||||||
backgroundDecoration: const BoxDecoration(
|
child: PhotoView(
|
||||||
color: Colors.transparent
|
backgroundDecoration: BoxDecoration(
|
||||||
),
|
color: FluentTheme.of(context).micaBackgroundColor),
|
||||||
filterQuality: FilterQuality.medium,
|
filterQuality: FilterQuality.medium,
|
||||||
imageProvider: widget.url.startsWith("file://")
|
imageProvider: widget.url.startsWith("file://")
|
||||||
? FileImage(File(widget.url.replaceFirst("file://", "")))
|
? FileImage(File(widget.url.replaceFirst("file://", "")))
|
||||||
@@ -74,16 +80,22 @@ class _ImagePageState extends State<ImagePage> with WindowListener{
|
|||||||
height: 36,
|
height: 36,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(width: 6,),
|
const SizedBox(
|
||||||
|
width: 6,
|
||||||
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(FluentIcons.back).paddingAll(2),
|
icon: const Icon(FluentIcons.back).paddingAll(2),
|
||||||
onPressed: () => context.pop()
|
onPressed: () => context.pop()),
|
||||||
),
|
|
||||||
const Expanded(
|
const Expanded(
|
||||||
child: DragToMoveArea(child: SizedBox.expand(),),
|
child: DragToMoveArea(
|
||||||
|
child: SizedBox.expand(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
if(App.isDesktop)
|
buildActions(),
|
||||||
WindowButtons(key: ValueKey(windowButtonKey),),
|
if (App.isDesktop)
|
||||||
|
WindowButtons(
|
||||||
|
key: ValueKey(windowButtonKey),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -92,4 +104,73 @@ class _ImagePageState extends State<ImagePage> with WindowListener{
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var menuController = FlyoutController();
|
||||||
|
|
||||||
|
Future<File?> getFile() async{
|
||||||
|
if (widget.url.startsWith("file://")) {
|
||||||
|
return File(widget.url.replaceFirst("file://", ""));
|
||||||
|
}
|
||||||
|
var res = await CacheManager().findCache(widget.url);
|
||||||
|
if(res == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return File(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showMenu() {
|
||||||
|
menuController.showFlyout(builder: (context) => MenuFlyout(
|
||||||
|
items: [
|
||||||
|
MenuFlyoutItem(text: Text("Save to".tl), onPressed: () async{
|
||||||
|
var file = await getFile();
|
||||||
|
if(file != null){
|
||||||
|
saveFile(file);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
MenuFlyoutItem(text: Text("Share".tl), onPressed: () async{
|
||||||
|
var file = await getFile();
|
||||||
|
if(file != null){
|
||||||
|
var ext = file.path.split('.').last;
|
||||||
|
var mediaType = switch(ext){
|
||||||
|
'jpg' => 'image/jpeg',
|
||||||
|
'jpeg' => 'image/jpeg',
|
||||||
|
'png' => 'image/png',
|
||||||
|
'gif' => 'image/gif',
|
||||||
|
'webp' => 'image/webp',
|
||||||
|
_ => 'application/octet-stream'
|
||||||
|
};
|
||||||
|
Share.shareXFiles([XFile(file.path, mimeType: mediaType, name: file.path.split('/').last)]);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildActions() {
|
||||||
|
var width = MediaQuery.of(context).size.width;
|
||||||
|
return FlyoutTarget(
|
||||||
|
controller: menuController,
|
||||||
|
child: width > 600
|
||||||
|
? Button(
|
||||||
|
onPressed: showMenu,
|
||||||
|
child: const Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
MdIcons.menu,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
Text('Actions'),
|
||||||
|
],
|
||||||
|
))
|
||||||
|
: IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
MdIcons.more_horiz,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
onPressed: showMenu),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -43,11 +43,28 @@ class _UserInfoPageState extends LoadingState<UserInfoPage, UserDetails> {
|
|||||||
|
|
||||||
void follow() async{
|
void follow() async{
|
||||||
if(isFollowing) return;
|
if(isFollowing) return;
|
||||||
|
String type = "";
|
||||||
|
if(!data!.isFollowed) {
|
||||||
|
await flyoutController.showFlyout(
|
||||||
|
navigatorKey: App.rootNavigatorKey.currentState,
|
||||||
|
builder: (context) =>
|
||||||
|
MenuFlyout(
|
||||||
|
items: [
|
||||||
|
MenuFlyoutItem(text: Text("Public".tl),
|
||||||
|
onPressed: () => type = "public"),
|
||||||
|
MenuFlyoutItem(text: Text("Private".tl),
|
||||||
|
onPressed: () => type = "private"),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if(type.isEmpty && !data!.isFollowed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
isFollowing = true;
|
isFollowing = true;
|
||||||
});
|
});
|
||||||
var method = data!.isFollowed ? "delete" : "add";
|
var method = data!.isFollowed ? "delete" : "add";
|
||||||
var res = await Network().follow(data!.id.toString(), method);
|
var res = await Network().follow(data!.id.toString(), method, type);
|
||||||
if(res.error) {
|
if(res.error) {
|
||||||
if(mounted) {
|
if(mounted) {
|
||||||
context.showToast(message: "Network Error");
|
context.showToast(message: "Network Error");
|
||||||
@@ -61,6 +78,8 @@ class _UserInfoPageState extends LoadingState<UserInfoPage, UserDetails> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var flyoutController = FlyoutController();
|
||||||
|
|
||||||
Widget buildUser() {
|
Widget buildUser() {
|
||||||
return SliverToBoxAdapter(
|
return SliverToBoxAdapter(
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -112,7 +131,10 @@ class _UserInfoPageState extends LoadingState<UserInfoPage, UserDetails> {
|
|||||||
),
|
),
|
||||||
))
|
))
|
||||||
else if (!data!.isFollowed)
|
else if (!data!.isFollowed)
|
||||||
Button(onPressed: follow, child: Text("Follow".tl))
|
FlyoutTarget(
|
||||||
|
controller: flyoutController,
|
||||||
|
child: Button(onPressed: follow, child: Text("Follow".tl))
|
||||||
|
)
|
||||||
else
|
else
|
||||||
Button(
|
Button(
|
||||||
onPressed: follow,
|
onPressed: follow,
|
||||||
|
@@ -1,4 +1,9 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:file_selector/file_selector.dart';
|
||||||
|
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
|
||||||
|
import 'package:pixes/foundation/app.dart';
|
||||||
|
|
||||||
extension FSExt on FileSystemEntity {
|
extension FSExt on FileSystemEntity {
|
||||||
Future<void> deleteIfExists() async {
|
Future<void> deleteIfExists() async {
|
||||||
@@ -44,3 +49,23 @@ String bytesToText(int bytes) {
|
|||||||
return "${(bytes / 1024 / 1024 / 1024).toStringAsFixed(2)} GB";
|
return "${(bytes / 1024 / 1024 / 1024).toStringAsFixed(2)} GB";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void saveFile(File file) async{
|
||||||
|
if(App.isDesktop) {
|
||||||
|
var fileName = file.path.split('/').last;
|
||||||
|
final FileSaveLocation? result =
|
||||||
|
await getSaveLocation(suggestedName: fileName);
|
||||||
|
if (result == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Uint8List fileData = await file.readAsBytes();
|
||||||
|
String mimeType = 'image/${fileName.split('.').last}';
|
||||||
|
final XFile textFile = XFile.fromData(
|
||||||
|
fileData, mimeType: mimeType, name: fileName);
|
||||||
|
await textFile.saveTo(result.path);
|
||||||
|
} else {
|
||||||
|
final params = SaveFileDialogParams(sourceFilePath: file.path);
|
||||||
|
await FlutterFileDialog.saveFile(params: params);
|
||||||
|
}
|
||||||
|
}
|
@@ -40,5 +40,7 @@
|
|||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
|
<key>com.apple.security.files.user-selected.read-write</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
144
pubspec.lock
144
pubspec.lock
@@ -49,6 +49,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.18.0"
|
version: "1.18.0"
|
||||||
|
cross_file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cross_file
|
||||||
|
sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.4+1"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -81,6 +89,86 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
|
file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file
|
||||||
|
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.0"
|
||||||
|
file_selector:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: file_selector
|
||||||
|
sha256: "5019692b593455127794d5718304ff1ae15447dea286cdda9f0db2a796a1b828"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.3"
|
||||||
|
file_selector_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file_selector_android
|
||||||
|
sha256: "57265ec9591e8fd8508f613544cde6f7d045731f6b09644057e49a4c9c672b7c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.1+1"
|
||||||
|
file_selector_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file_selector_ios
|
||||||
|
sha256: "7160121e434910ec23717bde3a0c514ca039e8c97b791ff35d1786da38abcb4a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.2"
|
||||||
|
file_selector_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file_selector_linux
|
||||||
|
sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.2+1"
|
||||||
|
file_selector_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file_selector_macos
|
||||||
|
sha256: f42eacb83b318e183b1ae24eead1373ab1334084404c8c16e0354f9a3e55d385
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.4"
|
||||||
|
file_selector_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file_selector_platform_interface
|
||||||
|
sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.6.2"
|
||||||
|
file_selector_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file_selector_web
|
||||||
|
sha256: "619e431b224711a3869e30dbd7d516f5f5a4f04b265013a50912f39e1abc88c8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.4+1"
|
||||||
|
file_selector_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file_selector_windows
|
||||||
|
sha256: d3547240c20cabf205c7c7f01a50ecdbc413755814d6677f3cb366f04abcead0
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.3+1"
|
||||||
|
fixnum:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fixnum
|
||||||
|
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
fluent_ui:
|
fluent_ui:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -94,6 +182,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_file_dialog:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_file_dialog
|
||||||
|
sha256: "5a1507833473b38839056d63c5125750a6d12e904f78131324fa4632504de513"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.1"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@@ -133,6 +229,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.0"
|
||||||
|
http:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http
|
||||||
|
sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -213,6 +317,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.0"
|
version: "1.11.0"
|
||||||
|
mime:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: mime
|
||||||
|
sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.5"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -318,6 +430,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.0"
|
version: "0.5.0"
|
||||||
|
share_plus:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: share_plus
|
||||||
|
sha256: ef3489a969683c4f3d0239010cc8b7a2a46543a8d139e111c06c558875083544
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.0.0"
|
||||||
|
share_plus_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: share_plus_platform_interface
|
||||||
|
sha256: "0f9e4418835d1b2c3ae78fdb918251959106cefdbc4dd43526e182f80e82f6d4"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.0"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -331,6 +459,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0"
|
version: "1.10.0"
|
||||||
|
sprintf:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sprintf
|
||||||
|
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.0"
|
||||||
sqlite3:
|
sqlite3:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -475,6 +611,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.1"
|
||||||
|
uuid:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: uuid
|
||||||
|
sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.4.0"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@@ -51,6 +51,9 @@ dependencies:
|
|||||||
git:
|
git:
|
||||||
url: https://github.com/wgh136/photo_view
|
url: https://github.com/wgh136/photo_view
|
||||||
ref: main
|
ref: main
|
||||||
|
share_plus: ^9.0.0
|
||||||
|
file_selector: ^1.0.1
|
||||||
|
flutter_file_dialog: 3.0.1
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
Reference in New Issue
Block a user