Add a cancel button in Connect

This commit is contained in:
Ming Ming 2022-11-27 15:27:11 +08:00
parent 97cf688359
commit 1bf6dfba5a
2 changed files with 95 additions and 24 deletions

View file

@ -3,7 +3,8 @@ import 'dart:io';
import 'package:bloc/bloc.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/account.dart';
import 'package:nc_photos/ci_string.dart';
import 'package:nc_photos/api/api_util.dart' as api_util;
import 'package:nc_photos/exception.dart';
@ -38,6 +39,13 @@ class AppPasswordExchangeBlocPoll extends AppPasswordExchangeBlocEvent {
final api_util.InitiateLoginPollOptions pollOptions;
}
class AppPasswordExchangeBlocCancel extends AppPasswordExchangeBlocEvent {
const AppPasswordExchangeBlocCancel();
@override
String toString() => "AppPasswordExchangeBlocCancel {}";
}
class _AppPasswordExchangeBlocAppPwReceived
extends AppPasswordExchangeBlocEvent {
const _AppPasswordExchangeBlocAppPwReceived(this.appPasswordResponse);
@ -113,6 +121,17 @@ class AppPasswordExchangeBlocFailure extends AppPasswordExchangeBlocState {
final dynamic exception;
}
class AppPasswordExchangeBlocResult extends AppPasswordExchangeBlocState {
const AppPasswordExchangeBlocResult(this.result);
@override
String toString() => "AppPasswordExchangeBlocResult {"
"result: $result, "
"}";
final Account? result;
}
/// Business Logic Component (BLoC) which handles the App password exchange.
///
/// The flow followed by this component is described in the Nextcloud documentation under
@ -136,6 +155,10 @@ class AppPasswordExchangeBloc
Future<void> _onEvent(AppPasswordExchangeBlocEvent event,
Emitter<AppPasswordExchangeBlocState> emit) async {
_log.info("[_onEvent] $event");
if (_isCanceled) {
_log.fine("[_onEvent] canceled = true, ignore event");
return;
}
if (event is AppPasswordExchangeBlocInitiateLogin) {
await _onEventInitiateLogin(event, emit);
} else if (event is AppPasswordExchangeBlocPoll) {
@ -144,6 +167,8 @@ class AppPasswordExchangeBloc
await _onEventAppPasswordReceived(event, emit);
} else if (event is _AppPasswordExchangeBlocAppPwFailed) {
await _onEventAppPasswordFailure(event, emit);
} else if (event is AppPasswordExchangeBlocCancel) {
await _onEventCancel(event, emit);
}
}
@ -191,7 +216,9 @@ class AppPasswordExchangeBloc
}
} catch (e, stacktrace) {
_log.shout(
"[_pollAppPasswordStreamListener] Failed while polling for password", e, stacktrace);
"[_pollAppPasswordStreamListener] Failed while polling for password",
e,
stacktrace);
add(_AppPasswordExchangeBlocAppPwFailed(e));
}
}
@ -200,7 +227,17 @@ class AppPasswordExchangeBloc
_AppPasswordExchangeBlocAppPwReceived ev,
Emitter<AppPasswordExchangeBlocState> emit) async {
try {
emit(AppPasswordExchangeBlocAppPwSuccess(ev.appPasswordResponse));
final response = ev.appPasswordResponse;
final account = Account(
Account.newId(),
response.server.scheme,
response.server.authority,
response.loginName.toCi(),
response.loginName,
response.appPassword,
[""],
);
emit(AppPasswordExchangeBlocResult(account));
} catch (e, stacktrace) {
_log.shout(
"[_onEventAppPasswordReceived] Failed while exchanging password",
@ -217,6 +254,13 @@ class AppPasswordExchangeBloc
emit(AppPasswordExchangeBlocFailure(ev.exception));
}
Future<void> _onEventCancel(AppPasswordExchangeBlocCancel ev,
Emitter<AppPasswordExchangeBlocState> emit) async {
await _pollPasswordSubscription?.cancel();
_isCanceled = true;
emit(const AppPasswordExchangeBlocResult(null));
}
@override
Future<void> close() {
_pollPasswordSubscription?.cancel();
@ -228,4 +272,5 @@ class AppPasswordExchangeBloc
StreamSubscription<Future<api_util.AppPasswordResponse>>?
_pollPasswordSubscription;
bool _isCanceled = false;
}

View file

@ -92,20 +92,48 @@ class _ConnectState extends State<Connect> {
}
Widget _buildContent(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Center(
return Center(
child: Container(
constraints: BoxConstraints(
maxWidth: Theme.of(context).widthLimitedContentMaxWidth,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CloudProgressIndicator(size: 192),
const SizedBox(height: 16),
Text(
L10n.global().connectingToServer2,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge,
)
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CloudProgressIndicator(size: 192),
const SizedBox(height: 16),
Text(
L10n.global().connectingToServer2,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge,
),
],
),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
TextButton(
onPressed: () {
_bloc.add(const AppPasswordExchangeBlocCancel());
},
child: Text(
MaterialLocalizations.of(context).cancelButtonLabel,
style: const TextStyle(color: Colors.white),
),
),
],
),
),
],
),
),
@ -119,15 +147,6 @@ class _ConnectState extends State<Connect> {
launch(state.result.login);
// and start polling the API for login credentials
_bloc.add(AppPasswordExchangeBlocPoll(state.result.poll));
} else if (state is AppPasswordExchangeBlocAppPwSuccess) {
final newAccount = Account(
Account.newId(),
state.result.server.scheme,
state.result.server.authority,
state.result.loginName.toCi(),
state.result.loginName,
state.result.appPassword, []);
_checkWebDavUrl(context, newAccount);
} else if (state is AppPasswordExchangeBlocFailure) {
if (features.isSupportSelfSignedCert &&
state.exception is HandshakeException) {
@ -146,6 +165,13 @@ class _ConnectState extends State<Connect> {
));
Navigator.of(context).pop(null);
}
} else if (state is AppPasswordExchangeBlocResult) {
if (state.result == null) {
// user canceled
Navigator.of(context).pop(null);
} else {
_checkWebDavUrl(context, state.result!);
}
}
}