link_expiry_picker_page.dart 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import 'package:flutter/material.dart';
  2. import "package:flutter_datetime_picker_bdaya/flutter_datetime_picker_bdaya.dart";
  3. import 'package:photos/ente_theme_data.dart';
  4. import "package:photos/generated/l10n.dart";
  5. import 'package:photos/models/collection/collection.dart';
  6. import 'package:photos/services/collections_service.dart';
  7. import 'package:photos/theme/ente_theme.dart';
  8. import 'package:photos/ui/components/captioned_text_widget.dart';
  9. import 'package:photos/ui/components/divider_widget.dart';
  10. import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart';
  11. import 'package:photos/ui/components/title_bar_title_widget.dart';
  12. import 'package:photos/ui/components/title_bar_widget.dart';
  13. import 'package:photos/utils/dialog_util.dart';
  14. import 'package:photos/utils/separators_util.dart';
  15. import 'package:tuple/tuple.dart';
  16. class LinkExpiryPickerPage extends StatelessWidget {
  17. final Collection collection;
  18. const LinkExpiryPickerPage(this.collection, {super.key});
  19. @override
  20. Widget build(BuildContext context) {
  21. return Scaffold(
  22. body: CustomScrollView(
  23. primary: false,
  24. slivers: <Widget>[
  25. TitleBarWidget(
  26. flexibleSpaceTitle: TitleBarTitleWidget(
  27. title: S.of(context).linkExpiry,
  28. ),
  29. ),
  30. SliverList(
  31. delegate: SliverChildBuilderDelegate(
  32. (context, index) {
  33. return Padding(
  34. padding: const EdgeInsets.symmetric(
  35. horizontal: 16,
  36. vertical: 20,
  37. ),
  38. child: Column(
  39. mainAxisSize: MainAxisSize.min,
  40. children: [
  41. ClipRRect(
  42. borderRadius:
  43. const BorderRadius.all(Radius.circular(8)),
  44. child: ItemsWidget(collection),
  45. ),
  46. ],
  47. ),
  48. );
  49. },
  50. childCount: 1,
  51. ),
  52. ),
  53. const SliverPadding(padding: EdgeInsets.symmetric(vertical: 12)),
  54. ],
  55. ),
  56. );
  57. }
  58. }
  59. class ItemsWidget extends StatefulWidget {
  60. final Collection collection;
  61. const ItemsWidget(this.collection, {super.key});
  62. @override
  63. State<ItemsWidget> createState() => _ItemsWidgetState();
  64. }
  65. class _ItemsWidgetState extends State<ItemsWidget> {
  66. // index, title, milliseconds in future post which link should expire (when >0)
  67. late final List<Tuple2<String, int>> _expiryOptions = [
  68. Tuple2(S.of(context).never, 0),
  69. Tuple2(S.of(context).after1Hour, const Duration(hours: 1).inMicroseconds),
  70. Tuple2(S.of(context).after1Day, const Duration(days: 1).inMicroseconds),
  71. Tuple2(S.of(context).after1Week, const Duration(days: 7).inMicroseconds),
  72. // todo: make this time calculation perfect
  73. Tuple2(S.of(context).after1Month, const Duration(days: 30).inMicroseconds),
  74. Tuple2(S.of(context).after1Year, const Duration(days: 365).inMicroseconds),
  75. Tuple2(S.of(context).custom, -1),
  76. ];
  77. @override
  78. Widget build(BuildContext context) {
  79. List<Widget> items = [];
  80. for (Tuple2<String, int> expiryOpiton in _expiryOptions) {
  81. items.add(
  82. _menuItemForPicker(context, expiryOpiton),
  83. );
  84. }
  85. items = addSeparators(
  86. items,
  87. DividerWidget(
  88. dividerType: DividerType.menuNoIcon,
  89. bgColor: getEnteColorScheme(context).fillFaint,
  90. ),
  91. );
  92. return Column(
  93. mainAxisSize: MainAxisSize.min,
  94. children: items,
  95. );
  96. }
  97. Widget _menuItemForPicker(
  98. BuildContext context,
  99. Tuple2<String, int> expiryOpiton,
  100. ) {
  101. return MenuItemWidget(
  102. menuItemColor: getEnteColorScheme(context).fillFaint,
  103. captionedTextWidget: CaptionedTextWidget(
  104. title: expiryOpiton.item1,
  105. ),
  106. alignCaptionedTextToLeft: true,
  107. isTopBorderRadiusRemoved: true,
  108. isBottomBorderRadiusRemoved: true,
  109. alwaysShowSuccessState: true,
  110. surfaceExecutionStates: expiryOpiton.item2 == -1 ? false : true,
  111. onTap: () async {
  112. int newValidTill = -1;
  113. final int expireAfterInMicroseconds = expiryOpiton.item2;
  114. // need to manually select time
  115. if (expireAfterInMicroseconds < 0) {
  116. final timeInMicrosecondsFromEpoch =
  117. await _showDateTimePicker(context);
  118. if (timeInMicrosecondsFromEpoch != null) {
  119. newValidTill = timeInMicrosecondsFromEpoch;
  120. }
  121. } else if (expireAfterInMicroseconds == 0) {
  122. // no expiry
  123. newValidTill = 0;
  124. } else {
  125. newValidTill =
  126. DateTime.now().microsecondsSinceEpoch + expireAfterInMicroseconds;
  127. }
  128. if (newValidTill >= 0) {
  129. debugPrint("Setting expirty $newValidTill");
  130. await updateTime(newValidTill, context);
  131. }
  132. },
  133. );
  134. }
  135. // _showDateTimePicker return null if user doesn't select date-time
  136. Future<int?> _showDateTimePicker(BuildContext context) async {
  137. final dateResult = await DatePickerBdaya.showDatePicker(
  138. context,
  139. minTime: DateTime.now(),
  140. currentTime: DateTime.now(),
  141. locale: LocaleType.en,
  142. theme: Theme.of(context).colorScheme.dateTimePickertheme,
  143. );
  144. if (dateResult == null) {
  145. return null;
  146. }
  147. final dateWithTimeResult = await DatePickerBdaya.showTime12hPicker(
  148. context,
  149. showTitleActions: true,
  150. currentTime: dateResult,
  151. locale: LocaleType.en,
  152. theme: Theme.of(context).colorScheme.dateTimePickertheme,
  153. );
  154. if (dateWithTimeResult == null) {
  155. return null;
  156. } else {
  157. return dateWithTimeResult.microsecondsSinceEpoch;
  158. }
  159. }
  160. Future<void> updateTime(int newValidTill, BuildContext context) async {
  161. await _updateUrlSettings(
  162. context,
  163. {'validTill': newValidTill},
  164. );
  165. }
  166. Future<void> _updateUrlSettings(
  167. BuildContext context,
  168. Map<String, dynamic> prop,
  169. ) async {
  170. try {
  171. await CollectionsService.instance.updateShareUrl(widget.collection, prop);
  172. } catch (e) {
  173. await showGenericErrorDialog(context: context, error: e);
  174. rethrow;
  175. }
  176. }
  177. }