From dec588d0c0855a8c2a45d132e6f410f449b0a24c Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 17 Aug 2024 12:16:55 +0200 Subject: [PATCH] refactor: Simplify login UX --- assets/l10n/intl_en.arb | 6 +- lib/config/routes.dart | 4 +- lib/config/themes.dart | 15 +- lib/pages/bootstrap/bootstrap_dialog.dart | 4 +- lib/pages/chat_list/chat_list_header.dart | 1 + lib/pages/chat_list/space_view.dart | 1 + .../homeserver_picker/homeserver_app_bar.dart | 109 ------- .../homeserver_bottom_sheet.dart | 53 ---- .../homeserver_picker/homeserver_picker.dart | 67 ++-- .../homeserver_picker_view.dart | 299 ++++++++---------- .../homeserver_picker/public_homeserver.dart | 73 ----- lib/pages/login/login_view.dart | 24 +- lib/widgets/layouts/login_scaffold.dart | 2 +- pubspec.lock | 4 +- pubspec.yaml | 2 +- 15 files changed, 175 insertions(+), 489 deletions(-) delete mode 100644 lib/pages/homeserver_picker/homeserver_app_bar.dart delete mode 100644 lib/pages/homeserver_picker/homeserver_bottom_sheet.dart delete mode 100644 lib/pages/homeserver_picker/public_homeserver.dart diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index aa2d0542..e9146f2c 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2756,5 +2756,9 @@ } }, "changelog": "Changelog", - "sendCanceled": "Sending canceled" + "sendCanceled": "Sending canceled", + "loginWithMatrixId": "Login with Matrix-ID", + "discoverHomeservers": "Discover homeservers", + "whatIsAHomeserver": "What is a homeserver?", + "homeserverDescription": "All your data is stored on the homeserver, just like an email provider. You can choose which homeserver you want to use, while you can still communicate with everyone. Learn more at at https://matrix.org." } diff --git a/lib/config/routes.dart b/lib/config/routes.dart index d11cd56d..b839dd3f 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -62,7 +62,7 @@ abstract class AppRoutes { pageBuilder: (context, state) => defaultPageBuilder( context, state, - const HomeserverPicker(), + const HomeserverPicker(addMultiAccount: false), ), redirect: loggedInRedirect, routes: [ @@ -242,7 +242,7 @@ abstract class AppRoutes { pageBuilder: (context, state) => defaultPageBuilder( context, state, - const HomeserverPicker(), + const HomeserverPicker(addMultiAccount: true), ), routes: [ GoRoute( diff --git a/lib/config/themes.dart b/lib/config/themes.dart index f83b69a2..4fcff7b7 100644 --- a/lib/config/themes.dart +++ b/lib/config/themes.dart @@ -91,11 +91,10 @@ abstract class FluffyThemes { ), inputDecorationTheme: InputDecorationTheme( border: OutlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), + borderRadius: BorderRadius.circular(AppConfig.borderRadius), ), contentPadding: const EdgeInsets.all(12), - filled: true, + filled: false, ), appBarTheme: AppBarTheme( toolbarHeight: FluffyThemes.isColumnMode(context) ? 72 : 56, @@ -114,13 +113,6 @@ abstract class FluffyThemes { systemNavigationBarColor: colorScheme.surface, ), ), - textButtonTheme: TextButtonThemeData( - style: TextButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), - ), - ), - ), outlinedButtonTheme: OutlinedButtonThemeData( style: OutlinedButton.styleFrom( side: BorderSide( @@ -145,9 +137,6 @@ abstract class FluffyThemes { elevation: 0, padding: const EdgeInsets.all(16), textStyle: const TextStyle(fontSize: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(AppConfig.borderRadius), - ), ), ), ); diff --git a/lib/pages/bootstrap/bootstrap_dialog.dart b/lib/pages/bootstrap/bootstrap_dialog.dart index 64e7feba..b816d2fc 100644 --- a/lib/pages/bootstrap/bootstrap_dialog.dart +++ b/lib/pages/bootstrap/bootstrap_dialog.dart @@ -264,7 +264,9 @@ class BootstrapDialogState extends State { hintStyle: TextStyle( fontFamily: theme.textTheme.bodyLarge?.fontFamily, ), - hintText: L10n.of(context)!.recoveryKey, + prefixIcon: const Icon(Icons.key_outlined), + labelText: L10n.of(context)!.recoveryKey, + hintText: 'Es** **** **** ****', errorText: _recoveryKeyInputError, errorMaxLines: 2, ), diff --git a/lib/pages/chat_list/chat_list_header.dart b/lib/pages/chat_list/chat_list_header.dart index 864a7a7f..f4299904 100644 --- a/lib/pages/chat_list/chat_list_header.dart +++ b/lib/pages/chat_list/chat_list_header.dart @@ -54,6 +54,7 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget { globalSearch: globalSearch, ), decoration: InputDecoration( + filled: true, fillColor: theme.colorScheme.secondaryContainer, border: OutlineInputBorder( borderSide: BorderSide.none, diff --git a/lib/pages/chat_list/space_view.dart b/lib/pages/chat_list/space_view.dart index 0295d2b5..69d9c969 100644 --- a/lib/pages/chat_list/space_view.dart +++ b/lib/pages/chat_list/space_view.dart @@ -361,6 +361,7 @@ class _SpaceViewState extends State { onChanged: (_) => setState(() {}), textInputAction: TextInputAction.search, decoration: InputDecoration( + filled: true, fillColor: theme.colorScheme.secondaryContainer, border: OutlineInputBorder( borderSide: BorderSide.none, diff --git a/lib/pages/homeserver_picker/homeserver_app_bar.dart b/lib/pages/homeserver_picker/homeserver_app_bar.dart deleted file mode 100644 index 3ba50645..00000000 --- a/lib/pages/homeserver_picker/homeserver_app_bar.dart +++ /dev/null @@ -1,109 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:flutter_typeahead/flutter_typeahead.dart'; - -import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/config/themes.dart'; -import 'package:fluffychat/pages/homeserver_picker/public_homeserver.dart'; -import 'package:fluffychat/utils/localized_exception_extension.dart'; -import 'homeserver_bottom_sheet.dart'; -import 'homeserver_picker.dart'; - -class HomeserverAppBar extends StatelessWidget { - final HomeserverPickerController controller; - - const HomeserverAppBar({super.key, required this.controller}); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - - return TypeAheadField( - decorationBuilder: (context, child) => ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 256), - child: Material( - borderRadius: BorderRadius.circular(AppConfig.borderRadius), - elevation: theme.appBarTheme.scrolledUnderElevation ?? 4, - shadowColor: theme.appBarTheme.shadowColor ?? Colors.black, - child: child, - ), - ), - emptyBuilder: (context) => ListTile( - leading: const Icon(Icons.search_outlined), - title: Text(L10n.of(context)!.nothingFound), - ), - loadingBuilder: (context) => ListTile( - leading: const CircularProgressIndicator.adaptive(strokeWidth: 2), - title: Text(L10n.of(context)!.loadingPleaseWait), - ), - errorBuilder: (context, error) => ListTile( - leading: const Icon(Icons.error_outlined), - title: Text( - error.toLocalizedString(context), - ), - ), - itemBuilder: (context, homeserver) => ListTile( - title: Text(homeserver.name), - subtitle: homeserver.description == null - ? null - : Text(homeserver.description ?? ''), - trailing: IconButton( - icon: const Icon(Icons.info_outlined), - onPressed: () => showModalBottomSheet( - context: context, - builder: (_) => HomeserverBottomSheet( - homeserver: homeserver, - ), - ), - ), - ), - suggestionsCallback: (pattern) async { - pattern = pattern.toLowerCase().trim(); - final homeservers = await controller.loadHomeserverList(); - final matches = homeservers - .where( - (homeserver) => - homeserver.name.toLowerCase().contains(pattern) || - (homeserver.description?.toLowerCase().contains(pattern) ?? - false), - ) - .toList(); - if (pattern.contains('.') && - pattern.split('.').any((part) => part.isNotEmpty) && - !matches.any((homeserver) => homeserver.name == pattern)) { - matches.add(PublicHomeserver(name: pattern)); - } - return matches; - }, - onSelected: (suggestion) { - controller.homeserverController.text = suggestion.name; - controller.checkHomeserverAction(); - }, - controller: controller.homeserverController, - builder: (context, textEditingController, focusNode) => TextField( - enabled: !controller.isLoggingIn, - controller: textEditingController, - focusNode: focusNode, - decoration: InputDecoration( - prefixIcon: Navigator.of(context).canPop() - ? IconButton( - onPressed: Navigator.of(context).pop, - icon: const Icon(Icons.arrow_back), - ) - : null, - fillColor: FluffyThemes.isColumnMode(context) - ? theme.colorScheme.surface - // ignore: deprecated_member_use - : theme.colorScheme.surfaceVariant, - prefixText: '${L10n.of(context)!.homeserver}: ', - hintText: L10n.of(context)!.enterYourHomeserver, - suffixIcon: const Icon(Icons.search), - ), - textInputAction: TextInputAction.search, - onSubmitted: controller.checkHomeserverAction, - autocorrect: false, - ), - ); - } -} diff --git a/lib/pages/homeserver_picker/homeserver_bottom_sheet.dart b/lib/pages/homeserver_picker/homeserver_bottom_sheet.dart deleted file mode 100644 index 18256d6f..00000000 --- a/lib/pages/homeserver_picker/homeserver_bottom_sheet.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:url_launcher/url_launcher_string.dart'; - -import 'package:fluffychat/pages/homeserver_picker/public_homeserver.dart'; - -class HomeserverBottomSheet extends StatelessWidget { - final PublicHomeserver homeserver; - const HomeserverBottomSheet({required this.homeserver, super.key}); - - @override - Widget build(BuildContext context) { - final description = homeserver.description; - final registration = homeserver.regLink; - final jurisdiction = homeserver.staffJur; - final homeserverSoftware = homeserver.software; - return Scaffold( - appBar: AppBar( - title: Text(homeserver.name), - ), - body: ListView( - children: [ - if (description != null && description.isNotEmpty) - ListTile( - leading: const Icon(Icons.info_outlined), - title: Text(description), - ), - if (jurisdiction != null && jurisdiction.isNotEmpty) - ListTile( - leading: const Icon(Icons.location_city_outlined), - title: Text(jurisdiction), - ), - if (homeserverSoftware != null && homeserverSoftware.isNotEmpty) - ListTile( - leading: const Icon(Icons.domain_outlined), - title: Text(homeserverSoftware), - ), - ListTile( - onTap: () => launchUrlString(homeserver.name), - leading: const Icon(Icons.link_outlined), - title: Text(homeserver.name), - ), - if (registration != null) - ListTile( - onTap: () => launchUrlString(registration), - leading: const Icon(Icons.person_add_outlined), - title: Text(registration), - ), - ], - ), - ); - } -} diff --git a/lib/pages/homeserver_picker/homeserver_picker.dart b/lib/pages/homeserver_picker/homeserver_picker.dart index 7ffe089a..b2f782f3 100644 --- a/lib/pages/homeserver_picker/homeserver_picker.dart +++ b/lib/pages/homeserver_picker/homeserver_picker.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -16,7 +15,6 @@ import 'package:universal_html/html.dart' as html; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/homeserver_picker/homeserver_picker_view.dart'; -import 'package:fluffychat/pages/homeserver_picker/public_homeserver.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/app_lock.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -26,7 +24,8 @@ import 'package:fluffychat/utils/tor_stub.dart' if (dart.library.html) 'package:tor_detector_web/tor_detector_web.dart'; class HomeserverPicker extends StatefulWidget { - const HomeserverPicker({super.key}); + final bool addMultiAccount; + const HomeserverPicker({required this.addMultiAccount, super.key}); @override HomeserverPickerController createState() => HomeserverPickerController(); @@ -64,6 +63,21 @@ class HomeserverPickerController extends State { String? _lastCheckedUrl; + Timer? _checkHomeserverCooldown; + + tryCheckHomeserverActionWithCooldown([_]) { + _checkHomeserverCooldown?.cancel(); + _checkHomeserverCooldown = Timer( + const Duration(milliseconds: 500), + checkHomeserverAction, + ); + } + + tryCheckHomeserverActionWithoutCooldown([_]) { + _checkHomeserverCooldown?.cancel(); + checkHomeserverAction(); + } + /// 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 @@ -74,7 +88,7 @@ class HomeserverPickerController extends State { if (homeserverController.text == _lastCheckedUrl) return; _lastCheckedUrl = homeserverController.text; setState(() { - error = _rawLoginTypes = loginFlows = null; + error = loginFlows = null; isLoading = true; }); @@ -86,12 +100,6 @@ class HomeserverPickerController extends State { final client = Matrix.of(context).getLoginClient(); final (_, _, loginFlows) = await client.checkHomeserver(homeserver); this.loginFlows = loginFlows; - if (supportsSso) { - _rawLoginTypes = await client.request( - RequestType.GET, - '/client/v3/login', - ); - } } catch (e) { setState(() => error = (e).toLocalizedString(context)); } finally { @@ -113,9 +121,7 @@ class HomeserverPickerController extends State { bool get supportsPasswordLogin => _supportsFlow('m.login.password'); - Map? _rawLoginTypes; - - void ssoLoginAction(String? id) async { + void ssoLoginAction() async { final redirectUrl = kIsWeb ? Uri.parse(html.window.location.href) .resolveUri( @@ -127,7 +133,7 @@ class HomeserverPickerController extends State { : 'http://localhost:3001//login'; final url = Matrix.of(context).getLoginClient().homeserver!.replace( - path: '/_matrix/client/v3/login/sso/redirect${id == null ? '' : '/$id'}', + path: '/_matrix/client/v3/login/sso/redirect', queryParameters: {'redirectUrl': redirectUrl}, ); @@ -164,39 +170,6 @@ class HomeserverPickerController extends State { } } - 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; - } - - List? cachedHomeservers; - - Future> loadHomeserverList() async { - if (cachedHomeservers != null) return cachedHomeservers!; - final result = await Matrix.of(context) - .getLoginClient() - .httpClient - .get(AppConfig.homeserverList); - final resultJson = jsonDecode(result.body)['public_servers'] as List; - final homeserverList = - resultJson.map((json) => PublicHomeserver.fromJson(json)).toList(); - return cachedHomeservers = homeserverList; - } - void login() => context.push( '${GoRouter.of(context).routeInformationProvider.value.uri.path}/login', ); diff --git a/lib/pages/homeserver_picker/homeserver_picker_view.dart b/lib/pages/homeserver_picker/homeserver_picker_view.dart index dd6c50d0..3fd7bd24 100644 --- a/lib/pages/homeserver_picker/homeserver_picker_view.dart +++ b/lib/pages/homeserver_picker/homeserver_picker_view.dart @@ -1,15 +1,13 @@ import 'package:flutter/material.dart'; -import 'package:collection/collection.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:url_launcher/url_launcher_string.dart'; +import 'package:flutter_linkify/flutter_linkify.dart'; +import 'package:url_launcher/url_launcher.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/widgets/layouts/login_scaffold.dart'; import 'package:fluffychat/widgets/matrix.dart'; import '../../config/themes.dart'; -import '../../widgets/mxc_image.dart'; -import 'homeserver_app_bar.dart'; import 'homeserver_picker.dart'; class HomeserverPickerView extends StatelessWidget { @@ -21,22 +19,14 @@ class HomeserverPickerView extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); - final identityProviders = controller.identityProviders; - final errorText = controller.error; - final publicHomeserver = controller.cachedHomeservers?.singleWhereOrNull( - (homeserver) => - homeserver.name == - controller.homeserverController.text.trim().toLowerCase(), - ); - final regLink = publicHomeserver?.regLink; return LoginScaffold( enforceMobileMode: Matrix.of(context).client.isLogged(), - appBar: AppBar( - titleSpacing: 12, - automaticallyImplyLeading: false, - surfaceTintColor: theme.colorScheme.surface, - title: HomeserverAppBar(controller: controller), - ), + appBar: controller.widget.addMultiAccount + ? AppBar( + centerTitle: true, + title: Text(L10n.of(context)!.addAccount), + ) + : null, body: Column( children: [ // display a prominent banner to import session for TOR browser @@ -62,164 +52,133 @@ class HomeserverPickerView extends StatelessWidget { ), ), ), + Image.asset( + 'assets/banner_transparent.png', + ), Expanded( - child: controller.isLoading - ? const Center(child: CircularProgressIndicator.adaptive()) - : ListView( - children: [ - if (errorText != null) ...[ - const SizedBox(height: 12), - const Center( - child: Icon( - Icons.error_outline, - size: 48, - color: Colors.orange, - ), - ), - const SizedBox(height: 12), - Center( - child: Text( - errorText, - textAlign: TextAlign.center, - style: TextStyle( - color: theme.colorScheme.error, - fontSize: 18, - ), - ), - ), - Center( - child: Text( - L10n.of(context)! - .pleaseTryAgainLaterOrChooseDifferentServer, - textAlign: TextAlign.center, - style: TextStyle( - color: theme.colorScheme.error, - fontSize: 12, - ), - ), - ), - const SizedBox(height: 36), - ] else - Padding( - padding: const EdgeInsets.only( - top: 0.0, - right: 8.0, - left: 8.0, - bottom: 16.0, - ), - child: Image.asset( - 'assets/banner_transparent.png', - ), - ), - if (identityProviders != null) ...[ - ...identityProviders.map( - (provider) => _LoginButton( - icon: provider.icon == null - ? const Icon( - Icons.open_in_new_outlined, - size: 16, - ) - : Material( - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), - clipBehavior: Clip.hardEdge, - child: MxcImage( - placeholder: (_) => const Icon( - Icons.open_in_new_outlined, - size: 16, - ), - uri: Uri.parse(provider.icon!), - width: 24, - height: 24, - isThumbnail: false, - //isThumbnail: false, - ), - ), - label: L10n.of(context)!.signInWith( - provider.name ?? - provider.brand ?? - L10n.of(context)!.singlesignon, - ), - onPressed: () => - controller.ssoLoginAction(provider.id), - ), - ), - ], - if (controller.supportsPasswordLogin) - _LoginButton( - onPressed: controller.login, - label: L10n.of(context)!.signInWithPassword, - icon: const Icon(Icons.lock_open_outlined, size: 16), - ), - if (regLink != null) - _LoginButton( - onPressed: () => launchUrlString(regLink), - icon: const Icon( - Icons.open_in_new_outlined, - size: 16, - ), - label: L10n.of(context)!.register, - ), - _LoginButton( - onPressed: controller.restoreBackup, - label: L10n.of(context)!.hydrate, - withBorder: false, + child: ListView( + padding: const EdgeInsets.only( + top: 16.0, + right: 8.0, + left: 8.0, + bottom: 16.0, + ), + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: TextField( + onChanged: controller.tryCheckHomeserverActionWithCooldown, + onEditingComplete: + controller.tryCheckHomeserverActionWithoutCooldown, + onSubmitted: + controller.tryCheckHomeserverActionWithoutCooldown, + onTap: controller.tryCheckHomeserverActionWithCooldown, + controller: controller.homeserverController, + 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), ), - const SizedBox(height: 16), - ], + 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 (controller.supportsPasswordLogin || controller.supportsSso) + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: 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)!.connect), + ), + ), + if (controller.supportsPasswordLogin && controller.supportsSso) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: 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), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: 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), + ), + ), + ], + ), ), ], ), ); } } - -class _LoginButton extends StatelessWidget { - final Widget? icon; - final String label; - final void Function() onPressed; - final bool withBorder; - - const _LoginButton({ - this.icon, - required this.label, - required this.onPressed, - this.withBorder = true, - }); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - - final icon = this.icon; - return Container( - margin: const EdgeInsets.only(bottom: 12), - padding: const EdgeInsets.symmetric(horizontal: 16), - alignment: Alignment.center, - child: SizedBox( - width: double.infinity, - child: OutlinedButton.icon( - style: OutlinedButton.styleFrom( - side: FluffyThemes.isColumnMode(context) - ? BorderSide.none - : BorderSide( - color: theme.colorScheme.outlineVariant, - width: 1, - ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(99), - ), - foregroundColor: theme.colorScheme.onSurface, - backgroundColor: - withBorder ? theme.colorScheme.surface : Colors.transparent, - ), - onPressed: onPressed, - label: Text(label), - icon: icon ?? const SizedBox.shrink(), - ), - ), - ); - } -} diff --git a/lib/pages/homeserver_picker/public_homeserver.dart b/lib/pages/homeserver_picker/public_homeserver.dart deleted file mode 100644 index 4eef2aa5..00000000 --- a/lib/pages/homeserver_picker/public_homeserver.dart +++ /dev/null @@ -1,73 +0,0 @@ -class PublicHomeserver { - final String name; - final String? clientDomain; - final String? isp; - final String? staffJur; - final bool? usingVanillaReg; - final String? description; - final String? regMethod; - final String? regLink; - final String? software; - final String? version; - final bool? captcha; - final bool? email; - final List? languages; - final List? features; - final int? onlineStatus; - final String? serverDomain; - final int? verStatus; - final int? roomDirectory; - final bool? slidingSync; - final bool? ipv6; - - const PublicHomeserver({ - required this.name, - this.clientDomain, - this.isp, - this.staffJur, - this.usingVanillaReg, - this.description, - this.regMethod, - this.regLink, - this.software, - this.version, - this.captcha, - this.email, - this.languages, - this.features, - this.onlineStatus, - this.serverDomain, - this.verStatus, - this.roomDirectory, - this.slidingSync, - this.ipv6, - }); - - factory PublicHomeserver.fromJson(Map json) => - PublicHomeserver( - name: json['name'], - clientDomain: json['client_domain'], - isp: json['isp'], - staffJur: json['staff_jur'], - usingVanillaReg: json['using_vanilla_reg'], - description: json['description'], - regMethod: json['reg_method'], - regLink: json['reg_link'], - software: json['software'], - version: json['version'], - captcha: json['captcha'], - email: json['email'], - languages: json['languages'] == null - ? null - : List.from(json['languages']), - features: json['features'] == null - ? null - : List.from(json['features']), - onlineStatus: json['online_status'], - serverDomain: json['server_domain'], - verStatus: json['ver_status'], - roomDirectory: json['room_directory'], - slidingSync: json['sliding_sync'], - ipv6: json['ipv6'], - ); -} diff --git a/lib/pages/login/login_view.dart b/lib/pages/login/login_view.dart index 0358c44a..a8ec3f50 100644 --- a/lib/pages/login/login_view.dart +++ b/lib/pages/login/login_view.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/widgets/layouts/login_scaffold.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'login.dart'; @@ -24,11 +23,6 @@ class LoginView extends StatelessWidget { final title = L10n.of(context)!.logInTo(homeserver); final titleParts = title.split(homeserver); - final textFieldFillColor = FluffyThemes.isColumnMode(context) - ? theme.colorScheme.surface - // ignore: deprecated_member_use - : theme.colorScheme.surfaceVariant; - return LoginScaffold( enforceMobileMode: Matrix.of(context).client.isLogged(), appBar: AppBar( @@ -73,8 +67,8 @@ class LoginView extends StatelessWidget { prefixIcon: const Icon(Icons.account_box_outlined), errorText: controller.usernameError, errorStyle: const TextStyle(color: Colors.orange), - fillColor: textFieldFillColor, - hintText: L10n.of(context)!.emailOrUsername, + hintText: '@username:localpart', + labelText: L10n.of(context)!.emailOrUsername, ), ), ), @@ -94,7 +88,6 @@ class LoginView extends StatelessWidget { prefixIcon: const Icon(Icons.lock_outlined), errorText: controller.passwordError, errorStyle: const TextStyle(color: Colors.orange), - fillColor: textFieldFillColor, suffixIcon: IconButton( onPressed: controller.toggleShowPassword, icon: Icon( @@ -104,21 +97,21 @@ class LoginView extends StatelessWidget { color: Colors.black, ), ), - hintText: L10n.of(context)!.password, + hintText: '******', + labelText: L10n.of(context)!.password, ), ), ), const SizedBox(height: 16), Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: ElevatedButton.icon( + child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: theme.colorScheme.primary, foregroundColor: theme.colorScheme.onPrimary, ), onPressed: controller.loading ? null : controller.login, - icon: const Icon(Icons.login_outlined), - label: controller.loading + child: controller.loading ? const LinearProgressIndicator() : Text(L10n.of(context)!.login), ), @@ -126,15 +119,14 @@ class LoginView extends StatelessWidget { const SizedBox(height: 16), Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: TextButton.icon( + child: TextButton( onPressed: controller.loading ? () {} : controller.passwordForgotten, style: TextButton.styleFrom( foregroundColor: theme.colorScheme.error, ), - icon: const Icon(Icons.safety_check_outlined), - label: Text(L10n.of(context)!.passwordForgotten), + child: Text(L10n.of(context)!.passwordForgotten), ), ), const SizedBox(height: 16), diff --git a/lib/widgets/layouts/login_scaffold.dart b/lib/widgets/layouts/login_scaffold.dart index 7337e8eb..08ed8d75 100644 --- a/lib/widgets/layouts/login_scaffold.dart +++ b/lib/widgets/layouts/login_scaffold.dart @@ -78,7 +78,7 @@ class LoginScaffold extends StatelessWidget { child: ConstrainedBox( constraints: isMobileMode ? const BoxConstraints() - : const BoxConstraints(maxWidth: 480, maxHeight: 720), + : const BoxConstraints(maxWidth: 480, maxHeight: 640), child: BackdropFilter( filter: ImageFilter.blur( sigmaX: 10.0, diff --git a/pubspec.lock b/pubspec.lock index 98aa4393..ede4347e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -2351,10 +2351,10 @@ packages: dependency: "direct overridden" description: name: win32 - sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" + sha256: "015002c060f1ae9f41a818f2d5640389cc05283e368be19dc8d77cecb43c40c9" url: "https://pub.dev" source: hosted - version: "5.5.0" + version: "5.5.3" win32_registry: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 853df174..d338f9a7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -160,4 +160,4 @@ dependency_overrides: git: url: https://github.com/TheOneWithTheBraid/keyboard_shortcuts.git ref: null-safety - win32: 5.5.0 + win32: 5.5.3