fix: use context menu on desktop

This commit is contained in:
Prateek Sunal 2024-03-29 16:21:30 +05:30
parent 056e29a5f5
commit f8fbedfe10
3 changed files with 130 additions and 196 deletions

View File

@ -18,8 +18,8 @@ import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/platform_util.dart'; import 'package:ente_auth/utils/platform_util.dart';
import 'package:ente_auth/utils/toast_util.dart'; import 'package:ente_auth/utils/toast_util.dart';
import 'package:ente_auth/utils/totp_util.dart'; import 'package:ente_auth/utils/totp_util.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_context_menu/flutter_context_menu.dart';
import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:move_to_background/move_to_background.dart'; import 'package:move_to_background/move_to_background.dart';
@ -87,106 +87,121 @@ class _CodeWidgetState extends State<CodeWidget> {
final l10n = context.l10n; final l10n = context.l10n;
return Container( return Container(
margin: const EdgeInsets.only(left: 16, right: 16, bottom: 8, top: 8), margin: const EdgeInsets.only(left: 16, right: 16, bottom: 8, top: 8),
child: Slidable( child: Builder(
enabled: PlatformUtil.isMobile(), builder: (context) {
key: ValueKey(widget.code.hashCode), if (PlatformUtil.isDesktop()) {
endActionPane: ActionPane( return ContextMenuRegion(
extentRatio: 0.60, contextMenu: ContextMenu(
motion: const ScrollMotion(), entries: <ContextMenuEntry>[
children: [ MenuItem(
const SizedBox( label: 'QR',
width: 4, icon: Icons.qr_code,
), onSelected: () => _onShowQrPressed(null),
SlidableAction(
onPressed: _onShowQrPressed,
backgroundColor: Colors.grey.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
foregroundColor:
Theme.of(context).colorScheme.inverseBackgroundColor,
icon: Icons.qr_code_2_outlined,
label: "QR",
padding: const EdgeInsets.only(left: 4, right: 0),
spacing: 8,
),
const SizedBox(
width: 4,
),
SlidableAction(
onPressed: _onEditPressed,
backgroundColor: Colors.grey.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
foregroundColor:
Theme.of(context).colorScheme.inverseBackgroundColor,
icon: Icons.edit_outlined,
label: l10n.edit,
padding: const EdgeInsets.only(left: 4, right: 0),
spacing: 8,
),
const SizedBox(
width: 4,
),
SlidableAction(
onPressed: _onDeletePressed,
backgroundColor: Colors.grey.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
foregroundColor: const Color(0xFFFE4A49),
icon: Icons.delete,
label: l10n.delete,
padding: const EdgeInsets.only(left: 0, right: 0),
spacing: 8,
),
],
),
child: Builder(
builder: (context) => RawGestureDetector(
gestures: {
PanGestureRecognizer:
GestureRecognizerFactoryWithHandlers<PanGestureRecognizer>(
() => PanGestureRecognizer(
debugOwner: this,
// This recognizer accepts any button press made with a secondary button.
allowedButtonsFilter: (int buttons) =>
buttons & kSecondaryButton != 0,
),
(PanGestureRecognizer instance) {
instance
..dragStartBehavior = DragStartBehavior.down
..onEnd = (DragEndDetails details) {
Slidable.of(context)?.openEndActionPane();
};
},
),
},
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Container(
color: Theme.of(context).colorScheme.codeCardBackgroundColor,
child: Material(
color: Colors.transparent,
child: InkWell(
customBorder: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
onTap: () {
_copyCurrentOTPToClipboard();
},
onDoubleTap: isMaskingEnabled
? () {
setState(
() {
_hideCode = !_hideCode;
},
);
}
: null,
onLongPress: () {
_copyCurrentOTPToClipboard();
},
child: _getCardContents(l10n),
), ),
), MenuItem(
label: 'Edit',
icon: Icons.edit,
onSelected: () => _onEditPressed(null),
),
const MenuDivider(),
MenuItem(
label: 'Delete',
value: "Delete",
icon: Icons.delete,
onSelected: () => _onDeletePressed(null),
),
],
padding: const EdgeInsets.all(8.0),
), ),
child: _clippedCard(l10n),
);
}
return Slidable(
key: ValueKey(widget.code.hashCode),
endActionPane: ActionPane(
extentRatio: 0.60,
motion: const ScrollMotion(),
children: [
const SizedBox(
width: 4,
),
SlidableAction(
onPressed: _onShowQrPressed,
backgroundColor: Colors.grey.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
foregroundColor:
Theme.of(context).colorScheme.inverseBackgroundColor,
icon: Icons.qr_code_2_outlined,
label: "QR",
padding: const EdgeInsets.only(left: 4, right: 0),
spacing: 8,
),
const SizedBox(
width: 4,
),
SlidableAction(
onPressed: _onEditPressed,
backgroundColor: Colors.grey.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
foregroundColor:
Theme.of(context).colorScheme.inverseBackgroundColor,
icon: Icons.edit_outlined,
label: l10n.edit,
padding: const EdgeInsets.only(left: 4, right: 0),
spacing: 8,
),
const SizedBox(
width: 4,
),
SlidableAction(
onPressed: _onDeletePressed,
backgroundColor: Colors.grey.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
foregroundColor: const Color(0xFFFE4A49),
icon: Icons.delete,
label: l10n.delete,
padding: const EdgeInsets.only(left: 0, right: 0),
spacing: 8,
),
],
), ),
child: Builder(
builder: (context) => _clippedCard(l10n),
),
);
},
),
);
}
Widget _clippedCard(AppLocalizations l10n) {
return ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Container(
color: Theme.of(context).colorScheme.codeCardBackgroundColor,
child: Material(
color: Colors.transparent,
child: InkWell(
customBorder: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
onTap: () {
_copyCurrentOTPToClipboard();
},
onDoubleTap: isMaskingEnabled
? () {
setState(
() {
_hideCode = !_hideCode;
},
);
}
: null,
onLongPress: () {
_copyCurrentOTPToClipboard();
},
child: _getCardContents(l10n),
), ),
), ),
), ),
@ -223,50 +238,6 @@ class _CodeWidgetState extends State<CodeWidget> {
const SizedBox( const SizedBox(
height: 20, height: 20,
), ),
if (PlatformUtil.isDesktop())
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SlideAction(
onPressed: _onShowQrPressed,
backgroundColor: Colors.grey.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
foregroundColor:
Theme.of(context).colorScheme.inverseBackgroundColor,
icon: Icons.qr_code_2_outlined,
label: "QR",
padding: const EdgeInsets.only(left: 4, right: 0),
spacing: 8,
),
const SizedBox(
width: 4,
),
SlideAction(
onPressed: _onEditPressed,
backgroundColor: Colors.grey.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
foregroundColor:
Theme.of(context).colorScheme.inverseBackgroundColor,
icon: Icons.edit_outlined,
label: l10n.edit,
padding: const EdgeInsets.only(left: 4, right: 0),
spacing: 8,
),
const SizedBox(
width: 4,
),
SlideAction(
onPressed: _onDeletePressed,
backgroundColor: Colors.grey.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
foregroundColor: const Color(0xFFFE4A49),
icon: Icons.delete,
label: l10n.delete,
padding: const EdgeInsets.only(left: 0, right: 0),
spacing: 8,
),
],
),
], ],
), ),
); );
@ -529,57 +500,3 @@ class _CodeWidgetState extends State<CodeWidget> {
return code; return code;
} }
} }
class SlideAction extends StatelessWidget {
const SlideAction({
super.key,
required this.onPressed,
required this.backgroundColor,
required this.borderRadius,
required this.foregroundColor,
required this.icon,
required this.label,
required this.padding,
required this.spacing,
});
final void Function(dynamic) onPressed;
final Color backgroundColor;
final BorderRadius borderRadius;
final Color foregroundColor;
final IconData icon;
final String label;
final EdgeInsets padding;
final double spacing;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () => onPressed(0),
child: Container(
color: backgroundColor,
height: 52,
width: 52,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
icon,
size: 16,
color: foregroundColor,
),
const SizedBox(
height: 4,
),
Text(
label,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: foregroundColor,
),
),
],
),
),
);
}
}

View File

@ -347,6 +347,14 @@ packages:
url: "https://github.com/ente-io/ente_crypto_dart.git" url: "https://github.com/ente-io/ente_crypto_dart.git"
source: git source: git
version: "1.0.0" version: "1.0.0"
equatable:
dependency: transitive
description:
name: equatable
sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2
url: "https://pub.dev"
source: hosted
version: "2.0.5"
event_bus: event_bus:
dependency: "direct main" dependency: "direct main"
description: description:
@ -440,6 +448,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.1.5" version: "8.1.5"
flutter_context_menu:
dependency: "direct main"
description:
name: flutter_context_menu
sha256: "9f220a8fa0290c68e38000d6d62a0dc4555d490c15a5bd856a6e6d255d81b8dc"
url: "https://pub.dev"
source: hosted
version: "0.1.3"
flutter_displaymode: flutter_displaymode:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -41,6 +41,7 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_bloc: ^8.0.1 flutter_bloc: ^8.0.1
flutter_context_menu: ^0.1.3
flutter_displaymode: ^0.6.0 flutter_displaymode: ^0.6.0
flutter_email_sender: ^6.0.2 flutter_email_sender: ^6.0.2
flutter_inappwebview: ^6.0.0 flutter_inappwebview: ^6.0.0