Merge pull request #679 from ente-io/bottom-action-bar

Bottom action bar
This commit is contained in:
Ashil 2022-12-05 20:51:18 +05:30 committed by GitHub
commit 7047f3a21f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 226 additions and 17 deletions

View file

@ -61,13 +61,23 @@ class _BlurMenuItemWidgetState extends State<BlurMenuItemWidget> {
)
: const SizedBox.shrink(),
widget.labelText != null
? Padding(
padding: const EdgeInsets.symmetric(horizontal: 2),
child: Text(
widget.labelText!,
style: getEnteTextTheme(context)
.bodyBold
.copyWith(color: colorScheme.blurTextBase),
? Flexible(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 2),
child: Row(
children: [
Flexible(
child: Text(
widget.labelText!,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: getEnteTextTheme(context)
.bodyBold
.copyWith(color: colorScheme.blurTextBase),
),
),
],
),
),
)
: const SizedBox.shrink(),

View file

@ -14,6 +14,7 @@ class BottomActionBarWidget extends StatelessWidget {
final Widget expandedMenu;
final SelectedFiles? selectedFiles;
final VoidCallback? onCancel;
BottomActionBarWidget({
required this.expandedMenu,
this.selectedFiles,
@ -37,13 +38,9 @@ class BottomActionBarWidget extends StatelessWidget {
filter: ImageFilter.blur(sigmaX: blurBase, sigmaY: blurBase),
child: Container(
color: colorScheme.backdropBase,
padding: const EdgeInsets.only(
padding: EdgeInsets.only(
top: 4,
// bottom: !_expandableController.expanded &&
// ((widget.selectedFiles?.files.isNotEmpty) ?? false)
// ? 24
// : 36,
bottom: 24,
bottom: (selectedFiles?.files.isNotEmpty) ?? false ? 24 : 36,
),
child: Column(
mainAxisSize: MainAxisSize.min,
@ -59,7 +56,7 @@ class BottomActionBarWidget extends StatelessWidget {
child: ActionBarWidget(
selectedFiles: selectedFiles,
text: text,
iconButtons: _iconButtons(),
iconButtons: _iconButtons(context),
),
),
expanded: expandedMenu,
@ -96,7 +93,7 @@ class BottomActionBarWidget extends StatelessWidget {
);
}
List<Widget> _iconButtons() {
List<Widget> _iconButtons(BuildContext context) {
final iconButtonsWithExpansionIcon = <Widget?>[
...?iconButtons,
ExpansionIconWidget(expandableController: _expandableController)

View file

@ -0,0 +1,68 @@
import 'package:flutter/material.dart';
import 'package:photos/ui/components/blur_menu_item_widget.dart';
import 'package:photos/ui/components/divider_widget.dart';
class ExpandedMenuWidget extends StatelessWidget {
final List<List<BlurMenuItemWidget>> items;
const ExpandedMenuWidget({
required this.items,
super.key,
});
@override
Widget build(BuildContext context) {
const double itemHeight = 48;
const double whiteSpaceBetweenSections = 16;
const double dividerHeightBetweenItems = 1;
int numberOfDividers = 0;
double combinedHeightOfItems = 0;
for (List<BlurMenuItemWidget> group in items) {
//no divider if there is only one item in the section/group
if (group.length != 1) {
numberOfDividers += (group.length - 1);
}
combinedHeightOfItems += group.length * itemHeight;
}
return Padding(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
child: SizedBox(
height: combinedHeightOfItems +
(dividerHeightBetweenItems * numberOfDividers) +
(whiteSpaceBetweenSections * (items.length - 1)),
child: ListView.separated(
padding: const EdgeInsets.all(0),
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, sectionIndex) {
return ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: SizedBox(
height: itemHeight * items[sectionIndex].length +
(dividerHeightBetweenItems *
(items[sectionIndex].length - 1)),
child: ListView.separated(
padding: const EdgeInsets.all(0),
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, itemIndex) {
return items[sectionIndex][itemIndex];
},
separatorBuilder: (context, index) {
return const DividerWidget(
dividerType: DividerType.bottomBar,
);
},
itemCount: items[sectionIndex].length,
),
),
);
},
separatorBuilder: (context, index) {
return const SizedBox(height: whiteSpaceBetweenSections);
},
itemCount: items.length,
),
),
);
}
}

View file

@ -10,11 +10,16 @@ import 'package:photos/models/file_load_result.dart';
import 'package:photos/models/gallery_type.dart';
import 'package:photos/models/selected_files.dart';
import 'package:photos/services/ignored_files_service.dart';
import 'package:photos/theme/ente_theme.dart';
import 'package:photos/ui/components/blur_menu_item_widget.dart';
import 'package:photos/ui/components/bottom_action_bar/bottom_action_bar_widget.dart';
import 'package:photos/ui/components/bottom_action_bar/expanded_menu_widget.dart';
import 'package:photos/ui/components/icon_button_widget.dart';
import 'package:photos/ui/viewer/gallery/empty_state.dart';
import 'package:photos/ui/viewer/gallery/gallery.dart';
import 'package:photos/ui/viewer/gallery/gallery_app_bar_widget.dart';
import 'package:photos/utils/delete_file_util.dart';
import 'package:photos/utils/share_util.dart';
class CollectionPage extends StatefulWidget {
final CollectionWithThumbnail c;
@ -36,6 +41,7 @@ class CollectionPage extends StatefulWidget {
class _CollectionPageState extends State<CollectionPage> {
final _selectedFiles = SelectedFiles();
final GlobalKey shareButtonKey = GlobalKey();
final ValueNotifier<double> _bottomPosition = ValueNotifier(-150.0);
@override
@ -111,6 +117,7 @@ class _CollectionPageState extends State<CollectionPage> {
ValueListenableBuilder(
valueListenable: _bottomPosition,
builder: (context, value, child) {
final colorScheme = getEnteColorScheme(context);
return AnimatedPositioned(
curve: Curves.easeInOutExpo,
bottom: _bottomPosition.value,
@ -119,21 +126,37 @@ class _CollectionPageState extends State<CollectionPage> {
duration: const Duration(milliseconds: 400),
child: BottomActionBarWidget(
selectedFiles: _selectedFiles,
expandedMenu: const SizedBox(height: 150),
expandedMenu: ExpandedMenuWidget(
items: [
[
BlurMenuItemWidget(
leadingIcon: Icons.add_outlined,
labelText: "One",
menuItemColor: colorScheme.fillFaint,
),
],
],
),
text: _selectedFiles.files.length.toString() + ' selected',
onCancel: () {
if (_selectedFiles.files.isNotEmpty) {
_selectedFiles.clearAll();
}
},
iconButtons: const [
iconButtons: [
IconButtonWidget(
icon: Icons.delete_outlined,
iconButtonType: IconButtonType.primary,
onTap: () => showDeleteSheet(context, _selectedFiles),
),
IconButtonWidget(
icon: Icons.ios_share_outlined,
iconButtonType: IconButtonType.primary,
onTap: () => shareSelected(
context,
shareButtonKey,
_selectedFiles.files,
),
),
],
),

View file

@ -6,6 +6,7 @@ import 'dart:io';
import 'dart:math';
import 'package:device_info/device_info.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:photo_manager/photo_manager.dart';
@ -16,6 +17,7 @@ import 'package:photos/events/collection_updated_event.dart';
import 'package:photos/events/files_updated_event.dart';
import 'package:photos/events/local_photos_updated_event.dart';
import 'package:photos/models/file.dart';
import 'package:photos/models/selected_files.dart';
import 'package:photos/models/trash_item_request.dart';
import 'package:photos/services/remote_sync_service.dart';
import 'package:photos/services/sync_service.dart';
@ -485,3 +487,100 @@ Future<bool> shouldProceedWithDeletion(BuildContext context) async {
);
return choice == DialogUserChoice.secondChoice;
}
void showDeleteSheet(BuildContext context, SelectedFiles selectedFiles) {
final count = selectedFiles.files.length;
bool containsUploadedFile = false, containsLocalFile = false;
for (final file in selectedFiles.files) {
if (file.uploadedFileID != null) {
containsUploadedFile = true;
}
if (file.localID != null) {
containsLocalFile = true;
}
}
final actions = <Widget>[];
if (containsUploadedFile && containsLocalFile) {
actions.add(
CupertinoActionSheetAction(
isDestructiveAction: true,
onPressed: () async {
Navigator.of(context, rootNavigator: true).pop();
await deleteFilesOnDeviceOnly(
context,
selectedFiles.files.toList(),
);
selectedFiles.clearAll();
showToast(context, "Files deleted from device");
},
child: const Text("Device"),
),
);
actions.add(
CupertinoActionSheetAction(
isDestructiveAction: true,
onPressed: () async {
Navigator.of(context, rootNavigator: true).pop();
await deleteFilesFromRemoteOnly(
context,
selectedFiles.files.toList(),
);
selectedFiles.clearAll();
showShortToast(context, "Moved to trash");
},
child: const Text("ente"),
),
);
actions.add(
CupertinoActionSheetAction(
isDestructiveAction: true,
onPressed: () async {
Navigator.of(context, rootNavigator: true).pop();
await deleteFilesFromEverywhere(
context,
selectedFiles.files.toList(),
);
selectedFiles.clearAll();
},
child: const Text("Everywhere"),
),
);
} else {
actions.add(
CupertinoActionSheetAction(
isDestructiveAction: true,
onPressed: () async {
Navigator.of(context, rootNavigator: true).pop();
await deleteFilesFromEverywhere(
context,
selectedFiles.files.toList(),
);
selectedFiles.clearAll();
},
child: const Text("Delete"),
),
);
}
final action = CupertinoActionSheet(
title: Text(
"Delete " +
count.toString() +
" file" +
(count == 1 ? "" : "s") +
(containsUploadedFile && containsLocalFile ? " from" : "?"),
),
actions: actions,
cancelButton: CupertinoActionSheetAction(
child: const Text("Cancel"),
onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
},
),
);
showCupertinoModalPopup(
context: context,
builder: (_) => action,
barrierColor: Colors.black.withOpacity(0.75),
);
}

View file

@ -131,3 +131,15 @@ DateTime parseDateFromFileNam1e(String fileName) {
);
}
}
void shareSelected(
BuildContext context,
GlobalKey shareButtonKey,
Set<File> selectedFiles,
) {
share(
context,
selectedFiles.toList(),
shareButtonKey: shareButtonKey,
);
}