|
@@ -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/toast_util.dart';
|
|
|
import 'package:ente_auth/utils/totp_util.dart';
|
|
|
-import 'package:flutter/gestures.dart';
|
|
|
import 'package:flutter/material.dart';
|
|
|
+import 'package:flutter_context_menu/flutter_context_menu.dart';
|
|
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
|
|
import 'package:logging/logging.dart';
|
|
|
import 'package:move_to_background/move_to_background.dart';
|
|
@@ -87,147 +87,46 @@ class _CodeWidgetState extends State<CodeWidget> {
|
|
|
final l10n = context.l10n;
|
|
|
return Container(
|
|
|
margin: const EdgeInsets.only(left: 16, right: 16, bottom: 8, top: 8),
|
|
|
- child: Slidable(
|
|
|
- enabled: PlatformUtil.isMobile(),
|
|
|
- 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) => 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),
|
|
|
+ child: Builder(
|
|
|
+ builder: (context) {
|
|
|
+ if (PlatformUtil.isDesktop()) {
|
|
|
+ return ContextMenuRegion(
|
|
|
+ contextMenu: ContextMenu(
|
|
|
+ entries: <ContextMenuEntry>[
|
|
|
+ MenuItem(
|
|
|
+ label: 'QR',
|
|
|
+ icon: Icons.qr_code,
|
|
|
+ onSelected: () => _onShowQrPressed(null),
|
|
|
),
|
|
|
- ),
|
|
|
+ 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),
|
|
|
+ );
|
|
|
+ }
|
|
|
|
|
|
- Widget _getCardContents(AppLocalizations l10n) {
|
|
|
- return SizedBox(
|
|
|
- child: Column(
|
|
|
- crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
- mainAxisAlignment: MainAxisAlignment.center,
|
|
|
- children: [
|
|
|
- if (widget.code.type == Type.totp)
|
|
|
- CodeTimerProgress(
|
|
|
- period: widget.code.period,
|
|
|
- ),
|
|
|
- const SizedBox(
|
|
|
- height: 16,
|
|
|
- ),
|
|
|
- Row(
|
|
|
- children: [
|
|
|
- _shouldShowLargeIcon ? _getIcon() : const SizedBox.shrink(),
|
|
|
- Expanded(
|
|
|
- child: Column(
|
|
|
- children: [
|
|
|
- _getTopRow(),
|
|
|
- const SizedBox(height: 4),
|
|
|
- _getBottomRow(l10n),
|
|
|
- ],
|
|
|
- ),
|
|
|
- ),
|
|
|
- ],
|
|
|
- ),
|
|
|
- const SizedBox(
|
|
|
- height: 20,
|
|
|
- ),
|
|
|
- if (PlatformUtil.isDesktop())
|
|
|
- Row(
|
|
|
- mainAxisAlignment: MainAxisAlignment.end,
|
|
|
+ return Slidable(
|
|
|
+ key: ValueKey(widget.code.hashCode),
|
|
|
+ endActionPane: ActionPane(
|
|
|
+ extentRatio: 0.60,
|
|
|
+ motion: const ScrollMotion(),
|
|
|
children: [
|
|
|
- SlideAction(
|
|
|
+ const SizedBox(
|
|
|
+ width: 4,
|
|
|
+ ),
|
|
|
+ SlidableAction(
|
|
|
onPressed: _onShowQrPressed,
|
|
|
backgroundColor: Colors.grey.withOpacity(0.1),
|
|
|
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
|
|
@@ -241,7 +140,7 @@ class _CodeWidgetState extends State<CodeWidget> {
|
|
|
const SizedBox(
|
|
|
width: 4,
|
|
|
),
|
|
|
- SlideAction(
|
|
|
+ SlidableAction(
|
|
|
onPressed: _onEditPressed,
|
|
|
backgroundColor: Colors.grey.withOpacity(0.1),
|
|
|
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
|
|
@@ -255,7 +154,7 @@ class _CodeWidgetState extends State<CodeWidget> {
|
|
|
const SizedBox(
|
|
|
width: 4,
|
|
|
),
|
|
|
- SlideAction(
|
|
|
+ SlidableAction(
|
|
|
onPressed: _onDeletePressed,
|
|
|
backgroundColor: Colors.grey.withOpacity(0.1),
|
|
|
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
|
|
@@ -267,6 +166,78 @@ class _CodeWidgetState extends State<CodeWidget> {
|
|
|
),
|
|
|
],
|
|
|
),
|
|
|
+ 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),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _getCardContents(AppLocalizations l10n) {
|
|
|
+ return SizedBox(
|
|
|
+ child: Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ mainAxisAlignment: MainAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ if (widget.code.type == Type.totp)
|
|
|
+ CodeTimerProgress(
|
|
|
+ period: widget.code.period,
|
|
|
+ ),
|
|
|
+ const SizedBox(
|
|
|
+ height: 16,
|
|
|
+ ),
|
|
|
+ Row(
|
|
|
+ children: [
|
|
|
+ _shouldShowLargeIcon ? _getIcon() : const SizedBox.shrink(),
|
|
|
+ Expanded(
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ _getTopRow(),
|
|
|
+ const SizedBox(height: 4),
|
|
|
+ _getBottomRow(l10n),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ const SizedBox(
|
|
|
+ height: 20,
|
|
|
+ ),
|
|
|
],
|
|
|
),
|
|
|
);
|
|
@@ -529,57 +500,3 @@ class _CodeWidgetState extends State<CodeWidget> {
|
|
|
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,
|
|
|
- ),
|
|
|
- ),
|
|
|
- ],
|
|
|
- ),
|
|
|
- ),
|
|
|
- );
|
|
|
- }
|
|
|
-}
|