refactor: Improve sso login UX on web
This commit is contained in:
parent
e4b8d45715
commit
7cbc518951
3 changed files with 45 additions and 84 deletions
|
|
@ -2822,5 +2822,13 @@
|
||||||
"addLink": "Add link",
|
"addLink": "Add link",
|
||||||
"unableToJoinChat": "Unable to join chat. Maybe the other party has already closed the conversation.",
|
"unableToJoinChat": "Unable to join chat. Maybe the other party has already closed the conversation.",
|
||||||
"previous": "Previous",
|
"previous": "Previous",
|
||||||
"otherPartyNotLoggedIn": "The other party is currently not logged in and therefore cannot receive messages!"
|
"otherPartyNotLoggedIn": "The other party is currently not logged in and therefore cannot receive messages!",
|
||||||
|
"appWantsToUseForLogin": "Use '{server}' to log in",
|
||||||
|
"@appWantsToUseForLogin": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"server": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"appWantsToUseForLoginDescription": "You hereby allow the app and website to share information about you."
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ class HomeserverPicker extends StatefulWidget {
|
||||||
|
|
||||||
class HomeserverPickerController extends State<HomeserverPicker> {
|
class HomeserverPickerController extends State<HomeserverPicker> {
|
||||||
bool isLoading = false;
|
bool isLoading = false;
|
||||||
bool isLoggingIn = false;
|
|
||||||
|
|
||||||
final TextEditingController homeserverController = TextEditingController(
|
final TextEditingController homeserverController = TextEditingController(
|
||||||
text: AppConfig.defaultHomeserver,
|
text: AppConfig.defaultHomeserver,
|
||||||
|
|
@ -61,58 +60,29 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
||||||
isTorBrowser = isTor;
|
isTorBrowser = isTor;
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _lastCheckedUrl;
|
|
||||||
|
|
||||||
Timer? _checkHomeserverCooldown;
|
|
||||||
|
|
||||||
tryCheckHomeserverActionWithCooldown([_]) {
|
|
||||||
_checkHomeserverCooldown?.cancel();
|
|
||||||
_checkHomeserverCooldown = Timer(
|
|
||||||
const Duration(milliseconds: 500),
|
|
||||||
checkHomeserverAction,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void tryCheckHomeserverActionWithoutCooldown([_]) {
|
|
||||||
_checkHomeserverCooldown?.cancel();
|
|
||||||
_lastCheckedUrl = null;
|
|
||||||
checkHomeserverAction();
|
|
||||||
}
|
|
||||||
|
|
||||||
void onSubmitted([_]) {
|
|
||||||
if (isLoading || _checkHomeserverCooldown?.isActive == true) {
|
|
||||||
return tryCheckHomeserverActionWithoutCooldown();
|
|
||||||
}
|
|
||||||
if (supportsSso) return ssoLoginAction();
|
|
||||||
if (supportsPasswordLogin) return login();
|
|
||||||
return tryCheckHomeserverActionWithoutCooldown();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Starts an analysis of the given homeserver. It uses the current domain and
|
/// Starts an analysis of the given homeserver. It uses the current domain and
|
||||||
/// makes sure that it is prefixed with https. Then it searches for the
|
/// makes sure that it is prefixed with https. Then it searches for the
|
||||||
/// well-known information and forwards to the login page depending on the
|
/// well-known information and forwards to the login page depending on the
|
||||||
/// login type.
|
/// login type.
|
||||||
Future<void> checkHomeserverAction([_]) async {
|
Future<void> checkHomeserverAction({bool legacyPasswordLogin = false}) async {
|
||||||
final homeserverInput =
|
final homeserverInput =
|
||||||
homeserverController.text.trim().toLowerCase().replaceAll(' ', '-');
|
homeserverController.text.trim().toLowerCase().replaceAll(' ', '-');
|
||||||
|
|
||||||
if (homeserverInput.isEmpty || !homeserverInput.contains('.')) {
|
if (homeserverInput.isEmpty) {
|
||||||
setState(() {
|
setState(() {
|
||||||
error = loginFlows = null;
|
error = loginFlows = null;
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
Matrix.of(context).getLoginClient().homeserver = null;
|
Matrix.of(context).getLoginClient().homeserver = null;
|
||||||
_lastCheckedUrl = null;
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_lastCheckedUrl == homeserverInput) return;
|
|
||||||
|
|
||||||
_lastCheckedUrl = homeserverInput;
|
|
||||||
setState(() {
|
setState(() {
|
||||||
error = loginFlows = null;
|
error = loginFlows = null;
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final l10n = L10n.of(context);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var homeserver = Uri.parse(homeserverInput);
|
var homeserver = Uri.parse(homeserverInput);
|
||||||
if (homeserver.scheme.isEmpty) {
|
if (homeserver.scheme.isEmpty) {
|
||||||
|
|
@ -121,6 +91,21 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
||||||
final client = Matrix.of(context).getLoginClient();
|
final client = Matrix.of(context).getLoginClient();
|
||||||
final (_, _, loginFlows) = await client.checkHomeserver(homeserver);
|
final (_, _, loginFlows) = await client.checkHomeserver(homeserver);
|
||||||
this.loginFlows = loginFlows;
|
this.loginFlows = loginFlows;
|
||||||
|
if (supportsSso && !legacyPasswordLogin) {
|
||||||
|
if (!PlatformInfos.isMobile) {
|
||||||
|
final consent = await showOkCancelAlertDialog(
|
||||||
|
context: context,
|
||||||
|
title: l10n.appWantsToUseForLogin(homeserverInput),
|
||||||
|
message: l10n.appWantsToUseForLoginDescription,
|
||||||
|
okLabel: l10n.continueText,
|
||||||
|
);
|
||||||
|
if (consent != OkCancelResult.ok) return;
|
||||||
|
}
|
||||||
|
return ssoLoginAction();
|
||||||
|
}
|
||||||
|
context.push(
|
||||||
|
'${GoRouter.of(context).routeInformationProvider.value.uri.path}/login',
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setState(
|
setState(
|
||||||
() => error = (e).toLocalizedString(
|
() => error = (e).toLocalizedString(
|
||||||
|
|
@ -176,7 +161,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
error = null;
|
error = null;
|
||||||
isLoading = isLoggingIn = true;
|
isLoading = true;
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await Matrix.of(context).getLoginClient().login(
|
await Matrix.of(context).getLoginClient().login(
|
||||||
|
|
@ -191,27 +176,16 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
||||||
} finally {
|
} finally {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
isLoading = isLoggingIn = false;
|
isLoading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void login() async {
|
|
||||||
if (!supportsPasswordLogin) {
|
|
||||||
homeserverController.text = AppConfig.defaultHomeserver;
|
|
||||||
await checkHomeserverAction();
|
|
||||||
}
|
|
||||||
context.push(
|
|
||||||
'${GoRouter.of(context).routeInformationProvider.value.uri.path}/login',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_checkTorBrowser();
|
_checkTorBrowser();
|
||||||
super.initState();
|
super.initState();
|
||||||
WidgetsBinding.instance.addPostFrameCallback(checkHomeserverAction);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -223,7 +197,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
||||||
if (file == null) return;
|
if (file == null) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
error = null;
|
error = null;
|
||||||
isLoading = isLoggingIn = true;
|
isLoading = true;
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
final client = Matrix.of(context).getLoginClient();
|
final client = Matrix.of(context).getLoginClient();
|
||||||
|
|
@ -236,7 +210,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
||||||
} finally {
|
} finally {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
isLoading = isLoggingIn = false;
|
isLoading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -245,7 +219,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
||||||
void onMoreAction(MoreLoginActions action) {
|
void onMoreAction(MoreLoginActions action) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case MoreLoginActions.passwordLogin:
|
case MoreLoginActions.passwordLogin:
|
||||||
login();
|
checkHomeserverAction(legacyPasswordLogin: true);
|
||||||
case MoreLoginActions.privacy:
|
case MoreLoginActions.privacy:
|
||||||
launchUrlString(AppConfig.privacyUrl);
|
launchUrlString(AppConfig.privacyUrl);
|
||||||
case MoreLoginActions.about:
|
case MoreLoginActions.about:
|
||||||
|
|
|
||||||
|
|
@ -142,30 +142,13 @@ class HomeserverPickerView extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
TextField(
|
TextField(
|
||||||
onChanged:
|
onSubmitted: (_) =>
|
||||||
controller.tryCheckHomeserverActionWithCooldown,
|
controller.checkHomeserverAction(),
|
||||||
onSubmitted: controller.onSubmitted,
|
|
||||||
onTap:
|
|
||||||
controller.tryCheckHomeserverActionWithCooldown,
|
|
||||||
controller: controller.homeserverController,
|
controller: controller.homeserverController,
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
keyboardType: TextInputType.url,
|
keyboardType: TextInputType.url,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: controller.isLoading
|
prefixIcon: const Icon(Icons.search_outlined),
|
||||||
? Container(
|
|
||||||
width: 16,
|
|
||||||
height: 16,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: const SizedBox(
|
|
||||||
width: 16,
|
|
||||||
height: 16,
|
|
||||||
child:
|
|
||||||
CircularProgressIndicator.adaptive(
|
|
||||||
strokeWidth: 2,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: const Icon(Icons.search_outlined),
|
|
||||||
filled: false,
|
filled: false,
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(
|
||||||
|
|
@ -219,25 +202,21 @@ class HomeserverPickerView extends StatelessWidget {
|
||||||
backgroundColor: theme.colorScheme.primary,
|
backgroundColor: theme.colorScheme.primary,
|
||||||
foregroundColor: theme.colorScheme.onPrimary,
|
foregroundColor: theme.colorScheme.onPrimary,
|
||||||
),
|
),
|
||||||
onPressed:
|
onPressed: controller.isLoading
|
||||||
controller.isLoggingIn || controller.isLoading
|
? null
|
||||||
? null
|
: controller.checkHomeserverAction,
|
||||||
: controller.supportsSso
|
child: controller.isLoading
|
||||||
? controller.ssoLoginAction
|
? const LinearProgressIndicator()
|
||||||
: controller.supportsPasswordLogin
|
: Text(L10n.of(context).continueText),
|
||||||
? controller.login
|
|
||||||
: null,
|
|
||||||
child: Text(L10n.of(context).continueText),
|
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: theme.colorScheme.secondary,
|
foregroundColor: theme.colorScheme.secondary,
|
||||||
textStyle: theme.textTheme.labelMedium,
|
textStyle: theme.textTheme.labelMedium,
|
||||||
),
|
),
|
||||||
onPressed:
|
onPressed: controller.isLoading
|
||||||
controller.isLoggingIn || controller.isLoading
|
? null
|
||||||
? null
|
: controller.restoreBackup,
|
||||||
: controller.restoreBackup,
|
|
||||||
child: Text(L10n.of(context).hydrate),
|
child: Text(L10n.of(context).hydrate),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue