fading_bottom_bar.dart 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. // @dart=2.9
  2. import 'dart:io';
  3. import 'package:flutter/cupertino.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
  6. import 'package:page_transition/page_transition.dart';
  7. import 'package:photos/core/configuration.dart';
  8. import 'package:photos/models/file.dart';
  9. import 'package:photos/models/file_type.dart';
  10. import 'package:photos/models/magic_metadata.dart';
  11. import 'package:photos/models/selected_files.dart';
  12. import 'package:photos/models/trash_file.dart';
  13. import 'package:photos/services/collections_service.dart';
  14. import 'package:photos/theme/colors.dart';
  15. import 'package:photos/theme/ente_theme.dart';
  16. import 'package:photos/ui/create_collection_page.dart';
  17. import 'package:photos/ui/viewer/file/file_info_widget.dart';
  18. import 'package:photos/utils/delete_file_util.dart';
  19. import 'package:photos/utils/magic_util.dart';
  20. import 'package:photos/utils/share_util.dart';
  21. class FadingBottomBar extends StatefulWidget {
  22. final File file;
  23. final Function(File) onEditRequested;
  24. final bool showOnlyInfoButton;
  25. const FadingBottomBar(
  26. this.file,
  27. this.onEditRequested,
  28. this.showOnlyInfoButton, {
  29. Key key,
  30. }) : super(key: key);
  31. @override
  32. FadingBottomBarState createState() => FadingBottomBarState();
  33. }
  34. class FadingBottomBarState extends State<FadingBottomBar> {
  35. bool _shouldHide = false;
  36. final GlobalKey shareButtonKey = GlobalKey();
  37. @override
  38. Widget build(BuildContext context) {
  39. return _getBottomBar();
  40. }
  41. void hide() {
  42. setState(() {
  43. _shouldHide = true;
  44. });
  45. }
  46. void show() {
  47. setState(() {
  48. _shouldHide = false;
  49. });
  50. }
  51. void safeRefresh() {
  52. if (mounted) {
  53. setState(() {});
  54. }
  55. }
  56. Widget _getBottomBar() {
  57. final List<Widget> children = [];
  58. children.add(
  59. Tooltip(
  60. message: "Info",
  61. child: Padding(
  62. padding: const EdgeInsets.only(top: 12, bottom: 12),
  63. child: IconButton(
  64. icon: Icon(
  65. Platform.isAndroid ? Icons.info_outline : CupertinoIcons.info,
  66. color: Colors.white,
  67. ),
  68. onPressed: () async {
  69. await _displayInfo(widget.file);
  70. 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
  71. await Future.delayed(
  72. const Duration(milliseconds: 500),
  73. ); //Waiting for some time till the caption gets updated in db if the user closes the bottom sheet without pressing 'done'
  74. safeRefresh();
  75. },
  76. ),
  77. ),
  78. ),
  79. );
  80. if (widget.file is TrashFile) {
  81. _addTrashOptions(children);
  82. }
  83. bool isUploadedByUser = widget.file.uploadedFileID != null &&
  84. widget.file.ownerID == Configuration.instance.getUserID();
  85. bool isFileHidden = false;
  86. if (isUploadedByUser) {
  87. isFileHidden = CollectionsService.instance
  88. .getCollectionByID(widget.file.collectionID)
  89. ?.isHidden() ??
  90. false;
  91. }
  92. if (!widget.showOnlyInfoButton && widget.file is! TrashFile) {
  93. if (widget.file.fileType == FileType.image ||
  94. widget.file.fileType == FileType.livePhoto) {
  95. children.add(
  96. Tooltip(
  97. message: "Edit",
  98. child: Padding(
  99. padding: const EdgeInsets.only(top: 12, bottom: 12),
  100. child: IconButton(
  101. icon: const Icon(
  102. Icons.tune_outlined,
  103. color: Colors.white,
  104. ),
  105. onPressed: () {
  106. widget.onEditRequested(widget.file);
  107. },
  108. ),
  109. ),
  110. ),
  111. );
  112. }
  113. if (isUploadedByUser && !isFileHidden) {
  114. final bool isArchived =
  115. widget.file.magicMetadata.visibility == visibilityArchive;
  116. children.add(
  117. Tooltip(
  118. message: isArchived ? "Unarchive" : "Archive",
  119. child: Padding(
  120. padding: const EdgeInsets.only(top: 12, bottom: 12),
  121. child: IconButton(
  122. icon: Icon(
  123. isArchived ? Icons.unarchive : Icons.archive_outlined,
  124. color: Colors.white,
  125. ),
  126. onPressed: () async {
  127. await changeVisibility(
  128. context,
  129. [widget.file],
  130. isArchived ? visibilityVisible : visibilityArchive,
  131. );
  132. safeRefresh();
  133. },
  134. ),
  135. ),
  136. ),
  137. );
  138. }
  139. children.add(
  140. Tooltip(
  141. message: "Share",
  142. child: Padding(
  143. padding: const EdgeInsets.only(top: 12, bottom: 12),
  144. child: IconButton(
  145. key: shareButtonKey,
  146. icon: Icon(
  147. Platform.isAndroid
  148. ? Icons.share_outlined
  149. : CupertinoIcons.share,
  150. color: Colors.white,
  151. ),
  152. onPressed: () {
  153. share(context, [widget.file], shareButtonKey: shareButtonKey);
  154. },
  155. ),
  156. ),
  157. ),
  158. );
  159. }
  160. final safeAreaBottomPadding = MediaQuery.of(context).padding.bottom * .5;
  161. return IgnorePointer(
  162. ignoring: _shouldHide,
  163. child: AnimatedOpacity(
  164. opacity: _shouldHide ? 0 : 1,
  165. duration: const Duration(milliseconds: 150),
  166. child: Align(
  167. alignment: Alignment.bottomCenter,
  168. child: Container(
  169. decoration: BoxDecoration(
  170. gradient: LinearGradient(
  171. begin: Alignment.topCenter,
  172. end: Alignment.bottomCenter,
  173. colors: [
  174. Colors.transparent,
  175. Colors.black.withOpacity(0.6),
  176. Colors.black.withOpacity(0.72),
  177. ],
  178. stops: const [0, 0.8, 1],
  179. ),
  180. ),
  181. child: Padding(
  182. padding: EdgeInsets.only(bottom: safeAreaBottomPadding),
  183. child: Column(
  184. mainAxisSize: MainAxisSize.min,
  185. children: [
  186. widget.file.caption.isNotEmpty
  187. ? Padding(
  188. padding: const EdgeInsets.fromLTRB(
  189. 16,
  190. 28,
  191. 16,
  192. 12,
  193. ),
  194. child: Text(
  195. widget.file.caption,
  196. style: getEnteTextTheme(context)
  197. .small
  198. .copyWith(color: textBaseDark),
  199. textAlign: TextAlign.center,
  200. ),
  201. )
  202. : const SizedBox.shrink(),
  203. Row(
  204. mainAxisAlignment: MainAxisAlignment.spaceAround,
  205. children: children,
  206. ),
  207. ],
  208. ),
  209. ),
  210. ),
  211. ),
  212. ),
  213. );
  214. }
  215. void _addTrashOptions(List<Widget> children) {
  216. children.add(
  217. Tooltip(
  218. message: "Restore",
  219. child: Padding(
  220. padding: const EdgeInsets.only(top: 12, bottom: 12),
  221. child: IconButton(
  222. icon: const Icon(
  223. Icons.restore_outlined,
  224. color: Colors.white,
  225. ),
  226. onPressed: () {
  227. final selectedFiles = SelectedFiles();
  228. selectedFiles.toggleSelection(widget.file);
  229. Navigator.push(
  230. context,
  231. PageTransition(
  232. type: PageTransitionType.bottomToTop,
  233. child: CreateCollectionPage(
  234. selectedFiles,
  235. null,
  236. actionType: CollectionActionType.restoreFiles,
  237. ),
  238. ),
  239. );
  240. },
  241. ),
  242. ),
  243. ),
  244. );
  245. children.add(
  246. Tooltip(
  247. message: "Delete",
  248. child: Padding(
  249. padding: const EdgeInsets.only(top: 12, bottom: 12),
  250. child: IconButton(
  251. icon: const Icon(
  252. Icons.delete_forever_outlined,
  253. color: Colors.white,
  254. ),
  255. onPressed: () async {
  256. final trashedFile = <TrashFile>[];
  257. trashedFile.add(widget.file);
  258. if (await deleteFromTrash(context, trashedFile) == true) {
  259. Navigator.pop(context);
  260. }
  261. },
  262. ),
  263. ),
  264. ),
  265. );
  266. }
  267. Future<void> _displayInfo(File file) async {
  268. final colorScheme = getEnteColorScheme(context);
  269. return showBarModalBottomSheet(
  270. topControl: const SizedBox.shrink(),
  271. shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0)),
  272. backgroundColor: colorScheme.backgroundBase,
  273. barrierColor: backdropFaintDark,
  274. context: context,
  275. builder: (BuildContext context) {
  276. return Padding(
  277. padding:
  278. EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
  279. child: FileInfoWidget(file),
  280. );
  281. },
  282. );
  283. }
  284. }