Skip to content

Commit

Permalink
feat: forceLogout flow, cupertino adjustments for hp, inital expenses
Browse files Browse the repository at this point in the history
  • Loading branch information
Jan Czizikow committed Apr 19, 2020
1 parent 4f2813e commit c6c2103
Show file tree
Hide file tree
Showing 22 changed files with 1,586 additions and 428 deletions.
133 changes: 108 additions & 25 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,25 @@ import 'package:provider/provider.dart';
import 'package:sentry/sentry.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sliceit/providers/invites.dart';
import 'package:tuple/tuple.dart';

import './services/api.dart';
import './providers/account.dart';
import './providers/auth.dart';
import './providers/groups.dart';
import './providers/theme.dart';
import './screens/edit_email.dart';
import './screens/edit_name.dart';
import './providers/expenses.dart';
import './screens/root.dart';
import './screens/forgot_password.dart';
import './screens/group.dart';
import './screens/group_invites.dart';
import './screens/login.dart';
import './screens/register.dart';
import './screens/root.dart';
import './screens/settings.dart';
import './services/api.dart';
import './screens/edit_email.dart';
import './screens/edit_name.dart';
import './screens/new_payment.dart';
import './screens/new_expense.dart';
import './widgets/no_animation_material_page_route.dart';

Future<Null> main() async {
Expand Down Expand Up @@ -68,7 +72,7 @@ class MyApp extends StatefulWidget {
}

class _MyAppState extends State<MyApp> {
final Api _api = Api();
final navigatorKey = GlobalKey<NavigatorState>();
ThemeProvider themeProvider;

@override
Expand All @@ -83,6 +87,29 @@ class _MyAppState extends State<MyApp> {
themeProvider.themeType = preferredTheme;
}

Future<void> _showForceLogoutDialog() async {
final context = navigatorKey.currentState.overlay.context;
Navigator.pushNamedAndRemoveUntil(context, '/', (_) => false);
Provider.of<Api>(context, listen: false).setForceLogoutTimestamp(null);
await showPlatformDialog(
context: context,
androidBarrierDismissible: false,
builder: (_) {
return PlatformAlertDialog(
title: const Text('Unauthorized'),
content: const Text('Session expired, you will be logged out now'),
actions: <Widget>[
PlatformDialogAction(
child: const Text('OK'),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
}

Route _generateRoute(RouteSettings settings) {
switch (settings.name) {
case Root.routeName:
Expand Down Expand Up @@ -127,8 +154,21 @@ class _MyAppState extends State<MyApp> {
builder: (context) => GroupInvitesScreen(
groupId: settings.arguments,
),
fullscreenDialog: true,
settings: settings,
);
case NewPaymentScreen.routeName:
return platformPageRoute(
context: context,
builder: (context) => NewPaymentScreen(),
fullscreenDialog: true,
);
case NewExpenseScreen.routeName:
return platformPageRoute(
context: context,
builder: (context) => NewExpenseScreen(),
fullscreenDialog: true,
);
case SettingsScreen.routeName:
return platformPageRoute(
context: context,
Expand Down Expand Up @@ -157,34 +197,77 @@ class _MyAppState extends State<MyApp> {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => Auth(_api),
),
ChangeNotifierProvider(
create: (_) => AccountProvider(_api),
create: (_) => Api(),
),
ChangeNotifierProvider(
create: (_) => themeProvider,
),
ChangeNotifierProxyProvider<Auth, GroupsProvider>(
create: (_) => GroupsProvider(api: _api, isAuthenticated: false),
update: (_, auth, previous) => GroupsProvider(
api: _api,
isAuthenticated: auth.isAuthenticated,
),
ChangeNotifierProxyProvider<Api, Auth>(
create: (_) => Auth()..restoreTokens(),
update: (_, api, auth) {
if (api.forceLogoutTimestamp() != null) {
auth.logout();
}
return auth;
},
),
ChangeNotifierProxyProvider<Api, AccountProvider>(
create: (_) => AccountProvider(),
update: (_, api, account) {
if (api.forceLogoutTimestamp() != null) {
account.reset();
}
return account;
},
),
ChangeNotifierProxyProvider2<Auth, Api, GroupsProvider>(
create: (_) => GroupsProvider(),
update: (_, auth, api, groups) {
if (api.forceLogoutTimestamp() != null) {
groups.reset();
}
groups.isAuthenticated = auth.isAuthenticated;
return groups;
}),
ChangeNotifierProxyProvider<Api, InvitesProvider>(
create: (_) => InvitesProvider(),
update: (_, api, invites) {
if (api.forceLogoutTimestamp() != null) {
invites.reset();
}
return invites;
},
),
ChangeNotifierProvider(
create: (_) => InvitesProvider(_api),
create: (_) => ExpensesProvider(),
),
],
child: Consumer<ThemeProvider>(
builder: (_, theme, __) => PlatformApp(
title: 'Sliceit',
initialRoute: Root.routeName,
android: (_) => MaterialAppData(
theme: theme.currentTheme,
),
onGenerateRoute: _generateRoute,
),
child: Selector2<ThemeProvider, Api, Tuple2<ThemeData, int>>(
selector: (
_,
theme,
api,
) =>
Tuple2(theme.currentTheme, api.forceLogoutTimestamp()),
builder: (_, data, __) {
if (data.item2 != null) {
_showForceLogoutDialog();
}
return PlatformApp(
title: 'Sliceit',
initialRoute: Root.routeName,
navigatorKey: navigatorKey,
ios: (_) => CupertinoAppData(
theme: CupertinoThemeData(
brightness: data.item1.brightness,
),
),
android: (_) => MaterialAppData(
theme: data.item1,
),
onGenerateRoute: _generateRoute,
);
},
),
);
}
Expand Down
20 changes: 19 additions & 1 deletion lib/models/expense.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import 'package:flutter/foundation.dart';
class Expense {
final String id;
final String name;
final double amount;
final int amount;
final String currency;
final DateTime date;
final bool isPayment;
final String payerId;
final String groupId;
final DateTime createdAt;
final DateTime updatedAt;

Expand All @@ -17,9 +18,26 @@ class Expense {
@required this.amount,
@required this.currency,
@required this.payerId,
@required this.groupId,
@required this.createdAt,
@required this.date,
this.updatedAt,
this.isPayment = false,
});

factory Expense.fromJson(Map<String, dynamic> json) {
return Expense(
id: json['id'],
name: json['name'],
amount: json['amount'],
currency: json['currency'],
payerId: json['payerId'],
groupId: json['groupId'],
isPayment: json['isPayment'],
date: DateTime.parse(json['date']),
createdAt: DateTime.parse(json['createdAt']),
updatedAt:
json['updatedAt'] != null ? DateTime.parse(json['updatedAt']) : null,
);
}
}
5 changes: 5 additions & 0 deletions lib/models/group.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/foundation.dart';
import 'package:provider/provider.dart';

import './member.dart';

Expand Down Expand Up @@ -46,4 +47,8 @@ class Group {
json.map<Group>((json) => Group.fromJson(json)).toList();
return result;
}

String memberFirstNameByUserId(String userId) {
return members.firstWhere((member) => member.userId == userId).firstName;
}
}
6 changes: 6 additions & 0 deletions lib/models/invite.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,10 @@ class Invite {
json['updatedAt'] != null ? DateTime.parse(json['updatedAt']) : null,
);
}

@override
int get hashCode => id.hashCode;

@override
bool operator ==(Object other) => other is Invite && other.id == id;
}
2 changes: 1 addition & 1 deletion lib/models/member.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Member {

factory Member.fromJson(Map<String, dynamic> json) {
return Member(
id: json['user']['id'],
id: json['id'],
userId: json['user']['id'],
groupId: json['groupId'],
firstName: json['user']['firstName'],
Expand Down
18 changes: 10 additions & 8 deletions lib/providers/account.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ import '../models/account.dart';
import '../services/api.dart';

class AccountProvider extends BaseProvider {
final Api api;
final Api _api = Api();
Account _account;

AccountProvider(this.api);

Account get account => _account;

String get fullName => account?.fullName ?? '';
Expand All @@ -17,14 +15,14 @@ class AccountProvider extends BaseProvider {
bool get hasAvatar => account?.avatar != null;

Future<void> fetchAccount() async {
Account account = await api.fetchAccount();
Account account = await _api.fetchAccount();
_account = account;
notifyListeners();
}

Future<void> updateAccount(
{String email, String firstName, String lastName}) async {
Account account = await api.updateAccount(
Account account = await _api.updateAccount(
email: email ?? _account.email,
firstName: firstName ?? _account.firstName,
lastName: lastName ?? _account.lastName,
Expand All @@ -37,7 +35,7 @@ class AccountProvider extends BaseProvider {
status = Status.PENDING;

try {
final response = await api.uploadAvatar(path);
final response = await _api.uploadAvatar(path);
if (response['status']) {
_account.avatar = response['data']['url'];
status = Status.RESOLVED;
Expand All @@ -50,14 +48,18 @@ class AccountProvider extends BaseProvider {
}

Future<void> removeAvatar() async {
await api.removeAvatar();
await _api.removeAvatar();
_account.avatar = null;
notifyListeners();
}

Future<void> deleteAccount() async {
await api.deleteAccount();
await _api.deleteAccount();
_account = null;
notifyListeners();
}

void reset() {
_account = null;
}
}
17 changes: 8 additions & 9 deletions lib/providers/auth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,17 @@ import '../utils/constants.dart';

class Auth extends BaseProvider {
final _storage = FlutterSecureStorage();
final Api api;
final Api _api = Api();
String _accessToken;

Auth(this.api);

get isAuthenticated => _accessToken != null;
get isFetching => status == Status.PENDING;
bool get isAuthenticated => _accessToken != null;
bool get isFetching => status == Status.PENDING;

Future<void> restoreTokens() async {
String accessToken = await _storage.read(key: ACCESS_TOKEN_KEY);

if (accessToken != _accessToken) {
api.accessToken = accessToken;
_api.accessToken = accessToken;
_accessToken = accessToken;
notifyListeners();
}
Expand All @@ -29,7 +28,7 @@ class Auth extends BaseProvider {
status = Status.PENDING;

try {
final res = await api.login(email, password);
final res = await _api.login(email, password);
_accessToken = res['accessToken'];
status = Status.RESOLVED;
} catch (err) {
Expand All @@ -47,7 +46,7 @@ class Auth extends BaseProvider {
status = Status.PENDING;

try {
final res = await api.register(
final res = await _api.register(
firstName: firstName,
lastName: lastName,
email: email,
Expand All @@ -64,7 +63,7 @@ class Auth extends BaseProvider {
Future<void> logout() async {
await _storage.deleteAll();
_accessToken = null;
api.accessToken = null;
_api.accessToken = null;
status = Status.IDLE;
}
}
Loading

0 comments on commit c6c2103

Please sign in to comment.