Merge branch 'main' into UI_fixes

This commit is contained in:
ashilkn 2023-02-24 18:15:55 +05:30
commit 58fe9e1500
12 changed files with 380 additions and 84 deletions

View file

@ -654,7 +654,7 @@ class CollectionsService {
}
rethrow;
} catch (e, s) {
_logger.severe("failed to rename collection", e, s);
_logger.severe("failed to update ShareUrl", e, s);
rethrow;
}
}

View file

@ -1,11 +1,15 @@
import "dart:async";
import 'dart:math';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import "package:fluttertoast/fluttertoast.dart";
import 'package:logging/logging.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
import 'package:photos/core/configuration.dart';
import "package:photos/core/event_bus.dart";
import 'package:photos/db/files_db.dart';
import "package:photos/events/tab_changed_event.dart";
import 'package:photos/models/collection.dart';
import 'package:photos/models/collection_items.dart';
import 'package:photos/models/file.dart';
@ -15,6 +19,7 @@ import 'package:photos/services/ignored_files_service.dart';
import 'package:photos/services/remote_sync_service.dart';
import 'package:photos/theme/colors.dart';
import 'package:photos/theme/ente_theme.dart';
import "package:photos/ui/actions/collection/collection_sharing_actions.dart";
import 'package:photos/ui/common/loading_widget.dart';
import 'package:photos/ui/components/album_list_item_widget.dart';
import 'package:photos/ui/components/bottom_of_title_bar_widget.dart';
@ -22,6 +27,7 @@ import 'package:photos/ui/components/button_widget.dart';
import 'package:photos/ui/components/models/button_type.dart';
import 'package:photos/ui/components/new_album_list_widget.dart';
import 'package:photos/ui/components/title_bar_title_widget.dart';
import "package:photos/ui/sharing/share_collection_page.dart";
import 'package:photos/ui/viewer/gallery/collection_page.dart';
import 'package:photos/utils/dialog_util.dart';
import 'package:photos/utils/navigation_util.dart';
@ -29,7 +35,14 @@ import 'package:photos/utils/share_util.dart';
import 'package:photos/utils/toast_util.dart';
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
enum CollectionActionType { addFiles, moveFiles, restoreFiles, unHide }
enum CollectionActionType {
addFiles,
moveFiles,
restoreFiles,
unHide,
shareCollection,
collectPhotos,
}
String _actionName(CollectionActionType type, bool plural) {
bool addTitleSuffix = false;
@ -50,21 +63,27 @@ String _actionName(CollectionActionType type, bool plural) {
case CollectionActionType.unHide:
text = "Unhide to album";
break;
case CollectionActionType.shareCollection:
text = "Share";
break;
case CollectionActionType.collectPhotos:
text = "Share";
break;
}
return addTitleSuffix ? text + titleSuffix : text;
}
void createCollectionSheet(
void showCollectionActionSheet(
BuildContext context, {
SelectedFiles? selectedFiles,
List<SharedMediaFile>? sharedFiles,
BuildContext context, {
CollectionActionType actionType = CollectionActionType.addFiles,
bool showOptionToCreateNewAlbum = true,
}) {
showBarModalBottomSheet(
context: context,
builder: (context) {
return CreateCollectionSheet(
return CollectionActionSheet(
selectedFiles: selectedFiles,
sharedFiles: sharedFiles,
actionType: actionType,
@ -84,12 +103,12 @@ void createCollectionSheet(
);
}
class CreateCollectionSheet extends StatefulWidget {
class CollectionActionSheet extends StatefulWidget {
final SelectedFiles? selectedFiles;
final List<SharedMediaFile>? sharedFiles;
final CollectionActionType actionType;
final bool showOptionToCreateNewAlbum;
const CreateCollectionSheet({
const CollectionActionSheet({
required this.selectedFiles,
required this.sharedFiles,
required this.actionType,
@ -98,17 +117,17 @@ class CreateCollectionSheet extends StatefulWidget {
});
@override
State<CreateCollectionSheet> createState() => _CreateCollectionSheetState();
State<CollectionActionSheet> createState() => _CollectionActionSheetState();
}
class _CreateCollectionSheetState extends State<CreateCollectionSheet> {
final _logger = Logger((_CreateCollectionSheetState).toString());
class _CollectionActionSheetState extends State<CollectionActionSheet> {
final _logger = Logger((_CollectionActionSheetState).toString());
@override
Widget build(BuildContext context) {
final filesCount = widget.sharedFiles != null
? widget.sharedFiles!.length
: widget.selectedFiles!.files.length;
: widget.selectedFiles?.files.length ?? 0;
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@ -125,7 +144,9 @@ class _CreateCollectionSheetState extends State<CreateCollectionSheet> {
title: TitleBarTitleWidget(
title: _actionName(widget.actionType, filesCount > 1),
),
caption: "Create or select album",
caption: widget.showOptionToCreateNewAlbum
? "Create or select album"
: "Select album",
),
Flexible(
child: Column(
@ -147,32 +168,18 @@ class _CreateCollectionSheetState extends State<CreateCollectionSheet> {
} else if (snapshot.hasData) {
final collectionsWithThumbnail = snapshot
.data as List<CollectionWithThumbnail>;
_removeIncomingCollections(
collectionsWithThumbnail,
);
return ListView.separated(
itemBuilder: (context, index) {
if (index == 0 &&
widget.showOptionToCreateNewAlbum) {
return GestureDetector(
onTap: () async {
final result =
await showTextInputDialog(
context,
title: "Album title",
submitButtonLabel: "OK",
hintText: "Enter album name",
onSubmit: _nameAlbum,
showOnlyLoadingState: true,
textCapitalization:
TextCapitalization.words,
await _createNewAlbumOnTap(
filesCount,
);
if (result is Exception) {
showGenericErrorDialog(
context: context,
);
_logger.severe(
"Failed to name album",
result,
);
}
},
behavior: HitTestBehavior.opaque,
child:
@ -244,12 +251,48 @@ class _CreateCollectionSheetState extends State<CreateCollectionSheet> {
);
}
Future<void> _createNewAlbumOnTap(int filesCount) async {
if (filesCount > 0) {
final result = await showTextInputDialog(
context,
title: "Album title",
submitButtonLabel: "OK",
hintText: "Enter album name",
onSubmit: _nameAlbum,
showOnlyLoadingState: true,
textCapitalization: TextCapitalization.words,
);
if (result is Exception) {
showGenericErrorDialog(
context: context,
);
_logger.severe(
"Failed to name album",
result,
);
}
} else {
Navigator.pop(context);
await showToast(
context,
"Long press to select photos and click + to create an album",
toastLength: Toast.LENGTH_LONG,
);
Bus.instance.fire(
TabChangedEvent(
0,
TabChangedEventSource.collectionsPage,
),
);
}
}
Future<void> _nameAlbum(String albumName) async {
if (albumName.isNotEmpty) {
final collection = await _createAlbum(albumName);
if (collection != null) {
if (await _runCollectionAction(
collectionID: collection.id,
collection: collection,
showProgressDialog: false,
)) {
if (widget.actionType == CollectionActionType.restoreFiles) {
@ -281,16 +324,29 @@ class _CreateCollectionSheetState extends State<CreateCollectionSheet> {
}
Future<void> _albumListItemOnTap(CollectionWithThumbnail item) async {
if (await _runCollectionAction(collectionID: item.collection.id)) {
showShortToast(
context,
widget.actionType == CollectionActionType.addFiles
? "Added successfully to " + item.collection.name!
: "Moved successfully to " + item.collection.name!,
);
_navigateToCollection(
item.collection,
);
if (await _runCollectionAction(collection: item.collection)) {
late final String toastMessage;
bool shouldNavigateToCollection = false;
if (widget.actionType == CollectionActionType.addFiles) {
toastMessage = "Added successfully to " + item.collection.name!;
shouldNavigateToCollection = true;
} else if (widget.actionType == CollectionActionType.moveFiles) {
toastMessage = "Moved successfully to " + item.collection.name!;
shouldNavigateToCollection = true;
} else {
toastMessage = "";
}
if (toastMessage.isNotEmpty) {
showShortToast(
context,
toastMessage,
);
}
if (shouldNavigateToCollection) {
_navigateToCollection(
item.collection,
);
}
}
}
@ -326,25 +382,117 @@ class _CreateCollectionSheetState extends State<CreateCollectionSheet> {
);
}
_removeIncomingCollections(List<CollectionWithThumbnail> items) {
if (widget.actionType == CollectionActionType.shareCollection ||
widget.actionType == CollectionActionType.collectPhotos) {
final ownerID = Configuration.instance.getUserID();
items.removeWhere(
(e) => !e.collection.isOwner(ownerID!),
);
}
}
Future<bool> _runCollectionAction({
required int collectionID,
required Collection collection,
bool showProgressDialog = true,
}) async {
switch (widget.actionType) {
case CollectionActionType.addFiles:
return _addToCollection(
collectionID: collectionID,
collectionID: collection.id,
showProgressDialog: showProgressDialog,
);
case CollectionActionType.moveFiles:
return _moveFilesToCollection(collectionID);
return _moveFilesToCollection(collection.id);
case CollectionActionType.unHide:
return _moveFilesToCollection(collectionID);
return _moveFilesToCollection(collection.id);
case CollectionActionType.restoreFiles:
return _restoreFilesToCollection(collectionID);
return _restoreFilesToCollection(collection.id);
case CollectionActionType.shareCollection:
return _showShareCollectionPage(collection);
case CollectionActionType.collectPhotos:
return _createCollaborativeLink(collection);
}
}
Future<bool> _createCollaborativeLink(Collection collection) async {
final CollectionActions collectionActions =
CollectionActions(CollectionsService.instance);
if (collection.hasLink) {
if (collection.publicURLs!.first!.enableCollect) {
if (Configuration.instance.getUserID() == collection.owner!.id) {
unawaited(
routeToPage(
context,
ShareCollectionPage(collection),
),
);
}
showToast(context, "This album already has a collaborative link");
return Future.value(false);
} else {
try {
unawaited(
routeToPage(
context,
ShareCollectionPage(collection),
),
);
CollectionsService.instance
.updateShareUrl(collection, {'enableCollect': true}).then(
(value) => showToast(
context,
"Collaborative link created for " + collection.name!,
),
);
return true;
} catch (e) {
showGenericErrorDialog(context: context);
return false;
}
}
}
final bool result = await collectionActions.enableUrl(
context,
collection,
enableCollect: true,
);
if (result) {
showToast(
context,
"Collaborative link created for " + collection.name!,
);
if (Configuration.instance.getUserID() == collection.owner!.id) {
unawaited(
routeToPage(
context,
ShareCollectionPage(collection),
),
);
} else {
showGenericErrorDialog(context: context);
_logger.severe("Cannot share collections owned by others");
}
}
return result;
}
Future<bool> _showShareCollectionPage(Collection collection) {
if (Configuration.instance.getUserID() == collection.owner!.id) {
unawaited(
routeToPage(
context,
ShareCollectionPage(collection),
),
);
} else {
showGenericErrorDialog(context: context);
_logger.severe("Cannot share collections owned by others");
}
return Future.value(true);
}
Future<bool> _addToCollection({
required int collectionID,
required bool showProgressDialog,

View file

@ -0,0 +1,34 @@
import "package:flutter/material.dart";
import "package:photos/theme/ente_theme.dart";
///https://www.figma.com/file/SYtMyLBs5SAOkTbfMMzhqt/ente-Visual-Design?node-id=11379%3A67490&t=VI5KulbW3HMM5MVz-4
class EmptyStateItemWidget extends StatelessWidget {
final String textContent;
const EmptyStateItemWidget(this.textContent, {super.key});
@override
Widget build(BuildContext context) {
final colorScheme = getEnteColorScheme(context);
final textTheme = getEnteTextTheme(context);
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
Icons.check_outlined,
size: 17,
color: colorScheme.strokeFaint,
),
const SizedBox(width: 6),
Flexible(
child: Text(
textContent,
style: textTheme.small.copyWith(
color: colorScheme.textFaint,
),
),
),
],
);
}
}

View file

@ -219,7 +219,7 @@ class _LandingPageWidgetState extends State<LandingPageWidget> {
ButtonWidget(
buttonType: ButtonType.neutral,
buttonAction: ButtonAction.first,
labelText: "Ok",
labelText: "OK",
isInAlert: true,
),
],

View file

@ -28,9 +28,9 @@ import 'package:photos/services/user_service.dart';
import 'package:photos/states/user_details_state.dart';
import 'package:photos/theme/colors.dart';
import 'package:photos/theme/ente_theme.dart';
import 'package:photos/ui/collection_action_sheet.dart';
import 'package:photos/ui/collections_gallery_widget.dart';
import 'package:photos/ui/common/bottom_shadow.dart';
import 'package:photos/ui/create_collection_sheet.dart';
import 'package:photos/ui/extents_page_view.dart';
import 'package:photos/ui/home/grant_permissions_widget.dart';
import 'package:photos/ui/home/header_widget.dart';
@ -43,7 +43,7 @@ import 'package:photos/ui/loading_photos_widget.dart';
import 'package:photos/ui/notification/update/change_log_page.dart';
import 'package:photos/ui/settings/app_update_dialog.dart';
import 'package:photos/ui/settings_page.dart';
import 'package:photos/ui/shared_collections_gallery.dart';
import "package:photos/ui/shared_collections_gallery.dart";
import 'package:photos/utils/dialog_util.dart';
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
import 'package:uni_links/uni_links.dart';
@ -334,10 +334,9 @@ class _HomeWidgetState extends State<HomeWidget> {
_shouldRenderCreateCollectionSheet = false;
ReceiveSharingIntent.reset();
Future.delayed(const Duration(milliseconds: 10), () {
createCollectionSheet(
null,
_sharedFiles,
showCollectionActionSheet(
context,
sharedFiles: _sharedFiles,
actionType: CollectionActionType.addFiles,
);
});

View file

@ -0,0 +1,118 @@
import "package:flutter/material.dart";
import "package:photos/core/constants.dart";
import "package:photos/theme/ente_theme.dart";
import "package:photos/ui/collection_action_sheet.dart";
import "package:photos/ui/components/button_widget.dart";
import "package:photos/ui/components/empty_state_item_widget.dart";
import "package:photos/ui/components/models/button_type.dart";
import "package:photos/utils/share_util.dart";
class NewSharedCollectionsGallery extends StatelessWidget {
const NewSharedCollectionsGallery({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: restrictedMaxWidth),
child: const EmptyStateWidget(),
),
);
}
}
class EmptyStateWidget extends StatelessWidget {
const EmptyStateWidget({super.key});
@override
Widget build(BuildContext context) {
final textTheme = getEnteTextTheme(context);
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 114),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 10),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 4, vertical: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Private sharing",
style: textTheme.h3Bold,
textAlign: TextAlign.start,
),
const SizedBox(height: 24),
Column(
mainAxisSize: MainAxisSize.min,
children: const [
EmptyStateItemWidget(
"Share only with the people you want",
),
SizedBox(height: 12),
EmptyStateItemWidget(
"Use public links for people not on ente",
),
SizedBox(height: 12),
EmptyStateItemWidget(
"Allow people to add photos",
),
],
),
],
),
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ButtonWidget(
buttonType: ButtonType.trailingIconPrimary,
labelText: "Share an album now",
icon: Icons.arrow_forward_outlined,
onTap: () async {
showCollectionActionSheet(
context,
actionType: CollectionActionType.shareCollection,
);
},
),
const SizedBox(height: 6),
ButtonWidget(
buttonType: ButtonType.trailingIconSecondary,
labelText: "Collect event photos",
icon: Icons.add_photo_alternate_outlined,
onTap: () async {
showCollectionActionSheet(
context,
actionType: CollectionActionType.collectPhotos,
);
},
),
const SizedBox(height: 6),
ButtonWidget(
buttonType: ButtonType.trailingIconSecondary,
labelText: "Invite your friends",
icon: Icons.ios_share_outlined,
onTap: () async {
shareText("Check out https://ente.io");
},
),
],
),
),
],
),
),
),
);
}
}

View file

@ -19,6 +19,7 @@ import 'package:photos/theme/colors.dart';
import 'package:photos/ui/collections/section_title.dart';
import 'package:photos/ui/common/gradient_button.dart';
import 'package:photos/ui/common/loading_widget.dart';
import "package:photos/ui/new_shared_collections_gallery.dart";
import 'package:photos/ui/sharing/user_avator_widget.dart';
import 'package:photos/ui/viewer/file/thumbnail_widget.dart';
import 'package:photos/ui/viewer/gallery/collection_page.dart';
@ -122,6 +123,10 @@ class _SharedCollectionGalleryState extends State<SharedCollectionGallery>
}),
builder: (context, snapshot) {
if (snapshot.hasData) {
if ((snapshot.data?.incoming.length ?? 0) == 0 &&
(snapshot.data?.outgoing.length ?? 0) == 0) {
return const Center(child: EmptyStateWidget());
}
return _getSharedCollectionsGallery(snapshot.data!);
} else if (snapshot.hasError) {
_logger.severe(

View file

@ -272,8 +272,10 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
}
Future<void> _updateUrlSettings(
BuildContext context, Map<String, dynamic> prop,
{bool showProgressDialog = true}) async {
BuildContext context,
Map<String, dynamic> prop, {
bool showProgressDialog = true,
}) async {
final dialog = showProgressDialog
? createProgressDialog(context, "Please wait...")
: null;

View file

@ -14,12 +14,12 @@ import 'package:photos/services/hidden_service.dart';
import 'package:photos/theme/ente_theme.dart';
import 'package:photos/ui/actions/collection/collection_file_actions.dart';
import 'package:photos/ui/actions/collection/collection_sharing_actions.dart';
import 'package:photos/ui/collection_action_sheet.dart';
import 'package:photos/ui/components/action_sheet_widget.dart';
import 'package:photos/ui/components/blur_menu_item_widget.dart';
import 'package:photos/ui/components/bottom_action_bar/expanded_menu_widget.dart';
import 'package:photos/ui/components/button_widget.dart';
import 'package:photos/ui/components/models/button_type.dart';
import 'package:photos/ui/create_collection_sheet.dart';
import 'package:photos/ui/sharing/manage_links_widget.dart';
import 'package:photos/utils/delete_file_util.dart';
import 'package:photos/utils/magic_util.dart';
@ -283,10 +283,9 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
widget.selectedFiles
.unSelectAll(split.ownedByOtherUsers.toSet(), skipNotify: true);
}
createCollectionSheet(
widget.selectedFiles,
null,
showCollectionActionSheet(
context,
selectedFiles: widget.selectedFiles,
actionType: CollectionActionType.moveFiles,
);
}
@ -296,11 +295,7 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
widget.selectedFiles
.unSelectAll(split.ownedByOtherUsers.toSet(), skipNotify: true);
}
createCollectionSheet(
widget.selectedFiles,
null,
context,
);
showCollectionActionSheet(context, selectedFiles: widget.selectedFiles);
}
Future<void> _onDeleteClick() async {
@ -381,10 +376,9 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
widget.selectedFiles
.unSelectAll(split.ownedByOtherUsers.toSet(), skipNotify: true);
}
createCollectionSheet(
widget.selectedFiles,
null,
showCollectionActionSheet(
context,
selectedFiles: widget.selectedFiles,
actionType: CollectionActionType.unHide,
);
}
@ -459,10 +453,9 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
}
void _restore() {
createCollectionSheet(
widget.selectedFiles,
null,
showCollectionActionSheet(
context,
selectedFiles: widget.selectedFiles,
actionType: CollectionActionType.restoreFiles,
);
}

View file

@ -5,9 +5,9 @@ import 'package:photos/models/gallery_type.dart';
import 'package:photos/models/magic_metadata.dart';
import 'package:photos/models/selected_files.dart';
import 'package:photos/theme/ente_theme.dart';
import 'package:photos/ui/collection_action_sheet.dart';
import 'package:photos/ui/components/bottom_action_bar/bottom_action_bar_widget.dart';
import 'package:photos/ui/components/icon_button_widget.dart';
import 'package:photos/ui/create_collection_sheet.dart';
import 'package:photos/ui/viewer/actions/file_selection_actions_widget.dart';
import 'package:photos/utils/delete_file_util.dart';
import 'package:photos/utils/magic_util.dart';
@ -85,10 +85,9 @@ class _FileSelectionOverlayBarState extends State<FileSelectionOverlayBar> {
iconButtonType: IconButtonType.primary,
iconColor: iconColor,
onTap: () {
createCollectionSheet(
widget.selectedFiles,
null,
showCollectionActionSheet(
context,
selectedFiles: widget.selectedFiles,
actionType: CollectionActionType.unHide,
);
},

View file

@ -22,8 +22,8 @@ import 'package:photos/services/favorites_service.dart';
import 'package:photos/services/hidden_service.dart';
import 'package:photos/services/ignored_files_service.dart';
import 'package:photos/services/local_sync_service.dart';
import 'package:photos/ui/collection_action_sheet.dart';
import 'package:photos/ui/common/progress_dialog.dart';
import 'package:photos/ui/create_collection_sheet.dart';
import 'package:photos/ui/viewer/file/custom_app_bar.dart';
import 'package:photos/utils/dialog_util.dart';
import 'package:photos/utils/file_util.dart';
@ -267,12 +267,11 @@ class FadingAppBarState extends State<FadingAppBar> {
}
Future<void> _handleUnHideRequest(BuildContext context) async {
final s = SelectedFiles();
s.files.add(widget.file);
createCollectionSheet(
s,
null,
final selectedFiles = SelectedFiles();
selectedFiles.files.add(widget.file);
showCollectionActionSheet(
context,
selectedFiles: selectedFiles,
actionType: CollectionActionType.unHide,
);
}

View file

@ -9,7 +9,7 @@ import 'package:photos/models/trash_file.dart';
import 'package:photos/theme/colors.dart';
import 'package:photos/theme/ente_theme.dart';
import "package:photos/ui/actions/file/file_actions.dart";
import 'package:photos/ui/create_collection_sheet.dart';
import 'package:photos/ui/collection_action_sheet.dart';
import 'package:photos/utils/delete_file_util.dart';
import 'package:photos/utils/share_util.dart';
@ -233,10 +233,9 @@ class FadingBottomBarState extends State<FadingBottomBar> {
onPressed: () {
final selectedFiles = SelectedFiles();
selectedFiles.toggleSelection(widget.file);
createCollectionSheet(
selectedFiles,
null,
showCollectionActionSheet(
context,
selectedFiles: selectedFiles,
actionType: CollectionActionType.restoreFiles,
);
},