mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-11-27 23:09:35 +01:00
Merge branch 'krille/joinmatrix-org-homeserver-list' into 'main'
feat: Onboarding with dynamic homeservers from joinmatrix.org See merge request famedly/fluffychat!830
This commit is contained in:
commit
dae25018de
71
lib/pages/homeserver_picker/homeserver_bottom_sheet.dart
Normal file
71
lib/pages/homeserver_picker/homeserver_bottom_sheet.dart
Normal file
@ -0,0 +1,71 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:matrix_homeserver_recommendations/matrix_homeserver_recommendations.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class HomeserverBottomSheet extends StatelessWidget {
|
||||
final HomeserverBenchmarkResult homeserver;
|
||||
const HomeserverBottomSheet({required this.homeserver, Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final responseTime = homeserver.responseTime;
|
||||
final description = homeserver.homeserver.description;
|
||||
final rules = homeserver.homeserver.rules;
|
||||
final privacy = homeserver.homeserver.privacyPolicy;
|
||||
final registration = homeserver.homeserver.registration;
|
||||
final jurisdiction = homeserver.homeserver.jurisdiction;
|
||||
final homeserverSoftware = homeserver.homeserver.homeserverSoftware;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(homeserver.homeserver.baseUrl.host),
|
||||
),
|
||||
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: () => launch(homeserver.homeserver.baseUrl.toString()),
|
||||
leading: const Icon(Icons.link_outlined),
|
||||
title: Text(homeserver.homeserver.baseUrl.toString()),
|
||||
),
|
||||
if (registration != null)
|
||||
ListTile(
|
||||
onTap: () => launch(registration.toString()),
|
||||
leading: const Icon(Icons.person_add_outlined),
|
||||
title: Text(registration.toString()),
|
||||
),
|
||||
if (rules != null)
|
||||
ListTile(
|
||||
onTap: () => launch(rules.toString()),
|
||||
leading: const Icon(Icons.visibility_outlined),
|
||||
title: Text(rules.toString()),
|
||||
),
|
||||
if (privacy != null)
|
||||
ListTile(
|
||||
onTap: () => launch(privacy.toString()),
|
||||
leading: const Icon(Icons.shield_outlined),
|
||||
title: Text(privacy.toString()),
|
||||
),
|
||||
if (responseTime != null)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.timer_outlined),
|
||||
title: Text('${responseTime.inMilliseconds}ms'),
|
||||
),
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
@ -3,9 +3,11 @@ import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:matrix_homeserver_recommendations/matrix_homeserver_recommendations.dart';
|
||||
import 'package:vrouter/vrouter.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pages/homeserver_picker/homeserver_bottom_sheet.dart';
|
||||
import 'package:fluffychat/pages/homeserver_picker/homeserver_picker_view.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../../utils/localized_exception_extension.dart';
|
||||
@ -19,9 +21,62 @@ class HomeserverPicker extends StatefulWidget {
|
||||
|
||||
class HomeserverPickerController extends State<HomeserverPicker> {
|
||||
bool isLoading = false;
|
||||
final TextEditingController homeserverController =
|
||||
TextEditingController(text: AppConfig.defaultHomeserver);
|
||||
final TextEditingController homeserverController = TextEditingController(
|
||||
text: AppConfig.defaultHomeserver,
|
||||
);
|
||||
final FocusNode homeserverFocusNode = FocusNode();
|
||||
String? error;
|
||||
List<HomeserverBenchmarkResult>? benchmarkResults;
|
||||
bool displayServerList = false;
|
||||
bool get loadingHomeservers =>
|
||||
AppConfig.allowOtherHomeservers && benchmarkResults == null;
|
||||
String searchTerm = '';
|
||||
|
||||
void _updateFocus() {
|
||||
if (benchmarkResults == null) _loadHomeserverList();
|
||||
setState(() {
|
||||
displayServerList = homeserverFocusNode.hasFocus;
|
||||
});
|
||||
}
|
||||
|
||||
void showServerInfo(HomeserverBenchmarkResult server) => showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (_) => HomeserverBottomSheet(
|
||||
homeserver: server,
|
||||
),
|
||||
);
|
||||
|
||||
void onChanged(String text) => setState(() {
|
||||
searchTerm = text;
|
||||
});
|
||||
|
||||
List<HomeserverBenchmarkResult> get filteredHomeservers => benchmarkResults!
|
||||
.where((element) =>
|
||||
element.homeserver.baseUrl.host.contains(searchTerm) ||
|
||||
(element.homeserver.description?.contains(searchTerm) ?? false))
|
||||
.toList();
|
||||
|
||||
void _loadHomeserverList() async {
|
||||
try {
|
||||
final homeserverList = await JoinmatrixOrgParser().fetchHomeservers();
|
||||
final benchmark = await HomeserverListProvider.benchmarkHomeserver(
|
||||
homeserverList,
|
||||
timeout: const Duration(seconds: 10),
|
||||
);
|
||||
setState(() {
|
||||
benchmarkResults = benchmark;
|
||||
});
|
||||
} catch (e, s) {
|
||||
Logs().e('Homeserver benchmark failed', e, s);
|
||||
benchmarkResults = [];
|
||||
}
|
||||
}
|
||||
|
||||
void setServer(String server) => setState(() {
|
||||
homeserverController.text = server;
|
||||
searchTerm = '';
|
||||
homeserverFocusNode.unfocus();
|
||||
});
|
||||
|
||||
/// 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
|
||||
@ -29,8 +84,11 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
||||
/// login type.
|
||||
Future<void> checkHomeserverAction() async {
|
||||
setState(() {
|
||||
homeserverFocusNode.unfocus();
|
||||
error = null;
|
||||
isLoading = true;
|
||||
searchTerm = '';
|
||||
displayServerList = false;
|
||||
});
|
||||
|
||||
try {
|
||||
@ -68,6 +126,18 @@ class HomeserverPickerController extends State<HomeserverPicker> {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
homeserverFocusNode.removeListener(_updateFocus);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
homeserverFocusNode.addListener(_updateFocus);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Matrix.of(context).navigatorContext = context;
|
||||
|
@ -17,6 +17,7 @@ class HomeserverPickerView extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final benchmarkResults = controller.benchmarkResults;
|
||||
return LoginScaffold(
|
||||
appBar: VRouter.of(context).path == '/home'
|
||||
? null
|
||||
@ -26,18 +27,19 @@ class HomeserverPickerView extends StatelessWidget {
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxHeight: 256),
|
||||
child: Image.asset(
|
||||
'assets/info-logo.png',
|
||||
),
|
||||
),
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: controller.displayServerList ? 0 : 256),
|
||||
alignment: Alignment.center,
|
||||
child: Image.asset('assets/info-logo.png'),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: TextField(
|
||||
focusNode: controller.homeserverFocusNode,
|
||||
controller: controller.homeserverController,
|
||||
onChanged: controller.onChanged,
|
||||
decoration: FluffyThemes.loginTextFieldDecoration(
|
||||
labelText: L10n.of(context)!.homeserver,
|
||||
hintText: L10n.of(context)!.enterYourHomeserver,
|
||||
@ -49,6 +51,42 @@ class HomeserverPickerView extends StatelessWidget {
|
||||
autocorrect: false,
|
||||
),
|
||||
),
|
||||
if (controller.displayServerList)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Material(
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius),
|
||||
color: Colors.white.withAlpha(200),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: benchmarkResults == null
|
||||
? const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: CircularProgressIndicator.adaptive(),
|
||||
))
|
||||
: Column(
|
||||
children: controller.filteredHomeservers
|
||||
.map(
|
||||
(server) => ListTile(
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.info_outlined),
|
||||
onPressed: () =>
|
||||
controller.showServerInfo(server),
|
||||
),
|
||||
onTap: () => controller.setServer(
|
||||
server.homeserver.baseUrl.host),
|
||||
title: Text(
|
||||
server.homeserver.baseUrl.host,
|
||||
),
|
||||
subtitle: Text(
|
||||
server.homeserver.description ?? ''),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
children: [
|
||||
|
@ -1019,6 +1019,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.5.3"
|
||||
matrix_homeserver_recommendations:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: matrix_homeserver_recommendations
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
matrix_link_text:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -58,6 +58,7 @@ dependencies:
|
||||
localstorage: ^4.0.0+1
|
||||
lottie: ^1.2.2
|
||||
matrix: ^0.8.20
|
||||
matrix_homeserver_recommendations: ^0.2.0
|
||||
matrix_link_text: ^1.0.2
|
||||
native_imaging:
|
||||
git: https://gitlab.com/famedly/company/frontend/libraries/native_imaging.git
|
||||
|
Loading…
Reference in New Issue
Block a user