import 'dart:async'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'package:photos/core/configuration.dart'; import 'package:photos/core/event_bus.dart'; import 'package:photos/events/collection_updated_event.dart'; import 'package:photos/events/local_photos_updated_event.dart'; import 'package:photos/events/user_logged_out_event.dart'; import "package:photos/generated/l10n.dart"; import 'package:photos/models/collection.dart'; import 'package:photos/models/collection_items.dart'; import 'package:photos/services/collections_service.dart'; import "package:photos/ui/collections/album/row_item.dart"; import "package:photos/ui/collections/collection_list_page.dart"; import 'package:photos/ui/common/loading_widget.dart'; import "package:photos/ui/components/buttons/button_widget.dart"; import "package:photos/ui/components/buttons/icon_button_widget.dart"; import "package:photos/ui/components/models/button_type.dart"; import 'package:photos/ui/tabs/section_title.dart'; import "package:photos/ui/tabs/shared/empty_state.dart"; import "package:photos/ui/tabs/shared/quick_link_album_item.dart"; import "package:photos/utils/navigation_util.dart"; import "package:photos/utils/share_util.dart"; class SharedCollectionsTab extends StatefulWidget { const SharedCollectionsTab({Key? key}) : super(key: key); @override State createState() => _SharedCollectionsTabState(); } class _SharedCollectionsTabState extends State with AutomaticKeepAliveClientMixin { final Logger _logger = Logger("SharedCollectionGallery"); late StreamSubscription _localFilesSubscription; late StreamSubscription _collectionUpdatesSubscription; late StreamSubscription _loggedOutEvent; @override void initState() { _localFilesSubscription = Bus.instance.on().listen((event) { debugPrint("SetState Shared Collections on ${event.reason}"); setState(() {}); }); _collectionUpdatesSubscription = Bus.instance.on().listen((event) { debugPrint("SetState Shared Collections on ${event.reason}"); setState(() {}); }); _loggedOutEvent = Bus.instance.on().listen((event) { setState(() {}); }); super.initState(); } @override Widget build(BuildContext context) { super.build(context); return FutureBuilder( future: Future.value(_getSharedCollections()), builder: (context, snapshot) { if (snapshot.hasData) { if ((snapshot.data?.incoming.length ?? 0) == 0 && (snapshot.data?.quickLinks.length ?? 0) == 0 && (snapshot.data?.outgoing.length ?? 0) == 0) { return const Center(child: SharedEmptyStateWidget()); } return _getSharedCollectionsGallery(snapshot.data!); } else if (snapshot.hasError) { _logger.severe( "critical: failed to load share gallery", snapshot.error, snapshot.stackTrace, ); return Center(child: Text(S.of(context).somethingWentWrong)); } else { return const EnteLoadingWidget(); } }, ); } Widget _getSharedCollectionsGallery(SharedCollections collections) { const maxThumbnailWidth = 160.0; final bool hasQuickLinks = collections.quickLinks.isNotEmpty; final SectionTitle sharedWithMe = SectionTitle(title: S.of(context).sharedWithMe); final SectionTitle sharedByMe = SectionTitle(title: S.of(context).sharedByMe); return SingleChildScrollView( child: Container( margin: const EdgeInsets.only(bottom: 50), child: Column( children: [ const SizedBox(height: 12), Hero( tag: "incoming", child: SectionOptions( sharedWithMe, trailingWidget: collections.incoming.isNotEmpty ? IconButtonWidget( icon: Icons.chevron_right, iconButtonType: IconButtonType.secondary, onTap: () { unawaited( routeToPage( context, CollectionListPage( collections.incoming, tag: "incoming", appTitle: sharedWithMe, ), ), ); }, ) : null, ), ), const SizedBox(height: 4), collections.incoming.isNotEmpty ? SizedBox( height: maxThumbnailWidth + 48, child: ListView.builder( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 8, ), scrollDirection: Axis.horizontal, itemBuilder: (context, index) { return Padding( padding: const EdgeInsets.only(right: 8.0), child: AlbumRowItemWidget( collections.incoming[index], maxThumbnailWidth, tag: "incoming", ), ); }, itemCount: collections.incoming.length, ), ) : const IncomingAlbumEmptyState(), const SizedBox(height: 16), Hero( tag: "outgoing", child: SectionOptions( sharedByMe, trailingWidget: collections.outgoing.isNotEmpty ? IconButtonWidget( icon: Icons.chevron_right, iconButtonType: IconButtonType.secondary, onTap: () { unawaited( routeToPage( context, CollectionListPage( collections.outgoing, tag: "outgoing", appTitle: sharedByMe, ), ), ); }, ) : null, ), ), const SizedBox(height: 4), collections.outgoing.isNotEmpty ? SizedBox( height: maxThumbnailWidth + 48, child: ListView.builder( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 2, ), scrollDirection: Axis.horizontal, itemBuilder: (context, index) { return Padding( padding: const EdgeInsets.only(right: 8.0), child: AlbumRowItemWidget( collections.outgoing[index], maxThumbnailWidth, tag: "outgoing", ), ); }, itemCount: collections.outgoing.length, ), ) : const OutgoingAlbumEmptyState(), if (hasQuickLinks) const SizedBox(height: 12), if (hasQuickLinks) SectionOptions(SectionTitle(title: S.of(context).quickLinks)), if (hasQuickLinks) const SizedBox(height: 4), if (hasQuickLinks) ListView.builder( shrinkWrap: true, padding: const EdgeInsets.only(bottom: 12), physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, index) { return QuickLinkAlbumItem( c: collections.quickLinks[index], ); }, itemCount: collections.quickLinks.length, ), const SizedBox(height: 48), Padding( padding: const EdgeInsets.symmetric(horizontal: 24.0), child: ButtonWidget( buttonType: ButtonType.trailingIcon, labelText: S.of(context).inviteYourFriends, icon: Icons.ios_share_outlined, onTap: () async { shareText(S.of(context).shareTextRecommendUsingEnte); }, ), ), const SizedBox(height: 32), ], ), ), ); } SharedCollections _getSharedCollections() { final List outgoing = []; final List incoming = []; final List quickLinks = []; final List collections = CollectionsService.instance.getCollectionsForUI(includedShared: true); for (final c in collections) { if (c.owner!.id == Configuration.instance.getUserID()) { if (c.hasSharees || c.hasLink && !c.isSharedFilesCollection()) { outgoing.add(c); } else if (c.isSharedFilesCollection()) { quickLinks.add(c); } } else { incoming.add(c); } } incoming.sort((first, second) { return second.updationTime.compareTo(first.updationTime); }); return SharedCollections(outgoing, incoming, quickLinks); } @override void dispose() { _localFilesSubscription.cancel(); _collectionUpdatesSubscription.cancel(); _loggedOutEvent.cancel(); super.dispose(); } @override bool get wantKeepAlive => true; }