Skip to content
This repository was archived by the owner on Sep 14, 2024. It is now read-only.

Commit d587391

Browse files
committed
use LoginFlowV2
1 parent 6521a3a commit d587391

16 files changed

+319
-591
lines changed

android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ android {
3636
defaultConfig {
3737
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
3838
applicationId "com.nextcloud_cookbook_flutter"
39-
minSdkVersion 18
39+
minSdkVersion 19
4040
targetSdkVersion flutter.targetSdkVersion
4141
versionCode flutterVersionCode.toInteger()
4242
versionName flutterVersionName

assets/icon.svg.vec

1.13 KB
Binary file not shown.

lib/main.dart

Lines changed: 18 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -109,58 +109,29 @@ class _AppState extends State<App> {
109109
case AuthenticationStatus.loading:
110110
return Container();
111111
case AuthenticationStatus.authenticated:
112-
return FutureBuilder(
113-
future: UserRepository().fetchApiVersion(),
114-
builder: (context, snapshot) {
115-
if (!snapshot.hasData) {
116-
return Container();
117-
}
118-
119-
if (snapshot.hasError) {
120-
ScaffoldMessenger.of(context).showSnackBar(
121-
SnackBar(
122-
content: Text(
123-
translate(
124-
'categories.errors.api_version_check_failed',
125-
args: {'error_msg': snapshot.error},
126-
),
127-
style: TextStyle(
128-
color: Theme.of(context).colorScheme.onError,
129-
),
130-
),
131-
backgroundColor: Theme.of(context).colorScheme.error,
132-
),
133-
);
134-
}
135-
136-
if (!UserRepository().isVersionSupported(snapshot.data!)) {
137-
ScaffoldMessenger.of(context).showSnackBar(
138-
SnackBar(
139-
content: Text(
140-
translate(
141-
'categories.errors.api_version_above_confirmed',
142-
args: {
143-
'version':
144-
'${snapshot.data!.major}.${snapshot.data!.minor}'
145-
},
146-
),
147-
),
148-
backgroundColor: Colors.orange,
112+
if (!UserRepository().isVersionSupported(state.apiVersion!)) {
113+
ScaffoldMessenger.of(context).showSnackBar(
114+
SnackBar(
115+
content: Text(
116+
translate(
117+
'categories.errors.api_version_above_confirmed',
118+
args: {
119+
'version':
120+
'${state.apiVersion!.major}.${state.apiVersion!.minor}'
121+
},
149122
),
150-
);
151-
}
123+
),
124+
backgroundColor: Colors.orange,
125+
),
126+
);
127+
}
152128

153-
IntentRepository().handleIntent();
154-
return const MainScreen();
155-
},
156-
);
129+
IntentRepository().handleIntent();
130+
return const MainScreen();
157131

158132
case AuthenticationStatus.unauthenticated:
159-
return const LoginScreen();
133+
return const LoginPage();
160134
case AuthenticationStatus.invalid:
161-
return const LoginScreen(
162-
invalidCredentials: true,
163-
);
164135
case AuthenticationStatus.error:
165136
return const LoadingErrorScreen();
166137
}
Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import 'package:equatable/equatable.dart';
22
import 'package:flutter_bloc/flutter_bloc.dart';
3+
import 'package:nc_cookbook_api/nc_cookbook_api.dart';
34
import 'package:nextcloud_cookbook_flutter/src/models/app_authentication.dart';
45
import 'package:nextcloud_cookbook_flutter/src/services/services.dart';
56

67
part 'authentication_event.dart';
8+
part 'authentication_exception.dart';
79
part 'authentication_state.dart';
810

911
class AuthenticationBloc
1012
extends Bloc<AuthenticationEvent, AuthenticationState> {
11-
AuthenticationBloc() : super(AuthenticationState()) {
13+
AuthenticationBloc() : super(const AuthenticationState()) {
1214
on<AppStarted>(_mapAppStartedEventToState);
1315
on<LoggedIn>(_mapLoggedInEventToState);
1416
on<LoggedOut>(_mapLoggedOutEventToState);
@@ -19,19 +21,28 @@ class AuthenticationBloc
1921
AppStarted event,
2022
Emitter<AuthenticationState> emit,
2123
) async {
22-
final hasToken = await userRepository.hasAppAuthentication();
23-
24-
if (hasToken) {
25-
await userRepository.loadAppAuthentication();
24+
if (userRepository.hasAuthentidation) {
2625
try {
26+
await userRepository.loadAppAuthentication();
27+
2728
final validCredentials = await userRepository.checkAppAuthentication();
2829

2930
if (validCredentials) {
30-
emit(AuthenticationState(status: AuthenticationStatus.authenticated));
31+
final apiVersion = await UserRepository().fetchApiVersion();
32+
emit(
33+
AuthenticationState(
34+
status: AuthenticationStatus.authenticated,
35+
apiVersion: apiVersion,
36+
),
37+
);
3138
} else {
3239
await userRepository.deleteAppAuthentication();
33-
emit(AuthenticationState(status: AuthenticationStatus.invalid));
40+
emit(const AuthenticationState(status: AuthenticationStatus.invalid));
3441
}
42+
} on LoadAuthException {
43+
emit(
44+
const AuthenticationState(status: AuthenticationStatus.authenticated),
45+
);
3546
} catch (e) {
3647
emit(
3748
AuthenticationState(
@@ -41,25 +52,50 @@ class AuthenticationBloc
4152
);
4253
}
4354
} else {
44-
emit(AuthenticationState(status: AuthenticationStatus.unauthenticated));
55+
emit(
56+
const AuthenticationState(
57+
status: AuthenticationStatus.unauthenticated,
58+
),
59+
);
4560
}
4661
}
4762

4863
Future<void> _mapLoggedInEventToState(
4964
LoggedIn event,
5065
Emitter<AuthenticationState> emit,
5166
) async {
52-
emit(AuthenticationState());
53-
await userRepository.persistAppAuthentication(event.appAuthentication);
54-
emit(AuthenticationState(status: AuthenticationStatus.authenticated));
67+
emit(const AuthenticationState());
68+
try {
69+
await userRepository.persistAppAuthentication(event.appAuthentication);
70+
71+
final apiVersion = await UserRepository().fetchApiVersion();
72+
emit(
73+
AuthenticationState(
74+
status: AuthenticationStatus.authenticated,
75+
apiVersion: apiVersion,
76+
),
77+
);
78+
} catch (e) {
79+
emit(
80+
AuthenticationState(
81+
status: AuthenticationStatus.error,
82+
error: e.toString(),
83+
),
84+
);
85+
}
5586
}
5687

5788
Future<void> _mapLoggedOutEventToState(
5889
LoggedOut event,
5990
Emitter<AuthenticationState> emit,
6091
) async {
61-
emit(AuthenticationState());
62-
await userRepository.deleteAppAuthentication();
63-
emit(AuthenticationState(status: AuthenticationStatus.unauthenticated));
92+
emit(const AuthenticationState());
93+
try {
94+
await userRepository.deleteAppAuthentication();
95+
} finally {
96+
emit(
97+
const AuthenticationState(status: AuthenticationStatus.unauthenticated),
98+
);
99+
}
64100
}
65101
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
part of 'authentication_bloc.dart';
2+
3+
abstract class AuthException implements Exception {}
4+
5+
class LoadAuthException extends AuthException {}

lib/src/blocs/authentication/authentication_state.dart

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,17 @@ class AuthenticationState extends Equatable {
2525
const AuthenticationState({
2626
this.status = AuthenticationStatus.loading,
2727
this.error,
28-
}) : assert(
29-
(status != AuthenticationStatus.error && error == null) ||
30-
(status == AuthenticationStatus.error && error != null),
28+
this.apiVersion,
29+
}) : assert(error == null || status == AuthenticationStatus.error),
30+
assert(
31+
apiVersion == null || status == AuthenticationStatus.authenticated,
3132
);
3233
final AuthenticationStatus status;
3334
final String? error;
3435

36+
/// The [APIVersion] authenticated against
37+
final APIVersion? apiVersion;
38+
3539
@override
36-
List<Object?> get props => [status, error];
40+
List<Object?> get props => [status, error, apiVersion];
3741
}
Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
import 'dart:async';
2+
13
import 'package:equatable/equatable.dart';
4+
import 'package:flutter/foundation.dart';
25
import 'package:flutter_bloc/flutter_bloc.dart';
6+
import 'package:nextcloud/nextcloud.dart';
37
import 'package:nextcloud_cookbook_flutter/src/blocs/authentication/authentication_bloc.dart';
8+
import 'package:nextcloud_cookbook_flutter/src/models/app_authentication.dart';
49
import 'package:nextcloud_cookbook_flutter/src/services/services.dart';
510
import 'package:nextcloud_cookbook_flutter/src/util/url_validator.dart';
611

@@ -10,37 +15,43 @@ part 'login_state.dart';
1015
class LoginBloc extends Bloc<LoginEvent, LoginState> {
1116
LoginBloc({
1217
required this.authenticationBloc,
13-
}) : super(LoginState()) {
14-
on<LoginButtonPressed>(_mapLoginButtonPressedEventToState);
18+
}) : super(const LoginState()) {
19+
on<LoginFlowStart>(_mapLoginFlowStartEventToState);
1520
}
1621
final UserRepository userRepository = UserRepository();
1722
final AuthenticationBloc authenticationBloc;
1823

19-
Future<void> _mapLoginButtonPressedEventToState(
20-
LoginButtonPressed event,
24+
Future<void> _mapLoginFlowStartEventToState(
25+
LoginFlowStart event,
2126
Emitter<LoginState> emit,
2227
) async {
23-
emit(LoginState(status: LoginStatus.loading));
24-
28+
assert(URLUtils.isSanitized(event.serverURL));
2529
try {
26-
assert(URLUtils.isSanitized(event.serverURL));
27-
28-
final appAuthentication = await userRepository.authenticateAppPassword(
29-
event.serverURL,
30-
event.username,
31-
event.appPassword,
32-
isSelfSignedCertificate: event.isSelfSignedCertificate,
33-
);
30+
final client = NextcloudClient(event.serverURL);
31+
final init = await client.core.initLoginFlow();
32+
emit(LoginState(status: LoginStatus.loading, url: init.login));
33+
Timer.periodic(const Duration(seconds: 2), (timer) async {
34+
try {
35+
final result =
36+
await client.core.getLoginFlowResult(token: init.poll.token);
37+
timer.cancel();
3438

35-
authenticationBloc.add(LoggedIn(appAuthentication: appAuthentication));
36-
emit(LoginState());
37-
} catch (error) {
38-
emit(
39-
LoginState(
40-
status: LoginStatus.failure,
41-
error: error.toString(),
42-
),
43-
);
39+
authenticationBloc.add(
40+
LoggedIn(
41+
appAuthentication: AppAuthentication(
42+
server: result.server,
43+
loginName: result.loginName,
44+
appPassword: result.appPassword,
45+
isSelfSignedCertificate: false,
46+
),
47+
),
48+
);
49+
} catch (e) {
50+
debugPrint(e.toString());
51+
}
52+
});
53+
} catch (e) {
54+
emit(LoginState(status: LoginStatus.failure, error: e.toString()));
4455
}
4556
}
4657
}

lib/src/blocs/login/login_event.dart

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,8 @@ abstract class LoginEvent extends Equatable {
77
List<Object> get props => [];
88
}
99

10-
class LoginButtonPressed extends LoginEvent {
11-
const LoginButtonPressed({
12-
required this.serverURL,
13-
required this.username,
14-
required this.appPassword,
15-
required this.isAppPassword,
16-
required this.isSelfSignedCertificate,
17-
});
18-
final String serverURL;
19-
final String username;
20-
final String appPassword;
21-
final bool isAppPassword;
22-
final bool isSelfSignedCertificate;
23-
24-
@override
25-
List<Object> get props =>
26-
[serverURL, username, isAppPassword, isSelfSignedCertificate];
10+
class LoginFlowStart extends LoginEvent {
11+
const LoginFlowStart(this.serverURL);
2712

28-
@override
29-
String toString() =>
30-
'LoginButtonPressed {serverURL: $serverURL, username: $username, isAppPassword: $isAppPassword}, isSelfSignedCertificate: $isSelfSignedCertificate';
13+
final String serverURL;
3114
}

lib/src/blocs/login/login_state.dart

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@ class LoginState extends Equatable {
1010
const LoginState({
1111
this.status = LoginStatus.initial,
1212
this.error,
13-
}) : assert(
14-
(status != LoginStatus.failure && error == null) ||
15-
(status == LoginStatus.failure && error != null),
16-
);
13+
this.url,
14+
}) : assert(error == null || status == LoginStatus.failure);
1715
final LoginStatus status;
1816
final String? error;
17+
final String? url;
1918

2019
@override
2120
List<Object?> get props => [status, error];

0 commit comments

Comments
 (0)