ente/lib/ui/tabs/user_collections_tab.dart

256 lines
8.6 KiB
Dart
Raw Normal View History

import 'dart:async';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
2020-10-28 15:25:32 +00:00
import 'package:logging/logging.dart';
import 'package:photos/core/configuration.dart';
import 'package:photos/core/constants.dart';
import 'package:photos/core/event_bus.dart';
2020-10-28 12:03:28 +00:00
import 'package:photos/events/collection_updated_event.dart';
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';
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";
import 'package:photos/ui/collections/device_folders_grid_view_widget.dart';
import 'package:photos/ui/collections/remote_collections_grid_view_widget.dart';
import 'package:photos/ui/common/loading_widget.dart';
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';
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';
import 'package:photos/utils/local_settings.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);
@override
2023-06-06 09:57:17 +00:00
State<UserCollectionsTab> createState() => _UserCollectionsTabState();
}
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());
late StreamSubscription<LocalPhotosUpdatedEvent> _localFilesSubscription;
late StreamSubscription<CollectionUpdatedEvent>
_collectionUpdatesSubscription;
late StreamSubscription<UserLoggedOutEvent> _loggedOutEvent;
AlbumSortKey? sortKey;
2022-05-05 06:46:31 +00:00
String _loadReason = "init";
@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;
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(() {});
});
sortKey = LocalSettings.instance.albumSortKey();
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");
return FutureBuilder<List<Collection>>(
future: _getCollections(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return _getCollectionsGalleryWidget(snapshot.data);
} else if (snapshot.hasError) {
return Text(snapshot.error.toString());
} else {
return const EnteLoadingWidget();
}
},
);
}
Future<List<Collection>> _getCollections() async {
final List<Collection> collections =
CollectionsService.instance.getCollectionsForUI();
2023-01-12 06:23:27 +00:00
// Remove uncategorized collection
collections.removeWhere(
(t) => t.type == CollectionType.uncategorized,
);
final ListMatch<Collection> favMathResult = collections.splitMatch(
(element) => element.type == CollectionType.favorites,
2022-11-16 17:01:57 +00:00
);
2023-06-22 09:19:34 +00:00
// Hide fav collection if it's empty
if (!FavoritesService.instance.hasFavorites()) {
favMathResult.matched.clear();
}
late Map<int, int> collectionIDToNewestPhotoTime;
if (sortKey == AlbumSortKey.newestPhoto) {
collectionIDToNewestPhotoTime =
await CollectionsService.instance.getCollectionIDToNewestFileTime();
}
favMathResult.unmatched.sort(
2022-04-28 13:08:27 +00:00
(first, second) {
if (sortKey == AlbumSortKey.albumName) {
return compareAsciiLowerCaseNatural(
first.displayName,
second.displayName,
);
2022-04-28 13:08:27 +00:00
} else if (sortKey == AlbumSortKey.newestPhoto) {
return (collectionIDToNewestPhotoTime[second.id] ?? -1 * intMaxValue)
.compareTo(
collectionIDToNewestPhotoTime[first.id] ?? -1 * intMaxValue,
);
2022-04-28 13:08:27 +00:00
} else {
return second.updationTime.compareTo(first.updationTime);
2022-04-28 13:08:27 +00:00
}
},
);
// This is a way to identify collection which were automatically created
// during create link flow for selected files
final ListMatch<Collection> potentialSharedLinkCollection =
favMathResult.unmatched.splitMatch(
(e) => (e.isSharedFilesCollection()),
2022-11-21 10:47:47 +00:00
);
return favMathResult.matched + potentialSharedLinkCollection.unmatched;
}
2022-09-12 12:11:42 +00:00
Widget _getCollectionsGalleryWidget(
List<Collection>? collections,
2022-09-12 12:11:42 +00:00
) {
final TextStyle trashAndHiddenTextStyle =
Theme.of(context).textTheme.titleMedium!.copyWith(
color: Theme.of(context)
.textTheme
.titleMedium!
.color!
.withOpacity(0.5),
);
2020-12-12 01:11:12 +00:00
return SingleChildScrollView(
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),
const SizedBox(height: 12),
2022-09-12 12:11:42 +00:00
const DeviceFoldersGridViewWidget(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SectionTitle(titleWithBrand: getOnEnteSection(context)),
_sortMenu(),
],
),
2023-06-22 08:49:42 +00:00
DeleteEmptyAlbums(collections ?? []),
Configuration.instance.hasConfiguredAccount()
2022-09-12 12:11:42 +00:00
? RemoteCollectionsGridViewWidget(collections)
: const EmptyState(),
2022-04-30 12:18:26 +00:00
const Divider(),
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
],
),
),
2023-03-07 13:54:26 +00:00
const SizedBox(height: 48),
],
),
2020-12-12 01:11:12 +00:00
),
);
}
Widget _sortMenu() {
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;
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;
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,
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
}
return Padding(
2023-03-07 13:54:26 +00:00
padding: const EdgeInsets.only(right: 8),
child: Theme(
data: Theme.of(context).copyWith(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
),
child: PopupMenuButton(
offset: const Offset(10, 50),
initialValue: sortKey?.index ?? 0,
child: const IconButtonWidget(
icon: Icons.sort_outlined,
iconButtonType: IconButtonType.secondary,
disableGestureDetector: true,
),
2023-03-07 13:54:26 +00:00
onSelected: (int index) async {
sortKey = AlbumSortKey.values[index];
await LocalSettings.instance.setAlbumSortKey(sortKey!);
setState(() {});
},
itemBuilder: (context) {
return List.generate(AlbumSortKey.values.length, (index) {
return PopupMenuItem(
value: index,
child: sortOptionText(AlbumSortKey.values[index]),
);
});
},
),
),
);
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;
}