fix: onboarding UX

- add missing label to progress indicator
- add option to enable locale based homeservers (disabled by default)

Signed-off-by: TheOneWithTheBraid <the-one@with-the-braid.cf>
This commit is contained in:
TheOneWithTheBraid 2022-05-21 10:46:39 +02:00
parent 014c1574ee
commit b4cc484f38
8 changed files with 416 additions and 34 deletions

View File

@ -376,6 +376,7 @@
"type": "text",
"placeholders": {}
},
"benchmarkingHomeserver": "Finding the fastest instances around you...",
"changeTheme": "Change your style",
"@changeTheme": {
"type": "text",

View File

@ -6,5 +6,6 @@
"privacy_url": "https://fluffychat.im/en/privacy.html",
"render_html": false,
"hide_redacted_events": false,
"hide_unknown_events": false
"hide_unknown_events": false,
"use_location_based_homeserver": false
}

View File

@ -4,10 +4,13 @@ import 'package:matrix/matrix.dart';
abstract class AppConfig {
static String _applicationName = 'FluffyChat';
static String get applicationName => _applicationName;
static String? _applicationWelcomeMessage;
static String? get applicationWelcomeMessage => _applicationWelcomeMessage;
static String _defaultHomeserver = 'matrix.org';
static String get defaultHomeserver => _defaultHomeserver;
static double bubbleSizeFactor = 1;
static double fontSizeFactor = 1;
@ -21,12 +24,14 @@ abstract class AppConfig {
static const Color secondaryColor = Color(0xFF41a2bc);
static String _privacyUrl =
'https://gitlab.com/famedly/fluffychat/-/blob/main/PRIVACY.md';
static String get privacyUrl => _privacyUrl;
static const String enablePushTutorial =
'https://www.reddit.com/r/fluffychat/comments/qn6liu/enable_push_notifications_without_google_services/';
static const String appId = 'im.fluffychat.FluffyChat';
static const String appOpenUrlScheme = 'im.fluffychat';
static String _webBaseUrl = 'https://fluffychat.im/web';
static String get webBaseUrl => _webBaseUrl;
static const String sourceCodeUrl = 'https://gitlab.com/famedly/fluffychat';
static const String supportUrl =
@ -60,6 +65,7 @@ abstract class AppConfig {
'https://github.com/googlefonts/noto-emoji/';
static const double borderRadius = 16.0;
static const double columnWidth = 360.0;
static bool useLocaleBasedHomeserver = false;
static void loadFromJson(Map<String, dynamic> json) {
if (json['chat_color'] != null) {
@ -95,5 +101,8 @@ abstract class AppConfig {
if (json['hide_unknown_events'] is bool) {
hideUnknownEvents = json['hide_unknown_events'];
}
if (json['use_location_based_homeserver'] is bool) {
useLocaleBasedHomeserver = json['use_location_based_homeserver'];
}
}
}

View File

@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
part 'homeserver_locale_map.dart';
class HomeserverLocale {
final Locale locale;
const HomeserverLocale(this.locale);
String? choose() {
if (locale.countryCode == null) return null;
final country = locale.countryCode!.toLowerCase();
try {
if (_homeserverByLocale.containsKey(country)) {
return _homeserverByLocale[country];
} else if (_countryCodeContinent.containsKey(country)) {
final continent = _countryCodeContinent[country];
return _homeserverByContinent[continent];
}
} catch (e) {
Logs().w('Error matching country code $country');
}
return null;
}
}

View File

@ -0,0 +1,281 @@
part of 'homeserver_locale.dart';
final Map<String, String> _homeserverByContinent = {
'af': 'sykorp.com',
'as': 'sibnsk.net',
'an': 'buyvm.net',
'aq': 'arcticfoxes.net',
'eu': 'gemeinsam.jetzt',
'sa': 'privex.io',
'oc': 'mtrx.nz',
};
final Map<String, String> _homeserverByLocale = {
'ae': 'sykorp.com',
'at': 'gemeinsam.jetzt',
'ch': 'pragma-messenger.ch',
'de': 'envs.de',
'hu': 'grin.hu',
'it': 'aria-net.org',
'nl': 'nltrix.net',
'nz': 'mtrx.nz',
'ru': 'rumatrix.org',
'us': 'buyvm.net',
};
final _countryCodeContinent = {
'af': 'as',
'al': 'eu',
'aq': 'an',
'dz': 'af',
'as': 'oc',
'ad': 'eu',
'ao': 'af',
'ag': 'na',
'az': 'eu',
'ar': 'sa',
'au': 'oc',
'at': 'eu',
'bs': 'na',
'bh': 'as',
'bd': 'as',
'am': 'eu',
'bb': 'na',
'be': 'eu',
'bm': 'na',
'bt': 'as',
'bo': 'sa',
'ba': 'eu',
'bw': 'af',
'bv': 'an',
'br': 'sa',
'bz': 'na',
'io': 'as',
'sb': 'oc',
'vg': 'na',
'bn': 'as',
'bg': 'eu',
'mm': 'as',
'bi': 'af',
'by': 'eu',
'kh': 'as',
'cm': 'af',
'ca': 'na',
'cv': 'af',
'ky': 'na',
'cf': 'af',
'lk': 'as',
'td': 'af',
'cl': 'sa',
'cn': 'as',
'tw': 'as',
'cx': 'as',
'cc': 'as',
'co': 'sa',
'km': 'af',
'yt': 'af',
'cg': 'af',
'cd': 'af',
'ck': 'oc',
'cr': 'na',
'hr': 'eu',
'cu': 'na',
'cy': 'eu',
'cz': 'eu',
'bj': 'af',
'dk': 'eu',
'dm': 'na',
'do': 'na',
'ec': 'sa',
'sv': 'na',
'gq': 'af',
'et': 'af',
'er': 'af',
'ee': 'eu',
'fo': 'eu',
'fk': 'sa',
'gs': 'an',
'fj': 'oc',
'fi': 'eu',
'ax': 'eu',
'fr': 'eu',
'gf': 'sa',
'pf': 'oc',
'tf': 'an',
'dj': 'af',
'ga': 'af',
'ge': 'eu',
'gm': 'af',
'ps': 'as',
'de': 'eu',
'gh': 'af',
'gi': 'eu',
'ki': 'oc',
'gr': 'eu',
'gl': 'na',
'gd': 'na',
'gp': 'na',
'gu': 'oc',
'gt': 'na',
'gn': 'af',
'gy': 'sa',
'ht': 'na',
'hm': 'an',
'va': 'eu',
'hn': 'na',
'hk': 'as',
'hu': 'eu',
'is': 'eu',
'in': 'as',
'id': 'as',
'ir': 'as',
'iq': 'as',
'ie': 'eu',
'il': 'as',
'it': 'eu',
'ci': 'af',
'jm': 'na',
'jp': 'as',
'kz': 'eu',
'jo': 'as',
'ke': 'af',
'kp': 'as',
'kr': 'as',
'kw': 'as',
'kg': 'as',
'la': 'as',
'lb': 'as',
'ls': 'af',
'lv': 'eu',
'lr': 'af',
'ly': 'af',
'li': 'eu',
'lt': 'eu',
'lu': 'eu',
'mo': 'as',
'mg': 'af',
'mw': 'af',
'my': 'as',
'mv': 'as',
'ml': 'af',
'mt': 'eu',
'mq': 'na',
'mr': 'af',
'mu': 'af',
'mx': 'na',
'mc': 'eu',
'mn': 'as',
'md': 'eu',
'me': 'eu',
'ms': 'na',
'ma': 'af',
'mz': 'af',
'om': 'as',
'na': 'af',
'nr': 'oc',
'np': 'as',
'nl': 'eu',
'an': 'na',
'cw': 'na',
'aw': 'na',
'sx': 'na',
'bq': 'na',
'nc': 'oc',
'vu': 'oc',
'nz': 'oc',
'ni': 'na',
'ne': 'af',
'ng': 'af',
'nu': 'oc',
'nf': 'oc',
'no': 'eu',
'mp': 'oc',
'um': 'oc',
'fm': 'oc',
'mh': 'oc',
'pw': 'oc',
'pk': 'as',
'pa': 'na',
'pg': 'oc',
'py': 'sa',
'pe': 'sa',
'ph': 'as',
'pn': 'oc',
'pl': 'eu',
'pt': 'eu',
'gw': 'af',
'tl': 'as',
'pr': 'na',
'qa': 'as',
're': 'af',
'ro': 'eu',
'ru': 'as',
'rw': 'af',
'bl': 'na',
'sh': 'af',
'kn': 'na',
'ai': 'na',
'lc': 'na',
'mf': 'na',
'pm': 'na',
'vc': 'na',
'sm': 'eu',
'st': 'af',
'sa': 'as',
'sn': 'af',
'rs': 'eu',
'sc': 'af',
'sl': 'af',
'sg': 'as',
'sk': 'eu',
'vn': 'as',
'si': 'eu',
'so': 'af',
'za': 'af',
'zw': 'af',
'es': 'eu',
'ss': 'af',
'eh': 'af',
'sd': 'af',
'sr': 'sa',
'sj': 'eu',
'sz': 'af',
'se': 'eu',
'ch': 'eu',
'sy': 'as',
'tj': 'as',
'th': 'as',
'tg': 'af',
'tk': 'oc',
'to': 'oc',
'tt': 'na',
'ae': 'as',
'tn': 'af',
'tr': 'as',
'tm': 'as',
'tc': 'na',
'tv': 'oc',
'ug': 'af',
'ua': 'eu',
'mk': 'eu',
'eg': 'af',
'gb': 'eu',
'gg': 'eu',
'je': 'eu',
'im': 'eu',
'tz': 'af',
'us': 'na',
'vi': 'na',
'bf': 'af',
'uy': 'sa',
'uz': 'as',
've': 'sa',
'wf': 'oc',
'ws': 'oc',
'ye': 'as',
'zm': 'af',
'xx': 'oc',
'xe': 'as',
'xd': 'as',
'xs': 'as',
};

View File

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart';
@ -7,6 +8,7 @@ import 'package:matrix_homeserver_recommendations/matrix_homeserver_recommendati
import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/homeserver_locale.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';
@ -28,6 +30,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
String? error;
List<HomeserverBenchmarkResult>? benchmarkResults;
bool displayServerList = false;
bool get loadingHomeservers =>
AppConfig.allowOtherHomeservers && benchmarkResults == null;
String searchTerm = '';
@ -129,6 +132,12 @@ class HomeserverPickerController extends State<HomeserverPicker> {
}
}
void _defaultHomeserverByLocale() {
final serverMatcher = HomeserverLocale(window.locale);
setState(() => homeserverController.text =
serverMatcher.choose() ?? AppConfig.defaultHomeserver);
}
@override
void dispose() {
homeserverFocusNode.removeListener(_updateFocus);
@ -138,6 +147,10 @@ class HomeserverPickerController extends State<HomeserverPicker> {
@override
void initState() {
homeserverFocusNode.addListener(_updateFocus);
if (AppConfig.useLocaleBasedHomeserver) {
WidgetsBinding.instance
.addPostFrameCallback((timeStamp) => _defaultHomeserverByLocale());
}
super.initState();
}

View File

@ -1,11 +1,13 @@
import 'package:flutter/material.dart';
import 'package:animations/animations.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/homeserver_picker/homeserver_tile.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/layouts/login_scaffold.dart';
import 'homeserver_picker.dart';
@ -63,40 +65,58 @@ class HomeserverPickerView extends StatelessWidget {
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,
color: Colors.black,
),
onPressed: () =>
controller.showServerInfo(server),
child: PageTransitionSwitcher(
transitionBuilder: (
Widget child,
Animation<double> primaryAnimation,
Animation<double> secondaryAnimation,
) {
return SharedAxisTransition(
animation: primaryAnimation,
secondaryAnimation: secondaryAnimation,
transitionType: SharedAxisTransitionType.scaled,
child: child,
fillColor: Colors.transparent,
);
},
child: ListTileTheme(
data: const ListTileThemeData(
iconColor: Colors.black,
textColor: Colors.black,
),
key: ValueKey(benchmarkResults),
child: benchmarkResults == null
? Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircularProgressIndicator
.adaptive(),
ListTile(
leading: const Icon(Icons.rocket),
title: Text(L10n.of(context)!
.benchmarkingHomeserver),
),
onTap: () => controller.setServer(
server.homeserver.baseUrl.host),
title: Text(
server.homeserver.baseUrl.host,
style: const TextStyle(
color: Colors.black),
),
subtitle: Text(
server.homeserver.description ?? '',
style: TextStyle(
color: Colors.grey.shade700),
),
),
)
.toList(),
),
],
),
))
: ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount:
controller.filteredHomeservers.length,
itemBuilder: (context, index) {
final server =
controller.filteredHomeservers[index];
return HomeserverTile(
server: server, controller: controller);
},
),
),
),
),
),
Wrap(

View File

@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:matrix_homeserver_recommendations/matrix_homeserver_recommendations.dart';
import 'package:fluffychat/pages/homeserver_picker/homeserver_picker.dart';
class HomeserverTile extends StatelessWidget {
final HomeserverBenchmarkResult server;
final HomeserverPickerController controller;
const HomeserverTile(
{Key? key, required this.server, required this.controller})
: super(key: key);
@override
Widget build(BuildContext context) {
return 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 ?? '',
style: TextStyle(color: Colors.grey.shade700),
),
);
}
}