Add session timeout

This commit is contained in:
Hillel Coren 2021-02-21 23:38:15 +02:00
parent 66591fd895
commit 1cb9bbb79d
5 changed files with 341 additions and 258 deletions

View File

@ -14,6 +14,7 @@ import 'package:invoiceninja_flutter/ui/app/app_builder.dart';
import 'package:invoiceninja_flutter/ui/app/change_layout_banner.dart'; import 'package:invoiceninja_flutter/ui/app/change_layout_banner.dart';
import 'package:invoiceninja_flutter/ui/app/main_screen.dart'; import 'package:invoiceninja_flutter/ui/app/main_screen.dart';
import 'package:invoiceninja_flutter/ui/app/screen_imports.dart'; import 'package:invoiceninja_flutter/ui/app/screen_imports.dart';
import 'package:invoiceninja_flutter/ui/app/session_timeout.dart';
import 'package:invoiceninja_flutter/ui/auth/init_screen.dart'; import 'package:invoiceninja_flutter/ui/auth/init_screen.dart';
import 'package:invoiceninja_flutter/ui/auth/lock_screen.dart'; import 'package:invoiceninja_flutter/ui/auth/lock_screen.dart';
import 'package:invoiceninja_flutter/ui/auth/login_vm.dart'; import 'package:invoiceninja_flutter/ui/auth/login_vm.dart';
@ -168,267 +169,283 @@ class InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return StoreProvider<AppState>( return StoreProvider<AppState>(
store: widget.store, store: widget.store,
child: AppBuilder(builder: (context) { child: SessionTimeout(
final store = widget.store; child: AppBuilder(builder: (context) {
final state = store.state; final store = widget.store;
final hasAccentColor = state.hasAccentColor; final state = store.state;
final accentColor = state.accentColor; final hasAccentColor = state.hasAccentColor;
const fontFamily = kIsWeb ? 'Roboto' : null; final accentColor = state.accentColor;
final pageTransitionsTheme = PageTransitionsTheme(builders: { const fontFamily = kIsWeb ? 'Roboto' : null;
TargetPlatform.android: ZoomPageTransitionsBuilder(), final pageTransitionsTheme = PageTransitionsTheme(builders: {
}); TargetPlatform.android: ZoomPageTransitionsBuilder(),
Intl.defaultLocale = localeSelector(state); });
final locale = AppLocalization.createLocale(localeSelector(state)); Intl.defaultLocale = localeSelector(state);
final locale = AppLocalization.createLocale(localeSelector(state));
return StyledToast( return StyledToast(
locale: locale,
duration: Duration(seconds: 3),
backgroundColor:
state.prefState.enableDarkMode ? Colors.white : Colors.black,
textStyle: TextStyle(
color:
state.prefState.enableDarkMode ? Colors.black87 : Colors.white,
),
child: MaterialApp(
supportedLocales: kLanguages
.map((String locale) => AppLocalization.createLocale(locale))
.toList(),
//debugShowCheckedModeBanner: false,
//showPerformanceOverlay: true,
navigatorObservers: [
SentryNavigatorObserver(),
],
localizationsDelegates: [
const AppLocalizationsDelegate(),
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalMaterialLocalizations.delegate
],
home: state.prefState.requireAuthentication && !_authenticated
? LockScreen(onAuthenticatePressed: _authenticate)
: InitScreen(),
locale: locale, locale: locale,
theme: state.prefState.enableDarkMode duration: Duration(seconds: 3),
? ThemeData( backgroundColor:
pageTransitionsTheme: pageTransitionsTheme, state.prefState.enableDarkMode ? Colors.white : Colors.black,
brightness: Brightness.dark, textStyle: TextStyle(
accentColor: accentColor, color: state.prefState.enableDarkMode
indicatorColor: accentColor, ? Colors.black87
textSelectionTheme: TextSelectionThemeData( : Colors.white,
selectionHandleColor: accentColor, ),
), child: MaterialApp(
fontFamily: fontFamily, supportedLocales: kLanguages
backgroundColor: Colors.black, .map((String locale) => AppLocalization.createLocale(locale))
canvasColor: Colors.black, .toList(),
cardColor: const Color(0xFF1B1C1E), //debugShowCheckedModeBanner: false,
bottomAppBarColor: const Color(0xFF1B1C1E), //showPerformanceOverlay: true,
primaryColorDark: Colors.black, navigatorObservers: [
buttonColor: accentColor, SentryNavigatorObserver(),
) ],
: ThemeData( localizationsDelegates: [
pageTransitionsTheme: pageTransitionsTheme, const AppLocalizationsDelegate(),
primaryColor: accentColor, GlobalCupertinoLocalizations.delegate,
accentColor: accentColor, GlobalWidgetsLocalizations.delegate,
indicatorColor: accentColor, GlobalMaterialLocalizations.delegate
textSelectionTheme: TextSelectionThemeData( ],
selectionColor: accentColor, home: state.prefState.requireAuthentication && !_authenticated
), ? LockScreen(onAuthenticatePressed: _authenticate)
fontFamily: fontFamily, : InitScreen(),
backgroundColor: Colors.white, locale: locale,
canvasColor: Colors.white, theme: state.prefState.enableDarkMode
cardColor: Colors.white, ? ThemeData(
bottomAppBarColor: Colors.white, pageTransitionsTheme: pageTransitionsTheme,
primaryColorDark: brightness: Brightness.dark,
hasAccentColor ? accentColor : const Color(0xFF0D5D91), accentColor: accentColor,
primaryColorLight: indicatorColor: accentColor,
hasAccentColor ? accentColor : const Color(0xFF5dabf4), textSelectionTheme: TextSelectionThemeData(
buttonColor: selectionHandleColor: accentColor,
hasAccentColor ? accentColor : const Color(0xFF0D5D91),
scaffoldBackgroundColor: const Color(0xFFE7EBEE),
tabBarTheme: TabBarTheme(
labelColor: hasAccentColor ? Colors.white : Colors.black,
unselectedLabelColor: hasAccentColor
? Colors.white.withOpacity(.65)
: Colors.black.withOpacity(.65),
),
/*
buttonTheme: ButtonThemeData(
textTheme: ButtonTextTheme.primary,
colorScheme: ColorScheme.light(
//primary: hasAccentColor ? Colors.white : Colors.black,
), ),
), fontFamily: fontFamily,
*/ backgroundColor: Colors.black,
iconTheme: IconThemeData( canvasColor: Colors.black,
color: hasAccentColor ? null : accentColor, cardColor: const Color(0xFF1B1C1E),
), bottomAppBarColor: const Color(0xFF1B1C1E),
appBarTheme: AppBarTheme( primaryColorDark: Colors.black,
brightness: Brightness.light, buttonColor: accentColor,
color: hasAccentColor ? accentColor : Colors.white, )
iconTheme: IconThemeData( : ThemeData(
color: hasAccentColor ? Colors.white : accentColor, pageTransitionsTheme: pageTransitionsTheme,
primaryColor: accentColor,
accentColor: accentColor,
indicatorColor: accentColor,
textSelectionTheme: TextSelectionThemeData(
selectionColor: accentColor,
), ),
textTheme: TextTheme( fontFamily: fontFamily,
headline6: Theme.of(context) backgroundColor: Colors.white,
.textTheme canvasColor: Colors.white,
.headline6 cardColor: Colors.white,
.copyWith( bottomAppBarColor: Colors.white,
color: primaryColorDark: hasAccentColor
hasAccentColor ? Colors.white : Colors.black, ? accentColor
), : const Color(0xFF0D5D91),
primaryColorLight: hasAccentColor
? accentColor
: const Color(0xFF5dabf4),
buttonColor: hasAccentColor
? accentColor
: const Color(0xFF0D5D91),
scaffoldBackgroundColor: const Color(0xFFE7EBEE),
tabBarTheme: TabBarTheme(
labelColor:
hasAccentColor ? Colors.white : Colors.black,
unselectedLabelColor: hasAccentColor
? Colors.white.withOpacity(.65)
: Colors.black.withOpacity(.65),
), ),
), /*
), buttonTheme: ButtonThemeData(
title: kAppName, textTheme: ButtonTextTheme.primary,
onGenerateRoute: isMobile(context) ? null : generateRoute, colorScheme: ColorScheme.light(
routes: isMobile(context) //primary: hasAccentColor ? Colors.white : Colors.black,
? {
LoginScreen.route: (context) => LoginScreen(),
MainScreen.route: (context) => MainScreen(),
DashboardScreenBuilder.route: (context) =>
ChangeLayoutBanner(
suggestedLayout: AppLayout.mobile,
appLayout: state.prefState.appLayout,
child: DashboardScreenBuilder(),
), ),
ProductScreen.route: (context) => ProductScreenBuilder(), ),
ProductViewScreen.route: (context) => ProductViewScreen(), */
ProductEditScreen.route: (context) => ProductEditScreen(), iconTheme: IconThemeData(
ClientScreen.route: (context) => ClientScreenBuilder(), color: hasAccentColor ? null : accentColor,
ClientViewScreen.route: (context) => ClientViewScreen(), ),
ClientEditScreen.route: (context) => ClientEditScreen(), appBarTheme: AppBarTheme(
InvoiceScreen.route: (context) => InvoiceScreenBuilder(), brightness: Brightness.light,
InvoiceViewScreen.route: (context) => InvoiceViewScreen(), color: hasAccentColor ? accentColor : Colors.white,
InvoiceEditScreen.route: (context) => InvoiceEditScreen(), iconTheme: IconThemeData(
InvoiceEmailScreen.route: (context) => InvoiceEmailScreen(), color: hasAccentColor ? Colors.white : accentColor,
InvoicePdfScreen.route: (context) => InvoicePdfScreen(), ),
DocumentScreen.route: (context) => DocumentScreenBuilder(), textTheme: TextTheme(
DocumentViewScreen.route: (context) => DocumentViewScreen(), headline6:
DocumentEditScreen.route: (context) => DocumentEditScreen(), Theme.of(context).textTheme.headline6.copyWith(
ExpenseScreen.route: (context) => ExpenseScreenBuilder(), color: hasAccentColor
ExpenseViewScreen.route: (context) => ExpenseViewScreen(), ? Colors.white
ExpenseEditScreen.route: (context) => ExpenseEditScreen(), : Colors.black,
VendorScreen.route: (context) => VendorScreenBuilder(), ),
VendorViewScreen.route: (context) => VendorViewScreen(), ),
VendorEditScreen.route: (context) => VendorEditScreen(), ),
TaskScreen.route: (context) => TaskScreenBuilder(), ),
TaskViewScreen.route: (context) => TaskViewScreen(), title: kAppName,
TaskEditScreen.route: (context) => TaskEditScreen(), onGenerateRoute: isMobile(context) ? null : generateRoute,
ProjectScreen.route: (context) => ProjectScreenBuilder(), routes: isMobile(context)
ProjectViewScreen.route: (context) => ProjectViewScreen(), ? {
ProjectEditScreen.route: (context) => ProjectEditScreen(), LoginScreen.route: (context) => LoginScreen(),
PaymentScreen.route: (context) => PaymentScreenBuilder(), MainScreen.route: (context) => MainScreen(),
PaymentViewScreen.route: (context) => PaymentViewScreen(), DashboardScreenBuilder.route: (context) =>
PaymentEditScreen.route: (context) => PaymentEditScreen(), ChangeLayoutBanner(
PaymentRefundScreen.route: (context) => suggestedLayout: AppLayout.mobile,
PaymentRefundScreen(), appLayout: state.prefState.appLayout,
QuoteScreen.route: (context) => QuoteScreenBuilder(), child: DashboardScreenBuilder(),
QuoteViewScreen.route: (context) => QuoteViewScreen(), ),
QuoteEditScreen.route: (context) => QuoteEditScreen(), ProductScreen.route: (context) => ProductScreenBuilder(),
QuoteEmailScreen.route: (context) => QuoteEmailScreen(), ProductViewScreen.route: (context) => ProductViewScreen(),
QuotePdfScreen.route: (context) => QuotePdfScreen(), ProductEditScreen.route: (context) => ProductEditScreen(),
// STARTER: routes - do not remove comment ClientScreen.route: (context) => ClientScreenBuilder(),
TaskStatusScreen.route: (context) => ClientViewScreen.route: (context) => ClientViewScreen(),
TaskStatusScreenBuilder(), ClientEditScreen.route: (context) => ClientEditScreen(),
TaskStatusViewScreen.route: (context) => InvoiceScreen.route: (context) => InvoiceScreenBuilder(),
TaskStatusViewScreen(), InvoiceViewScreen.route: (context) => InvoiceViewScreen(),
TaskStatusEditScreen.route: (context) => InvoiceEditScreen.route: (context) => InvoiceEditScreen(),
TaskStatusEditScreen(), InvoiceEmailScreen.route: (context) =>
ExpenseCategoryScreen.route: (context) => InvoiceEmailScreen(),
ExpenseCategoryScreenBuilder(), InvoicePdfScreen.route: (context) => InvoicePdfScreen(),
ExpenseCategoryViewScreen.route: (context) => DocumentScreen.route: (context) =>
ExpenseCategoryViewScreen(), DocumentScreenBuilder(),
ExpenseCategoryEditScreen.route: (context) => DocumentViewScreen.route: (context) =>
ExpenseCategoryEditScreen(), DocumentViewScreen(),
RecurringInvoiceScreen.route: (context) => DocumentEditScreen.route: (context) =>
RecurringInvoiceScreenBuilder(), DocumentEditScreen(),
RecurringInvoiceViewScreen.route: (context) => ExpenseScreen.route: (context) => ExpenseScreenBuilder(),
RecurringInvoiceViewScreen(), ExpenseViewScreen.route: (context) => ExpenseViewScreen(),
RecurringInvoiceEditScreen.route: (context) => ExpenseEditScreen.route: (context) => ExpenseEditScreen(),
RecurringInvoiceEditScreen(), VendorScreen.route: (context) => VendorScreenBuilder(),
RecurringInvoicePdfScreen.route: (context) => VendorViewScreen.route: (context) => VendorViewScreen(),
RecurringInvoicePdfScreen(), VendorEditScreen.route: (context) => VendorEditScreen(),
WebhookScreen.route: (context) => WebhookScreenBuilder(), TaskScreen.route: (context) => TaskScreenBuilder(),
WebhookViewScreen.route: (context) => WebhookViewScreen(), TaskViewScreen.route: (context) => TaskViewScreen(),
WebhookEditScreen.route: (context) => WebhookEditScreen(), TaskEditScreen.route: (context) => TaskEditScreen(),
TokenScreen.route: (context) => TokenScreenBuilder(), ProjectScreen.route: (context) => ProjectScreenBuilder(),
TokenViewScreen.route: (context) => TokenViewScreen(), ProjectViewScreen.route: (context) => ProjectViewScreen(),
TokenEditScreen.route: (context) => TokenEditScreen(), ProjectEditScreen.route: (context) => ProjectEditScreen(),
PaymentTermScreen.route: (context) => PaymentScreen.route: (context) => PaymentScreenBuilder(),
PaymentTermScreenBuilder(), PaymentViewScreen.route: (context) => PaymentViewScreen(),
PaymentTermEditScreen.route: (context) => PaymentEditScreen.route: (context) => PaymentEditScreen(),
PaymentTermEditScreen(), PaymentRefundScreen.route: (context) =>
PaymentTermViewScreen.route: (context) => PaymentRefundScreen(),
PaymentTermViewScreen(), QuoteScreen.route: (context) => QuoteScreenBuilder(),
DesignScreen.route: (context) => DesignScreenBuilder(), QuoteViewScreen.route: (context) => QuoteViewScreen(),
DesignViewScreen.route: (context) => DesignViewScreen(), QuoteEditScreen.route: (context) => QuoteEditScreen(),
DesignEditScreen.route: (context) => DesignEditScreen(), QuoteEmailScreen.route: (context) => QuoteEmailScreen(),
CreditScreen.route: (context) => CreditScreenBuilder(), QuotePdfScreen.route: (context) => QuotePdfScreen(),
CreditViewScreen.route: (context) => CreditViewScreen(), // STARTER: routes - do not remove comment
CreditEditScreen.route: (context) => CreditEditScreen(), TaskStatusScreen.route: (context) =>
CreditEmailScreen.route: (context) => CreditEmailScreen(), TaskStatusScreenBuilder(),
CreditPdfScreen.route: (context) => CreditPdfScreen(), TaskStatusViewScreen.route: (context) =>
UserScreen.route: (context) => UserScreenBuilder(), TaskStatusViewScreen(),
UserViewScreen.route: (context) => UserViewScreen(), TaskStatusEditScreen.route: (context) =>
UserEditScreen.route: (context) => UserEditScreen(), TaskStatusEditScreen(),
GroupSettingsScreen.route: (context) => ExpenseCategoryScreen.route: (context) =>
GroupScreenBuilder(), ExpenseCategoryScreenBuilder(),
GroupViewScreen.route: (context) => GroupViewScreen(), ExpenseCategoryViewScreen.route: (context) =>
GroupEditScreen.route: (context) => GroupEditScreen(), ExpenseCategoryViewScreen(),
SettingsScreen.route: (context) => SettingsScreenBuilder(), ExpenseCategoryEditScreen.route: (context) =>
ReportsScreen.route: (context) => ReportsScreenBuilder(), ExpenseCategoryEditScreen(),
CompanyDetailsScreen.route: (context) => RecurringInvoiceScreen.route: (context) =>
CompanyDetailsScreen(), RecurringInvoiceScreenBuilder(),
UserDetailsScreen.route: (context) => UserDetailsScreen(), RecurringInvoiceViewScreen.route: (context) =>
LocalizationScreen.route: (context) => LocalizationScreen(), RecurringInvoiceViewScreen(),
OnlinePaymentsScreen.route: (context) => RecurringInvoiceEditScreen.route: (context) =>
OnlinePaymentsScreen(), RecurringInvoiceEditScreen(),
CompanyGatewayScreen.route: (context) => RecurringInvoicePdfScreen.route: (context) =>
CompanyGatewayScreenBuilder(), RecurringInvoicePdfScreen(),
CompanyGatewayViewScreen.route: (context) => WebhookScreen.route: (context) => WebhookScreenBuilder(),
CompanyGatewayViewScreen(), WebhookViewScreen.route: (context) => WebhookViewScreen(),
CompanyGatewayEditScreen.route: (context) => WebhookEditScreen.route: (context) => WebhookEditScreen(),
CompanyGatewayEditScreen(), TokenScreen.route: (context) => TokenScreenBuilder(),
TaxSettingsScreen.route: (context) => TaxSettingsScreen(), TokenViewScreen.route: (context) => TokenViewScreen(),
TaxRateSettingsScreen.route: (context) => TokenEditScreen.route: (context) => TokenEditScreen(),
TaxRateScreenBuilder(), PaymentTermScreen.route: (context) =>
TaxRateViewScreen.route: (context) => TaxRateViewScreen(), PaymentTermScreenBuilder(),
TaxRateEditScreen.route: (context) => TaxRateEditScreen(), PaymentTermEditScreen.route: (context) =>
ProductSettingsScreen.route: (context) => PaymentTermEditScreen(),
ProductSettingsScreen(), PaymentTermViewScreen.route: (context) =>
ExpenseSettingsScreen.route: (context) => PaymentTermViewScreen(),
ExpenseSettingsScreen(), DesignScreen.route: (context) => DesignScreenBuilder(),
TaskSettingsScreen.route: (context) => TaskSettingsScreen(), DesignViewScreen.route: (context) => DesignViewScreen(),
IntegrationSettingsScreen.route: (context) => DesignEditScreen.route: (context) => DesignEditScreen(),
IntegrationSettingsScreen(), CreditScreen.route: (context) => CreditScreenBuilder(),
ImportExportScreen.route: (context) => ImportExportScreen(), CreditViewScreen.route: (context) => CreditViewScreen(),
DeviceSettingsScreen.route: (context) => CreditEditScreen.route: (context) => CreditEditScreen(),
DeviceSettingsScreen(), CreditEmailScreen.route: (context) => CreditEmailScreen(),
AccountManagementScreen.route: (context) => CreditPdfScreen.route: (context) => CreditPdfScreen(),
AccountManagementScreen(), UserScreen.route: (context) => UserScreenBuilder(),
CustomFieldsScreen.route: (context) => CustomFieldsScreen(), UserViewScreen.route: (context) => UserViewScreen(),
GeneratedNumbersScreen.route: (context) => UserEditScreen.route: (context) => UserEditScreen(),
GeneratedNumbersScreen(), GroupSettingsScreen.route: (context) =>
WorkflowSettingsScreen.route: (context) => GroupScreenBuilder(),
WorkflowSettingsScreen(), GroupViewScreen.route: (context) => GroupViewScreen(),
InvoiceDesignScreen.route: (context) => GroupEditScreen.route: (context) => GroupEditScreen(),
InvoiceDesignScreen(), SettingsScreen.route: (context) =>
ClientPortalScreen.route: (context) => ClientPortalScreen(), SettingsScreenBuilder(),
BuyNowButtonsScreen.route: (context) => ReportsScreen.route: (context) => ReportsScreenBuilder(),
BuyNowButtonsScreen(), CompanyDetailsScreen.route: (context) =>
EmailSettingsScreen.route: (context) => CompanyDetailsScreen(),
EmailSettingsScreen(), UserDetailsScreen.route: (context) => UserDetailsScreen(),
TemplatesAndRemindersScreen.route: (context) => LocalizationScreen.route: (context) =>
TemplatesAndRemindersScreen(), LocalizationScreen(),
CreditCardsAndBanksScreen.route: (context) => OnlinePaymentsScreen.route: (context) =>
CreditCardsAndBanksScreen(), OnlinePaymentsScreen(),
DataVisualizationsScreen.route: (context) => CompanyGatewayScreen.route: (context) =>
DataVisualizationsScreen(), CompanyGatewayScreenBuilder(),
} CompanyGatewayViewScreen.route: (context) =>
: {}, CompanyGatewayViewScreen(),
), CompanyGatewayEditScreen.route: (context) =>
); CompanyGatewayEditScreen(),
}), TaxSettingsScreen.route: (context) => TaxSettingsScreen(),
TaxRateSettingsScreen.route: (context) =>
TaxRateScreenBuilder(),
TaxRateViewScreen.route: (context) => TaxRateViewScreen(),
TaxRateEditScreen.route: (context) => TaxRateEditScreen(),
ProductSettingsScreen.route: (context) =>
ProductSettingsScreen(),
ExpenseSettingsScreen.route: (context) =>
ExpenseSettingsScreen(),
TaskSettingsScreen.route: (context) =>
TaskSettingsScreen(),
IntegrationSettingsScreen.route: (context) =>
IntegrationSettingsScreen(),
ImportExportScreen.route: (context) =>
ImportExportScreen(),
DeviceSettingsScreen.route: (context) =>
DeviceSettingsScreen(),
AccountManagementScreen.route: (context) =>
AccountManagementScreen(),
CustomFieldsScreen.route: (context) =>
CustomFieldsScreen(),
GeneratedNumbersScreen.route: (context) =>
GeneratedNumbersScreen(),
WorkflowSettingsScreen.route: (context) =>
WorkflowSettingsScreen(),
InvoiceDesignScreen.route: (context) =>
InvoiceDesignScreen(),
ClientPortalScreen.route: (context) =>
ClientPortalScreen(),
BuyNowButtonsScreen.route: (context) =>
BuyNowButtonsScreen(),
EmailSettingsScreen.route: (context) =>
EmailSettingsScreen(),
TemplatesAndRemindersScreen.route: (context) =>
TemplatesAndRemindersScreen(),
CreditCardsAndBanksScreen.route: (context) =>
CreditCardsAndBanksScreen(),
DataVisualizationsScreen.route: (context) =>
DataVisualizationsScreen(),
}
: {},
),
);
}),
),
); );
} }
} }

