file_actions.dart 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import "package:flutter/cupertino.dart";
  2. import "package:modal_bottom_sheet/modal_bottom_sheet.dart";
  3. import "package:photos/generated/l10n.dart";
  4. import "package:photos/models/file.dart";
  5. import "package:photos/models/file_type.dart";
  6. import "package:photos/theme/colors.dart";
  7. import "package:photos/theme/ente_theme.dart";
  8. import "package:photos/ui/components/action_sheet_widget.dart";
  9. import 'package:photos/ui/components/buttons/button_widget.dart';
  10. import "package:photos/ui/components/models/button_type.dart";
  11. import 'package:photos/ui/viewer/file/file_details_widget.dart';
  12. import "package:photos/utils/delete_file_util.dart";
  13. import "package:photos/utils/dialog_util.dart";
  14. import "package:photos/utils/toast_util.dart";
  15. Future<void> showSingleFileDeleteSheet(
  16. BuildContext context,
  17. File file, {
  18. Function(File)? onFileRemoved,
  19. }) async {
  20. final List<ButtonWidget> buttons = [];
  21. final String fileType = file.fileType == FileType.video
  22. ? S.of(context).videoSmallCase
  23. : S.of(context).photoSmallCase;
  24. final bool isBothLocalAndRemote =
  25. file.uploadedFileID != null && file.localID != null;
  26. final bool isLocalOnly = file.uploadedFileID == null && file.localID != null;
  27. final bool isRemoteOnly = file.uploadedFileID != null && file.localID == null;
  28. String bodyHighlight = S.of(context).singleFileDeleteHighlight;
  29. String body = "";
  30. if (isBothLocalAndRemote) {
  31. body = S.of(context).singleFileInBothLocalAndRemote(fileType);
  32. } else if (isRemoteOnly) {
  33. body = S.of(context).singleFileInRemoteOnly(fileType);
  34. } else if (isLocalOnly) {
  35. body = S.of(context).singleFileDeleteFromDevice(fileType);
  36. } else {
  37. throw AssertionError("Unexpected state");
  38. }
  39. // Add option to delete from ente
  40. if (isBothLocalAndRemote || isRemoteOnly) {
  41. buttons.add(
  42. ButtonWidget(
  43. labelText: isBothLocalAndRemote
  44. ? S.of(context).deleteFromEnte
  45. : S.of(context).yesDelete,
  46. buttonType: ButtonType.neutral,
  47. buttonSize: ButtonSize.large,
  48. shouldStickToDarkTheme: true,
  49. buttonAction: ButtonAction.first,
  50. shouldSurfaceExecutionStates: true,
  51. isInAlert: true,
  52. onTap: () async {
  53. await deleteFilesFromRemoteOnly(context, [file]);
  54. showShortToast(context, S.of(context).movedToTrash);
  55. if (isRemoteOnly) {
  56. Navigator.of(context, rootNavigator: true).pop();
  57. if (onFileRemoved != null) {
  58. onFileRemoved(file);
  59. }
  60. }
  61. },
  62. ),
  63. );
  64. }
  65. // Add option to delete from local
  66. if (isBothLocalAndRemote || isLocalOnly) {
  67. buttons.add(
  68. ButtonWidget(
  69. labelText: isBothLocalAndRemote
  70. ? S.of(context).deleteFromDevice
  71. : S.of(context).yesDelete,
  72. buttonType: ButtonType.neutral,
  73. buttonSize: ButtonSize.large,
  74. shouldStickToDarkTheme: true,
  75. buttonAction: ButtonAction.second,
  76. shouldSurfaceExecutionStates: false,
  77. isInAlert: true,
  78. onTap: () async {
  79. await deleteFilesOnDeviceOnly(context, [file]);
  80. if (isLocalOnly) {
  81. Navigator.of(context, rootNavigator: true).pop();
  82. if (onFileRemoved != null) {
  83. onFileRemoved(file);
  84. }
  85. }
  86. },
  87. ),
  88. );
  89. }
  90. if (isBothLocalAndRemote) {
  91. buttons.add(
  92. ButtonWidget(
  93. labelText: S.of(context).deleteFromBoth,
  94. buttonType: ButtonType.neutral,
  95. buttonSize: ButtonSize.large,
  96. shouldStickToDarkTheme: true,
  97. buttonAction: ButtonAction.third,
  98. shouldSurfaceExecutionStates: true,
  99. isInAlert: true,
  100. onTap: () async {
  101. await deleteFilesFromEverywhere(context, [file]);
  102. Navigator.of(context, rootNavigator: true).pop();
  103. if (onFileRemoved != null) {
  104. onFileRemoved(file);
  105. }
  106. },
  107. ),
  108. );
  109. }
  110. buttons.add(
  111. ButtonWidget(
  112. labelText: S.of(context).cancel,
  113. buttonType: ButtonType.secondary,
  114. buttonSize: ButtonSize.large,
  115. shouldStickToDarkTheme: true,
  116. buttonAction: ButtonAction.fourth,
  117. isInAlert: true,
  118. ),
  119. );
  120. final actionResult = await showActionSheet(
  121. context: context,
  122. buttons: buttons,
  123. actionSheetType: ActionSheetType.defaultActionSheet,
  124. body: body,
  125. bodyHighlight: bodyHighlight,
  126. );
  127. if (actionResult?.action != null &&
  128. actionResult!.action == ButtonAction.error) {
  129. showGenericErrorDialog(context: context);
  130. }
  131. }
  132. Future<void> showDetailsSheet(BuildContext context, File file) async {
  133. final colorScheme = getEnteColorScheme(context);
  134. return showBarModalBottomSheet(
  135. topControl: const SizedBox.shrink(),
  136. shape: const RoundedRectangleBorder(
  137. borderRadius: BorderRadius.vertical(
  138. top: Radius.circular(5),
  139. ),
  140. ),
  141. backgroundColor: colorScheme.backgroundElevated,
  142. barrierColor: backdropFaintDark,
  143. context: context,
  144. builder: (BuildContext context) {
  145. return Padding(
  146. padding:
  147. EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
  148. child: FileDetailsWidget(file),
  149. );
  150. },
  151. );
  152. }