Settings
This commit is contained in:
parent
1792adf4f7
commit
3c5d04433a
|
|
@ -0,0 +1,74 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/entities.dart';
|
||||
import 'package:invoiceninja_flutter/ui/settings/device_settings_list_vm.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
||||
class DeviceSettingsList extends StatelessWidget {
|
||||
const DeviceSettingsList({
|
||||
Key key,
|
||||
@required this.viewModel,
|
||||
}) : super(key: key);
|
||||
|
||||
final DeviceSettingsListVM viewModel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView(
|
||||
children: <Widget>[
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalization.of(context).darkMode),
|
||||
value: viewModel.enableDarkMode,
|
||||
onChanged: (value) => viewModel.onDarkModeChanged(context, value),
|
||||
secondary: Icon(FontAwesomeIcons.moon),
|
||||
activeColor: Theme.of(context).accentColor,
|
||||
),
|
||||
FutureBuilder(
|
||||
future: viewModel.authenticationSupported,
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
if (snapshot.hasData && snapshot.data == true) {
|
||||
return SwitchListTile(
|
||||
title:
|
||||
Text(AppLocalization.of(context).biometricAuthentication),
|
||||
value: viewModel.requireAuthentication,
|
||||
onChanged: (value) =>
|
||||
viewModel.onRequireAuthenticationChanged(context, value),
|
||||
secondary: Icon(viewModel.requireAuthentication
|
||||
? FontAwesomeIcons.lock
|
||||
: FontAwesomeIcons.unlockAlt),
|
||||
activeColor: Theme.of(context).accentColor,
|
||||
);
|
||||
} else {
|
||||
return SizedBox();
|
||||
}
|
||||
},
|
||||
),
|
||||
viewModel.state.selectedCompany.isModuleEnabled(EntityType.task)
|
||||
? SwitchListTile(
|
||||
title: Text(AppLocalization.of(context).autoStartTasks),
|
||||
value: viewModel.autoStartTasks,
|
||||
onChanged: (value) =>
|
||||
viewModel.onAutoStartTasksChanged(context, value),
|
||||
secondary: Icon(FontAwesomeIcons.clock),
|
||||
activeColor: Theme.of(context).accentColor,
|
||||
)
|
||||
: SizedBox(),
|
||||
ListTile(
|
||||
leading: Icon(FontAwesomeIcons.syncAlt),
|
||||
title: Text(AppLocalization.of(context).refreshData),
|
||||
onTap: () {
|
||||
viewModel.onRefreshTap(context);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(FontAwesomeIcons.powerOff),
|
||||
title: Text(AppLocalization.of(context).logout),
|
||||
onTap: () {
|
||||
viewModel.onLogoutTap(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:invoiceninja_flutter/constants.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/dashboard/dashboard_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/dialogs/loading_dialog.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/app_builder.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
import 'package:invoiceninja_flutter/utils/platforms.dart';
|
||||
import 'package:local_auth/local_auth.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:invoiceninja_flutter/ui/settings/device_settings_list.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja_flutter/redux/auth/auth_actions.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class DeviceSettingsListBuilder extends StatelessWidget {
|
||||
const DeviceSettingsListBuilder({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StoreConnector<AppState, DeviceSettingsListVM>(
|
||||
converter: DeviceSettingsListVM.fromStore,
|
||||
builder: (context, viewModel) {
|
||||
return DeviceSettingsList(viewModel: viewModel);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DeviceSettingsListVM {
|
||||
DeviceSettingsListVM({
|
||||
@required this.state,
|
||||
@required this.onLogoutTap,
|
||||
@required this.onRefreshTap,
|
||||
@required this.onDarkModeChanged,
|
||||
@required this.enableDarkMode,
|
||||
@required this.autoStartTasks,
|
||||
@required this.onAutoStartTasksChanged,
|
||||
@required this.onRequireAuthenticationChanged,
|
||||
@required this.requireAuthentication,
|
||||
@required this.authenticationSupported,
|
||||
});
|
||||
|
||||
static DeviceSettingsListVM fromStore(Store<AppState> store) {
|
||||
void _refreshData(BuildContext context) async {
|
||||
final completer = snackBarCompleter(
|
||||
context, AppLocalization.of(context).refreshComplete,
|
||||
shouldPop: true);
|
||||
store.dispatch(RefreshData(
|
||||
platform: getPlatform(context),
|
||||
completer: completer,
|
||||
));
|
||||
await showDialog<AlertDialog>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) => SimpleDialog(
|
||||
children: <Widget>[LoadingDialog()],
|
||||
));
|
||||
AppBuilder.of(context).rebuild();
|
||||
store.dispatch(LoadDashboard());
|
||||
}
|
||||
|
||||
void _confirmLogout(BuildContext context) {
|
||||
final localization = AppLocalization.of(context);
|
||||
showDialog<AlertDialog>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => AlertDialog(
|
||||
semanticLabel: localization.areYouSure,
|
||||
title: Text(localization.areYouSure),
|
||||
actions: <Widget>[
|
||||
new FlatButton(
|
||||
child: Text(localization.cancel.toUpperCase()),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
}),
|
||||
new FlatButton(
|
||||
child: Text(localization.ok.toUpperCase()),
|
||||
onPressed: () {
|
||||
store.dispatch(UserLogout(context));
|
||||
})
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return DeviceSettingsListVM(
|
||||
state: store.state,
|
||||
onLogoutTap: (BuildContext context) => _confirmLogout(context),
|
||||
onRefreshTap: (BuildContext context) => _refreshData(context),
|
||||
onDarkModeChanged: (BuildContext context, bool value) async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
prefs.setBool(kSharedPrefEnableDarkMode, value);
|
||||
store.dispatch(UserSettingsChanged(enableDarkMode: value));
|
||||
AppBuilder.of(context).rebuild();
|
||||
},
|
||||
onAutoStartTasksChanged: (BuildContext context, bool value) async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
prefs.setBool(kSharedPrefAutoStartTasks, value);
|
||||
store.dispatch(UserSettingsChanged(autoStartTasks: value));
|
||||
AppBuilder.of(context).rebuild();
|
||||
},
|
||||
onRequireAuthenticationChanged: (BuildContext context, bool value) async {
|
||||
bool authenticated = false;
|
||||
try {
|
||||
authenticated = await LocalAuthentication()
|
||||
.authenticateWithBiometrics(
|
||||
localizedReason:
|
||||
AppLocalization.of(context).authenticateToChangeSetting,
|
||||
useErrorDialogs: true,
|
||||
stickyAuth: false);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
if (authenticated) {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
prefs.setBool(kSharedPrefRequireAuthentication, value);
|
||||
store.dispatch(UserSettingsChanged(requireAuthentication: value));
|
||||
} else {}
|
||||
},
|
||||
autoStartTasks: store.state.uiState.autoStartTasks,
|
||||
enableDarkMode: store.state.uiState.enableDarkMode,
|
||||
requireAuthentication: store.state.uiState.requireAuthentication,
|
||||
//authenticationSupported: LocalAuthentication().canCheckBiometrics,
|
||||
// TODO remove this once issue is resolved:
|
||||
// https://github.com/flutter/flutter/issues/24339
|
||||
authenticationSupported: Future<bool>(() async {
|
||||
bool enable = false;
|
||||
try {
|
||||
enable = await LocalAuthentication().canCheckBiometrics;
|
||||
} catch (e) {
|
||||
// do nothing
|
||||
}
|
||||
return enable;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
final AppState state;
|
||||
final Function(BuildContext context) onLogoutTap;
|
||||
final Function(BuildContext context) onRefreshTap;
|
||||
final Function(BuildContext context, bool value) onDarkModeChanged;
|
||||
final Function(BuildContext context, bool value) onAutoStartTasksChanged;
|
||||
final bool enableDarkMode;
|
||||
final bool autoStartTasks;
|
||||
final Function(BuildContext context, bool value)
|
||||
onRequireAuthenticationChanged;
|
||||
final bool requireAuthentication;
|
||||
final Future<bool> authenticationSupported;
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/entities.dart';
|
||||
import 'package:invoiceninja_flutter/ui/settings/settings_list_vm.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
||||
class SettingsList extends StatelessWidget {
|
||||
|
|
@ -15,58 +13,12 @@ class SettingsList extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localization = AppLocalization.of(context);
|
||||
|
||||
return ListView(
|
||||
children: <Widget>[
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalization.of(context).darkMode),
|
||||
value: viewModel.enableDarkMode,
|
||||
onChanged: (value) => viewModel.onDarkModeChanged(context, value),
|
||||
secondary: Icon(FontAwesomeIcons.moon),
|
||||
activeColor: Theme.of(context).accentColor,
|
||||
),
|
||||
FutureBuilder(
|
||||
future: viewModel.authenticationSupported,
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
if (snapshot.hasData && snapshot.data == true) {
|
||||
return SwitchListTile(
|
||||
title:
|
||||
Text(AppLocalization.of(context).biometricAuthentication),
|
||||
value: viewModel.requireAuthentication,
|
||||
onChanged: (value) =>
|
||||
viewModel.onRequireAuthenticationChanged(context, value),
|
||||
secondary: Icon(viewModel.requireAuthentication
|
||||
? FontAwesomeIcons.lock
|
||||
: FontAwesomeIcons.unlockAlt),
|
||||
activeColor: Theme.of(context).accentColor,
|
||||
);
|
||||
} else {
|
||||
return SizedBox();
|
||||
}
|
||||
},
|
||||
),
|
||||
viewModel.state.selectedCompany.isModuleEnabled(EntityType.task)
|
||||
? SwitchListTile(
|
||||
title: Text(AppLocalization.of(context).autoStartTasks),
|
||||
value: viewModel.autoStartTasks,
|
||||
onChanged: (value) =>
|
||||
viewModel.onAutoStartTasksChanged(context, value),
|
||||
secondary: Icon(FontAwesomeIcons.clock),
|
||||
activeColor: Theme.of(context).accentColor,
|
||||
)
|
||||
: SizedBox(),
|
||||
ListTile(
|
||||
leading: Icon(FontAwesomeIcons.syncAlt),
|
||||
title: Text(AppLocalization.of(context).refreshData),
|
||||
onTap: () {
|
||||
viewModel.onRefreshTap(context);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(FontAwesomeIcons.powerOff),
|
||||
title: Text(AppLocalization.of(context).logout),
|
||||
onTap: () {
|
||||
viewModel.onLogoutTap(context);
|
||||
},
|
||||
title: Text(localization.companyDetails),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,12 +6,13 @@ import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
|
|||
import 'package:invoiceninja_flutter/redux/dashboard/dashboard_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/dialogs/loading_dialog.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/app_builder.dart';
|
||||
import 'package:invoiceninja_flutter/ui/settings/settings_list.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
import 'package:invoiceninja_flutter/utils/platforms.dart';
|
||||
import 'package:local_auth/local_auth.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:invoiceninja_flutter/ui/settings/settings_list.dart';
|
||||
import 'package:invoiceninja_flutter/ui/settings/device_settings_list.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja_flutter/redux/auth/auth_actions.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
|
@ -33,120 +34,13 @@ class SettingsListBuilder extends StatelessWidget {
|
|||
class SettingsListVM {
|
||||
SettingsListVM({
|
||||
@required this.state,
|
||||
@required this.onLogoutTap,
|
||||
@required this.onRefreshTap,
|
||||
@required this.onDarkModeChanged,
|
||||
@required this.enableDarkMode,
|
||||
@required this.autoStartTasks,
|
||||
@required this.onAutoStartTasksChanged,
|
||||
@required this.onRequireAuthenticationChanged,
|
||||
@required this.requireAuthentication,
|
||||
@required this.authenticationSupported,
|
||||
});
|
||||
|
||||
static SettingsListVM fromStore(Store<AppState> store) {
|
||||
void _refreshData(BuildContext context) async {
|
||||
final completer = snackBarCompleter(
|
||||
context, AppLocalization.of(context).refreshComplete,
|
||||
shouldPop: true);
|
||||
store.dispatch(RefreshData(
|
||||
platform: getPlatform(context),
|
||||
completer: completer,
|
||||
));
|
||||
await showDialog<AlertDialog>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) => SimpleDialog(
|
||||
children: <Widget>[LoadingDialog()],
|
||||
));
|
||||
AppBuilder.of(context).rebuild();
|
||||
store.dispatch(LoadDashboard());
|
||||
}
|
||||
|
||||
void _confirmLogout(BuildContext context) {
|
||||
final localization = AppLocalization.of(context);
|
||||
showDialog<AlertDialog>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => AlertDialog(
|
||||
semanticLabel: localization.areYouSure,
|
||||
title: Text(localization.areYouSure),
|
||||
actions: <Widget>[
|
||||
new FlatButton(
|
||||
child: Text(localization.cancel.toUpperCase()),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
}),
|
||||
new FlatButton(
|
||||
child: Text(localization.ok.toUpperCase()),
|
||||
onPressed: () {
|
||||
store.dispatch(UserLogout(context));
|
||||
})
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return SettingsListVM(
|
||||
state: store.state,
|
||||
onLogoutTap: (BuildContext context) => _confirmLogout(context),
|
||||
onRefreshTap: (BuildContext context) => _refreshData(context),
|
||||
onDarkModeChanged: (BuildContext context, bool value) async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
prefs.setBool(kSharedPrefEnableDarkMode, value);
|
||||
store.dispatch(UserSettingsChanged(enableDarkMode: value));
|
||||
AppBuilder.of(context).rebuild();
|
||||
},
|
||||
onAutoStartTasksChanged: (BuildContext context, bool value) async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
prefs.setBool(kSharedPrefAutoStartTasks, value);
|
||||
store.dispatch(UserSettingsChanged(autoStartTasks: value));
|
||||
AppBuilder.of(context).rebuild();
|
||||
},
|
||||
onRequireAuthenticationChanged: (BuildContext context, bool value) async {
|
||||
bool authenticated = false;
|
||||
try {
|
||||
authenticated = await LocalAuthentication()
|
||||
.authenticateWithBiometrics(
|
||||
localizedReason:
|
||||
AppLocalization.of(context).authenticateToChangeSetting,
|
||||
useErrorDialogs: true,
|
||||
stickyAuth: false);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
if (authenticated) {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
prefs.setBool(kSharedPrefRequireAuthentication, value);
|
||||
store.dispatch(UserSettingsChanged(requireAuthentication: value));
|
||||
} else {}
|
||||
},
|
||||
autoStartTasks: store.state.uiState.autoStartTasks,
|
||||
enableDarkMode: store.state.uiState.enableDarkMode,
|
||||
requireAuthentication: store.state.uiState.requireAuthentication,
|
||||
//authenticationSupported: LocalAuthentication().canCheckBiometrics,
|
||||
// TODO remove this once issue is resolved:
|
||||
// https://github.com/flutter/flutter/issues/24339
|
||||
authenticationSupported: Future<bool>(() async {
|
||||
bool enable = false;
|
||||
try {
|
||||
enable = await LocalAuthentication().canCheckBiometrics;
|
||||
} catch (e) {
|
||||
// do nothing
|
||||
}
|
||||
return enable;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
final AppState state;
|
||||
final Function(BuildContext context) onLogoutTap;
|
||||
final Function(BuildContext context) onRefreshTap;
|
||||
final Function(BuildContext context, bool value) onDarkModeChanged;
|
||||
final Function(BuildContext context, bool value) onAutoStartTasksChanged;
|
||||
final bool enableDarkMode;
|
||||
final bool autoStartTasks;
|
||||
final Function(BuildContext context, bool value)
|
||||
onRequireAuthenticationChanged;
|
||||
final bool requireAuthentication;
|
||||
final Future<bool> authenticationSupported;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/app_drawer_vm.dart';
|
||||
import 'package:invoiceninja_flutter/ui/settings/device_settings_list_vm.dart';
|
||||
import 'package:invoiceninja_flutter/ui/settings/settings_list_vm.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,20 @@ abstract class LocaleCodeAware {
|
|||
mixin LocalizationsProvider on LocaleCodeAware {
|
||||
static final Map<String, Map<String, String>> _localizedValues = {
|
||||
'en': {
|
||||
'company_details': 'Company Details',
|
||||
'user_details': 'User Details',
|
||||
'localization': 'Localization',
|
||||
'online_payments': 'Online Payments',
|
||||
'tax_rates': 'Tax Rates',
|
||||
'notifications': 'Notifications',
|
||||
'import_export': 'Import | Export',
|
||||
'invoice_settings': 'Invoice Settings',
|
||||
'invoice_design': 'Invoice Design',
|
||||
'buy_now_buttons': 'Buy Now Buttons',
|
||||
'email_settings': 'Email Settings',
|
||||
'templates_and_reminders': 'Templates & Reminders',
|
||||
'credit_cards_and_banks': 'Credit Cards & Banks',
|
||||
'data_visualizations': 'Data Visualizations',
|
||||
'price': 'Price',
|
||||
'email_sign_up': 'Email Sign Up',
|
||||
'google_sign_up': 'Google Sign Up',
|
||||
|
|
@ -14613,6 +14627,38 @@ mixin LocalizationsProvider on LocaleCodeAware {
|
|||
|
||||
String get price => _localizedValues[localeCode]['price'];
|
||||
|
||||
String get companyDetails => _localizedValues[localeCode]['company_details'];
|
||||
|
||||
String get userDetails => _localizedValues[localeCode]['user_details'];
|
||||
|
||||
String get localization => _localizedValues[localeCode]['localization'];
|
||||
|
||||
String get onlinePayments => _localizedValues[localeCode]['online_payments'];
|
||||
|
||||
String get taxRates => _localizedValues[localeCode]['tax_rates'];
|
||||
|
||||
String get notifications => _localizedValues[localeCode]['notifications'];
|
||||
|
||||
String get importExport => _localizedValues[localeCode]['import_export'];
|
||||
|
||||
String get invoiceSettings =>
|
||||
_localizedValues[localeCode]['invoice_settings'];
|
||||
|
||||
String get invoiceDesign => _localizedValues[localeCode]['invoice_design'];
|
||||
|
||||
String get buyNowButtons => _localizedValues[localeCode]['buy_now_buttons'];
|
||||
|
||||
String get emailSettings => _localizedValues[localeCode]['email_settings'];
|
||||
|
||||
String get templatesAndReminders =>
|
||||
_localizedValues[localeCode]['templates_and_reminders'];
|
||||
|
||||
String get creditCardsAndBanks =>
|
||||
_localizedValues[localeCode]['credit_cards_and_banks'];
|
||||
|
||||
String get dataVisualizations =>
|
||||
_localizedValues[localeCode]['data_visualizations'];
|
||||
|
||||
String lookup(String key) {
|
||||
final lookupKey = toSnakeCase(key);
|
||||
return _localizedValues[localeCode][lookupKey] ??
|
||||
|
|
|
|||
Loading…
Reference in New Issue