refactor: Simplify login UX
parent
f17b09f56c
commit
dec588d0c0
@ -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<PublicHomeserver>(
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<String>? languages;
|
|
||||||
final List<Object>? 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<String, dynamic> 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<String>.from(json['languages']),
|
|
||||||
features: json['features'] == null
|
|
||||||
? null
|
|
||||||
: List<Object>.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'],
|
|
||||||
);
|
|
||||||
}
|
|
Loading…
Reference in New Issue