share_collection_page.dart 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. import 'package:collection/collection.dart';
  2. import 'package:fast_base58/fast_base58.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter/services.dart';
  5. import 'package:photos/models/collection.dart';
  6. import 'package:photos/services/collections_service.dart';
  7. import 'package:photos/theme/ente_theme.dart';
  8. import 'package:photos/ui/actions/collection/collection_sharing_actions.dart';
  9. import 'package:photos/ui/components/captioned_text_widget.dart';
  10. import 'package:photos/ui/components/divider_widget.dart';
  11. import 'package:photos/ui/components/menu_item_widget.dart';
  12. import 'package:photos/ui/components/menu_section_description_widget.dart';
  13. import 'package:photos/ui/components/menu_section_title.dart';
  14. import 'package:photos/ui/sharing/add_partipant_page.dart';
  15. import 'package:photos/ui/sharing/album_participants_page.dart';
  16. import 'package:photos/ui/sharing/manage_links_widget.dart';
  17. import 'package:photos/ui/sharing/user_avator_widget.dart';
  18. import 'package:photos/utils/navigation_util.dart';
  19. import 'package:photos/utils/share_util.dart';
  20. import 'package:photos/utils/toast_util.dart';
  21. class ShareCollectionPage extends StatefulWidget {
  22. final Collection collection;
  23. const ShareCollectionPage(this.collection, {Key? key}) : super(key: key);
  24. @override
  25. State<ShareCollectionPage> createState() => _ShareCollectionPageState();
  26. }
  27. class _ShareCollectionPageState extends State<ShareCollectionPage> {
  28. late List<User?> _sharees;
  29. final CollectionActions collectionActions =
  30. CollectionActions(CollectionsService.instance);
  31. Future<void> _navigateToManageUser() async {
  32. await routeToPage(
  33. context,
  34. AlbumParticipantsPage(widget.collection),
  35. );
  36. if (mounted) {
  37. setState(() => {});
  38. }
  39. }
  40. @override
  41. Widget build(BuildContext context) {
  42. _sharees = widget.collection.sharees ?? [];
  43. final bool hasUrl = widget.collection.publicURLs?.isNotEmpty ?? false;
  44. final children = <Widget>[];
  45. children.add(
  46. MenuSectionTitle(
  47. title: _sharees.isEmpty
  48. ? "Share with specific people"
  49. : "Shared with ${_sharees.length} ${_sharees.length == 1 ? 'person' : 'people'}",
  50. iconData: Icons.workspaces,
  51. ),
  52. );
  53. children.add(
  54. EmailItemWidget(
  55. widget.collection,
  56. onTap: _navigateToManageUser,
  57. ),
  58. );
  59. children.add(
  60. MenuItemWidget(
  61. captionedTextWidget: CaptionedTextWidget(
  62. title: _sharees.isEmpty ? "Add email" : "Add more",
  63. makeTextBold: true,
  64. ),
  65. leadingIcon: Icons.add,
  66. menuItemColor: getEnteColorScheme(context).fillFaint,
  67. pressedColor: getEnteColorScheme(context).fillFaint,
  68. borderRadius: 4.0,
  69. isTopBorderRadiusRemoved: _sharees.isNotEmpty,
  70. onTap: () async {
  71. routeToPage(context, AddParticipantPage(widget.collection)).then(
  72. (value) => {
  73. if (mounted) {setState(() => {})}
  74. },
  75. );
  76. },
  77. ),
  78. );
  79. if (_sharees.isEmpty && !hasUrl) {
  80. children.add(
  81. const MenuSectionDescriptionWidget(
  82. content:
  83. "Create shared and collaborative albums with other ente users, "
  84. "including users on free plans.",
  85. ),
  86. );
  87. }
  88. final bool hasExpired =
  89. widget.collection.publicURLs?.firstOrNull?.isExpired ?? false;
  90. children.addAll([
  91. const SizedBox(
  92. height: 24,
  93. ),
  94. MenuSectionTitle(
  95. title: hasUrl
  96. ? "Public link enabled"
  97. : (_sharees.isEmpty ? "Or share a link" : "Share a link"),
  98. iconData: Icons.public,
  99. ),
  100. ]);
  101. if (hasUrl) {
  102. if (hasExpired) {
  103. children.add(
  104. MenuItemWidget(
  105. captionedTextWidget: CaptionedTextWidget(
  106. title: "Link has expired",
  107. textColor: getEnteColorScheme(context).warning500,
  108. ),
  109. leadingIcon: Icons.error_outline,
  110. leadingIconColor: getEnteColorScheme(context).warning500,
  111. menuItemColor: getEnteColorScheme(context).fillFaint,
  112. pressedColor: getEnteColorScheme(context).fillFaint,
  113. onTap: () async {},
  114. isBottomBorderRadiusRemoved: true,
  115. ),
  116. );
  117. } else {
  118. final String collectionKey = Base58Encode(
  119. CollectionsService.instance.getCollectionKey(widget.collection.id),
  120. );
  121. final String url =
  122. "${widget.collection.publicURLs!.first!.url}#$collectionKey";
  123. children.addAll(
  124. [
  125. MenuItemWidget(
  126. captionedTextWidget: const CaptionedTextWidget(
  127. title: "Copy link",
  128. makeTextBold: true,
  129. ),
  130. leadingIcon: Icons.copy,
  131. menuItemColor: getEnteColorScheme(context).fillFaint,
  132. pressedColor: getEnteColorScheme(context).fillFaint,
  133. onTap: () async {
  134. await Clipboard.setData(ClipboardData(text: url));
  135. showShortToast(context, "Link copied to clipboard");
  136. },
  137. isBottomBorderRadiusRemoved: true,
  138. ),
  139. DividerWidget(
  140. dividerType: DividerType.menu,
  141. bgColor: getEnteColorScheme(context).fillFaint,
  142. ),
  143. MenuItemWidget(
  144. captionedTextWidget: const CaptionedTextWidget(
  145. title: "Send link",
  146. makeTextBold: true,
  147. ),
  148. leadingIcon: Icons.adaptive.share,
  149. menuItemColor: getEnteColorScheme(context).fillFaint,
  150. pressedColor: getEnteColorScheme(context).fillFaint,
  151. onTap: () async {
  152. shareText(url);
  153. },
  154. isTopBorderRadiusRemoved: true,
  155. isBottomBorderRadiusRemoved: true,
  156. ),
  157. ],
  158. );
  159. }
  160. children.addAll(
  161. [
  162. DividerWidget(
  163. dividerType: DividerType.menu,
  164. bgColor: getEnteColorScheme(context).fillFaint,
  165. ),
  166. MenuItemWidget(
  167. captionedTextWidget: const CaptionedTextWidget(
  168. title: "Manage link",
  169. makeTextBold: true,
  170. ),
  171. leadingIcon: Icons.link,
  172. trailingIcon: Icons.navigate_next,
  173. menuItemColor: getEnteColorScheme(context).fillFaint,
  174. pressedColor: getEnteColorScheme(context).fillFaint,
  175. trailingIconIsMuted: true,
  176. onTap: () async {
  177. routeToPage(
  178. context,
  179. ManageSharedLinkWidget(collection: widget.collection),
  180. ).then(
  181. (value) => {
  182. if (mounted) {setState(() => {})}
  183. },
  184. );
  185. },
  186. isTopBorderRadiusRemoved: true,
  187. ),
  188. ],
  189. );
  190. } else {
  191. children.add(
  192. MenuItemWidget(
  193. captionedTextWidget: const CaptionedTextWidget(
  194. title: "Create public link",
  195. ),
  196. leadingIcon: Icons.link,
  197. menuItemColor: getEnteColorScheme(context).fillFaint,
  198. pressedColor: getEnteColorScheme(context).fillFaint,
  199. onTap: () async {
  200. final bool result = await collectionActions.publicLinkToggle(
  201. context,
  202. widget.collection,
  203. true,
  204. );
  205. if (result && mounted) {
  206. setState(() => {});
  207. }
  208. },
  209. ),
  210. );
  211. if (_sharees.isEmpty && !hasUrl) {
  212. children.add(
  213. const MenuSectionDescriptionWidget(
  214. content:
  215. "Links allow people without an ente account to view and add photos to your shared albums.",
  216. ),
  217. );
  218. }
  219. }
  220. return Scaffold(
  221. appBar: AppBar(
  222. title: Text(
  223. widget.collection.name ?? "Unnamed",
  224. style: Theme.of(context).textTheme.headline5?.copyWith(fontSize: 16),
  225. ),
  226. elevation: 0,
  227. centerTitle: false,
  228. ),
  229. body: SingleChildScrollView(
  230. child: ListBody(
  231. children: <Widget>[
  232. Padding(
  233. padding:
  234. const EdgeInsets.symmetric(vertical: 4.0, horizontal: 16),
  235. child: Column(
  236. children: children,
  237. ),
  238. ),
  239. ],
  240. ),
  241. ),
  242. );
  243. }
  244. }
  245. class EmailItemWidget extends StatelessWidget {
  246. final Collection collection;
  247. final Function? onTap;
  248. const EmailItemWidget(
  249. this.collection, {
  250. this.onTap,
  251. Key? key,
  252. }) : super(key: key);
  253. @override
  254. Widget build(BuildContext context) {
  255. if (collection.getSharees().isEmpty) {
  256. return const SizedBox.shrink();
  257. } else if (collection.getSharees().length == 1) {
  258. return Column(
  259. mainAxisAlignment: MainAxisAlignment.start,
  260. children: [
  261. MenuItemWidget(
  262. captionedTextWidget: CaptionedTextWidget(
  263. title: collection.getSharees().firstOrNull?.email ?? '',
  264. ),
  265. leadingIconWidget: UserAvatarWidget(
  266. collection.getSharees().first,
  267. thumbnailView: true,
  268. ),
  269. leadingIconSize: 24,
  270. menuItemColor: getEnteColorScheme(context).fillFaint,
  271. pressedColor: getEnteColorScheme(context).fillFaint,
  272. trailingIconIsMuted: true,
  273. trailingIcon: Icons.chevron_right,
  274. onTap: () async {
  275. if (onTap != null) {
  276. onTap!();
  277. }
  278. },
  279. isBottomBorderRadiusRemoved: true,
  280. ),
  281. DividerWidget(
  282. dividerType: DividerType.menu,
  283. bgColor: getEnteColorScheme(context).fillFaint,
  284. ),
  285. ],
  286. );
  287. } else {
  288. return Column(
  289. mainAxisAlignment: MainAxisAlignment.start,
  290. children: [
  291. MenuItemWidget(
  292. captionedTextWidget: const CaptionedTextWidget(
  293. title: 'Manage',
  294. ),
  295. leadingIcon: Icons.people_outline,
  296. menuItemColor: getEnteColorScheme(context).fillFaint,
  297. pressedColor: getEnteColorScheme(context).fillFaint,
  298. trailingIconIsMuted: true,
  299. trailingIcon: Icons.chevron_right,
  300. onTap: () async {
  301. if (onTap != null) {
  302. onTap!();
  303. }
  304. },
  305. isBottomBorderRadiusRemoved: true,
  306. ),
  307. DividerWidget(
  308. dividerType: DividerType.menu,
  309. bgColor: getEnteColorScheme(context).fillFaint,
  310. ),
  311. ],
  312. );
  313. }
  314. }
  315. }