View File

@ -95,9 +95,10 @@ class RecoverPasswordFailure implements StopLoading {
} }
class UserLogout implements PersistData, PersistUI { class UserLogout implements PersistData, PersistUI {
UserLogout(this.context); UserLogout(this.context, {this.navigate = true});
final BuildContext context; final BuildContext context;
final bool navigate;
} }
class UserSignUpRequest implements StartLoading { class UserSignUpRequest implements StartLoading {

View File

@ -56,8 +56,10 @@ Middleware<AppState> _createUserLogout() {
next(action); next(action);
Navigator.of(action.context).pushNamedAndRemoveUntil( if (action.navigate) {
LoginScreen.route, (Route<dynamic> route) => false); Navigator.of(action.context).pushNamedAndRemoveUntil(
LoginScreen.route, (Route<dynamic> route) => false);
}
store.dispatch(UpdateCurrentRoute(LoginScreen.route)); store.dispatch(UpdateCurrentRoute(LoginScreen.route));
}; };

View File

@ -0,0 +1,59 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/redux/auth/auth_actions.dart';
import 'package:invoiceninja_flutter/utils/web_stub.dart'
if (dart.library.html) 'package:invoiceninja_flutter/utils/web.dart';
class SessionTimeout extends StatefulWidget {
const SessionTimeout({this.child});
final Widget child;
@override
_SessionTimeoutState createState() => _SessionTimeoutState();
}
class _SessionTimeoutState extends State<SessionTimeout> {
Timer _timer;
@override
void initState() {
super.initState();
if (!kIsWeb) {
return;
}
_timer = Timer.periodic(
Duration(seconds: 1),
(Timer timer) {
final store = StoreProvider.of<AppState>(context);
final state = store.state;
final sessionTimeout = state.company.sessionTimeout;
final sessionLength = DateTime.now().millisecondsSinceEpoch -
state.userCompanyState.lastUpdated;
print('## Timeout: $sessionTimeout, Length: $sessionLength');
if (sessionTimeout != 0 && sessionLength > sessionTimeout) {
store.dispatch(UserLogout(context, navigate: false));
WebUtils.reloadBrowser();
}
},
);
}
@override
void dispose() {
_timer?.cancel();
_timer = null;
super.dispose();
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}

View File

@ -136,6 +136,10 @@ class _AccountManagementState extends State<AccountManagement>
child: Text(localization.never), child: Text(localization.never),
value: 0, value: 0,
), ),
DropdownMenuItem<int>(
child: Text('1 minute'),
value: 1000 * 60,
),
DropdownMenuItem<int>( DropdownMenuItem<int>(
child: Text(localization.countHours child: Text(localization.countHours
.replaceFirst(':count', '8')), .replaceFirst(':count', '8')),