file_actions.dart 5.1 KB

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