ente/lib/ui/tabs/user_collections_tab.dart

312 lines
11 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";
import "package:photos/theme/ente_theme.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/create_new_album_widget.dart";
2023-06-24 06:04:47 +00:00
import "package:photos/ui/collections/device/device_folders_grid_view.dart";
2023-06-24 07:17:53 +00:00
import "package:photos/ui/collections/device/device_folders_vertical_grid_view.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";
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';
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);
@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;
}
Widget _getCollectionsGalleryWidget(List<Collection> collections) {
final TextStyle trashAndHiddenTextStyle =
Theme.of(context).textTheme.titleMedium!.copyWith(
color: Theme.of(context)
.textTheme
.titleMedium!
.color!
.withOpacity(0.5),
);
final enteTextTheme = getEnteTextTheme(context);
2020-12-12 01:11:12 +00:00
return SingleChildScrollView(
child: Container(
margin: const EdgeInsets.only(bottom: 50),
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(left: 12.0, bottom: 8, right: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
S.of(context).albums,
style: enteTextTheme.h2Bold,
),
const CreateNewAlbumIcon(),
],
),
),
2023-06-23 12:24:26 +00:00
SectionTitleRow(
SectionTitle(title: S.of(context).onDevice),
2023-06-24 07:17:53 +00:00
trailingWidget: IconButtonWidget(
icon: Icons.chevron_right,
iconButtonType: IconButtonType.secondary,
onTap: () {
unawaited(
routeToPage(
context,
DeviceFolderVerticalGridView(
appTitle: SectionTitle(
titleWithBrand:
SectionTitle(title: S.of(context).onDevice),
),
),
),
);
},
),
2023-06-23 12:24:26 +00:00
),
2023-06-24 06:04:47 +00:00
const DeviceFoldersGridView(),
2023-06-23 12:24:26 +00:00
SectionTitleRow(
SectionTitle(titleWithBrand: getOnEnteSection(context)),
trailingWidget: _sortMenu(collections),
),
2023-06-22 08:49:42 +00:00
DeleteEmptyAlbums(collections ?? []),
Configuration.instance.hasConfiguredAccount()
? CollectionsHorizontalGridView(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: 12),
2022-04-28 13:08:27 +00:00
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(List<Collection> collections) {
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
}
2023-06-23 12:24:26 +00:00
return Theme(
data: Theme.of(context).copyWith(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
),
child: Row(
children: [
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];
2023-06-23 12:36:51 +00:00
;
2023-06-23 12:24:26 +00:00
await LocalSettings.instance.setAlbumSortKey(sortKey!);
setState(() {});
}
},
child: const IconButtonWidget(
icon: Icons.sort_outlined,
iconButtonType: IconButtonType.secondary,
2023-06-23 12:24:26 +00:00
),
),
IconButtonWidget(
icon: Icons.chevron_right,
iconButtonType: IconButtonType.secondary,
onTap: () {
unawaited(
routeToPage(
context,
CollectionVerticalGridView(
collections,
appTitle: SectionTitle(
titleWithBrand: getOnEnteSection(context),
),
),
2023-06-23 12:24:26 +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;
}