mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-11-27 14:59:29 +01:00
feat: Nicer registration form
This commit is contained in:
parent
bc78647fb6
commit
b48cf2ecdc
@ -220,7 +220,10 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void signUpAction() => VRouter.of(context).to('signup');
|
void signUpAction() => VRouter.of(context).to(
|
||||||
|
'signup',
|
||||||
|
queryParameters: {'domain': domain},
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
|
import 'package:vrouter/vrouter.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/pages/views/signup_view.dart';
|
import 'package:fluffychat/pages/views/signup_view.dart';
|
||||||
import 'package:fluffychat/utils/platform_infos.dart';
|
import 'package:fluffychat/utils/platform_infos.dart';
|
||||||
@ -17,29 +18,78 @@ class SignupPage extends StatefulWidget {
|
|||||||
class SignupPageController extends State<SignupPage> {
|
class SignupPageController extends State<SignupPage> {
|
||||||
final TextEditingController usernameController = TextEditingController();
|
final TextEditingController usernameController = TextEditingController();
|
||||||
final TextEditingController passwordController = TextEditingController();
|
final TextEditingController passwordController = TextEditingController();
|
||||||
String usernameError;
|
final TextEditingController passwordController2 = TextEditingController();
|
||||||
String passwordError;
|
final TextEditingController emailController = TextEditingController();
|
||||||
|
String error;
|
||||||
bool loading = false;
|
bool loading = false;
|
||||||
bool showPassword = true;
|
bool showPassword = false;
|
||||||
|
|
||||||
void toggleShowPassword() => setState(() => showPassword = !showPassword);
|
void toggleShowPassword() => setState(() => showPassword = !showPassword);
|
||||||
|
|
||||||
|
String get domain => VRouter.of(context).queryParameters['domain'];
|
||||||
|
|
||||||
|
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
|
String usernameTextFieldValidator(String value) {
|
||||||
|
usernameController.text =
|
||||||
|
usernameController.text.trim().toLowerCase().replaceAll(' ', '_');
|
||||||
|
if (value.isEmpty) {
|
||||||
|
return L10n.of(context).pleaseChooseAUsername;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String password1TextFieldValidator(String value) {
|
||||||
|
const minLength = 8;
|
||||||
|
if (value.isEmpty) {
|
||||||
|
return L10n.of(context).chooseAStrongPassword;
|
||||||
|
}
|
||||||
|
if (value.length < minLength) {
|
||||||
|
return 'Please choose at least $minLength characters.';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String password2TextFieldValidator(String value) {
|
||||||
|
if (value.isEmpty) {
|
||||||
|
return L10n.of(context).chooseAStrongPassword;
|
||||||
|
}
|
||||||
|
if (value != passwordController.text) {
|
||||||
|
return 'Passwords do not match!';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String emailTextFieldValidator(String value) {
|
||||||
|
if (value.isNotEmpty && !value.contains('@')) {
|
||||||
|
return 'Please enter a valid email address.';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
void signup([_]) async {
|
void signup([_]) async {
|
||||||
usernameError = passwordError = null;
|
setState(() {
|
||||||
|
error = null;
|
||||||
|
});
|
||||||
|
if (!formKey.currentState.validate()) return;
|
||||||
|
|
||||||
if (usernameController.text.isEmpty) {
|
setState(() {
|
||||||
return setState(
|
loading = true;
|
||||||
() => usernameError = L10n.of(context).pleaseChooseAUsername);
|
});
|
||||||
}
|
|
||||||
if (passwordController.text.isEmpty) {
|
|
||||||
return setState(
|
|
||||||
() => passwordError = L10n.of(context).chooseAStrongPassword);
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(() => loading = true);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final client = Matrix.of(context).getLoginClient();
|
final client = Matrix.of(context).getLoginClient();
|
||||||
|
final email = emailController.text;
|
||||||
|
if (email.isNotEmpty) {
|
||||||
|
Matrix.of(context).currentClientSecret =
|
||||||
|
DateTime.now().millisecondsSinceEpoch.toString();
|
||||||
|
Matrix.of(context).currentThreepidCreds =
|
||||||
|
await client.requestTokenToRegisterEmail(
|
||||||
|
Matrix.of(context).currentClientSecret,
|
||||||
|
email,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
await client.uiaRequestBackground(
|
await client.uiaRequestBackground(
|
||||||
(auth) => client.register(
|
(auth) => client.register(
|
||||||
username: usernameController.text,
|
username: usernameController.text,
|
||||||
@ -49,7 +99,7 @@ class SignupPageController extends State<SignupPage> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
passwordError = (e as Object).toLocalizedString(context);
|
error = (e as Object).toLocalizedString(context);
|
||||||
} finally {
|
} finally {
|
||||||
setState(() => loading = false);
|
setState(() => loading = false);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/widgets/layouts/one_page_card.dart';
|
import 'package:fluffychat/widgets/layouts/one_page_card.dart';
|
||||||
import 'package:fluffychat/widgets/matrix.dart';
|
|
||||||
import '../signup.dart';
|
import '../signup.dart';
|
||||||
|
|
||||||
class SignupPageView extends StatelessWidget {
|
class SignupPageView extends StatelessWidget {
|
||||||
@ -17,76 +16,116 @@ class SignupPageView extends StatelessWidget {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(L10n.of(context).signUp),
|
title: Text(L10n.of(context).signUp),
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: Form(
|
||||||
children: [
|
key: controller.formKey,
|
||||||
ListTile(
|
child: ListView(
|
||||||
title: Text(L10n.of(context).pleaseChooseAUsername),
|
children: [
|
||||||
subtitle: Text(L10n.of(context).newUsernameDescription),
|
Padding(
|
||||||
),
|
padding: const EdgeInsets.all(12.0),
|
||||||
Padding(
|
child: TextFormField(
|
||||||
padding: const EdgeInsets.all(12.0),
|
readOnly: controller.loading,
|
||||||
child: TextField(
|
autocorrect: false,
|
||||||
readOnly: controller.loading,
|
controller: controller.usernameController,
|
||||||
autocorrect: false,
|
autofillHints:
|
||||||
autofocus: true,
|
controller.loading ? null : [AutofillHints.username],
|
||||||
controller: controller.usernameController,
|
validator: controller.usernameTextFieldValidator,
|
||||||
autofillHints:
|
decoration: InputDecoration(
|
||||||
controller.loading ? null : [AutofillHints.username],
|
prefixIcon: const Icon(Icons.account_circle_outlined),
|
||||||
decoration: InputDecoration(
|
hintText: L10n.of(context).username,
|
||||||
prefixIcon: const Icon(Icons.account_box_outlined),
|
labelText: L10n.of(context).username,
|
||||||
hintText: L10n.of(context).username,
|
prefixText: '@',
|
||||||
errorText: controller.usernameError,
|
prefixStyle: const TextStyle(fontWeight: FontWeight.bold),
|
||||||
labelText: L10n.of(context).username,
|
suffixStyle: const TextStyle(fontWeight: FontWeight.w200),
|
||||||
prefixText: '@',
|
suffixText: ':${controller.domain}'),
|
||||||
suffixText:
|
),
|
||||||
':${Matrix.of(context).getLoginClient().homeserver.host}'),
|
|
||||||
),
|
),
|
||||||
),
|
Padding(
|
||||||
const Divider(),
|
padding: const EdgeInsets.all(12.0),
|
||||||
ListTile(
|
child: TextFormField(
|
||||||
title: Text(L10n.of(context).chooseAStrongPassword),
|
readOnly: controller.loading,
|
||||||
subtitle: Text(L10n.of(context).newPasswordDescription),
|
autocorrect: false,
|
||||||
),
|
autofillHints:
|
||||||
Padding(
|
controller.loading ? null : [AutofillHints.password],
|
||||||
padding: const EdgeInsets.all(12.0),
|
controller: controller.passwordController,
|
||||||
child: TextField(
|
obscureText: !controller.showPassword,
|
||||||
readOnly: controller.loading,
|
validator: controller.password1TextFieldValidator,
|
||||||
autocorrect: false,
|
decoration: InputDecoration(
|
||||||
autofillHints:
|
prefixIcon: const Icon(Icons.vpn_key_outlined),
|
||||||
controller.loading ? null : [AutofillHints.password],
|
hintText: '****',
|
||||||
controller: controller.passwordController,
|
suffixIcon: IconButton(
|
||||||
obscureText: !controller.showPassword,
|
tooltip: L10n.of(context).showPassword,
|
||||||
onSubmitted: controller.signup,
|
icon: Icon(controller.showPassword
|
||||||
decoration: InputDecoration(
|
? Icons.visibility_off_outlined
|
||||||
prefixIcon: const Icon(Icons.lock_outlined),
|
: Icons.visibility_outlined),
|
||||||
hintText: '****',
|
onPressed: controller.toggleShowPassword,
|
||||||
errorText: controller.passwordError,
|
),
|
||||||
suffixIcon: IconButton(
|
labelText: L10n.of(context).password,
|
||||||
tooltip: L10n.of(context).showPassword,
|
|
||||||
icon: Icon(controller.showPassword
|
|
||||||
? Icons.visibility_off_outlined
|
|
||||||
: Icons.visibility_outlined),
|
|
||||||
onPressed: controller.toggleShowPassword,
|
|
||||||
),
|
),
|
||||||
labelText: L10n.of(context).password,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Padding(
|
||||||
const Divider(),
|
padding: const EdgeInsets.all(12.0),
|
||||||
const SizedBox(height: 12),
|
child: TextFormField(
|
||||||
Hero(
|
readOnly: controller.loading,
|
||||||
tag: 'loginButton',
|
autocorrect: false,
|
||||||
child: Padding(
|
autofillHints:
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
controller.loading ? null : [AutofillHints.password],
|
||||||
child: ElevatedButton(
|
controller: controller.passwordController2,
|
||||||
onPressed: controller.loading ? null : controller.signup,
|
obscureText: true,
|
||||||
child: controller.loading
|
validator: controller.password2TextFieldValidator,
|
||||||
? const LinearProgressIndicator()
|
decoration: const InputDecoration(
|
||||||
: Text(L10n.of(context).signUp),
|
prefixIcon: Icon(Icons.repeat_outlined),
|
||||||
|
hintText: '****',
|
||||||
|
labelText: 'Repeat password',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Padding(
|
||||||
],
|
padding: const EdgeInsets.all(12.0),
|
||||||
|
child: TextFormField(
|
||||||
|
readOnly: controller.loading,
|
||||||
|
autocorrect: false,
|
||||||
|
controller: controller.emailController,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
autofillHints:
|
||||||
|
controller.loading ? null : [AutofillHints.username],
|
||||||
|
validator: controller.emailTextFieldValidator,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
prefixIcon: const Icon(Icons.mail_outlined),
|
||||||
|
labelText: L10n.of(context).addEmail,
|
||||||
|
hintText: 'email@example.abc',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
if (controller.error != null) ...[
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
child: Text(
|
||||||
|
controller.error,
|
||||||
|
style: const TextStyle(color: Colors.red),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
const Divider(),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
],
|
||||||
|
Hero(
|
||||||
|
tag: 'loginButton',
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: controller.loading ? null : controller.signup,
|
||||||
|
child: controller.loading
|
||||||
|
? const LinearProgressIndicator()
|
||||||
|
: Text(L10n.of(context).signUp),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -5,6 +5,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
|
||||||
|
import 'uia_request_manager.dart';
|
||||||
|
|
||||||
extension LocalizedExceptionExtension on Object {
|
extension LocalizedExceptionExtension on Object {
|
||||||
String toLocalizedString(BuildContext context) {
|
String toLocalizedString(BuildContext context) {
|
||||||
if (this is MatrixException) {
|
if (this is MatrixException) {
|
||||||
@ -48,6 +50,7 @@ extension LocalizedExceptionExtension on Object {
|
|||||||
if (this is MatrixConnectionException || this is SocketException) {
|
if (this is MatrixConnectionException || this is SocketException) {
|
||||||
return L10n.of(context).noConnectionToTheServer;
|
return L10n.of(context).noConnectionToTheServer;
|
||||||
}
|
}
|
||||||
|
if (this is UiaException) return toString();
|
||||||
Logs().w('Something went wrong: ', this);
|
Logs().w('Something went wrong: ', this);
|
||||||
return L10n.of(context).oopsSomethingWentWrong;
|
return L10n.of(context).oopsSomethingWentWrong;
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,24 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
|
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
import 'package:fluffychat/utils/platform_infos.dart';
|
||||||
import 'package:fluffychat/widgets/matrix.dart';
|
import 'package:fluffychat/widgets/matrix.dart';
|
||||||
|
|
||||||
extension UiaRequestManager on MatrixState {
|
extension UiaRequestManager on MatrixState {
|
||||||
Future uiaRequestHandler(UiaRequest uiaRequest) async {
|
Future uiaRequestHandler(UiaRequest uiaRequest) async {
|
||||||
try {
|
try {
|
||||||
if (uiaRequest.state != UiaRequestState.waitForUser ||
|
if (uiaRequest.state != UiaRequestState.waitForUser ||
|
||||||
uiaRequest.nextStages.isEmpty) return;
|
uiaRequest.nextStages.isEmpty) {
|
||||||
|
Logs().d('Uia Request Stage: ${uiaRequest.state}');
|
||||||
|
return;
|
||||||
|
}
|
||||||
final stage = uiaRequest.nextStages.first;
|
final stage = uiaRequest.nextStages.first;
|
||||||
|
Logs().d('Uia Request Stage: $stage');
|
||||||
switch (stage) {
|
switch (stage) {
|
||||||
case AuthenticationTypes.password:
|
case AuthenticationTypes.password:
|
||||||
final input = cachedPassword ??
|
final input = cachedPassword ??
|
||||||
@ -31,7 +37,9 @@ extension UiaRequestManager on MatrixState {
|
|||||||
],
|
],
|
||||||
))
|
))
|
||||||
?.single;
|
?.single;
|
||||||
if (input?.isEmpty ?? true) return;
|
if (input?.isEmpty ?? true) {
|
||||||
|
return uiaRequest.cancel();
|
||||||
|
}
|
||||||
return uiaRequest.completeStage(
|
return uiaRequest.completeStage(
|
||||||
AuthenticationPassword(
|
AuthenticationPassword(
|
||||||
session: uiaRequest.session,
|
session: uiaRequest.session,
|
||||||
@ -40,35 +48,18 @@ extension UiaRequestManager on MatrixState {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
case AuthenticationTypes.emailIdentity:
|
case AuthenticationTypes.emailIdentity:
|
||||||
final emailInput = await showTextInputDialog(
|
if (currentThreepidCreds == null || currentClientSecret == null) {
|
||||||
context: navigatorContext,
|
return uiaRequest.cancel(
|
||||||
message: L10n.of(context).serverRequiresEmail,
|
UiaException(L10n.of(widget.context).serverRequiresEmail),
|
||||||
okLabel: L10n.of(context).next,
|
);
|
||||||
cancelLabel: L10n.of(context).cancel,
|
|
||||||
textFields: [
|
|
||||||
DialogTextField(
|
|
||||||
hintText: L10n.of(context).addEmail,
|
|
||||||
keyboardType: TextInputType.emailAddress,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
if (emailInput == null || emailInput.isEmpty) {
|
|
||||||
return uiaRequest
|
|
||||||
.cancel(Exception(L10n.of(context).serverRequiresEmail));
|
|
||||||
}
|
}
|
||||||
final clientSecret = DateTime.now().millisecondsSinceEpoch.toString();
|
|
||||||
final currentThreepidCreds = await client.requestTokenToRegisterEmail(
|
|
||||||
clientSecret,
|
|
||||||
emailInput.single,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
final auth = AuthenticationThreePidCreds(
|
final auth = AuthenticationThreePidCreds(
|
||||||
session: uiaRequest.session,
|
session: uiaRequest.session,
|
||||||
type: AuthenticationTypes.emailIdentity,
|
type: AuthenticationTypes.emailIdentity,
|
||||||
threepidCreds: [
|
threepidCreds: [
|
||||||
ThreepidCreds(
|
ThreepidCreds(
|
||||||
sid: currentThreepidCreds.sid,
|
sid: currentThreepidCreds.sid,
|
||||||
clientSecret: clientSecret,
|
clientSecret: currentClientSecret,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -92,24 +83,41 @@ extension UiaRequestManager on MatrixState {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
await launch(
|
final url = Uri.parse(client.homeserver.toString() +
|
||||||
client.homeserver.toString() +
|
'/_matrix/client/r0/auth/$stage/fallback/web?session=${uiaRequest.session}');
|
||||||
'/_matrix/client/r0/auth/$stage/fallback/web?session=${uiaRequest.session}',
|
if (PlatformInfos.isMobile) {
|
||||||
);
|
final browser = UiaFallbackBrowser();
|
||||||
if (OkCancelResult.ok ==
|
browser.addMenuItem(
|
||||||
await showOkCancelAlertDialog(
|
ChromeSafariBrowserMenuItem(
|
||||||
useRootNavigator: false,
|
action: (_, __) {
|
||||||
message: L10n.of(context).pleaseFollowInstructionsOnWeb,
|
uiaRequest.cancel();
|
||||||
context: navigatorContext,
|
},
|
||||||
okLabel: L10n.of(context).next,
|
label: L10n.of(context).cancel,
|
||||||
cancelLabel: L10n.of(context).cancel,
|
id: 0,
|
||||||
)) {
|
),
|
||||||
return uiaRequest.completeStage(
|
|
||||||
AuthenticationData(session: uiaRequest.session),
|
|
||||||
);
|
);
|
||||||
|
await browser.open(url: url);
|
||||||
|
await browser.whenClosed.stream.first;
|
||||||
} else {
|
} else {
|
||||||
return uiaRequest.cancel();
|
launch(url.toString());
|
||||||
|
if (OkCancelResult.ok ==
|
||||||
|
await showOkCancelAlertDialog(
|
||||||
|
useRootNavigator: false,
|
||||||
|
message: L10n.of(context).pleaseFollowInstructionsOnWeb,
|
||||||
|
context: navigatorContext,
|
||||||
|
okLabel: L10n.of(context).next,
|
||||||
|
cancelLabel: L10n.of(context).cancel,
|
||||||
|
)) {
|
||||||
|
return uiaRequest.completeStage(
|
||||||
|
AuthenticationData(session: uiaRequest.session),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return uiaRequest.cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
await uiaRequest.completeStage(
|
||||||
|
AuthenticationData(session: uiaRequest.session),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Logs().e('Error while background UIA', e, s);
|
Logs().e('Error while background UIA', e, s);
|
||||||
@ -117,3 +125,19 @@ extension UiaRequestManager on MatrixState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class UiaException implements Exception {
|
||||||
|
final String reason;
|
||||||
|
|
||||||
|
UiaException(this.reason);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
class UiaFallbackBrowser extends ChromeSafariBrowser {
|
||||||
|
final StreamController<bool> whenClosed = StreamController<bool>.broadcast();
|
||||||
|
|
||||||
|
@override
|
||||||
|
onClosed() => whenClosed.add(true);
|
||||||
|
}
|
||||||
|
@ -75,6 +75,9 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
|||||||
int getClientIndexByMatrixId(String matrixId) =>
|
int getClientIndexByMatrixId(String matrixId) =>
|
||||||
widget.clients.indexWhere((client) => client.userID == matrixId);
|
widget.clients.indexWhere((client) => client.userID == matrixId);
|
||||||
|
|
||||||
|
String currentClientSecret;
|
||||||
|
RequestTokenResponse currentThreepidCreds;
|
||||||
|
|
||||||
int get _safeActiveClient {
|
int get _safeActiveClient {
|
||||||
if (widget.clients.isEmpty) {
|
if (widget.clients.isEmpty) {
|
||||||
widget.clients.add(getLoginClient());
|
widget.clients.add(getLoginClient());
|
||||||
|
Loading…
Reference in New Issue
Block a user