file_bottom_bar.dart 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. import 'dart:io';
  2. import 'package:flutter/cupertino.dart';
  3. import 'package:flutter/material.dart';
  4. import "package:photos/generated/l10n.dart";
  5. import 'package:photos/models/file.dart';
  6. import 'package:photos/models/file_type.dart';
  7. import 'package:photos/models/selected_files.dart';
  8. import 'package:photos/models/trash_file.dart';
  9. import 'package:photos/theme/colors.dart';
  10. import 'package:photos/theme/ente_theme.dart';
  11. import "package:photos/ui/actions/file/file_actions.dart";
  12. import 'package:photos/ui/collections/collection_action_sheet.dart';
  13. import 'package:photos/utils/delete_file_util.dart';
  14. import 'package:photos/utils/share_util.dart';
  15. class FileBottomBar extends StatefulWidget {
  16. final EnteFile file;
  17. final Function(EnteFile) onEditRequested;
  18. final Function(EnteFile) onFileRemoved;
  19. final bool showOnlyInfoButton;
  20. final int? userID;
  21. final ValueNotifier<bool> enableFullScreenNotifier;
  22. const FileBottomBar(
  23. this.file,
  24. this.onEditRequested,
  25. this.showOnlyInfoButton, {
  26. required this.onFileRemoved,
  27. required this.enableFullScreenNotifier,
  28. this.userID,
  29. Key? key,
  30. }) : super(key: key);
  31. @override
  32. FileBottomBarState createState() => FileBottomBarState();
  33. }
  34. class FileBottomBarState extends State<FileBottomBar> {
  35. final GlobalKey shareButtonKey = GlobalKey();
  36. @override
  37. Widget build(BuildContext context) {
  38. return _getBottomBar();
  39. }
  40. void safeRefresh() {
  41. if (mounted) {
  42. setState(() {});
  43. }
  44. }
  45. Widget _getBottomBar() {
  46. final List<Widget> children = [];
  47. final bool isOwnedByUser =
  48. widget.file.ownerID == null || widget.file.ownerID == widget.userID;
  49. children.add(
  50. Tooltip(
  51. message: "Info",
  52. child: Padding(
  53. padding: const EdgeInsets.only(top: 12, bottom: 12),
  54. child: IconButton(
  55. icon: Icon(
  56. Platform.isAndroid ? Icons.info_outline : CupertinoIcons.info,
  57. color: Colors.white,
  58. ),
  59. onPressed: () async {
  60. await _displayDetails(widget.file);
  61. safeRefresh(); //to instantly show the new caption if keypad is closed after pressing 'done' - here the caption will be updated before the bottom sheet is closed
  62. await Future.delayed(
  63. const Duration(milliseconds: 500),
  64. ); //Waiting for some time till the caption gets updated in db if the user closes the bottom sheet without pressing 'done'
  65. safeRefresh();
  66. },
  67. ),
  68. ),
  69. ),
  70. );
  71. if (widget.file is TrashFile) {
  72. _addTrashOptions(children);
  73. }
  74. if (!widget.showOnlyInfoButton && widget.file is! TrashFile) {
  75. if (widget.file.fileType == FileType.image ||
  76. widget.file.fileType == FileType.livePhoto) {
  77. children.add(
  78. Tooltip(
  79. message: "Edit",
  80. child: Padding(
  81. padding: const EdgeInsets.only(top: 12, bottom: 12),
  82. child: IconButton(
  83. icon: const Icon(
  84. Icons.tune_outlined,
  85. color: Colors.white,
  86. ),
  87. onPressed: () {
  88. widget.onEditRequested(widget.file);
  89. },
  90. ),
  91. ),
  92. ),
  93. );
  94. }
  95. if (isOwnedByUser) {
  96. children.add(
  97. Tooltip(
  98. message: S.of(context).delete,
  99. child: Padding(
  100. padding: const EdgeInsets.only(top: 12, bottom: 12),
  101. child: IconButton(
  102. icon: Icon(
  103. Platform.isAndroid
  104. ? Icons.delete_outline
  105. : CupertinoIcons.delete,
  106. color: Colors.white,
  107. ),
  108. onPressed: () async {
  109. await _showSingleFileDeleteSheet(widget.file);
  110. },
  111. ),
  112. ),
  113. ),
  114. );
  115. }
  116. children.add(
  117. Tooltip(
  118. message: S.of(context).share,
  119. child: Padding(
  120. padding: const EdgeInsets.only(top: 12, bottom: 12),
  121. child: IconButton(
  122. key: shareButtonKey,
  123. icon: Icon(
  124. Platform.isAndroid
  125. ? Icons.share_outlined
  126. : CupertinoIcons.share,
  127. color: Colors.white,
  128. ),
  129. onPressed: () {
  130. share(context, [widget.file], shareButtonKey: shareButtonKey);
  131. },
  132. ),
  133. ),
  134. ),
  135. );
  136. }
  137. final safeAreaBottomPadding = MediaQuery.of(context).padding.bottom * .5;
  138. return ValueListenableBuilder(
  139. valueListenable: widget.enableFullScreenNotifier,
  140. builder: (BuildContext context, bool isFullScreen, _) {
  141. return IgnorePointer(
  142. ignoring: isFullScreen,
  143. child: AnimatedOpacity(
  144. opacity: isFullScreen ? 0 : 1,
  145. duration: const Duration(milliseconds: 150),
  146. child: Align(
  147. alignment: Alignment.bottomCenter,
  148. child: Container(
  149. decoration: BoxDecoration(
  150. gradient: LinearGradient(
  151. begin: Alignment.topCenter,
  152. end: Alignment.bottomCenter,
  153. colors: [
  154. Colors.transparent,
  155. Colors.black.withOpacity(0.6),
  156. Colors.black.withOpacity(0.72),
  157. ],
  158. stops: const [0, 0.8, 1],
  159. ),
  160. ),
  161. child: Padding(
  162. padding: EdgeInsets.only(bottom: safeAreaBottomPadding),
  163. child: Column(
  164. mainAxisSize: MainAxisSize.min,
  165. children: [
  166. widget.file.caption?.isNotEmpty ?? false
  167. ? Padding(
  168. padding: const EdgeInsets.fromLTRB(
  169. 16,
  170. 28,
  171. 16,
  172. 12,
  173. ),
  174. child: Text(
  175. widget.file.caption!,
  176. overflow: TextOverflow.ellipsis,
  177. maxLines: 4,
  178. style: getEnteTextTheme(context)
  179. .small
  180. .copyWith(color: textBaseDark),
  181. textAlign: TextAlign.center,
  182. ),
  183. )
  184. : const SizedBox.shrink(),
  185. Row(
  186. mainAxisAlignment: MainAxisAlignment.spaceAround,
  187. children: children,
  188. ),
  189. ],
  190. ),
  191. ),
  192. ),
  193. ),
  194. ),
  195. );
  196. },
  197. );
  198. }
  199. Future<void> _showSingleFileDeleteSheet(EnteFile file) async {
  200. await showSingleFileDeleteSheet(
  201. context,
  202. file,
  203. onFileRemoved: widget.onFileRemoved,
  204. );
  205. }
  206. void _addTrashOptions(List<Widget> children) {
  207. children.add(
  208. Tooltip(
  209. message: S.of(context).restore,
  210. child: Padding(
  211. padding: const EdgeInsets.only(top: 12, bottom: 12),
  212. child: IconButton(
  213. icon: const Icon(
  214. Icons.restore_outlined,
  215. color: Colors.white,
  216. ),
  217. onPressed: () {
  218. final selectedFiles = SelectedFiles();
  219. selectedFiles.toggleSelection(widget.file);
  220. showCollectionActionSheet(
  221. context,
  222. selectedFiles: selectedFiles,
  223. actionType: CollectionActionType.restoreFiles,
  224. );
  225. },
  226. ),
  227. ),
  228. ),
  229. );
  230. children.add(
  231. Tooltip(
  232. message: S.of(context).delete,
  233. child: Padding(
  234. padding: const EdgeInsets.only(top: 12, bottom: 12),
  235. child: IconButton(
  236. icon: const Icon(
  237. Icons.delete_forever_outlined,
  238. color: Colors.white,
  239. ),
  240. onPressed: () async {
  241. final trashedFile = <TrashFile>[];
  242. trashedFile.add(widget.file as TrashFile);
  243. if (await deleteFromTrash(context, trashedFile) == true) {
  244. Navigator.pop(context);
  245. }
  246. },
  247. ),
  248. ),
  249. ),
  250. );
  251. }
  252. Future<void> _displayDetails(EnteFile file) async {
  253. await showDetailsSheet(context, file);
  254. }
  255. }