2021-11-09 11:40:40 +00:00
|
|
|
import 'package:flutter/material.dart';
|
2021-10-26 10:37:14 +00:00
|
|
|
import 'package:logging/logging.dart';
|
2021-11-09 11:40:40 +00:00
|
|
|
import 'package:path/path.dart';
|
2021-10-26 10:37:14 +00:00
|
|
|
import 'package:photos/core/event_bus.dart';
|
2023-05-26 04:35:19 +00:00
|
|
|
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";
|
2023-05-25 05:51:56 +00:00
|
|
|
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';
|
2022-11-04 11:47:48 +00:00
|
|
|
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');
|
|
|
|
|
|
|
|
Future<void> changeVisibility(
|
2022-06-11 08:23:52 +00:00
|
|
|
BuildContext context,
|
|
|
|
List<File> files,
|
|
|
|
int newVisibility,
|
|
|
|
) async {
|
|
|
|
final dialog = createProgressDialog(
|
|
|
|
context,
|
2023-05-25 05:51:56 +00:00
|
|
|
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,
|
2023-05-25 05:51:56 +00:00
|
|
|
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(
|
2022-06-11 08:23:52 +00:00
|
|
|
BuildContext context,
|
|
|
|
Collection collection,
|
2023-05-29 13:37:02 +00:00
|
|
|
int newVisibility, {
|
|
|
|
bool isOwner = true,
|
|
|
|
}) async {
|
2022-06-11 08:23:52 +00:00
|
|
|
final dialog = createProgressDialog(
|
|
|
|
context,
|
2023-05-25 05:51:56 +00:00
|
|
|
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
|
|
|
);
|
2022-03-21 09:32:24 +00:00
|
|
|
await dialog.show();
|
|
|
|
try {
|
2022-09-19 11:52:20 +00:00
|
|
|
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);
|
|
|
|
}
|
2022-03-21 09:32:24 +00:00
|
|
|
// Force reload home gallery to pull in the now unarchived files
|
2022-11-11 10:23:35 +00:00
|
|
|
Bus.instance.fire(ForceReloadHomeGalleryEvent("CollectionArchiveChange"));
|
2022-06-10 14:29:56 +00:00
|
|
|
showShortToast(
|
2022-06-11 08:23:52 +00:00
|
|
|
context,
|
2023-05-25 05:51:56 +00:00
|
|
|
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
|
|
|
);
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-26 02:33:58 +00:00
|
|
|
Future<void> changeSortOrder(
|
|
|
|
BuildContext context,
|
|
|
|
Collection collection,
|
|
|
|
bool sortedInAscOrder,
|
|
|
|
) async {
|
|
|
|
try {
|
|
|
|
final Map<String, dynamic> update = {"asc": sortedInAscOrder};
|
|
|
|
await CollectionsService.instance
|
|
|
|
.updatePublicMagicMetadata(collection, update);
|
2023-05-26 04:35:19 +00:00
|
|
|
Bus.instance.fire(
|
|
|
|
CollectionMetaEvent(collection.id, CollectionMetaEventType.sortChanged),
|
|
|
|
);
|
2023-05-26 02:33:58 +00:00
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-24 07:38:00 +00:00
|
|
|
// changeCoverPhoto is used to change cover photo for a collection. To reset to
|
2023-07-24 07:53:47 +00:00
|
|
|
// default cover photo, pass uploadedFileID as 0
|
2023-06-22 10:51:56 +00:00
|
|
|
Future<void> changeCoverPhoto(
|
|
|
|
BuildContext context,
|
|
|
|
Collection collection,
|
2023-07-24 07:38:00 +00:00
|
|
|
int uploadedFileID,
|
2023-06-22 10:51:56 +00:00
|
|
|
) async {
|
|
|
|
try {
|
2023-07-24 07:38:00 +00:00
|
|
|
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,
|
2023-05-25 05:51:56 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-07 09:09:34 +00:00
|
|
|
Future<void> editFilename(
|
2021-11-09 11:40:40 +00:00
|
|
|
BuildContext context,
|
|
|
|
File file,
|
|
|
|
) async {
|
2023-02-07 09:09:34 +00:00
|
|
|
final fileName = file.displayName;
|
|
|
|
final nameWithoutExt = basenameWithoutExtension(fileName);
|
|
|
|
final extName = extension(fileName);
|
2023-02-09 01:21:00 +00:00
|
|
|
final result = await showTextInputDialog(
|
2023-02-07 09:09:34 +00:00
|
|
|
context,
|
2023-04-07 06:42:07 +00:00
|
|
|
title: S.of(context).renameFile,
|
|
|
|
submitButtonLabel: S.of(context).rename,
|
2023-02-07 09:09:34 +00:00
|
|
|
initialValue: nameWithoutExt,
|
2023-02-08 11:52:42 +00:00
|
|
|
message: extName.toUpperCase(),
|
2023-02-07 09:09:34 +00:00
|
|
|
alignMessage: Alignment.centerRight,
|
2023-04-07 06:42:07 +00:00
|
|
|
hintText: S.of(context).enterFileName,
|
2023-02-07 09:09:34 +00:00
|
|
|
maxLength: 50,
|
2023-02-09 04:56:06 +00:00
|
|
|
alwaysShowSuccessState: true,
|
2023-02-08 05:43:46 +00:00
|
|
|
onSubmit: (String text) async {
|
2023-02-09 01:21:00 +00:00
|
|
|
if (text.isEmpty || text.trim() == nameWithoutExt.trim()) {
|
|
|
|
return;
|
2023-02-07 09:09:34 +00:00
|
|
|
}
|
2023-02-09 01:21:00 +00:00
|
|
|
final newName = text + extName;
|
|
|
|
await _updatePublicMetadata(
|
|
|
|
context,
|
|
|
|
List.of([file]),
|
2023-05-25 05:51:56 +00:00
|
|
|
editNameKey,
|
2023-02-09 01:21:00 +00:00
|
|
|
newName,
|
|
|
|
showProgressDialogs: false,
|
|
|
|
showDoneToast: false,
|
|
|
|
);
|
2023-02-07 09:09:34 +00:00
|
|
|
},
|
|
|
|
);
|
2023-02-09 01:21:00 +00:00
|
|
|
if (result is Exception) {
|
|
|
|
_logger.severe("Failed to rename file");
|
|
|
|
showGenericErrorDialog(context: context);
|
|
|
|
}
|
2022-11-04 10:10:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> editFileCaption(
|
2022-12-30 09:08:46 +00:00
|
|
|
BuildContext? context,
|
2022-11-04 10:10:11 +00:00
|
|
|
File file,
|
|
|
|
String caption,
|
|
|
|
) async {
|
|
|
|
try {
|
2022-11-07 08:09:04 +00:00
|
|
|
await _updatePublicMetadata(
|
|
|
|
context,
|
|
|
|
[file],
|
2023-05-25 05:51:56 +00:00
|
|
|
captionKey,
|
2022-11-07 08:09:04 +00:00
|
|
|
caption,
|
|
|
|
showDoneToast: false,
|
|
|
|
);
|
2022-11-04 10:10:11 +00:00
|
|
|
return true;
|
|
|
|
} catch (e) {
|
2022-11-04 11:47:48 +00:00
|
|
|
if (context != null) {
|
2023-04-07 06:42:07 +00:00
|
|
|
showShortToast(context, S.of(context).somethingWentWrong);
|
2022-11-04 11:47:48 +00:00
|
|
|
}
|
2021-11-09 11:40:40 +00:00
|
|
|
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,
|
2022-11-07 08:09:04 +00:00
|
|
|
dynamic value, {
|
|
|
|
bool showDoneToast = true,
|
2023-02-07 11:14:02 +00:00
|
|
|
bool showProgressDialogs = true,
|
2022-11-07 08:09:04 +00:00
|
|
|
}) async {
|
2021-10-26 10:37:14 +00:00
|
|
|
if (files.isEmpty) {
|
|
|
|
return;
|
|
|
|
}
|
2022-12-30 09:08:46 +00:00
|
|
|
ProgressDialog? dialog;
|
2023-02-07 11:14:02 +00:00
|
|
|
if (context != null && showProgressDialogs) {
|
2023-04-07 06:42:07 +00:00
|
|
|
dialog = createProgressDialog(context, S.of(context).pleaseWait);
|
2022-11-04 11:47:48 +00:00
|
|
|
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);
|
2022-11-04 11:47:48 +00:00
|
|
|
if (context != null) {
|
2022-11-07 08:09:04 +00:00
|
|
|
if (showDoneToast) {
|
2023-04-07 06:42:07 +00:00
|
|
|
showShortToast(context, S.of(context).done);
|
2022-11-07 08:09:04 +00:00
|
|
|
}
|
2022-12-30 09:08:46 +00:00
|
|
|
await dialog?.hide();
|
2022-11-04 11:47:48 +00:00
|
|
|
}
|
|
|
|
|
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);
|
2022-11-04 11:47:48 +00:00
|
|
|
if (context != null) {
|
2022-12-30 09:08:46 +00:00
|
|
|
await dialog?.hide();
|
2022-11-04 11:47:48 +00:00
|
|
|
}
|
2021-10-26 10:37:14 +00:00
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool _shouldReloadGallery(String key) {
|
2023-05-25 05:51:56 +00:00
|
|
|
return key == editTimeKey;
|
2021-10-26 10:37:14 +00:00
|
|
|
}
|