diff --git a/lib/pages/homeserver_picker/homeserver_picker.dart b/lib/pages/homeserver_picker/homeserver_picker.dart index 8854b1934..41377d9af 100644 --- a/lib/pages/homeserver_picker/homeserver_picker.dart +++ b/lib/pages/homeserver_picker/homeserver_picker.dart @@ -6,6 +6,7 @@ import 'package:file_picker/file_picker.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/homeserver_picker/homeserver_picker_view.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:fluffychat/pangea/utils/firebase_analytics.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/tor_stub.dart' if (dart.library.html) 'package:tor_detector_web/tor_detector_web.dart'; @@ -35,9 +36,11 @@ class HomeserverPickerController extends State { bool isLoading = false; bool isLoggingIn = false; - final TextEditingController homeserverController = TextEditingController( - text: AppConfig.defaultHomeserver, - ); + // #Pangea + // final TextEditingController homeserverController = TextEditingController( + // text: AppConfig.defaultHomeserver, + // ); + // Pangea# String? error; @@ -79,38 +82,59 @@ class HomeserverPickerController extends State { checkHomeserverAction(); } + // #Pangea + Map? _rawLoginTypes; + // Pangea# + /// 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 /// well-known information and forwards to the login page depending on the /// login type. Future checkHomeserverAction([_]) async { - homeserverController.text = - homeserverController.text.trim().toLowerCase().replaceAll(' ', '-'); + // #Pangea + // homeserverController.text = + // homeserverController.text.trim().toLowerCase().replaceAll(' ', '-'); - if (homeserverController.text.isEmpty) { - setState(() { - error = loginFlows = null; - isLoading = false; - Matrix.of(context).getLoginClient().homeserver = null; - }); - return; - } - if (_lastCheckedUrl == homeserverController.text) return; + // if (homeserverController.text.isEmpty) { + // setState(() { + // error = loginFlows = null; + // isLoading = false; + // Matrix.of(context).getLoginClient().homeserver = null; + // }); + // return; + // } + // if (_lastCheckedUrl == homeserverController.text) return; - _lastCheckedUrl = homeserverController.text; + // _lastCheckedUrl = homeserverController.text; + _lastCheckedUrl = AppConfig.defaultHomeserver; + // Pangea# setState(() { error = loginFlows = null; isLoading = true; }); try { - var homeserver = Uri.parse(homeserverController.text); + // #Pangea + // var homeserver = Uri.parse(homeserverController.text); + // if (homeserver.scheme.isEmpty) { + // homeserver = Uri.https(homeserverController.text, ''); + // } + var homeserver = Uri.parse(AppConfig.defaultHomeserver); if (homeserver.scheme.isEmpty) { - homeserver = Uri.https(homeserverController.text, ''); + homeserver = Uri.https(AppConfig.defaultHomeserver, ''); } + // Pangea# final client = Matrix.of(context).getLoginClient(); final (_, _, loginFlows) = await client.checkHomeserver(homeserver); this.loginFlows = loginFlows; + // #Pangea + if (supportsSso) { + _rawLoginTypes = await client.request( + RequestType.GET, + '/client/v3/login', + ); + } + // Pangea# } catch (e) { setState( () => error = (e).toLocalizedString( @@ -137,7 +161,11 @@ class HomeserverPickerController extends State { bool get supportsPasswordLogin => _supportsFlow('m.login.password'); - void ssoLoginAction() async { + void ssoLoginAction( + // #Pangea + IdentityProvider provider, + // Pangea# + ) async { final redirectUrl = kIsWeb ? Uri.parse(html.window.location.href) .resolveUri( @@ -149,7 +177,11 @@ class HomeserverPickerController extends State { : 'http://localhost:3001//login'; final url = Matrix.of(context).getLoginClient().homeserver!.replace( - path: '/_matrix/client/v3/login/sso/redirect', + // #Pangea + // path: '/_matrix/client/v3/login/sso/redirect', + path: + '/_matrix/client/v3/login/sso/redirect${provider.id == null ? '' : '/${provider.id}'}', + // Pangea# queryParameters: {'redirectUrl': redirectUrl}, ); @@ -197,7 +229,7 @@ class HomeserverPickerController extends State { initialDeviceDisplayName: PlatformInfos.clientName, ); // #Pangea - // GoogleAnalytics.login(provider.name!, loginRes.userId); + GoogleAnalytics.login(provider.name!, loginRes.userId); // Pangea# } catch (e) { setState(() { @@ -258,6 +290,27 @@ class HomeserverPickerController extends State { } } } + + // #Pangea + List? get identityProviders { + final loginTypes = _rawLoginTypes; + if (loginTypes == null) return null; + final List? rawProviders = + loginTypes.tryGetList('flows')?.singleWhereOrNull( + (flow) => flow['type'] == AuthenticationTypes.sso, + )['identity_providers'] ?? + [ + {'id': null}, + ]; + if (rawProviders == null) return null; + final list = + rawProviders.map((json) => IdentityProvider.fromJson(json)).toList(); + if (PlatformInfos.isCupertinoStyle) { + list.sort((a, b) => a.brand == 'apple' ? -1 : 1); + } + return list; + } + // Pangea# } class IdentityProvider { diff --git a/lib/pages/homeserver_picker/homeserver_picker_view.dart b/lib/pages/homeserver_picker/homeserver_picker_view.dart index dd73b2c03..1b0c7f59f 100644 --- a/lib/pages/homeserver_picker/homeserver_picker_view.dart +++ b/lib/pages/homeserver_picker/homeserver_picker_view.dart @@ -1,9 +1,10 @@ import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/pangea/pages/connect/p_sso_button.dart'; +import 'package:fluffychat/pangea/widgets/common/pangea_logo_svg.dart'; +import 'package:fluffychat/pangea/widgets/signup/signup_buttons.dart'; import 'package:fluffychat/widgets/layouts/login_scaffold.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:flutter_linkify/flutter_linkify.dart'; -import 'package:url_launcher/url_launcher.dart'; import 'homeserver_picker.dart'; @@ -70,116 +71,190 @@ class HomeserverPickerView extends StatelessWidget { // repeat: ImageRepeat.repeat, // ), // ), + // Padding( + // padding: const EdgeInsets.all(32.0), + // child: TextField( + // onChanged: controller.tryCheckHomeserverActionWithCooldown, + // onEditingComplete: + // controller.tryCheckHomeserverActionWithoutCooldown, + // onSubmitted: controller.tryCheckHomeserverActionWithoutCooldown, + // onTap: controller.tryCheckHomeserverActionWithCooldown, + // controller: controller.homeserverController, + // autocorrect: false, + // keyboardType: TextInputType.url, + // decoration: InputDecoration( + // prefixIcon: controller.isLoading + // ? 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, + // border: OutlineInputBorder( + // borderRadius: BorderRadius.circular(AppConfig.borderRadius), + // ), + // hintText: AppConfig.defaultHomeserver, + // labelText: L10n.of(context)!.homeserver, + // errorText: controller.error, + // suffixIcon: IconButton( + // onPressed: () { + // showDialog( + // context: context, + // builder: (context) => AlertDialog.adaptive( + // title: Text(L10n.of(context)!.whatIsAHomeserver), + // content: Linkify( + // text: L10n.of(context)!.homeserverDescription, + // ), + // actions: [ + // TextButton( + // onPressed: () => launchUrl( + // Uri.https('servers.joinmatrix.org'), + // ), + // child: Text( + // L10n.of(context)!.discoverHomeservers, + // ), + // ), + // TextButton( + // onPressed: Navigator.of(context).pop, + // child: Text(L10n.of(context)!.close), + // ), + // ], + // ), + // ); + // }, + // icon: const Icon(Icons.info_outlined), + // ), + // ), + // ), + // ), + // if (MediaQuery.of(context).size.height > 512) const Spacer(), + // ListView( + // shrinkWrap: true, + // padding: const EdgeInsets.symmetric( + // horizontal: 32.0, + // vertical: 32.0, + // ), + // children: [ + // TextButton( + // style: TextButton.styleFrom( + // textStyle: theme.textTheme.labelMedium, + // foregroundColor: theme.colorScheme.secondary, + // ), + // onPressed: controller.isLoggingIn || controller.isLoading + // ? null + // : controller.restoreBackup, + // child: Text(L10n.of(context)!.hydrate), + // ), + // if (controller.supportsPasswordLogin && controller.supportsSso) + // TextButton( + // style: TextButton.styleFrom( + // foregroundColor: theme.colorScheme.secondary, + // textStyle: theme.textTheme.labelMedium, + // ), + // onPressed: controller.isLoggingIn || controller.isLoading + // ? null + // : controller.login, + // child: Text(L10n.of(context)!.loginWithMatrixId), + // ), + // const SizedBox(height: 8.0), + // if (controller.supportsPasswordLogin || controller.supportsSso) + // ElevatedButton( + // style: ElevatedButton.styleFrom( + // backgroundColor: theme.colorScheme.primary, + // foregroundColor: theme.colorScheme.onPrimary, + // ), + // onPressed: controller.isLoggingIn || controller.isLoading + // ? null + // : controller.supportsSso + // ? controller.ssoLoginAction + // : controller.login, + // child: Text(L10n.of(context)!.next), + // ), + // ], + // ), + Expanded( + child: controller.isLoading + ? const Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Colors.black), + ), + ) + : ListView( + children: [ + if (controller.error != null) ...[ + const SizedBox(height: 12), + const Center( + child: Icon( + Icons.error_outline, + size: 48, + color: Colors.orange, + ), + ), + const SizedBox(height: 12), + Center( + child: Text( + controller.error!, + textAlign: TextAlign.center, + style: TextStyle( + color: Theme.of(context).colorScheme.error, + fontSize: 18, + ), + ), + ), + const SizedBox(height: 36), + ] else + const SignupButtons(), + if (controller.identityProviders != null) ...[ + ...controller.identityProviders!.map( + (provider) => Padding( + padding: const EdgeInsets.all(12.0), + child: Hero( + tag: "ssobutton ${provider.id ?? provider.name}", + child: PangeaSsoButton( + identityProvider: provider, + onPressed: () => + controller.ssoLoginAction(provider), + ), + ), + ), + ), + if (controller.supportsPasswordLogin) + Padding( + padding: const EdgeInsets.all(12.0), + child: Hero( + tag: 'signinButton', + child: ElevatedButton( + onPressed: controller.login, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const PangeaLogoSvg(width: 20), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + ), + child: Text( + "${L10n.of(context)!.loginOrSignup} Pangea Chat", + ), + ), + ], + ), + ), + ), + ), + ], + ], + ), + ), // Pangea# - Padding( - padding: const EdgeInsets.all(32.0), - child: TextField( - onChanged: controller.tryCheckHomeserverActionWithCooldown, - onEditingComplete: - controller.tryCheckHomeserverActionWithoutCooldown, - onSubmitted: controller.tryCheckHomeserverActionWithoutCooldown, - onTap: controller.tryCheckHomeserverActionWithCooldown, - controller: controller.homeserverController, - autocorrect: false, - keyboardType: TextInputType.url, - decoration: InputDecoration( - prefixIcon: controller.isLoading - ? 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, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(AppConfig.borderRadius), - ), - hintText: AppConfig.defaultHomeserver, - labelText: L10n.of(context)!.homeserver, - errorText: controller.error, - suffixIcon: IconButton( - onPressed: () { - showDialog( - context: context, - builder: (context) => AlertDialog.adaptive( - title: Text(L10n.of(context)!.whatIsAHomeserver), - content: Linkify( - text: L10n.of(context)!.homeserverDescription, - ), - actions: [ - TextButton( - onPressed: () => launchUrl( - Uri.https('servers.joinmatrix.org'), - ), - child: Text( - L10n.of(context)!.discoverHomeservers, - ), - ), - TextButton( - onPressed: Navigator.of(context).pop, - child: Text(L10n.of(context)!.close), - ), - ], - ), - ); - }, - icon: const Icon(Icons.info_outlined), - ), - ), - ), - ), - if (MediaQuery.of(context).size.height > 512) const Spacer(), - ListView( - shrinkWrap: true, - padding: const EdgeInsets.symmetric( - horizontal: 32.0, - vertical: 32.0, - ), - children: [ - TextButton( - style: TextButton.styleFrom( - textStyle: theme.textTheme.labelMedium, - foregroundColor: theme.colorScheme.secondary, - ), - onPressed: controller.isLoggingIn || controller.isLoading - ? null - : controller.restoreBackup, - child: Text(L10n.of(context)!.hydrate), - ), - if (controller.supportsPasswordLogin && controller.supportsSso) - TextButton( - style: TextButton.styleFrom( - foregroundColor: theme.colorScheme.secondary, - textStyle: theme.textTheme.labelMedium, - ), - onPressed: controller.isLoggingIn || controller.isLoading - ? null - : controller.login, - child: Text(L10n.of(context)!.loginWithMatrixId), - ), - const SizedBox(height: 8.0), - if (controller.supportsPasswordLogin || controller.supportsSso) - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: theme.colorScheme.primary, - foregroundColor: theme.colorScheme.onPrimary, - ), - onPressed: controller.isLoggingIn || controller.isLoading - ? null - : controller.supportsSso - ? controller.ssoLoginAction - : controller.login, - child: Text(L10n.of(context)!.next), - ), - ], - ), ], ), );