Tablet layout

This commit is contained in:
Hillel Coren 2019-08-14 16:39:37 +03:00
parent 6081af2314
commit c6096efb2f
9 changed files with 58 additions and 22 deletions

View File

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:invoiceninja_flutter/constants.dart'; import 'package:invoiceninja_flutter/constants.dart';
import 'package:invoiceninja_flutter/data/file_storage.dart'; import 'package:invoiceninja_flutter/data/file_storage.dart';
@ -15,8 +16,10 @@ import 'package:invoiceninja_flutter/redux/dashboard/dashboard_actions.dart';
import 'package:invoiceninja_flutter/redux/static/static_state.dart'; import 'package:invoiceninja_flutter/redux/static/static_state.dart';
import 'package:invoiceninja_flutter/redux/ui/ui_state.dart'; import 'package:invoiceninja_flutter/redux/ui/ui_state.dart';
import 'package:invoiceninja_flutter/ui/app/app_builder.dart'; import 'package:invoiceninja_flutter/ui/app/app_builder.dart';
import 'package:invoiceninja_flutter/ui/app/dialogs/alert_dialog.dart';
import 'package:invoiceninja_flutter/ui/app/main_screen.dart'; import 'package:invoiceninja_flutter/ui/app/main_screen.dart';
import 'package:invoiceninja_flutter/ui/auth/login_vm.dart'; import 'package:invoiceninja_flutter/ui/auth/login_vm.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart'; import 'package:invoiceninja_flutter/utils/platforms.dart';
import 'package:redux/redux.dart'; import 'package:redux/redux.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
@ -421,3 +424,16 @@ void _setLastLoadWasSuccesfull() async {
prefs.setBool('initialized', true); prefs.setBool('initialized', true);
} }
*/ */
bool hasChanges(Store<AppState> store, dynamic action) {
if (store.state.hasChanges() && !isMobile(action.context) && !action.force) {
showDialog<MessageDialog>(
context: action.context,
builder: (BuildContext context) {
return MessageDialog(AppLocalization.of(context).errorUnsavedChanges);
});
return true;
} else {
return false;
}
}

View File

