ente/lib/ui/sharing/share_collection_page.dart

372 lines
11 KiB
Dart
Raw Normal View History

// @dart=2.9
import 'dart:async';
import 'package:fast_base58/fast_base58.dart';
2020-05-17 12:39:38 +00:00
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
2020-10-31 13:17:17 +00:00
import 'package:logging/logging.dart';
import 'package:photos/core/configuration.dart';
2022-07-12 06:30:02 +00:00
import 'package:photos/ente_theme_data.dart';
2020-10-09 23:51:20 +00:00
import 'package:photos/models/collection.dart';
import 'package:photos/services/collections_service.dart';
import 'package:photos/services/user_service.dart';
import 'package:photos/theme/ente_theme.dart';
import 'package:photos/ui/actions/collection/collection_sharing_actions.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.dart';
import 'package:photos/ui/components/menu_section_title.dart';
import 'package:photos/ui/payment/subscription.dart';
import 'package:photos/ui/sharing/add_partipant_page.dart';
2022-11-21 10:39:14 +00:00
import 'package:photos/ui/sharing/manage_album_participant.dart';
2022-07-01 14:39:02 +00:00
import 'package:photos/ui/sharing/manage_links_widget.dart';
2020-08-07 10:21:56 +00:00
import 'package:photos/utils/dialog_util.dart';
2020-10-09 23:51:20 +00:00
import 'package:photos/utils/email_util.dart';
2022-02-21 02:13:10 +00:00
import 'package:photos/utils/navigation_util.dart';
2020-10-09 23:51:20 +00:00
import 'package:photos/utils/share_util.dart';
2020-07-07 21:46:14 +00:00
import 'package:photos/utils/toast_util.dart';
2020-05-17 12:39:38 +00:00
2022-11-20 11:55:12 +00:00
class ShareCollectionPage extends StatefulWidget {
2020-10-13 06:23:45 +00:00
final Collection collection;
2022-11-20 11:55:12 +00:00
const ShareCollectionPage(this.collection, {Key key}) : super(key: key);
2020-10-13 06:23:45 +00:00
@override
2022-11-20 11:55:12 +00:00
State<ShareCollectionPage> createState() => _ShareCollectionPageState();
2020-10-13 06:23:45 +00:00
}
2022-11-20 11:55:12 +00:00
class _ShareCollectionPageState extends State<ShareCollectionPage> {
2020-11-02 14:38:59 +00:00
List<User> _sharees;
final Logger _logger = Logger("SharingDialogState");
final CollectionSharingActions sharingActions =
CollectionSharingActions(CollectionsService.instance);
2020-10-13 06:23:45 +00:00
@override
Widget build(BuildContext context) {
2020-12-03 22:41:01 +00:00
_sharees = widget.collection.sharees;
final children = <Widget>[];
2022-11-21 10:39:14 +00:00
children.add(
MenuSectionTitle(
title: _sharees.isEmpty
? "Share with specific people"
: "Shared with ${_sharees.length} people",
iconData: Icons.workspaces,
),
);
2022-11-22 06:39:48 +00:00
for (final user in _sharees) {
children.add(
EmailItemWidget(
widget.collection,
user.email,
user,
),
);
2020-08-07 10:21:56 +00:00
}
2022-11-22 06:39:48 +00:00
children.add(
MenuItemWidget(
captionedTextWidget: CaptionedTextWidget(
title: _sharees.isEmpty ? "Add email" : "Add more",
makeTextBold: true,
2020-10-09 23:51:20 +00:00
),
leadingIcon: Icons.add,
menuItemColor: getEnteColorScheme(context).fillFaint,
pressedColor: getEnteColorScheme(context).fillFaint,
onTap: () async {
unawaited(
routeToPage(context, AddParticipantPage(widget.collection)));
},
),
);
2020-10-13 05:22:20 +00:00
2022-09-15 05:34:22 +00:00
final bool hasUrl = widget.collection.publicURLs?.isNotEmpty ?? false;
children.addAll([
const SizedBox(
height: 24,
),
MenuSectionTitle(
title: hasUrl ? "Public link enabled" : "Share a public link",
iconData: Icons.public,
),
2022-09-15 05:34:22 +00:00
]);
if (hasUrl) {
final String collectionKey = Base58Encode(
CollectionsService.instance.getCollectionKey(widget.collection.id),
);
final String url =
"${widget.collection.publicURLs.first.url}#$collectionKey";
children.addAll(
[
MenuItemWidget(
captionedTextWidget: const CaptionedTextWidget(
title: "Copy link",
2022-11-21 10:39:14 +00:00
makeTextBold: true,
),
leadingIcon: Icons.copy,
menuItemColor: getEnteColorScheme(context).fillFaint,
pressedColor: getEnteColorScheme(context).fillFaint,
onTap: () async {
await Clipboard.setData(ClipboardData(text: url));
showToast(context, "Link copied to clipboard");
},
isBottomBorderRadiusRemoved: true,
),
DividerWidget(
dividerType: DividerType.menu,
bgColor: getEnteColorScheme(context).blurStrokeFaint,
),
MenuItemWidget(
captionedTextWidget: const CaptionedTextWidget(
title: "Send link",
2022-11-21 10:39:14 +00:00
makeTextBold: true,
),
leadingIcon: Icons.adaptive.share,
menuItemColor: getEnteColorScheme(context).fillFaint,
pressedColor: getEnteColorScheme(context).fillFaint,
onTap: () async {
shareText(url);
},
isTopBorderRadiusRemoved: true,
),
DividerWidget(
dividerType: DividerType.menu,
bgColor: getEnteColorScheme(context).blurStrokeFaint,
),
MenuItemWidget(
captionedTextWidget: const CaptionedTextWidget(
title: "Manage link",
2022-11-21 10:39:14 +00:00
makeTextBold: true,
),
leadingIcon: Icons.link,
trailingIcon: Icons.navigate_next,
menuItemColor: getEnteColorScheme(context).fillFaint,
pressedColor: getEnteColorScheme(context).fillFaint,
2022-11-21 10:39:14 +00:00
trailingIconIsMuted: true,
onTap: () async {
routeToPage(
context,
ManageSharedLinkWidget(collection: widget.collection),
).then(
(value) => {
if (mounted) {setState(() => {})}
},
);
},
isTopBorderRadiusRemoved: true,
),
],
);
} else {
2022-09-15 05:34:22 +00:00
children.add(
MenuItemWidget(
captionedTextWidget: const CaptionedTextWidget(
title: "Create public link",
),
leadingIcon: Icons.link,
menuItemColor: getEnteColorScheme(context).fillFaint,
pressedColor: getEnteColorScheme(context).fillFaint,
onTap: () async {
final bool result = await sharingActions.publicLinkToggle(
context,
widget.collection,
true,
);
if (result && mounted) {
setState(() => {});
}
},
2022-09-15 05:34:22 +00:00
),
);
}
return Scaffold(
appBar: AppBar(title: const Text("Sharing")),
body: SingleChildScrollView(
2020-10-13 05:22:20 +00:00
child: ListBody(
children: <Widget>[
Padding(
padding:
const EdgeInsets.symmetric(vertical: 4.0, horizontal: 16),
2022-06-11 08:23:52 +00:00
child: Column(
children: children,
),
),
2020-10-13 05:22:20 +00:00
],
),
),
);
2020-10-09 23:51:20 +00:00
}
Future<void> _addEmailToCollection(
String email, {
String publicKey,
}) async {
if (!isValidEmail(email)) {
2022-06-11 08:23:52 +00:00
showErrorDialog(
context,
"Invalid email address",
"Please enter a valid email address.",
);
return;
} else if (email == Configuration.instance.getEmail()) {
2022-05-17 11:38:21 +00:00
showErrorDialog(context, "Oops", "You cannot share with yourself");
2020-10-09 23:51:20 +00:00
return;
} else if (widget.collection.sharees.any((user) => user.email == email)) {
showErrorDialog(
2022-06-11 08:23:52 +00:00
context,
"Oops",
"You're already sharing this with " + email,
);
return;
2020-10-09 23:51:20 +00:00
}
if (publicKey == null) {
2022-05-17 11:38:21 +00:00
final dialog = createProgressDialog(context, "Searching for user...");
await dialog.show();
2020-10-29 12:56:30 +00:00
publicKey = await UserService.instance.getPublicKey(email);
await dialog.hide();
}
2020-10-09 23:51:20 +00:00
if (publicKey == null) {
2021-03-21 11:21:45 +00:00
Navigator.of(context, rootNavigator: true).pop('dialog');
2020-10-09 23:51:20 +00:00
final dialog = AlertDialog(
2022-07-04 06:02:17 +00:00
title: const Text("Invite to ente?"),
2021-07-28 12:20:27 +00:00
content: Text(
2022-07-03 09:49:33 +00:00
"Looks like " +
email +
" hasn't signed up for ente yet. would you like to invite them?",
2022-07-04 06:02:17 +00:00
style: const TextStyle(
2021-07-28 12:20:27 +00:00
height: 1.4,
),
),
2020-10-09 23:51:20 +00:00
actions: [
2021-07-28 12:20:27 +00:00
TextButton(
child: Text(
2022-05-17 11:38:21 +00:00
"Invite",
2021-07-28 12:20:27 +00:00
style: TextStyle(
2022-07-12 06:30:02 +00:00
color: Theme.of(context).colorScheme.greenAlternative,
2021-07-28 12:20:27 +00:00
),
),
2020-10-09 23:51:20 +00:00
onPressed: () {
shareText(
2022-06-11 08:23:52 +00:00
"Hey, I have some photos to share. Please install https://ente.io so that I can share them privately.",
);
2020-10-09 23:51:20 +00:00
},
),
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return dialog;
2020-08-07 10:21:56 +00:00
},
2020-10-09 23:51:20 +00:00
);
} else {
2022-05-17 11:38:21 +00:00
final dialog = createProgressDialog(context, "Sharing...");
await dialog.show();
try {
2022-07-03 09:49:33 +00:00
await CollectionsService.instance
.share(widget.collection.id, email, publicKey);
await dialog.hide();
2022-06-10 14:29:56 +00:00
showShortToast(context, "Shared successfully!");
2020-10-13 06:23:45 +00:00
setState(() {
2020-11-02 14:38:59 +00:00
_sharees.add(User(email: email));
2020-10-13 06:23:45 +00:00
});
} catch (e) {
await dialog.hide();
if (e is SharingNotPermittedForFreeAccountsError) {
2022-01-24 10:49:47 +00:00
_showUnSupportedAlert();
} else {
_logger.severe("failed to share collection", e);
showGenericErrorDialog(context);
}
}
2020-10-09 23:51:20 +00:00
}
2020-08-07 10:21:56 +00:00
}
2022-01-24 10:49:47 +00:00
void _showUnSupportedAlert() {
2022-08-29 14:43:31 +00:00
final AlertDialog alert = AlertDialog(
2022-07-04 06:02:17 +00:00
title: const Text("Sorry"),
content: const Text(
"Sharing is not permitted for free accounts, please subscribe",
),
2022-01-24 10:49:47 +00:00
actions: [
TextButton(
2022-02-05 18:31:59 +00:00
child: Text(
2022-05-03 11:40:45 +00:00
"Subscribe",
2022-02-05 18:31:59 +00:00
style: TextStyle(
2022-07-12 06:30:02 +00:00
color: Theme.of(context).colorScheme.greenAlternative,
2022-02-05 18:31:59 +00:00
),
),
2022-01-24 10:49:47 +00:00
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (BuildContext context) {
return getSubscriptionPage();
},
),
);
},
),
TextButton(
2022-02-05 18:31:59 +00:00
child: Text(
2022-05-16 20:38:11 +00:00
"Ok",
2022-02-05 18:31:59 +00:00
style: TextStyle(
2022-05-16 20:38:11 +00:00
color: Theme.of(context).colorScheme.onSurface,
2022-02-05 18:31:59 +00:00
),
),
2022-01-24 10:49:47 +00:00
onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
},
),
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
2020-08-07 10:21:56 +00:00
}
class EmailItemWidget extends StatelessWidget {
2021-03-21 11:21:45 +00:00
final Collection collection;
2020-08-07 10:21:56 +00:00
final String email;
2022-11-21 10:39:14 +00:00
final User user;
2020-10-31 13:17:17 +00:00
2020-08-07 10:21:56 +00:00
const EmailItemWidget(
2021-03-21 11:21:45 +00:00
this.collection,
2022-11-21 10:39:14 +00:00
this.email,
this.user, {
2020-08-07 10:21:56 +00:00
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(8, 0, 0, 0),
2022-11-21 10:39:14 +00:00
child: GestureDetector(
onTap: () async {
await routeToPage(
context,
ManageIndividualParticipant(collection: collection, user: user),
);
},
child: Text(
email,
style: const TextStyle(fontSize: 16),
),
),
),
2022-07-04 06:02:17 +00:00
const Expanded(child: SizedBox()),
],
);
2020-05-17 12:39:38 +00:00
}
}