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

View file

@ -92,20 +92,48 @@ class _ConnectState extends State<Connect> {
} }
Widget _buildContent(BuildContext context) { Widget _buildContent(BuildContext context) {
return Padding( return Center(
padding: const EdgeInsets.symmetric(horizontal: 16), child: Container(
child: Center( constraints: BoxConstraints(
maxWidth: Theme.of(context).widthLimitedContentMaxWidth,
),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const CloudProgressIndicator(size: 192), Expanded(
const SizedBox(height: 16), child: Padding(
Text( padding: const EdgeInsets.symmetric(horizontal: 16),
L10n.global().connectingToServer2, child: Column(
textAlign: TextAlign.center, mainAxisSize: MainAxisSize.min,
style: Theme.of(context).textTheme.titleLarge, 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); launch(state.result.login);
// and start polling the API for login credentials // and start polling the API for login credentials
_bloc.add(AppPasswordExchangeBlocPoll(state.result.poll)); _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) { } else if (state is AppPasswordExchangeBlocFailure) {
if (features.isSupportSelfSignedCert && if (features.isSupportSelfSignedCert &&
state.exception is HandshakeException) { state.exception is HandshakeException) {
@ -146,6 +165,13 @@ class _ConnectState extends State<Connect> {
)); ));
Navigator.of(context).pop(null); 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!);
}
} }
} }