mirror of
https://github.com/Myzel394/quid_faciam_hodie.git
synced 2025-06-19 07:35:26 +02:00
added download functionality
This commit is contained in:
parent
351bd6fec9
commit
a2c5dd8237
@ -12,11 +12,12 @@ extension ShowSnackBar on BuildContext {
|
||||
required final String message,
|
||||
final Color backgroundColor = Colors.white,
|
||||
final Duration duration = const Duration(seconds: 4),
|
||||
final BuildContext? context,
|
||||
}) {
|
||||
pendingSnackBar?.close();
|
||||
pendingSnackBar = null;
|
||||
|
||||
return ScaffoldMessenger.of(this).showSnackBar(
|
||||
return ScaffoldMessenger.of(context ?? this).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(message),
|
||||
backgroundColor: backgroundColor,
|
||||
@ -25,14 +26,20 @@ extension ShowSnackBar on BuildContext {
|
||||
);
|
||||
}
|
||||
|
||||
void showErrorSnackBar({required final String message}) {
|
||||
void showErrorSnackBar({
|
||||
required final String message,
|
||||
final BuildContext? context,
|
||||
}) {
|
||||
showSnackBar(
|
||||
message: message,
|
||||
backgroundColor: Colors.red,
|
||||
);
|
||||
}
|
||||
|
||||
void showSuccessSnackBar({required final String message}) {
|
||||
void showSuccessSnackBar({
|
||||
required final String message,
|
||||
final BuildContext? context,
|
||||
}) {
|
||||
showSnackBar(
|
||||
message: message,
|
||||
duration: const Duration(milliseconds: 550),
|
||||
@ -40,7 +47,10 @@ extension ShowSnackBar on BuildContext {
|
||||
);
|
||||
}
|
||||
|
||||
void showPendingSnackBar({required final String message}) {
|
||||
void showPendingSnackBar({
|
||||
required final String message,
|
||||
final BuildContext? context,
|
||||
}) {
|
||||
pendingSnackBar = showSnackBar(
|
||||
message: message,
|
||||
backgroundColor: Colors.yellow,
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:share_location/enums.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
@ -65,7 +66,7 @@ class FileManager {
|
||||
location.split('.').last == 'jpg' ? MemoryType.photo : MemoryType.video;
|
||||
|
||||
try {
|
||||
final file = await downloadFile('memories', location);
|
||||
final file = await getFileData('memories', location);
|
||||
|
||||
return [file, memoryType];
|
||||
} catch (error) {
|
||||
@ -73,13 +74,11 @@ class FileManager {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<Uint8List> downloadFile(
|
||||
final String table,
|
||||
final String path,
|
||||
) async {
|
||||
static Future<Uint8List> getFileData(final String table, final String path,
|
||||
{final bool disableCache = false}) async {
|
||||
final key = '$table:$path';
|
||||
|
||||
if (fileCache.containsKey(key)) {
|
||||
if (!disableCache && fileCache.containsKey(key)) {
|
||||
return fileCache[key]!;
|
||||
}
|
||||
|
||||
@ -96,6 +95,30 @@ class FileManager {
|
||||
return data;
|
||||
}
|
||||
|
||||
static Future<File> downloadFile(
|
||||
final String table,
|
||||
final String path, {
|
||||
final bool disableDownloadCache = false,
|
||||
final bool disableFileCache = false,
|
||||
}) async {
|
||||
final tempDirectory = await getTemporaryDirectory();
|
||||
final filename = '${tempDirectory.path}/$path';
|
||||
final file = File(filename);
|
||||
|
||||
if (!disableFileCache && (await file.exists())) {
|
||||
return file;
|
||||
}
|
||||
|
||||
final data =
|
||||
await getFileData(table, path, disableCache: disableDownloadCache);
|
||||
|
||||
// Create file
|
||||
await file.create(recursive: true);
|
||||
await file.writeAsBytes(data);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
static Future<void> deleteFile(final String path) async {
|
||||
final response =
|
||||
await supabase.from('memories').delete().eq('location', path).execute();
|
||||
|
@ -1,20 +1,31 @@
|
||||
mixin Loadable {
|
||||
bool _isLoading = false;
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
bool get isLoading => _isLoading;
|
||||
const uuid = Uuid();
|
||||
|
||||
mixin Loadable {
|
||||
static final String _generalLoadingID = '#_loadable-${uuid.v4()}';
|
||||
|
||||
final Set<String> _IDs = <String>{};
|
||||
|
||||
bool get isLoading => _IDs.contains(_generalLoadingID);
|
||||
bool getIsLoadingSpecificID(final String id) => _IDs.contains(id);
|
||||
bool getIsLoading(final String id) => isLoading || getIsLoadingSpecificID(id);
|
||||
|
||||
void setState(void Function() callback);
|
||||
|
||||
Future<void> callWithLoading(Future<void> Function() callback) async {
|
||||
Future<void> callWithLoading(
|
||||
Future<void> Function() callback, [
|
||||
final String? id,
|
||||
]) async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
_IDs.add(id ?? _generalLoadingID);
|
||||
});
|
||||
|
||||
try {
|
||||
await callback();
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
_IDs.remove(id ?? _generalLoadingID);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ class _MemoryViewState extends AuthRequiredState<MemoryView> {
|
||||
location.split('.').last == 'jpg' ? MemoryType.photo : MemoryType.video;
|
||||
|
||||
try {
|
||||
final fileData = await FileManager.downloadFile('memories', location);
|
||||
final fileData = await FileManager.getFileData('memories', location);
|
||||
|
||||
if (!mounted) {
|
||||
return;
|
||||
|
@ -1,5 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:gallery_saver/gallery_saver.dart';
|
||||
import 'package:share_location/constants/spacing.dart';
|
||||
import 'package:share_location/enums.dart';
|
||||
import 'package:share_location/extensions/snackbar.dart';
|
||||
import 'package:share_location/foreign_types/memory.dart';
|
||||
import 'package:share_location/managers/file_manager.dart';
|
||||
import 'package:share_location/utils/loadable.dart';
|
||||
@ -8,11 +12,13 @@ import 'package:share_location/widgets/modal_sheet.dart';
|
||||
class MemorySheet extends StatefulWidget {
|
||||
final Memory memory;
|
||||
final VoidCallback onMemoryDeleted;
|
||||
final BuildContext sheetContext;
|
||||
|
||||
const MemorySheet({
|
||||
Key? key,
|
||||
required this.memory,
|
||||
required this.onMemoryDeleted,
|
||||
required this.sheetContext,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@ -23,6 +29,55 @@ class _MemorySheetState extends State<MemorySheet> with Loadable {
|
||||
Future<void> deleteFile() async {
|
||||
await FileManager.deleteFile(widget.memory.location);
|
||||
widget.onMemoryDeleted();
|
||||
|
||||
if (mounted) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> downloadFile() async {
|
||||
try {
|
||||
final file =
|
||||
await FileManager.downloadFile('memories', widget.memory.location);
|
||||
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (widget.memory.type) {
|
||||
case MemoryType.photo:
|
||||
await GallerySaver.saveImage(file.path);
|
||||
break;
|
||||
case MemoryType.video:
|
||||
await GallerySaver.saveVideo(file.path);
|
||||
break;
|
||||
}
|
||||
|
||||
Navigator.pop(context);
|
||||
|
||||
context.showSuccessSnackBar(message: 'File saved to Gallery!');
|
||||
} catch (error) {
|
||||
context.showErrorSnackBar(message: 'There was an error');
|
||||
Fluttertoast.showToast(
|
||||
msg: 'There was an error',
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
backgroundColor: Colors.red,
|
||||
textColor: Colors.white,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget buildLoadingIndicator() {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return SizedBox(
|
||||
width: theme.textTheme.titleLarge!.fontSize,
|
||||
height: theme.textTheme.titleLarge!.fontSize,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: theme.textTheme.bodyText1!.color,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -38,18 +93,26 @@ class _MemorySheetState extends State<MemorySheet> with Loadable {
|
||||
),
|
||||
const SizedBox(height: MEDIUM_SPACE),
|
||||
ListTile(
|
||||
leading: Icon(Icons.delete_forever_sharp),
|
||||
title: Text('Delete Memory'),
|
||||
onTap: isLoading
|
||||
leading: const Icon(Icons.download),
|
||||
title: const Text('Download to Gallery'),
|
||||
enabled: !getIsLoadingSpecificID('download'),
|
||||
onTap: getIsLoadingSpecificID('download')
|
||||
? null
|
||||
: () async {
|
||||
await callWithLoading(deleteFile);
|
||||
|
||||
if (mounted) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
trailing: isLoading ? const CircularProgressIndicator() : null,
|
||||
: () => callWithLoading(downloadFile, 'download'),
|
||||
trailing: getIsLoadingSpecificID('download')
|
||||
? buildLoadingIndicator()
|
||||
: null,
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.delete_forever_sharp),
|
||||
title: const Text('Delete Memory'),
|
||||
enabled: !getIsLoadingSpecificID('delete'),
|
||||
onTap: getIsLoadingSpecificID('delete')
|
||||
? null
|
||||
: () => callWithLoading(deleteFile, 'delete'),
|
||||
trailing: getIsLoadingSpecificID('delete')
|
||||
? buildLoadingIndicator()
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -19,20 +19,19 @@ class ModalSheet extends StatelessWidget {
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(MEDIUM_SPACE),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.bottomSheetTheme.modalBackgroundColor ??
|
||||
theme.bottomAppBarColor,
|
||||
child: Material(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(LARGE_SPACE),
|
||||
topRight: Radius.circular(LARGE_SPACE),
|
||||
),
|
||||
),
|
||||
color: theme.bottomSheetTheme.modalBackgroundColor ??
|
||||
theme.bottomAppBarColor,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(MEDIUM_SPACE),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -119,8 +119,9 @@ class _TimelinePageState extends State<TimelinePage> {
|
||||
context: context,
|
||||
backgroundColor: Colors.transparent,
|
||||
isScrollControlled: true,
|
||||
builder: (_) => MemorySheet(
|
||||
builder: (sheetContext) => MemorySheet(
|
||||
memory: timeline.currentMemory,
|
||||
sheetContext: sheetContext,
|
||||
onMemoryDeleted: timeline.removeEmptyDates,
|
||||
),
|
||||
);
|
||||
|
14
pubspec.lock
14
pubspec.lock
@ -205,6 +205,13 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
fluttertoast:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: fluttertoast
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "8.0.9"
|
||||
functions_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -212,6 +219,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.0.1-dev.5"
|
||||
gallery_saver:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: gallery_saver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
gotrue:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -46,6 +46,8 @@ dependencies:
|
||||
property_change_notifier: ^0.3.0
|
||||
path: ^1.8.1
|
||||
provider: ^6.0.3
|
||||
gallery_saver: ^2.3.2
|
||||
fluttertoast: ^8.0.9
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Loading…
x
Reference in New Issue
Block a user