magic_util.dart 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import 'package:flutter/material.dart';
  2. import 'package:logging/logging.dart';
  3. import 'package:path/path.dart';
  4. import 'package:photos/core/event_bus.dart';
  5. import "package:photos/events/collection_meta_event.dart";
  6. import 'package:photos/events/force_reload_home_gallery_event.dart';
  7. import "package:photos/generated/l10n.dart";
  8. import 'package:photos/models/collection.dart';
  9. import 'package:photos/models/file.dart';
  10. import "package:photos/models/metadata/common_keys.dart";
  11. import "package:photos/models/metadata/file_magic.dart";
  12. import 'package:photos/services/collections_service.dart';
  13. import 'package:photos/services/file_magic_service.dart';
  14. import 'package:photos/ui/common/progress_dialog.dart';
  15. import 'package:photos/utils/dialog_util.dart';
  16. import 'package:photos/utils/toast_util.dart';
  17. final _logger = Logger('MagicUtil');
  18. Future<void> changeVisibility(
  19. BuildContext context,
  20. List<File> files,
  21. int newVisibility,
  22. ) async {
  23. final dialog = createProgressDialog(
  24. context,
  25. newVisibility == archiveVisibility
  26. ? S.of(context).archiving
  27. : S.of(context).unarchiving,
  28. );
  29. await dialog.show();
  30. try {
  31. await FileMagicService.instance.changeVisibility(files, newVisibility);
  32. showShortToast(
  33. context,
  34. newVisibility == archiveVisibility
  35. ? S.of(context).successfullyArchived
  36. : S.of(context).successfullyUnarchived,
  37. );
  38. await dialog.hide();
  39. } catch (e, s) {
  40. _logger.severe("failed to update file visibility", e, s);
  41. await dialog.hide();
  42. rethrow;
  43. }
  44. }
  45. Future<void> changeCollectionVisibility(
  46. BuildContext context,
  47. Collection collection,
  48. int newVisibility,
  49. ) async {
  50. final dialog = createProgressDialog(
  51. context,
  52. newVisibility == archiveVisibility
  53. ? S.of(context).archiving
  54. : S.of(context).unarchiving,
  55. );
  56. await dialog.show();
  57. try {
  58. final Map<String, dynamic> update = {magicKeyVisibility: newVisibility};
  59. await CollectionsService.instance.updateMagicMetadata(collection, update);
  60. // Force reload home gallery to pull in the now unarchived files
  61. Bus.instance.fire(ForceReloadHomeGalleryEvent("CollectionArchiveChange"));
  62. showShortToast(
  63. context,
  64. newVisibility == archiveVisibility
  65. ? S.of(context).successfullyArchived
  66. : S.of(context).successfullyUnarchived,
  67. );
  68. await dialog.hide();
  69. } catch (e, s) {
  70. _logger.severe("failed to update collection visibility", e, s);
  71. await dialog.hide();
  72. rethrow;
  73. }
  74. }
  75. Future<void> changeSortOrder(
  76. BuildContext context,
  77. Collection collection,
  78. bool sortedInAscOrder,
  79. ) async {
  80. try {
  81. final Map<String, dynamic> update = {"asc": sortedInAscOrder};
  82. await CollectionsService.instance
  83. .updatePublicMagicMetadata(collection, update);
  84. Bus.instance.fire(
  85. CollectionMetaEvent(collection.id, CollectionMetaEventType.sortChanged),
  86. );
  87. } catch (e, s) {
  88. _logger.severe("failed to update collection visibility", e, s);
  89. showShortToast(context, S.of(context).somethingWentWrong);
  90. rethrow;
  91. }
  92. }
  93. Future<bool> editTime(
  94. BuildContext context,
  95. List<File> files,
  96. int editedTime,
  97. ) async {
  98. try {
  99. await _updatePublicMetadata(
  100. context,
  101. files,
  102. editTimeKey,
  103. editedTime,
  104. );
  105. return true;
  106. } catch (e) {
  107. showShortToast(context, S.of(context).somethingWentWrong);
  108. return false;
  109. }
  110. }
  111. Future<void> editFilename(
  112. BuildContext context,
  113. File file,
  114. ) async {
  115. final fileName = file.displayName;
  116. final nameWithoutExt = basenameWithoutExtension(fileName);
  117. final extName = extension(fileName);
  118. final result = await showTextInputDialog(
  119. context,
  120. title: S.of(context).renameFile,
  121. submitButtonLabel: S.of(context).rename,
  122. initialValue: nameWithoutExt,
  123. message: extName.toUpperCase(),
  124. alignMessage: Alignment.centerRight,
  125. hintText: S.of(context).enterFileName,
  126. maxLength: 50,
  127. alwaysShowSuccessState: true,
  128. onSubmit: (String text) async {
  129. if (text.isEmpty || text.trim() == nameWithoutExt.trim()) {
  130. return;
  131. }
  132. final newName = text + extName;
  133. await _updatePublicMetadata(
  134. context,
  135. List.of([file]),
  136. editNameKey,
  137. newName,
  138. showProgressDialogs: false,
  139. showDoneToast: false,
  140. );
  141. },
  142. );
  143. if (result is Exception) {
  144. _logger.severe("Failed to rename file");
  145. showGenericErrorDialog(context: context);
  146. }
  147. }
  148. Future<bool> editFileCaption(
  149. BuildContext? context,
  150. File file,
  151. String caption,
  152. ) async {
  153. try {
  154. await _updatePublicMetadata(
  155. context,
  156. [file],
  157. captionKey,
  158. caption,
  159. showDoneToast: false,
  160. );
  161. return true;
  162. } catch (e) {
  163. if (context != null) {
  164. showShortToast(context, S.of(context).somethingWentWrong);
  165. }
  166. return false;
  167. }
  168. }
  169. Future<void> _updatePublicMetadata(
  170. BuildContext? context,
  171. List<File> files,
  172. String key,
  173. dynamic value, {
  174. bool showDoneToast = true,
  175. bool showProgressDialogs = true,
  176. }) async {
  177. if (files.isEmpty) {
  178. return;
  179. }
  180. ProgressDialog? dialog;
  181. if (context != null && showProgressDialogs) {
  182. dialog = createProgressDialog(context, S.of(context).pleaseWait);
  183. await dialog.show();
  184. }
  185. try {
  186. final Map<String, dynamic> update = {key: value};
  187. await FileMagicService.instance.updatePublicMagicMetadata(files, update);
  188. if (context != null) {
  189. if (showDoneToast) {
  190. showShortToast(context, S.of(context).done);
  191. }
  192. await dialog?.hide();
  193. }
  194. if (_shouldReloadGallery(key)) {
  195. Bus.instance.fire(ForceReloadHomeGalleryEvent("FileMetadataChange-$key"));
  196. }
  197. } catch (e, s) {
  198. _logger.severe("failed to update $key = $value", e, s);
  199. if (context != null) {
  200. await dialog?.hide();
  201. }
  202. rethrow;
  203. }
  204. }
  205. bool _shouldReloadGallery(String key) {
  206. return key == editTimeKey;
  207. }