Add session timeout
This commit is contained in:
parent
66591fd895
commit
1cb9bbb79d
|
|
@ -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,6 +169,7 @@ 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: SessionTimeout(
|
||||||
child: AppBuilder(builder: (context) {
|
child: AppBuilder(builder: (context) {
|
||||||
final store = widget.store;
|
final store = widget.store;
|
||||||
final state = store.state;
|
final state = store.state;
|
||||||
|
|
@ -186,8 +188,9 @@ class InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
state.prefState.enableDarkMode ? Colors.white : Colors.black,
|
state.prefState.enableDarkMode ? Colors.white : Colors.black,
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
color:
|
color: state.prefState.enableDarkMode
|
||||||
state.prefState.enableDarkMode ? Colors.black87 : Colors.white,
|
? Colors.black87
|
||||||
|
: Colors.white,
|
||||||
),
|
),
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
supportedLocales: kLanguages
|
supportedLocales: kLanguages
|
||||||
|
|
@ -238,15 +241,19 @@ class InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
|
||||||
canvasColor: Colors.white,
|
canvasColor: Colors.white,
|
||||||
cardColor: Colors.white,
|
cardColor: Colors.white,
|
||||||
bottomAppBarColor: Colors.white,
|
bottomAppBarColor: Colors.white,
|
||||||
primaryColorDark:
|
primaryColorDark: hasAccentColor
|
||||||
hasAccentColor ? accentColor : const Color(0xFF0D5D91),
|
? accentColor
|
||||||
primaryColorLight:
|
: const Color(0xFF0D5D91),
|
||||||
hasAccentColor ? accentColor : const Color(0xFF5dabf4),
|
primaryColorLight: hasAccentColor
|
||||||
buttonColor:
|
? accentColor
|
||||||
hasAccentColor ? accentColor : const Color(0xFF0D5D91),
|
: const Color(0xFF5dabf4),
|
||||||
|
buttonColor: hasAccentColor
|
||||||
|
? accentColor
|
||||||
|
: const Color(0xFF0D5D91),
|
||||||
scaffoldBackgroundColor: const Color(0xFFE7EBEE),
|
scaffoldBackgroundColor: const Color(0xFFE7EBEE),
|
||||||
tabBarTheme: TabBarTheme(
|
tabBarTheme: TabBarTheme(
|
||||||
labelColor: hasAccentColor ? Colors.white : Colors.black,
|
labelColor:
|
||||||
|
hasAccentColor ? Colors.white : Colors.black,
|
||||||
unselectedLabelColor: hasAccentColor
|
unselectedLabelColor: hasAccentColor
|
||||||
? Colors.white.withOpacity(.65)
|
? Colors.white.withOpacity(.65)
|
||||||
: Colors.black.withOpacity(.65),
|
: Colors.black.withOpacity(.65),
|
||||||
|
|
@ -269,12 +276,11 @@ class InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
|
||||||
color: hasAccentColor ? Colors.white : accentColor,
|
color: hasAccentColor ? Colors.white : accentColor,
|
||||||
),
|
),
|
||||||
textTheme: TextTheme(
|
textTheme: TextTheme(
|
||||||
headline6: Theme.of(context)
|
headline6:
|
||||||
.textTheme
|
Theme.of(context).textTheme.headline6.copyWith(
|
||||||
.headline6
|
color: hasAccentColor
|
||||||
.copyWith(
|
? Colors.white
|
||||||
color:
|
: Colors.black,
|
||||||
hasAccentColor ? Colors.white : Colors.black,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -300,11 +306,15 @@ class InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
|
||||||
InvoiceScreen.route: (context) => InvoiceScreenBuilder(),
|
InvoiceScreen.route: (context) => InvoiceScreenBuilder(),
|
||||||
InvoiceViewScreen.route: (context) => InvoiceViewScreen(),
|
InvoiceViewScreen.route: (context) => InvoiceViewScreen(),
|
||||||
InvoiceEditScreen.route: (context) => InvoiceEditScreen(),
|
InvoiceEditScreen.route: (context) => InvoiceEditScreen(),
|
||||||
InvoiceEmailScreen.route: (context) => InvoiceEmailScreen(),
|
InvoiceEmailScreen.route: (context) =>
|
||||||
|
InvoiceEmailScreen(),
|
||||||
InvoicePdfScreen.route: (context) => InvoicePdfScreen(),
|
InvoicePdfScreen.route: (context) => InvoicePdfScreen(),
|
||||||
DocumentScreen.route: (context) => DocumentScreenBuilder(),
|
DocumentScreen.route: (context) =>
|
||||||
DocumentViewScreen.route: (context) => DocumentViewScreen(),
|
DocumentScreenBuilder(),
|
||||||
DocumentEditScreen.route: (context) => DocumentEditScreen(),
|
DocumentViewScreen.route: (context) =>
|
||||||
|
DocumentViewScreen(),
|
||||||
|
DocumentEditScreen.route: (context) =>
|
||||||
|
DocumentEditScreen(),
|
||||||
ExpenseScreen.route: (context) => ExpenseScreenBuilder(),
|
ExpenseScreen.route: (context) => ExpenseScreenBuilder(),
|
||||||
ExpenseViewScreen.route: (context) => ExpenseViewScreen(),
|
ExpenseViewScreen.route: (context) => ExpenseViewScreen(),
|
||||||
ExpenseEditScreen.route: (context) => ExpenseEditScreen(),
|
ExpenseEditScreen.route: (context) => ExpenseEditScreen(),
|
||||||
|
|
@ -375,12 +385,14 @@ class InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
|
||||||
GroupScreenBuilder(),
|
GroupScreenBuilder(),
|
||||||
GroupViewScreen.route: (context) => GroupViewScreen(),
|
GroupViewScreen.route: (context) => GroupViewScreen(),
|
||||||
GroupEditScreen.route: (context) => GroupEditScreen(),
|
GroupEditScreen.route: (context) => GroupEditScreen(),
|
||||||
SettingsScreen.route: (context) => SettingsScreenBuilder(),
|
SettingsScreen.route: (context) =>
|
||||||
|
SettingsScreenBuilder(),
|
||||||
ReportsScreen.route: (context) => ReportsScreenBuilder(),
|
ReportsScreen.route: (context) => ReportsScreenBuilder(),
|
||||||
CompanyDetailsScreen.route: (context) =>
|
CompanyDetailsScreen.route: (context) =>
|
||||||
CompanyDetailsScreen(),
|
CompanyDetailsScreen(),
|
||||||
UserDetailsScreen.route: (context) => UserDetailsScreen(),
|
UserDetailsScreen.route: (context) => UserDetailsScreen(),
|
||||||
LocalizationScreen.route: (context) => LocalizationScreen(),
|
LocalizationScreen.route: (context) =>
|
||||||
|
LocalizationScreen(),
|
||||||
OnlinePaymentsScreen.route: (context) =>
|
OnlinePaymentsScreen.route: (context) =>
|
||||||
OnlinePaymentsScreen(),
|
OnlinePaymentsScreen(),
|
||||||
CompanyGatewayScreen.route: (context) =>
|
CompanyGatewayScreen.route: (context) =>
|
||||||
|
|
@ -398,22 +410,26 @@ class InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
|
||||||
ProductSettingsScreen(),
|
ProductSettingsScreen(),
|
||||||
ExpenseSettingsScreen.route: (context) =>
|
ExpenseSettingsScreen.route: (context) =>
|
||||||
ExpenseSettingsScreen(),
|
ExpenseSettingsScreen(),
|
||||||
TaskSettingsScreen.route: (context) => TaskSettingsScreen(),
|
TaskSettingsScreen.route: (context) =>
|
||||||
|
TaskSettingsScreen(),
|
||||||
IntegrationSettingsScreen.route: (context) =>
|
IntegrationSettingsScreen.route: (context) =>
|
||||||
IntegrationSettingsScreen(),
|
IntegrationSettingsScreen(),
|
||||||
ImportExportScreen.route: (context) => ImportExportScreen(),
|
ImportExportScreen.route: (context) =>
|
||||||
|
ImportExportScreen(),
|
||||||
DeviceSettingsScreen.route: (context) =>
|
DeviceSettingsScreen.route: (context) =>
|
||||||
DeviceSettingsScreen(),
|
DeviceSettingsScreen(),
|
||||||
AccountManagementScreen.route: (context) =>
|
AccountManagementScreen.route: (context) =>
|
||||||
AccountManagementScreen(),
|
AccountManagementScreen(),
|
||||||
CustomFieldsScreen.route: (context) => CustomFieldsScreen(),
|
CustomFieldsScreen.route: (context) =>
|
||||||
|
CustomFieldsScreen(),
|
||||||
GeneratedNumbersScreen.route: (context) =>
|
GeneratedNumbersScreen.route: (context) =>
|
||||||
GeneratedNumbersScreen(),
|
GeneratedNumbersScreen(),
|
||||||
WorkflowSettingsScreen.route: (context) =>
|
WorkflowSettingsScreen.route: (context) =>
|
||||||
WorkflowSettingsScreen(),
|
WorkflowSettingsScreen(),
|
||||||
InvoiceDesignScreen.route: (context) =>
|
InvoiceDesignScreen.route: (context) =>
|
||||||
InvoiceDesignScreen(),
|
InvoiceDesignScreen(),
|
||||||
ClientPortalScreen.route: (context) => ClientPortalScreen(),
|
ClientPortalScreen.route: (context) =>
|
||||||
|
ClientPortalScreen(),
|
||||||
BuyNowButtonsScreen.route: (context) =>
|
BuyNowButtonsScreen.route: (context) =>
|
||||||
BuyNowButtonsScreen(),
|
BuyNowButtonsScreen(),
|
||||||
EmailSettingsScreen.route: (context) =>
|
EmailSettingsScreen.route: (context) =>
|
||||||
|
|
@ -429,6 +445,7 @@ class InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,10 @@ Middleware<AppState> _createUserLogout() {
|
||||||
|
|
||||||
next(action);
|
next(action);
|
||||||
|
|
||||||
|
if (action.navigate) {
|
||||||
Navigator.of(action.context).pushNamedAndRemoveUntil(
|
Navigator.of(action.context).pushNamedAndRemoveUntil(
|
||||||
LoginScreen.route, (Route<dynamic> route) => false);
|
LoginScreen.route, (Route<dynamic> route) => false);
|
||||||
|
}
|
||||||
|
|
||||||
store.dispatch(UpdateCurrentRoute(LoginScreen.route));
|
store.dispatch(UpdateCurrentRoute(LoginScreen.route));
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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')),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue