import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart'; import 'package:invoiceninja_flutter/data/models/entities.dart'; import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/redux/app/app_actions.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/ui/app/dialogs/alert_dialog.dart'; import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart'; import 'package:invoiceninja_flutter/ui/app/forms/decorated_form_field.dart'; import 'package:invoiceninja_flutter/ui/app/forms/save_cancel_buttons.dart'; import 'package:invoiceninja_flutter/utils/icons.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; void showErrorDialog({ @required BuildContext context, String message, bool clearErrorOnDismiss = false, }) { showDialog( context: context, builder: (BuildContext context) { return ErrorDialog(message, clearErrorOnDismiss: clearErrorOnDismiss); }); } void showMessageDialog({ @required BuildContext context, @required String message, List secondaryActions, }) { showDialog( context: context, builder: (BuildContext context) { return MessageDialog( message, secondaryActions: secondaryActions, ); }); } void confirmCallback({ @required BuildContext context, @required VoidCallback callback, String message, String typeToConfirm, }) { final localization = AppLocalization.of(context); final title = message == null ? localization.areYouSure : message; final content = message == null ? null : localization.areYouSure; showDialog( context: context, builder: (BuildContext context) { String _typed = ''; return AlertDialog( semanticLabel: localization.areYouSure, title: Text(title), content: typeToConfirm != null ? Row( children: [ Flexible( child: Text(localization.pleaseTypeToConfirm .replaceFirst(':value', typeToConfirm) + ':'), ), SizedBox(width: 16), Expanded( child: DecoratedFormField( autofocus: true, onChanged: (value) => _typed = value, hint: typeToConfirm, ), ), ], ) : content == null ? null : Text(content), actions: [ TextButton( child: Text(localization.cancel.toUpperCase()), onPressed: () { Navigator.pop(context); }), TextButton( child: Text(localization.ok.toUpperCase()), onPressed: () { if (typeToConfirm == null || typeToConfirm.toLowerCase() == _typed.toLowerCase()) { Navigator.pop(context); callback(); } }) ], ); }, ); } void passwordCallback({ BuildContext context, Function(String) callback, bool alwaysRequire = false, }) { final state = StoreProvider.of(context).state; if (state.authState.hasRecentlyEnteredPassword && !alwaysRequire) { callback(null); } else { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return PasswordConfirmation( callback: callback, ); }, ); } } class PasswordConfirmation extends StatefulWidget { const PasswordConfirmation({@required this.callback}); final Function(String) callback; @override _PasswordConfirmationState createState() => _PasswordConfirmationState(); } class _PasswordConfirmationState extends State { String _password; bool _isPasswordObscured = true; void _submit() { widget.callback(_password); } @override Widget build(BuildContext context) { final localization = AppLocalization.of(context); return AlertDialog( title: Text(localization.verifyPassword), content: TextField( autofocus: true, onChanged: (value) => _password = value, obscureText: _isPasswordObscured, keyboardType: TextInputType.visiblePassword, decoration: InputDecoration( labelText: localization.password, suffixIcon: IconButton( alignment: Alignment.bottomCenter, icon: Icon( _isPasswordObscured ? Icons.visibility : Icons.visibility_off, color: Colors.grey, ), onPressed: () { setState(() { _isPasswordObscured = !_isPasswordObscured; }); }, ), ), onSubmitted: (value) => _submit(), ), actions: [ SaveCancelButtons( isHeader: false, saveLabel: localization.submit.toUpperCase(), onSavePressed: (context) { if ((_password ?? '').isEmpty) { return; } Navigator.pop(context); widget.callback(_password); }, cancelLabel: localization.cancel.toUpperCase(), onCancelPressed: (context) { Navigator.pop(context); }, ), ], ); } } void fieldCallback({ BuildContext context, String title, String field, Function(String) callback, int maxLength, List secondaryActions, }) { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return FieldConfirmation( callback: callback, field: field, title: title, maxLength: maxLength, secondaryActions: secondaryActions, ); }, ); } class FieldConfirmation extends StatefulWidget { const FieldConfirmation({ @required this.callback, @required this.title, @required this.field, this.maxLength, this.secondaryActions, }); final Function(String) callback; final String title; final String field; final int maxLength; final List secondaryActions; @override _FieldConfirmationState createState() => _FieldConfirmationState(); } class _FieldConfirmationState extends State { String _field; void _submit() { widget.callback(_field); } @override Widget build(BuildContext context) { final localization = AppLocalization.of(context); return AlertDialog( title: Text(widget.title), content: TextField( autofocus: true, onChanged: (value) => _field = value, maxLength: widget.maxLength, maxLengthEnforced: widget.maxLength != null, buildCounter: (_, {currentLength, maxLength, isFocused}) => null, decoration: InputDecoration( labelText: widget.field, ), onSubmitted: (value) => _submit(), ), actions: [ ...widget.secondaryActions ?? [], SizedBox(width: 6), SaveCancelButtons( isHeader: false, saveLabel: localization.save.toUpperCase(), onSavePressed: (context) { if ((_field ?? '').isEmpty) { return; } Navigator.pop(context); widget.callback(_field); }, cancelLabel: localization.cancel.toUpperCase(), onCancelPressed: (context) { Navigator.pop(context); }, ), ], ); } } void cloneToDialog({ @required BuildContext context, @required InvoiceEntity invoice, }) { final localization = AppLocalization.of(context); final store = StoreProvider.of(context); final state = store.state; final userCompany = state.userCompany; showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: Text(localization.cloneTo), content: Column( mainAxisSize: MainAxisSize.min, children: [ if (userCompany.canCreate(EntityType.invoice)) ListTile( leading: Icon(getEntityIcon(EntityType.invoice)), title: Text(localization.invoice), onTap: () { handleEntityAction( context, invoice, EntityAction.cloneToInvoice); Navigator.of(context).pop(); }, ), if (userCompany.canCreate(EntityType.quote)) ListTile( leading: Icon(getEntityIcon(EntityType.quote)), title: Text(localization.quote), onTap: () { handleEntityAction( context, invoice, EntityAction.cloneToQuote); Navigator.of(context).pop(); }, ), if (userCompany.canCreate(EntityType.credit)) ListTile( leading: Icon(getEntityIcon(EntityType.credit)), title: Text(localization.credit), onTap: () { handleEntityAction( context, invoice, EntityAction.cloneToCredit); Navigator.of(context).pop(); }, ), if (userCompany.canCreate(EntityType.recurringInvoice)) ListTile( leading: Icon(getEntityIcon(EntityType.recurringInvoice)), title: Text(localization.recurringInvoice), onTap: () { handleEntityAction( context, invoice, EntityAction.cloneToRecurring); Navigator.of(context).pop(); }, ), ], ), actions: [ TextButton( child: Text(localization.close.toUpperCase()), onPressed: () => Navigator.of(context).pop(), ) ], ); }); }