// Flutter imports: import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; // Package imports: import 'package:built_collection/built_collection.dart'; import 'package:flutter_redux/flutter_redux.dart'; import 'package:local_auth/local_auth.dart'; import 'package:redux/redux.dart'; // Project imports: import 'package:invoiceninja_flutter/constants.dart'; import 'package:invoiceninja_flutter/redux/app/app_actions.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/redux/auth/auth_actions.dart'; import 'package:invoiceninja_flutter/redux/dashboard/dashboard_actions.dart'; import 'package:invoiceninja_flutter/redux/settings/settings_actions.dart'; import 'package:invoiceninja_flutter/redux/ui/pref_state.dart'; import 'package:invoiceninja_flutter/ui/app/app_builder.dart'; import 'package:invoiceninja_flutter/ui/settings/device_settings.dart'; import 'package:invoiceninja_flutter/utils/completers.dart'; import 'package:invoiceninja_flutter/utils/dialogs.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; class DeviceSettingsScreen extends StatelessWidget { const DeviceSettingsScreen({Key key}) : super(key: key); static const String route = '/$kSettings/$kSettingsDeviceSettings'; @override Widget build(BuildContext context) { return StoreConnector( converter: DeviceSettingsVM.fromStore, builder: (context, viewModel) { return DeviceSettings(viewModel: viewModel); }, ); } } class DeviceSettingsVM { DeviceSettingsVM({ @required this.state, @required this.onRefreshTap, @required this.onLogoutTap, @required this.onDarkModeChanged, @required this.onLayoutChanged, @required this.onRequireAuthenticationChanged, @required this.onLongPressSelectionIsDefault, @required this.authenticationSupported, @required this.onMenuModeChanged, @required this.onHistoryModeChanged, @required this.onColorThemeChanged, @required this.onCustomColorsChanged, @required this.onPersistDataChanged, @required this.onPersistUiChanged, @required this.onShowPdfChanged, @required this.onEnableNativeBrowserChanged, @required this.onShowPdfSideBySideChanged, @required this.onTapSelectedChanged, @required this.onTextScaleFactorChanged, @required this.onEditAfterSavingChanged, @required this.onEnableTouchEventsChanged, @required this.onEnableTooltipsChanged, @required this.onEnableFlexibleSearchChanged, }); static DeviceSettingsVM fromStore(Store store) { return DeviceSettingsVM( state: store.state, onRefreshTap: (BuildContext context) => showRefreshDataDialog(context: context, includeStatic: true), onLogoutTap: (BuildContext context) { final completer = snackBarCompleter( context, AppLocalization.of(context).endedAllSessions); store.dispatch(UserLogoutAll(completer: completer)); }, onDarkModeChanged: (BuildContext context, bool value) async { store.dispatch(UpdateUserPreferences( enableDarkMode: value, colorTheme: value ? kColorThemeDark : kColorThemeLight, customColors: value ? BuiltMap() : BuiltMap(PrefState.CONTRAST_COLORS))); store.dispatch(UpdatedSetting()); AppBuilder.of(context).rebuild(); }, onLongPressSelectionIsDefault: (BuildContext context, bool value) async { store.dispatch( UpdateUserPreferences(longPressSelectionIsDefault: value)); }, onMenuModeChanged: (context, value) async { if (store.state.prefState.menuSidebarMode == value) { return; } store.dispatch(UpdateUserPreferences(menuMode: value)); }, onHistoryModeChanged: (context, value) async { if (store.state.prefState.historySidebarMode == value) { return; } store.dispatch(UpdateUserPreferences(historyMode: value)); }, onTapSelectedChanged: (context, value) async { store.dispatch(UpdateUserPreferences(tapSelectedToEdit: value)); }, onEnableTouchEventsChanged: (context, value) async { store.dispatch(UpdateUserPreferences(enableTouchEvents: value)); store.dispatch(UpdatedSetting()); AppBuilder.of(context).rebuild(); }, onShowPdfChanged: (context, value) { store.dispatch(UpdateUserPreferences(showPdfPreview: value)); }, onEnableNativeBrowserChanged: (context, value) { store.dispatch(UpdateUserPreferences(enableNativeBrowser: value)); }, onShowPdfSideBySideChanged: (context, value) { store.dispatch(UpdateUserPreferences(showPdfPreviewSideBySide: value)); }, onTextScaleFactorChanged: (context, value) { store.dispatch(UpdateUserPreferences(textScaleFactor: value)); }, onEnableTooltipsChanged: (context, value) { store.dispatch(UpdateUserPreferences(enableTooltips: value)); }, onEnableFlexibleSearchChanged: (context, value) { store.dispatch(UpdateUserPreferences(flexibleSearch: value)); }, onColorThemeChanged: (context, value) async { if (store.state.prefState.colorTheme != value) { store.dispatch(UpdateUserPreferences(colorTheme: value)); } }, onEditAfterSavingChanged: (context, value) async { store.dispatch(UpdateUserPreferences(editAfterSaving: value)); }, onLayoutChanged: (BuildContext context, AppLayout value) async { if (store.state.prefState.appLayout == value) { return; } store.dispatch(UpdateUserPreferences(appLayout: value)); AppBuilder.of(context).rebuild(); WidgetsBinding.instance.addPostFrameCallback((duration) { if (value == AppLayout.mobile) { store.dispatch(ViewDashboard()); } else { store.dispatch(ViewMainScreen(addDelay: true)); } }); }, onRequireAuthenticationChanged: (BuildContext context, bool value) async { bool authenticated = false; try { authenticated = await LocalAuthentication().authenticate( localizedReason: AppLocalization.of(context).authenticateToChangeSetting, options: const AuthenticationOptions( biometricOnly: true, useErrorDialogs: true, stickyAuth: false)); } catch (e) { print(e); } if (authenticated) { store.dispatch(UpdateUserPreferences(requireAuthentication: value)); } else {} }, //authenticationSupported: LocalAuthentication().canCheckBiometrics, // TODO remove this once issue is resolved: // https://github.com/flutter/flutter/issues/24339 authenticationSupported: Future( () async { bool enable = false; try { enable = await LocalAuthentication().canCheckBiometrics; } catch (e) { // do nothing } return enable; }, ), onCustomColorsChanged: (context, customColors) { store.dispatch(UpdateUserPreferences(customColors: customColors)); store.dispatch(UpdatedSetting()); }, onPersistDataChanged: (context, value) { store.dispatch(UpdateUserPreferences(persistData: value)); if (value) { store.dispatch(UserLoginSuccess()); } else { store.dispatch(ClearPersistedData()); } }, onPersistUiChanged: (context, value) { store.dispatch(UpdateUserPreferences(persistUi: value)); if (value) { store.dispatch(PersistUI()); } }, ); } final AppState state; final Function(BuildContext) onRefreshTap; final Function(BuildContext) onLogoutTap; final Function(BuildContext, bool) onDarkModeChanged; final Function(BuildContext, BuiltMap) onCustomColorsChanged; final Function(BuildContext, AppLayout) onLayoutChanged; final Function(BuildContext, AppSidebarMode) onMenuModeChanged; final Function(BuildContext, AppSidebarMode) onHistoryModeChanged; final Function(BuildContext, String) onColorThemeChanged; final Function(BuildContext, bool) onLongPressSelectionIsDefault; final Function(BuildContext, bool) onTapSelectedChanged; final Function(BuildContext, bool) onEditAfterSavingChanged; final Function(BuildContext, bool) onRequireAuthenticationChanged; final Function(BuildContext, bool) onPersistDataChanged; final Function(BuildContext, bool) onPersistUiChanged; final Function(BuildContext, bool) onShowPdfChanged; final Function(BuildContext, bool) onEnableNativeBrowserChanged; final Function(BuildContext, bool) onShowPdfSideBySideChanged; final Function(BuildContext, bool) onEnableTouchEventsChanged; final Function(BuildContext, bool) onEnableTooltipsChanged; final Function(BuildContext, bool) onEnableFlexibleSearchChanged; final Function(BuildContext, double) onTextScaleFactorChanged; final Future authenticationSupported; }