ente/lib/utils/magic_util.dart

331 lines
8.8 KiB
Dart
Raw Normal View History

import 'package:flutter/material.dart';
2021-10-26 10:37:14 +00:00
import 'package:logging/logging.dart';
import 'package:path/path.dart';
2021-10-26 10:37:14 +00:00
import 'package:photos/core/event_bus.dart';
import "package:photos/events/collection_meta_event.dart";
2023-06-22 10:51:56 +00:00
import "package:photos/events/collection_updated_event.dart";
import "package:photos/events/files_updated_event.dart";
2021-10-26 10:37:14 +00:00
import 'package:photos/events/force_reload_home_gallery_event.dart';
2023-04-07 06:42:07 +00:00
import "package:photos/generated/l10n.dart";
2022-03-21 09:32:24 +00:00
import 'package:photos/models/collection.dart';
2021-10-26 10:37:14 +00:00
import 'package:photos/models/file.dart';
2023-06-30 04:31:15 +00:00
import "package:photos/models/metadata/collection_magic.dart";
import "package:photos/models/metadata/common_keys.dart";
import "package:photos/models/metadata/file_magic.dart";
2022-03-21 09:32:24 +00:00
import 'package:photos/services/collections_service.dart';
2021-10-26 10:37:14 +00:00
import 'package:photos/services/file_magic_service.dart';
import 'package:photos/ui/common/progress_dialog.dart';
2021-10-26 10:37:14 +00:00
import 'package:photos/utils/dialog_util.dart';
import 'package:photos/utils/toast_util.dart';
final _logger = Logger('MagicUtil');
enum _VisibilityAction { hide, unHide, archive, unarchive }
2021-10-26 10:37:14 +00:00
Future<void> changeVisibility(
2022-06-11 08:23:52 +00:00
BuildContext context,
List<File> files,
int newVisibility,
) async {
final dialog = createProgressDialog(
context,
newVisibility == archiveVisibility
2023-04-07 06:42:07 +00:00
? S.of(context).archiving
: S.of(context).unarchiving,
2022-06-11 08:23:52 +00:00
);
2021-10-26 10:37:14 +00:00
await dialog.show();
try {
await FileMagicService.instance.changeVisibility(files, newVisibility);
2022-06-10 14:29:56 +00:00
showShortToast(
2022-06-11 08:23:52 +00:00
context,
newVisibility == archiveVisibility
2023-04-07 06:42:07 +00:00
? S.of(context).successfullyArchived
: S.of(context).successfullyUnarchived,
2022-06-11 08:23:52 +00:00
);
2021-10-26 10:37:14 +00:00
await dialog.hide();
} catch (e, s) {
_logger.severe("failed to update file visibility", e, s);
await dialog.hide();
rethrow;
}
}
2022-03-21 10:07:02 +00:00
Future<void> changeCollectionVisibility(
BuildContext context, {
required Collection collection,
required int newVisibility,
required int prevVisibility,
2023-05-29 13:37:02 +00:00
bool isOwner = true,
}) async {
final visibilityAction =
_getVisibilityAction(context, newVisibility, prevVisibility);
2022-06-11 08:23:52 +00:00
final dialog = createProgressDialog(
context,
_visActionProgressDialogText(
context,
visibilityAction,
),
2022-06-11 08:23:52 +00:00
);
2022-03-21 09:32:24 +00:00
await dialog.show();
try {
final Map<String, dynamic> update = {magicKeyVisibility: newVisibility};
2023-05-29 13:37:02 +00:00
if (isOwner) {
await CollectionsService.instance.updateMagicMetadata(collection, update);
} else {
await CollectionsService.instance
.updateShareeMagicMetadata(collection, update);
}
// Force reload home gallery to pull in/remove the now visibility changed
// files
Bus.instance.fire(
ForceReloadHomeGalleryEvent(
"CollectionVisibilityChange: $visibilityAction",
),
);
2022-06-10 14:29:56 +00:00
showShortToast(
2022-06-11 08:23:52 +00:00
context,
_visActionSuccessfulText(
context,
visibilityAction,
),
2022-06-11 08:23:52 +00:00
);
2022-03-21 09:32:24 +00:00
await dialog.hide();
} catch (e, s) {
_logger.severe("failed to update collection visibility", e, s);
await dialog.hide();
rethrow;
}
}
Future<void> changeSortOrder(
BuildContext context,
Collection collection,
bool sortedInAscOrder,
) async {
try {
final Map<String, dynamic> update = {"asc": sortedInAscOrder};
await CollectionsService.instance
.updatePublicMagicMetadata(collection, update);
Bus.instance.fire(
CollectionMetaEvent(collection.id, CollectionMetaEventType.sortChanged),
);
} catch (e, s) {
_logger.severe("failed to update collection visibility", e, s);
showShortToast(context, S.of(context).somethingWentWrong);
rethrow;
}
}
2023-06-30 04:31:15 +00:00
Future<void> updateOrder(
BuildContext context,
Collection collection,
int order,
) async {
try {
final Map<String, dynamic> update = {
orderKey: order,
};
await CollectionsService.instance.updateMagicMetadata(collection, update);
Bus.instance.fire(
CollectionMetaEvent(collection.id, CollectionMetaEventType.orderChanged),
);
} catch (e, s) {
_logger.severe("failed to update order", e, s);
showShortToast(context, S.of(context).somethingWentWrong);
rethrow;
}
}
// changeCoverPhoto is used to change cover photo for a collection. To reset to
// default cover photo, pass uploadedFileID as 0
2023-06-22 10:51:56 +00:00
Future<void> changeCoverPhoto(
BuildContext context,
Collection collection,
int uploadedFileID,
2023-06-22 10:51:56 +00:00
) async {
try {
final Map<String, dynamic> update = {"coverID": uploadedFileID};
2023-06-22 10:51:56 +00:00
await CollectionsService.instance
.updatePublicMagicMetadata(collection, update);
Bus.instance.fire(
CollectionUpdatedEvent(
collection.id,
<File>[],
"cover_change",
type: EventType.coverChanged,
),
);
} catch (e, s) {
_logger.severe("failed to update cover", e, s);
showShortToast(context, S.of(context).somethingWentWrong);
rethrow;
}
}
2021-10-26 10:37:14 +00:00
Future<bool> editTime(
2022-06-11 08:23:52 +00:00
BuildContext context,
List<File> files,
int editedTime,
) async {
2021-10-26 10:37:14 +00:00
try {
await _updatePublicMetadata(
2022-06-11 08:23:52 +00:00
context,
files,
editTimeKey,
2022-06-11 08:23:52 +00:00
editedTime,
);
2021-10-26 10:37:14 +00:00
return true;
2022-07-03 10:09:01 +00:00
} catch (e) {
2023-04-07 06:42:07 +00:00
showShortToast(context, S.of(context).somethingWentWrong);
2021-10-26 10:37:14 +00:00
return false;
}
}
Future<void> editFilename(
BuildContext context,
File file,
) async {
final fileName = file.displayName;
final nameWithoutExt = basenameWithoutExtension(fileName);
final extName = extension(fileName);
final result = await showTextInputDialog(
context,
2023-04-07 06:42:07 +00:00
title: S.of(context).renameFile,
submitButtonLabel: S.of(context).rename,
initialValue: nameWithoutExt,
message: extName.toUpperCase(),
alignMessage: Alignment.centerRight,
2023-04-07 06:42:07 +00:00
hintText: S.of(context).enterFileName,
maxLength: 50,
alwaysShowSuccessState: true,
onSubmit: (String text) async {
if (text.isEmpty || text.trim() == nameWithoutExt.trim()) {
return;
}
final newName = text + extName;
await _updatePublicMetadata(
context,
List.of([file]),
editNameKey,
newName,
showProgressDialogs: false,
showDoneToast: false,
);
},
);
if (result is Exception) {
_logger.severe("Failed to rename file");
showGenericErrorDialog(context: context);
}
}
Future<bool> editFileCaption(
2022-12-30 09:08:46 +00:00
BuildContext? context,
File file,
String caption,
) async {
try {
await _updatePublicMetadata(
context,
[file],
captionKey,
caption,
showDoneToast: false,
);
return true;
} catch (e) {
if (context != null) {
2023-04-07 06:42:07 +00:00
showShortToast(context, S.of(context).somethingWentWrong);
}
return false;
}
}
2021-10-26 10:37:14 +00:00
Future<void> _updatePublicMetadata(
2022-12-30 09:08:46 +00:00
BuildContext? context,
2022-06-11 08:23:52 +00:00
List<File> files,
String key,
dynamic value, {
bool showDoneToast = true,
bool showProgressDialogs = true,
}) async {
2021-10-26 10:37:14 +00:00
if (files.isEmpty) {
return;
}
2022-12-30 09:08:46 +00:00
ProgressDialog? dialog;
if (context != null && showProgressDialogs) {
2023-04-07 06:42:07 +00:00
dialog = createProgressDialog(context, S.of(context).pleaseWait);
await dialog.show();
}
2021-10-26 10:37:14 +00:00
try {
2022-08-29 14:43:31 +00:00
final Map<String, dynamic> update = {key: value};
2021-10-26 10:37:14 +00:00
await FileMagicService.instance.updatePublicMagicMetadata(files, update);
if (context != null) {
if (showDoneToast) {
2023-04-07 06:42:07 +00:00
showShortToast(context, S.of(context).done);
}
2022-12-30 09:08:46 +00:00
await dialog?.hide();
}
2021-10-26 10:37:14 +00:00
if (_shouldReloadGallery(key)) {
2022-11-11 10:23:35 +00:00
Bus.instance.fire(ForceReloadHomeGalleryEvent("FileMetadataChange-$key"));
2021-10-26 10:37:14 +00:00
}
} catch (e, s) {
_logger.severe("failed to update $key = $value", e, s);
if (context != null) {
2022-12-30 09:08:46 +00:00
await dialog?.hide();
}
2021-10-26 10:37:14 +00:00
rethrow;
}
}
bool _shouldReloadGallery(String key) {
return key == editTimeKey;
2021-10-26 10:37:14 +00:00
}
_visActionProgressDialogText(BuildContext context, _VisibilityAction action) {
switch (action) {
case _VisibilityAction.archive:
return S.of(context).archiving;
case _VisibilityAction.hide:
2023-08-10 10:05:12 +00:00
return S.of(context).hiding;
case _VisibilityAction.unarchive:
return S.of(context).unarchiving;
case _VisibilityAction.unHide:
2023-08-10 10:05:12 +00:00
return S.of(context).unhiding;
}
}
_visActionSuccessfulText(BuildContext context, _VisibilityAction action) {
switch (action) {
case _VisibilityAction.archive:
return S.of(context).successfullyArchived;
case _VisibilityAction.hide:
2023-08-10 10:05:12 +00:00
return S.of(context).successfullyHid;
case _VisibilityAction.unarchive:
return S.of(context).successfullyUnarchived;
case _VisibilityAction.unHide:
2023-08-10 10:05:12 +00:00
return S.of(context).successfullyUnhid;
}
}
_VisibilityAction _getVisibilityAction(
context,
int newVisibility,
int prevVisibility,
) {
if (newVisibility == archiveVisibility) {
return _VisibilityAction.archive;
} else if (newVisibility == hiddenVisibility) {
return _VisibilityAction.hide;
} else if (newVisibility == visibleVisibility &&
prevVisibility == archiveVisibility) {
return _VisibilityAction.unarchive;
} else {
return _VisibilityAction.unHide;
}
}