magic_util.dart 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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. bool isOwner = true,
  50. }) async {
  51. final dialog = createProgressDialog(
  52. context,
  53. newVisibility == archiveVisibility
  54. ? S.of(context).archiving
  55. : S.of(context).unarchiving,
  56. );
  57. await dialog.show();
  58. try {
  59. final Map<String, dynamic> update = {magicKeyVisibility: newVisibility};
  60. if (isOwner) {
  61. await CollectionsService.instance.updateMagicMetadata(collection, update);
  62. } else {
  63. await CollectionsService.instance
  64. .updateShareeMagicMetadata(collection, update);
  65. }
  66. // Force reload home gallery to pull in the now unarchived files
  67. Bus.instance.fire(ForceReloadHomeGalleryEvent("CollectionArchiveChange"));
  68. showShortToast(
  69. context,
  70. newVisibility == archiveVisibility
  71. ? S.of(context).successfullyArchived
  72. : S.of(context).successfullyUnarchived,
  73. );
  74. await dialog.hide();
  75. } catch (e, s) {
  76. _logger.severe("failed to update collection visibility", e, s);
  77. await dialog.hide();
  78. rethrow;
  79. }
  80. }
  81. Future<void> changeSortOrder(
  82. BuildContext context,
  83. Collection collection,
  84. bool sortedInAscOrder,
  85. ) async {
  86. try {
  87. final Map<String, dynamic> update = {"asc": sortedInAscOrder};
  88. await CollectionsService.instance
  89. .updatePublicMagicMetadata(collection, update);
  90. Bus.instance.fire(
  91. CollectionMetaEvent(collection.id, CollectionMetaEventType.sortChanged),
  92. );
  93. } catch (e, s) {
  94. _logger.severe("failed to update collection visibility", e, s);
  95. showShortToast(context, S.of(context).somethingWentWrong);
  96. rethrow;
  97. }
  98. }
  99. Future<bool> editTime(
  100. BuildContext context,
  101. List<File> files,
  102. int editedTime,
  103. ) async {
  104. try {
  105. await _updatePublicMetadata(
  106. context,
  107. files,
  108. editTimeKey,
  109. editedTime,
  110. );
  111. return true;
  112. } catch (e) {
  113. showShortToast(context, S.of(context).somethingWentWrong);
  114. return false;
  115. }
  116. }
  117. Future<void> editFilename(
  118. BuildContext context,
  119. File file,
  120. ) async {
  121. final fileName = file.displayName;
  122. final nameWithoutExt = basenameWithoutExtension(fileName);
  123. final extName = extension(fileName);
  124. final result = await showTextInputDialog(
  125. context,
  126. title: S.of(context).renameFile,
  127. submitButtonLabel: S.of(context).rename,
  128. initialValue: nameWithoutExt,
  129. message: extName.toUpperCase(),
  130. alignMessage: Alignment.centerRight,
  131. hintText: S.of(context).enterFileName,
  132. maxLength: 50,
  133. alwaysShowSuccessState: true,
  134. onSubmit: (String text) async {
  135. if (text.isEmpty || text.trim() == nameWithoutExt.trim()) {
  136. return;
  137. }
  138. final newName = text + extName;
  139. await _updatePublicMetadata(
  140. context,
  141. List.of([file]),
  142. editNameKey,
  143. newName,
  144. showProgressDialogs: false,
  145. showDoneToast: false,
  146. );
  147. },
  148. );
  149. if (result is Exception) {
  150. _logger.severe("Failed to rename file");
  151. showGenericErrorDialog(context: context);
  152. }
  153. }
  154. Future<bool> editFileCaption(
  155. BuildContext? context,
  156. File file,
  157. String caption,
  158. ) async {
  159. try {
  160. await _updatePublicMetadata(
  161. context,
  162. [file],
  163. captionKey,
  164. caption,
  165. showDoneToast: false,
  166. );
  167. return true;
  168. } catch (e) {
  169. if (context != null) {
  170. showShortToast(context, S.of(context).somethingWentWrong);
  171. }
  172. return false;
  173. }
  174. }
  175. Future<void> _updatePublicMetadata(
  176. BuildContext? context,
  177. List<File> files,
  178. String key,
  179. dynamic value, {
  180. bool showDoneToast = true,
  181. bool showProgressDialogs = true,
  182. }) async {
  183. if (files.isEmpty) {
  184. return;
  185. }
  186. ProgressDialog? dialog;
  187. if (context != null && showProgressDialogs) {
  188. dialog = createProgressDialog(context, S.of(context).pleaseWait);
  189. await dialog.show();
  190. }
  191. try {
  192. final Map<String, dynamic> update = {key: value};
  193. await FileMagicService.instance.updatePublicMagicMetadata(files, update);
  194. if (context != null) {
  195. if (showDoneToast) {
  196. showShortToast(context, S.of(context).done);
  197. }
  198. await dialog?.hide();
  199. }
  200. if (_shouldReloadGallery(key)) {
  201. Bus.instance.fire(ForceReloadHomeGalleryEvent("FileMetadataChange-$key"));
  202. }
  203. } catch (e, s) {
  204. _logger.severe("failed to update $key = $value", e, s);
  205. if (context != null) {
  206. await dialog?.hide();
  207. }
  208. rethrow;
  209. }
  210. }
  211. bool _shouldReloadGallery(String key) {
  212. return key == editTimeKey;
  213. }