@ -12,23 +12,29 @@ import 'package:invoiceninja_flutter/utils/completers.dart';
import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:invoiceninja_flutter/utils/localization.dart';
class ViewClientList implements PersistUI { class ViewClientList implements PersistUI {
ViewClientList(this.context); ViewClientList({@required this.context, this.force = false});
final BuildContext context; final BuildContext context;
final bool force;
} }
class ViewClient implements PersistUI { class ViewClient implements PersistUI {
ViewClient({this.clientId, this.context}); ViewClient({
@required this.clientId,
@required this.context,
this.force = false,
});
final int clientId; final int clientId;
final BuildContext context; final BuildContext context;
final bool force;
} }
class EditClient implements PersistUI { class EditClient implements PersistUI {
EditClient( EditClient(
{this.client, {@required this.client,
@required this.context,
this.contact, this.contact,
this.context,
this.completer, this.completer,
this.trackRoute = true}); this.trackRoute = true});

View File

@ -1,13 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/redux/app/app_middleware.dart';
import 'package:invoiceninja_flutter/redux/product/product_actions.dart'; import 'package:invoiceninja_flutter/redux/product/product_actions.dart';
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart'; import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
import 'package:invoiceninja_flutter/ui/app/dialogs/alert_dialog.dart';
import 'package:invoiceninja_flutter/ui/client/client_screen.dart'; import 'package:invoiceninja_flutter/ui/client/client_screen.dart';
import 'package:invoiceninja_flutter/ui/client/edit/client_edit_vm.dart'; import 'package:invoiceninja_flutter/ui/client/edit/client_edit_vm.dart';
import 'package:invoiceninja_flutter/ui/client/view/client_view_vm.dart'; import 'package:invoiceninja_flutter/ui/client/view/client_view_vm.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart'; import 'package:invoiceninja_flutter/utils/platforms.dart';
import 'package:redux/redux.dart'; import 'package:redux/redux.dart';
import 'package:invoiceninja_flutter/redux/client/client_actions.dart'; import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
@ -42,13 +41,17 @@ List<Middleware<AppState>> createStoreClientsMiddleware([
Middleware<AppState> _editClient() { Middleware<AppState> _editClient() {
return (Store<AppState> store, dynamic action, NextDispatcher next) async { return (Store<AppState> store, dynamic action, NextDispatcher next) async {
if (hasChanges(store, action)) {
return;
}
next(action); next(action);
if (action.trackRoute) { if (action.trackRoute) {
store.dispatch(UpdateCurrentRoute(ClientEditScreen.route)); store.dispatch(UpdateCurrentRoute(ClientEditScreen.route));
} }
if (action.context != null && isMobile(action.context)) { if (isMobile(action.context)) {
final client = final client =
await Navigator.of(action.context).pushNamed(ClientEditScreen.route); await Navigator.of(action.context).pushNamed(ClientEditScreen.route);
@ -61,17 +64,12 @@ Middleware<AppState> _editClient() {
Middleware<AppState> _viewClient() { Middleware<AppState> _viewClient() {
return (Store<AppState> store, dynamic action, NextDispatcher next) async { return (Store<AppState> store, dynamic action, NextDispatcher next) async {
next(action); if (hasChanges(store, action)) {
if (store.state.hasChanges() && !isMobile(action.context)) {
showDialog<AlertDialog>(
context: action.context,
builder: (BuildContext context) {
return MessageDialog(AppLocalization.of(context).errorUnsavedChanges);
});
return; return;
} }
next(action);
store.dispatch(UpdateCurrentRoute(ClientViewScreen.route)); store.dispatch(UpdateCurrentRoute(ClientViewScreen.route));
if (isMobile(action.context)) { if (isMobile(action.context)) {
@ -82,15 +80,15 @@ Middleware<AppState> _viewClient() {
Middleware<AppState> _viewClientList() { Middleware<AppState> _viewClientList() {
return (Store<AppState> store, dynamic action, NextDispatcher next) { return (Store<AppState> store, dynamic action, NextDispatcher next) {
next(action); if (hasChanges(store, action)) {
if (store.state.hasChanges()) {
return; return;
} }
next(action);
store.dispatch(UpdateCurrentRoute(ClientScreen.route)); store.dispatch(UpdateCurrentRoute(ClientScreen.route));
if (action.context != null && isMobile(action.context)) { if (isMobile(action.context)) {
Navigator.of(action.context).pushNamedAndRemoveUntil( Navigator.of(action.context).pushNamedAndRemoveUntil(
ClientScreen.route, (Route<dynamic> route) => false); ClientScreen.route, (Route<dynamic> route) => false);
} }

View File

@ -42,6 +42,8 @@ final editingReducer = combineReducers<ClientEntity>([
TypedReducer<ClientEntity, AddContact>(_addContact), TypedReducer<ClientEntity, AddContact>(_addContact),
TypedReducer<ClientEntity, DeleteContact>(_removeContact), TypedReducer<ClientEntity, DeleteContact>(_removeContact),
TypedReducer<ClientEntity, UpdateContact>(_updateContact), TypedReducer<ClientEntity, UpdateContact>(_updateContact),
TypedReducer<ClientEntity, ViewClient>(_clearEditing),
TypedReducer<ClientEntity, ViewClientList>(_clearEditing),
TypedReducer<ClientEntity, SelectCompany>(_clearEditing), TypedReducer<ClientEntity, SelectCompany>(_clearEditing),
]); ]);

View File

@ -3,7 +3,6 @@ import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart'; import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart'; import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
import 'package:invoiceninja_flutter/ui/app/dialogs/alert_dialog.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/product/edit/product_edit_vm.dart'; import 'package:invoiceninja_flutter/ui/product/edit/product_edit_vm.dart';
import 'package:invoiceninja_flutter/ui/product/product_screen.dart'; import 'package:invoiceninja_flutter/ui/product/product_screen.dart';
import 'package:invoiceninja_flutter/ui/product/view/product_view_vm.dart'; import 'package:invoiceninja_flutter/ui/product/view/product_view_vm.dart';

View File

@ -2,7 +2,6 @@ import 'package:built_collection/built_collection.dart';
import 'package:built_value/built_value.dart'; import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart'; import 'package:built_value/serializer.dart';
import 'package:invoiceninja_flutter/data/models/company_model.dart'; import 'package:invoiceninja_flutter/data/models/company_model.dart';
import 'package:invoiceninja_flutter/data/models/entities.dart';
import 'package:invoiceninja_flutter/redux/client/client_state.dart'; import 'package:invoiceninja_flutter/redux/client/client_state.dart';
import 'package:invoiceninja_flutter/redux/dashboard/dashboard_state.dart'; import 'package:invoiceninja_flutter/redux/dashboard/dashboard_state.dart';
import 'package:invoiceninja_flutter/redux/invoice/invoice_state.dart'; import 'package:invoiceninja_flutter/redux/invoice/invoice_state.dart';

View File

@ -178,7 +178,7 @@ class AppDrawer extends StatelessWidget {
entityType: EntityType.client, entityType: EntityType.client,
icon: getEntityIcon(EntityType.client), icon: getEntityIcon(EntityType.client),
title: localization.clients, title: localization.clients,
onTap: () => store.dispatch(ViewClientList(context)), onTap: () => store.dispatch(ViewClientList(context: context)),
onCreateTap: () { onCreateTap: () {
if (isMobile(context)) { if (isMobile(context)) {
navigator.pop(); navigator.pop();

View File

@ -57,6 +57,11 @@ class _ClientEditState extends State<ClientEdit>
title: Text( title: Text(
client.isNew ? localization.newClient : localization.editClient), client.isNew ? localization.newClient : localization.editClient),
actions: <Widget>[ actions: <Widget>[
if (!isMobile(context))
FlatButton(
child: Text(localization.cancel),
onPressed: () => viewModel.onCancelPressed(context),
),
ActionIconButton( ActionIconButton(
icon: Icons.cloud_upload, icon: Icons.cloud_upload,
tooltip: localization.save, tooltip: localization.save,

View File

@ -48,6 +48,7 @@ class ClientEditVM {
@required this.onChanged, @required this.onChanged,
@required this.onSavePressed, @required this.onSavePressed,
@required this.onBackPressed, @required this.onBackPressed,
@required this.onCancelPressed,
@required this.staticState, @required this.staticState,
@required this.copyBillingAddress, @required this.copyBillingAddress,
@required this.copyShippingAddress, @required this.copyShippingAddress,
@ -87,6 +88,15 @@ class ClientEditVM {
..state = client.shippingState ..state = client.shippingState
..postalCode = client.shippingPostalCode ..postalCode = client.shippingPostalCode
..countryId = client.shippingCountryId))), ..countryId = client.shippingCountryId))),
onCancelPressed: (BuildContext context) {
store.dispatch(EditClient(client: ClientEntity(), context: context));
if (client.isNew) {
store.dispatch(ViewClientList(context: context, force: true));
} else {
store.dispatch(
ViewClient(context: context, clientId: client.id, force: true));
}
},
onSavePressed: (BuildContext context) { onSavePressed: (BuildContext context) {
if (!client.hasNameSet) { if (!client.hasNameSet) {
showDialog<ErrorDialog>( showDialog<ErrorDialog>(
@ -138,6 +148,7 @@ class ClientEditVM {
final Function(ClientEntity) onChanged; final Function(ClientEntity) onChanged;
final Function(BuildContext) onSavePressed; final Function(BuildContext) onSavePressed;
final Function onBackPressed; final Function onBackPressed;
final Function(BuildContext) onCancelPressed;
final StaticState staticState; final StaticState staticState;
final Function() copyShippingAddress; final Function() copyShippingAddress;
final Function() copyBillingAddress; final Function() copyBillingAddress;