2023-02-03 06:57:48 +00:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
|
|
|
|
import 'package:photos/ente_theme_data.dart';
|
2023-04-05 04:30:41 +00:00
|
|
|
import "package:photos/generated/l10n.dart";
|
2023-02-03 06:57:48 +00:00
|
|
|
import 'package:photos/models/collection.dart';
|
|
|
|
import 'package:photos/services/collections_service.dart';
|
|
|
|
import 'package:photos/theme/ente_theme.dart';
|
|
|
|
import 'package:photos/ui/components/captioned_text_widget.dart';
|
|
|
|
import 'package:photos/ui/components/divider_widget.dart';
|
|
|
|
import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart';
|
|
|
|
import 'package:photos/ui/components/title_bar_title_widget.dart';
|
|
|
|
import 'package:photos/ui/components/title_bar_widget.dart';
|
|
|
|
import 'package:photos/utils/dialog_util.dart';
|
|
|
|
import 'package:photos/utils/separators_util.dart';
|
|
|
|
import 'package:tuple/tuple.dart';
|
|
|
|
|
|
|
|
class LinkExpiryPickerPage extends StatelessWidget {
|
|
|
|
final Collection collection;
|
|
|
|
const LinkExpiryPickerPage(this.collection, {super.key});
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return Scaffold(
|
|
|
|
body: CustomScrollView(
|
|
|
|
primary: false,
|
|
|
|
slivers: <Widget>[
|
2023-04-05 04:30:41 +00:00
|
|
|
TitleBarWidget(
|
2023-02-03 06:57:48 +00:00
|
|
|
flexibleSpaceTitle: TitleBarTitleWidget(
|
2023-04-05 04:30:41 +00:00
|
|
|
title: S.of(context).linkExpiry,
|
2023-02-03 06:57:48 +00:00
|
|
|
),
|
|
|
|
),
|
|
|
|
SliverList(
|
|
|
|
delegate: SliverChildBuilderDelegate(
|
|
|
|
(context, index) {
|
|
|
|
return Padding(
|
|
|
|
padding: const EdgeInsets.symmetric(
|
|
|
|
horizontal: 16,
|
|
|
|
vertical: 20,
|
|
|
|
),
|
|
|
|
child: Column(
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
children: [
|
|
|
|
ClipRRect(
|
|
|
|
borderRadius:
|
|
|
|
const BorderRadius.all(Radius.circular(8)),
|
|
|
|
child: ItemsWidget(collection),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
childCount: 1,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const SliverPadding(padding: EdgeInsets.symmetric(vertical: 12)),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-05 04:30:41 +00:00
|
|
|
class ItemsWidget extends StatefulWidget {
|
2023-02-03 06:57:48 +00:00
|
|
|
final Collection collection;
|
|
|
|
ItemsWidget(this.collection, {super.key});
|
|
|
|
|
2023-04-05 04:30:41 +00:00
|
|
|
@override
|
|
|
|
State<ItemsWidget> createState() => _ItemsWidgetState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _ItemsWidgetState extends State<ItemsWidget> {
|
2023-02-03 06:57:48 +00:00
|
|
|
// index, title, milliseconds in future post which link should expire (when >0)
|
2023-04-05 04:30:41 +00:00
|
|
|
late final List<Tuple2<String, int>> _expiryOptions = [
|
|
|
|
Tuple2(S.of(context).never, 0),
|
|
|
|
Tuple2(S.of(context).after1Hour, const Duration(hours: 1).inMicroseconds),
|
|
|
|
Tuple2(S.of(context).after1Day, const Duration(days: 1).inMicroseconds),
|
|
|
|
Tuple2(S.of(context).after1Week, const Duration(days: 7).inMicroseconds),
|
2023-02-03 06:57:48 +00:00
|
|
|
// todo: make this time calculation perfect
|
2023-04-05 04:30:41 +00:00
|
|
|
Tuple2(S.of(context).after1Month, const Duration(days: 30).inMicroseconds),
|
|
|
|
Tuple2(S.of(context).after1Year, const Duration(days: 365).inMicroseconds),
|
|
|
|
Tuple2(S.of(context).custom, -1),
|
2023-02-03 06:57:48 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
List<Widget> items = [];
|
|
|
|
for (Tuple2<String, int> expiryOpiton in _expiryOptions) {
|
|
|
|
items.add(
|
|
|
|
_menuItemForPicker(context, expiryOpiton),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
items = addSeparators(
|
|
|
|
items,
|
|
|
|
DividerWidget(
|
|
|
|
dividerType: DividerType.menuNoIcon,
|
|
|
|
bgColor: getEnteColorScheme(context).fillFaint,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
return Column(
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
children: items,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _menuItemForPicker(
|
|
|
|
BuildContext context,
|
|
|
|
Tuple2<String, int> expiryOpiton,
|
|
|
|
) {
|
|
|
|
return MenuItemWidget(
|
|
|
|
menuItemColor: getEnteColorScheme(context).fillFaint,
|
|
|
|
captionedTextWidget: CaptionedTextWidget(
|
|
|
|
title: expiryOpiton.item1,
|
|
|
|
),
|
|
|
|
alignCaptionedTextToLeft: true,
|
|
|
|
isTopBorderRadiusRemoved: true,
|
|
|
|
isBottomBorderRadiusRemoved: true,
|
|
|
|
alwaysShowSuccessState: true,
|
2023-02-03 07:33:27 +00:00
|
|
|
surfaceExecutionStates: expiryOpiton.item2 == -1 ? false : true,
|
2023-02-03 06:57:48 +00:00
|
|
|
onTap: () async {
|
|
|
|
int newValidTill = -1;
|
|
|
|
final int expireAfterInMicroseconds = expiryOpiton.item2;
|
|
|
|
// need to manually select time
|
|
|
|
if (expireAfterInMicroseconds < 0) {
|
|
|
|
final timeInMicrosecondsFromEpoch =
|
|
|
|
await _showDateTimePicker(context);
|
|
|
|
if (timeInMicrosecondsFromEpoch != null) {
|
|
|
|
newValidTill = timeInMicrosecondsFromEpoch;
|
|
|
|
}
|
|
|
|
} else if (expireAfterInMicroseconds == 0) {
|
|
|
|
// no expiry
|
|
|
|
newValidTill = 0;
|
|
|
|
} else {
|
|
|
|
newValidTill =
|
|
|
|
DateTime.now().microsecondsSinceEpoch + expireAfterInMicroseconds;
|
|
|
|
}
|
|
|
|
if (newValidTill >= 0) {
|
|
|
|
debugPrint("Setting expirty $newValidTill");
|
|
|
|
await updateTime(newValidTill, context);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// _showDateTimePicker return null if user doesn't select date-time
|
|
|
|
Future<int?> _showDateTimePicker(BuildContext context) async {
|
|
|
|
final dateResult = await DatePicker.showDatePicker(
|
|
|
|
context,
|
|
|
|
minTime: DateTime.now(),
|
|
|
|
currentTime: DateTime.now(),
|
|
|
|
locale: LocaleType.en,
|
|
|
|
theme: Theme.of(context).colorScheme.dateTimePickertheme,
|
|
|
|
);
|
|
|
|
if (dateResult == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
final dateWithTimeResult = await DatePicker.showTime12hPicker(
|
|
|
|
context,
|
|
|
|
showTitleActions: true,
|
|
|
|
currentTime: dateResult,
|
|
|
|
locale: LocaleType.en,
|
|
|
|
theme: Theme.of(context).colorScheme.dateTimePickertheme,
|
|
|
|
);
|
|
|
|
if (dateWithTimeResult == null) {
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
return dateWithTimeResult.microsecondsSinceEpoch;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> updateTime(int newValidTill, BuildContext context) async {
|
|
|
|
await _updateUrlSettings(
|
|
|
|
context,
|
|
|
|
{'validTill': newValidTill},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _updateUrlSettings(
|
|
|
|
BuildContext context,
|
|
|
|
Map<String, dynamic> prop,
|
|
|
|
) async {
|
|
|
|
try {
|
2023-04-05 04:30:41 +00:00
|
|
|
await CollectionsService.instance.updateShareUrl(widget.collection, prop);
|
2023-02-03 06:57:48 +00:00
|
|
|
} catch (e) {
|
|
|
|
showGenericErrorDialog(context: context);
|
|
|
|
rethrow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|