2020-10-24 23:10:37 +00:00
|
|
|
import 'dart:async';
|
|
|
|
|
2022-09-16 04:45:32 +00:00
|
|
|
import 'package:collection/collection.dart';
|
2020-10-24 23:10:37 +00:00
|
|
|
import 'package:flutter/material.dart';
|
2020-10-28 15:25:32 +00:00
|
|
|
import 'package:logging/logging.dart';
|
|
|
|
import 'package:photos/core/configuration.dart';
|
2022-10-19 11:28:03 +00:00
|
|
|
import 'package:photos/core/constants.dart';
|
2020-10-24 23:10:37 +00:00
|
|
|
import 'package:photos/core/event_bus.dart';
|
2020-10-28 12:03:28 +00:00
|
|
|
import 'package:photos/events/collection_updated_event.dart';
|
2020-10-24 23:10:37 +00:00
|
|
|
import 'package:photos/events/local_photos_updated_event.dart';
|
2021-03-17 21:07:17 +00:00
|
|
|
import 'package:photos/events/user_logged_out_event.dart';
|
2022-11-16 17:01:57 +00:00
|
|
|
import 'package:photos/extensions/list.dart';
|
2023-04-06 09:01:16 +00:00
|
|
|
import "package:photos/generated/l10n.dart";
|
2022-09-15 08:14:08 +00:00
|
|
|
import 'package:photos/models/collection.dart';
|
2021-07-23 12:28:02 +00:00
|
|
|
import 'package:photos/services/collections_service.dart';
|
2023-06-22 09:19:34 +00:00
|
|
|
import "package:photos/services/favorites_service.dart";
|
2023-06-06 16:43:32 +00:00
|
|
|
import "package:photos/ui/collections/button/archived_button.dart";
|
|
|
|
import "package:photos/ui/collections/button/hidden_button.dart";
|
|
|
|
import "package:photos/ui/collections/button/trash_button.dart";
|
|
|
|
import "package:photos/ui/collections/button/uncategorized_button.dart";
|
2022-08-11 12:28:06 +00:00
|
|
|
import 'package:photos/ui/collections/device_folders_grid_view_widget.dart';
|
2023-06-22 19:13:30 +00:00
|
|
|
import 'package:photos/ui/collections/horizontal_grid_view.dart';
|
|
|
|
import "package:photos/ui/collections/vertical_grid_view.dart";
|
2022-07-01 14:18:05 +00:00
|
|
|
import 'package:photos/ui/common/loading_widget.dart';
|
2023-03-10 08:08:51 +00:00
|
|
|
import 'package:photos/ui/components/buttons/icon_button_widget.dart';
|
2023-06-06 16:43:32 +00:00
|
|
|
import 'package:photos/ui/tabs/section_title.dart';
|
2023-01-03 08:29:26 +00:00
|
|
|
import 'package:photos/ui/viewer/actions/delete_empty_albums.dart';
|
2022-09-12 12:16:34 +00:00
|
|
|
import 'package:photos/ui/viewer/gallery/empty_state.dart';
|
2021-09-08 20:06:37 +00:00
|
|
|
import 'package:photos/utils/local_settings.dart';
|
2023-06-22 19:11:23 +00:00
|
|
|
import "package:photos/utils/navigation_util.dart";
|
2021-09-17 04:44:10 +00:00
|
|
|
|
2023-06-06 09:57:17 +00:00
|
|
|
class UserCollectionsTab extends StatefulWidget {
|
|
|
|
const UserCollectionsTab({Key? key}) : super(key: key);
|
2020-10-24 23:10:37 +00:00
|
|
|
|
|
|
|
@override
|
2023-06-06 09:57:17 +00:00
|
|
|
State<UserCollectionsTab> createState() => _UserCollectionsTabState();
|
2020-10-24 23:10:37 +00:00
|
|
|
}
|
|
|
|
|
2023-06-06 09:57:17 +00:00
|
|
|
class _UserCollectionsTabState extends State<UserCollectionsTab>
|
2020-11-10 11:36:51 +00:00
|
|
|
with AutomaticKeepAliveClientMixin {
|
2023-06-06 09:57:17 +00:00
|
|
|
final _logger = Logger((_UserCollectionsTabState).toString());
|
2022-12-30 12:10:17 +00:00
|
|
|
late StreamSubscription<LocalPhotosUpdatedEvent> _localFilesSubscription;
|
2023-01-03 08:29:26 +00:00
|
|
|
late StreamSubscription<CollectionUpdatedEvent>
|
|
|
|
_collectionUpdatesSubscription;
|
2022-12-30 12:10:17 +00:00
|
|
|
late StreamSubscription<UserLoggedOutEvent> _loggedOutEvent;
|
|
|
|
AlbumSortKey? sortKey;
|
2022-05-05 06:46:31 +00:00
|
|
|
String _loadReason = "init";
|
2020-10-24 23:10:37 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
2022-07-03 09:49:33 +00:00
|
|
|
_localFilesSubscription =
|
|
|
|
Bus.instance.on<LocalPhotosUpdatedEvent>().listen((event) {
|
2022-11-11 10:44:12 +00:00
|
|
|
_loadReason = event.reason;
|
2020-10-28 12:03:28 +00:00
|
|
|
setState(() {});
|
|
|
|
});
|
2022-07-03 09:49:33 +00:00
|
|
|
_collectionUpdatesSubscription =
|
|
|
|
Bus.instance.on<CollectionUpdatedEvent>().listen((event) {
|
2022-11-11 10:44:12 +00:00
|
|
|
_loadReason = event.reason;
|
2020-10-24 23:10:37 +00:00
|
|
|
setState(() {});
|
|
|
|
});
|
2021-03-17 21:07:17 +00:00
|
|
|
_loggedOutEvent = Bus.instance.on<UserLoggedOutEvent>().listen((event) {
|
2022-11-11 10:44:12 +00:00
|
|
|
_loadReason = event.reason;
|
2021-03-17 21:07:17 +00:00
|
|
|
setState(() {});
|
|
|
|
});
|
2021-09-08 20:06:37 +00:00
|
|
|
sortKey = LocalSettings.instance.albumSortKey();
|
2020-10-24 23:10:37 +00:00
|
|
|
super.initState();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2020-11-16 08:00:31 +00:00
|
|
|
super.build(context);
|
2022-05-05 06:46:31 +00:00
|
|
|
_logger.info("Building, trigger: $_loadReason");
|
2023-06-22 08:30:20 +00:00
|
|
|
return FutureBuilder<List<Collection>>(
|
2020-10-24 23:10:37 +00:00
|
|
|
future: _getCollections(),
|
|
|
|
builder: (context, snapshot) {
|
|
|
|
if (snapshot.hasData) {
|
2023-06-22 19:37:15 +00:00
|
|
|
return _getCollectionsGalleryWidget(snapshot.data!);
|
2020-10-24 23:10:37 +00:00
|
|
|
} else if (snapshot.hasError) {
|
|
|
|
return Text(snapshot.error.toString());
|
|
|
|
} else {
|
2022-07-03 06:04:42 +00:00
|
|
|
return const EnteLoadingWidget();
|
2020-10-24 23:10:37 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-06-22 08:30:20 +00:00
|
|
|
Future<List<Collection>> _getCollections() async {
|
|
|
|
final List<Collection> collections =
|
|
|
|
CollectionsService.instance.getCollectionsForUI();
|
2023-01-12 06:23:27 +00:00
|
|
|
|
2023-02-27 04:09:43 +00:00
|
|
|
// Remove uncategorized collection
|
2023-06-22 08:30:20 +00:00
|
|
|
collections.removeWhere(
|
|
|
|
(t) => t.type == CollectionType.uncategorized,
|
2023-02-18 09:28:11 +00:00
|
|
|
);
|
2023-06-22 08:30:20 +00:00
|
|
|
final ListMatch<Collection> favMathResult = collections.splitMatch(
|
|
|
|
(element) => element.type == CollectionType.favorites,
|
2022-11-16 17:01:57 +00:00
|
|
|
);
|
2023-01-12 05:48:36 +00:00
|
|
|
|
2023-06-22 09:19:34 +00:00
|
|
|
// Hide fav collection if it's empty
|
|
|
|
if (!FavoritesService.instance.hasFavorites()) {
|
|
|
|
favMathResult.matched.clear();
|
|
|
|
}
|
|
|
|
|
2023-06-22 08:30:20 +00:00
|
|
|
late Map<int, int> collectionIDToNewestPhotoTime;
|
|
|
|
if (sortKey == AlbumSortKey.newestPhoto) {
|
|
|
|
collectionIDToNewestPhotoTime =
|
|
|
|
await CollectionsService.instance.getCollectionIDToNewestFileTime();
|
|
|
|
}
|
2022-12-19 10:05:46 +00:00
|
|
|
|
|
|
|
favMathResult.unmatched.sort(
|
2022-04-28 13:08:27 +00:00
|
|
|
(first, second) {
|
|
|
|
if (sortKey == AlbumSortKey.albumName) {
|
2022-09-16 04:45:32 +00:00
|
|
|
return compareAsciiLowerCaseNatural(
|
2023-06-22 08:30:20 +00:00
|
|
|
first.displayName,
|
|
|
|
second.displayName,
|
2022-09-16 04:45:32 +00:00
|
|
|
);
|
2022-04-28 13:08:27 +00:00
|
|
|
} else if (sortKey == AlbumSortKey.newestPhoto) {
|
2023-06-22 08:30:20 +00:00
|
|
|
return (collectionIDToNewestPhotoTime[second.id] ?? -1 * intMaxValue)
|
|
|
|
.compareTo(
|
|
|
|
collectionIDToNewestPhotoTime[first.id] ?? -1 * intMaxValue,
|
|
|
|
);
|
2022-04-28 13:08:27 +00:00
|
|
|
} else {
|
2023-06-22 08:30:20 +00:00
|
|
|
return second.updationTime.compareTo(first.updationTime);
|
2022-04-28 13:08:27 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
2022-12-19 10:05:46 +00:00
|
|
|
// This is a way to identify collection which were automatically created
|
|
|
|
// during create link flow for selected files
|
2023-06-22 08:30:20 +00:00
|
|
|
final ListMatch<Collection> potentialSharedLinkCollection =
|
2022-12-19 10:05:46 +00:00
|
|
|
favMathResult.unmatched.splitMatch(
|
2023-06-22 08:30:20 +00:00
|
|
|
(e) => (e.isSharedFilesCollection()),
|
2022-11-21 10:47:47 +00:00
|
|
|
);
|
2022-12-19 10:05:46 +00:00
|
|
|
|
2022-12-25 06:08:03 +00:00
|
|
|
return favMathResult.matched + potentialSharedLinkCollection.unmatched;
|
2020-10-24 23:10:37 +00:00
|
|
|
}
|
|
|
|
|
2023-06-22 19:37:15 +00:00
|
|
|
Widget _getCollectionsGalleryWidget(List<Collection> collections) {
|
2023-06-22 08:30:20 +00:00
|
|
|
final TextStyle trashAndHiddenTextStyle =
|
|
|
|
Theme.of(context).textTheme.titleMedium!.copyWith(
|
|
|
|
color: Theme.of(context)
|
|
|
|
.textTheme
|
|
|
|
.titleMedium!
|
|
|
|
.color!
|
|
|
|
.withOpacity(0.5),
|
|
|
|
);
|
2022-08-11 12:28:06 +00:00
|
|
|
|
2020-12-12 01:11:12 +00:00
|
|
|
return SingleChildScrollView(
|
2021-02-08 09:26:49 +00:00
|
|
|
child: Container(
|
|
|
|
margin: const EdgeInsets.only(bottom: 50),
|
|
|
|
child: Column(
|
|
|
|
children: [
|
2022-06-11 09:21:39 +00:00
|
|
|
const SizedBox(height: 12),
|
2023-04-21 14:47:47 +00:00
|
|
|
SectionTitle(title: S.of(context).onDevice),
|
2022-06-12 16:39:28 +00:00
|
|
|
const SizedBox(height: 12),
|
2022-09-12 12:11:42 +00:00
|
|
|
const DeviceFoldersGridViewWidget(),
|
2021-09-09 06:14:07 +00:00
|
|
|
Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
children: [
|
2022-10-26 07:46:36 +00:00
|
|
|
SectionTitle(titleWithBrand: getOnEnteSection(context)),
|
2023-06-22 19:11:23 +00:00
|
|
|
_sortMenu(collections),
|
2021-09-09 06:14:07 +00:00
|
|
|
],
|
|
|
|
),
|
2023-06-22 08:49:42 +00:00
|
|
|
DeleteEmptyAlbums(collections ?? []),
|
2021-05-19 15:51:38 +00:00
|
|
|
Configuration.instance.hasConfiguredAccount()
|
2023-06-22 19:11:23 +00:00
|
|
|
? CollectionsGridViewHorizontal(collections)
|
2022-07-03 05:57:27 +00:00
|
|
|
: const EmptyState(),
|
2022-04-30 12:18:26 +00:00
|
|
|
const Divider(),
|
2022-06-10 09:05:35 +00:00
|
|
|
const SizedBox(height: 16),
|
2022-04-28 13:08:27 +00:00
|
|
|
Padding(
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
|
|
child: Column(
|
|
|
|
children: [
|
2023-06-06 16:43:32 +00:00
|
|
|
UnCategorizedCollections(trashAndHiddenTextStyle),
|
2022-12-26 05:31:32 +00:00
|
|
|
const SizedBox(height: 12),
|
2023-06-06 16:43:32 +00:00
|
|
|
ArchivedCollectionsButton(trashAndHiddenTextStyle),
|
2022-10-31 09:56:59 +00:00
|
|
|
const SizedBox(height: 12),
|
2022-11-02 09:53:24 +00:00
|
|
|
HiddenCollectionsButtonWidget(trashAndHiddenTextStyle),
|
|
|
|
const SizedBox(height: 12),
|
2023-06-06 16:43:32 +00:00
|
|
|
TrashSectionButton(trashAndHiddenTextStyle),
|
2022-04-28 13:08:27 +00:00
|
|
|
],
|
|
|
|
),
|
2021-10-13 12:05:20 +00:00
|
|
|
),
|
2023-03-07 13:54:26 +00:00
|
|
|
const SizedBox(height: 48),
|
2021-02-08 09:26:49 +00:00
|
|
|
],
|
|
|
|
),
|
2020-12-12 01:11:12 +00:00
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-06-22 19:37:15 +00:00
|
|
|
Widget _sortMenu(List<Collection> collections) {
|
2021-09-09 06:14:07 +00:00
|
|
|
Text sortOptionText(AlbumSortKey key) {
|
|
|
|
String text = key.toString();
|
2021-09-08 20:19:25 +00:00
|
|
|
switch (key) {
|
|
|
|
case AlbumSortKey.albumName:
|
2023-04-06 09:01:16 +00:00
|
|
|
text = S.of(context).name;
|
2021-09-09 06:14:07 +00:00
|
|
|
break;
|
2022-04-28 13:08:27 +00:00
|
|
|
case AlbumSortKey.newestPhoto:
|
2023-04-06 09:01:16 +00:00
|
|
|
text = S.of(context).newest;
|
2021-09-09 06:14:07 +00:00
|
|
|
break;
|
2022-04-28 13:08:27 +00:00
|
|
|
case AlbumSortKey.lastUpdated:
|
2023-04-06 09:01:16 +00:00
|
|
|
text = S.of(context).lastUpdated;
|
2021-09-08 20:19:25 +00:00
|
|
|
}
|
2022-06-11 08:23:52 +00:00
|
|
|
return Text(
|
|
|
|
text,
|
2023-06-13 06:41:31 +00:00
|
|
|
style: Theme.of(context).textTheme.titleMedium!.copyWith(
|
2022-04-28 13:08:27 +00:00
|
|
|
fontSize: 14,
|
2022-12-30 12:10:17 +00:00
|
|
|
color: Theme.of(context).iconTheme.color!.withOpacity(0.7),
|
2022-06-11 08:23:52 +00:00
|
|
|
),
|
|
|
|
);
|
2021-09-08 20:19:25 +00:00
|
|
|
}
|
2021-09-09 06:14:07 +00:00
|
|
|
|
|
|
|
return Padding(
|
2023-06-22 19:11:23 +00:00
|
|
|
padding: const EdgeInsets.only(right: 4),
|
2023-03-07 13:54:26 +00:00
|
|
|
child: Theme(
|
|
|
|
data: Theme.of(context).copyWith(
|
|
|
|
highlightColor: Colors.transparent,
|
|
|
|
splashColor: Colors.transparent,
|
|
|
|
),
|
2023-06-22 19:11:23 +00:00
|
|
|
child: Row(
|
|
|
|
children: [
|
2023-06-22 19:37:15 +00:00
|
|
|
GestureDetector(
|
|
|
|
onTapDown: (TapDownDetails details) async {
|
|
|
|
final int? selectedValue = await showMenu<int>(
|
|
|
|
context: context,
|
|
|
|
position: RelativeRect.fromLTRB(
|
|
|
|
details.globalPosition.dx,
|
|
|
|
details.globalPosition.dy,
|
|
|
|
details.globalPosition.dx,
|
|
|
|
details.globalPosition.dy + 50,
|
|
|
|
),
|
|
|
|
items: List.generate(AlbumSortKey.values.length, (index) {
|
|
|
|
return PopupMenuItem(
|
|
|
|
value: index,
|
|
|
|
child: sortOptionText(AlbumSortKey.values[index]),
|
|
|
|
);
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
if (selectedValue != null) {
|
|
|
|
sortKey = AlbumSortKey.values[selectedValue];
|
|
|
|
await LocalSettings.instance.setAlbumSortKey(sortKey!);
|
|
|
|
setState(() {});
|
|
|
|
}
|
|
|
|
},
|
2023-06-22 19:11:23 +00:00
|
|
|
child: const IconButtonWidget(
|
|
|
|
icon: Icons.sort_outlined,
|
|
|
|
iconButtonType: IconButtonType.secondary,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
IconButtonWidget(
|
|
|
|
icon: Icons.chevron_right,
|
|
|
|
iconButtonType: IconButtonType.secondary,
|
|
|
|
onTap: () {
|
|
|
|
unawaited(
|
|
|
|
routeToPage(
|
|
|
|
context,
|
2023-06-22 19:13:30 +00:00
|
|
|
CollectionVerticalGridView(
|
2023-06-22 19:37:15 +00:00
|
|
|
collections,
|
2023-06-22 19:11:23 +00:00
|
|
|
appTitle: SectionTitle(
|
|
|
|
titleWithBrand: getOnEnteSection(context),
|
|
|
|
skipMargin: true,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
)
|
|
|
|
],
|
2021-09-09 06:14:07 +00:00
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
2021-09-08 20:19:25 +00:00
|
|
|
}
|
|
|
|
|
2021-04-27 15:29:34 +00:00
|
|
|
@override
|
|
|
|
void dispose() {
|
|
|
|
_localFilesSubscription.cancel();
|
|
|
|
_collectionUpdatesSubscription.cancel();
|
|
|
|
_loggedOutEvent.cancel();
|
|
|
|
super.dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
bool get wantKeepAlive => true;
|
|
|
|
}
|