Merge pull request #679 from ente-io/bottom-action-bar
Bottom action bar
This commit is contained in:
commit
7047f3a21f
|
@ -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(),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -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),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -131,3 +131,15 @@ DateTime parseDateFromFileNam1e(String fileName) {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
void shareSelected(
|
||||
BuildContext context,
|
||||
GlobalKey shareButtonKey,
|
||||
Set<File> selectedFiles,
|
||||
) {
|
||||
share(
|
||||
context,
|
||||
selectedFiles.toList(),
|
||||
shareButtonKey: shareButtonKey,
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue