1
0
Fork 0
forked from Nexus/nexus

Compare commits

..

No commits in common. "28be105552b31cb70dbda92dea62f5f769221787" and "3be13f5589583cb26eced7881cfebb0ed3b1965f" have entirely different histories.

4 changed files with 175 additions and 182 deletions

View file

@ -263,12 +263,12 @@ class ClientController extends AsyncNotifier<int> {
} }
} }
Future<Uri?> discoverHomeserver(Uri homeserver) async { Future<String?> discoverHomeserver(Uri homeserver) async {
try { try {
final response = await _sendCommand("discover_homeserver", { final response = await _sendCommand("discover_homeserver", {
"user_id": "@fake-user:${homeserver.host}", "user_id": "@fake-user:${homeserver.host}",
}); });
return Uri.parse(response["m.homeserver"]?["base_url"]); return response["m.homeserver"]?["base_url"];
} catch (error) { } catch (error) {
return null; return null;
} }

View file

@ -123,7 +123,7 @@ class App extends StatelessWidget {
return Loading(); return Loading();
} }
if (!clientState.isLoggedIn) { if (!clientState.isLoggedIn) {
return SelectServerPage(); return SelectServerPage();
} else if (!clientState.isVerified) { } else if (!clientState.isVerified) {
return VerifyPage(); return VerifyPage();

View file

@ -15,8 +15,17 @@ class LoginPage extends HookConsumerWidget {
final client = ref.watch(ClientController.provider.notifier); final client = ref.watch(ClientController.provider.notifier);
final isLoggingIn = useState(false); final isLoggingIn = useState(false);
final hasError = useState(false); final hasError = useState(false);
final userNameFocusNode = useFocusNode();
final passwordFocusNode = useFocusNode(); final passwordFocusNode = useFocusNode();
//This is the safe way to request things directly after page load.
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((_) {
userNameFocusNode.requestFocus();
});
return null;
}, []);
final theme = Theme.of(context); final theme = Theme.of(context);
final username = useTextEditingController(); final username = useTextEditingController();
@ -31,8 +40,8 @@ class LoginPage extends HookConsumerWidget {
LoginRequest( LoginRequest(
username: username.text, username: username.text,
password: password.text, password: password.text,
homeserverUrl: homeserver.origin, homeserverUrl: homeserver.origin
), )
); );
if (!context.mounted) return; if (!context.mounted) return;
@ -43,13 +52,16 @@ class LoginPage extends HookConsumerWidget {
SnackBar( SnackBar(
content: Text( content: Text(
"Login failed. Is your password right?\nError: $error", "Login failed. Is your password right?\nError: $error",
style: TextStyle(color: theme.colorScheme.onErrorContainer), style: TextStyle(
color: theme.colorScheme.onErrorContainer,
),
), ),
backgroundColor: theme.colorScheme.errorContainer, backgroundColor: theme.colorScheme.errorContainer,
), ),
); );
isLoggingIn.value = false; isLoggingIn.value = false;
} else { }
else{
Navigator.pop(context); Navigator.pop(context);
} }
passwordFocusNode.requestFocus(); passwordFocusNode.requestFocus();
@ -60,8 +72,8 @@ class LoginPage extends HookConsumerWidget {
appBar: Appbar( appBar: Appbar(
leading: IconButton( leading: IconButton(
icon: Icon(Icons.arrow_back), icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context)
), ),
), ),
body: AlertDialog( body: AlertDialog(
title: Text("Login to ${homeserver.host}"), title: Text("Login to ${homeserver.host}"),
@ -69,10 +81,12 @@ class LoginPage extends HookConsumerWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text("Enter your login credentials:"), Text(
"Enter your login credentials:",
),
SizedBox(height: 12), SizedBox(height: 12),
TextField( TextField(
autofocus: true, focusNode: userNameFocusNode,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
onChanged: (newVal) { onChanged: (newVal) {
if (hasError.value) { if (hasError.value) {
@ -81,22 +95,14 @@ class LoginPage extends HookConsumerWidget {
}, },
decoration: InputDecoration( decoration: InputDecoration(
label: Text("Username"), label: Text("Username"),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: hasError.value ? 4 : 2,
color: hasError.value
? theme.colorScheme.error
: theme.colorScheme.primary,
),
),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
width: hasError.value ? 4 : 2, width: hasError.value ? 4 : 2,
color: hasError.value color: hasError.value
? theme.colorScheme.error ? theme.colorScheme.error
: theme.colorScheme.primary, : theme.colorScheme.primary
), ),
), )
), ),
controller: username, controller: username,
), ),
@ -117,18 +123,18 @@ class LoginPage extends HookConsumerWidget {
borderSide: BorderSide( borderSide: BorderSide(
width: hasError.value ? 4 : 2, width: hasError.value ? 4 : 2,
color: hasError.value color: hasError.value
? theme.colorScheme.error ? theme.colorScheme.error
: theme.colorScheme.primary, : theme.colorScheme.primary
), )
), ),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
width: hasError.value ? 4 : 2, width: hasError.value ? 4 : 2,
color: hasError.value color: hasError.value
? theme.colorScheme.error ? theme.colorScheme.error
: theme.colorScheme.primary, : theme.colorScheme.primary
), ),
), )
), ),
controller: password, controller: password,
obscureText: true, obscureText: true,
@ -136,7 +142,10 @@ class LoginPage extends HookConsumerWidget {
], ],
), ),
actions: [ actions: [
TextButton(onPressed: () => tryLogin(), child: Text("Sign In")), TextButton(
onPressed: () => tryLogin(),
child: Text("Sign In"),
),
], ],
), ),
); );

View file

@ -27,7 +27,7 @@ class SelectServerPage extends HookConsumerWidget {
Future<void> setHomeserver(Uri? newHomeserver) async { Future<void> setHomeserver(Uri? newHomeserver) async {
isLoading.value = true; isLoading.value = true;
if (newHomeserver?.hasScheme == false) { if(newHomeserver?.hasScheme == false){
newHomeserver = Uri.https(newHomeserver!.path); newHomeserver = Uri.https(newHomeserver!.path);
} }
@ -49,9 +49,7 @@ class SelectServerPage extends HookConsumerWidget {
); );
} else { } else {
homeserverUrl.text = newHomeserver!.origin; homeserverUrl.text = newHomeserver!.origin;
await Navigator.of(context).push( Navigator.push(context, MaterialPageRoute(builder: (_) => LoginPage(homeserver: Uri.parse(newUrl))));
MaterialPageRoute(builder: (_) => LoginPage(homeserver: newUrl)),
);
} }
} }
@ -62,162 +60,148 @@ class SelectServerPage extends HookConsumerWidget {
return Scaffold( return Scaffold(
appBar: Appbar(), appBar: Appbar(),
body: isLoading.value body: isLoading.value
? const Loading() ? const Loading()
: Center( : Center(
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 600), constraints: BoxConstraints(maxWidth: 600),
child: Column( child: Column(
children: [ children: [
Row( Row(
children: [
SvgPicture.asset("assets/icon.svg", width: 128),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SvgPicture.asset("assets/icon.svg", width: 128), Text("Nexus", style: theme.textTheme.displayMedium),
SizedBox(width: 12), Text(
Expanded( "A Simple Matrix Client",
child: Column( style: theme.textTheme.headlineMedium,
crossAxisAlignment: CrossAxisAlignment.start, overflow: TextOverflow.ellipsis,
children: [
Text(
"Nexus",
style: theme.textTheme.displayMedium,
),
Text(
"A Simple Matrix Client",
style: theme.textTheme.headlineMedium,
overflow: TextOverflow.ellipsis,
),
],
),
), ),
], ],
), ),
Padding( ),
padding: EdgeInsetsGeometry.symmetric(vertical: 12), ],
child: Divider(), ),
), Padding(
DividerText("Enter a homeserver domain:"), padding: EdgeInsetsGeometry.symmetric(vertical: 12),
Row( child: Divider(),
spacing: 8, ),
children: [ DividerText("Enter a homeserver domain:"),
Expanded( Row(
child: TextField( spacing: 8,
focusNode: homeserverFocusNode, children: [
textInputAction: TextInputAction.done, Expanded(
onSubmitted: (_) => child: TextField(
setHomeserver(Uri.tryParse(homeserverUrl.text)), focusNode: homeserverFocusNode,
onChanged: (newVal) { textInputAction: TextInputAction.done,
if (hasError.value) { onSubmitted: (_) => setHomeserver(Uri.tryParse(homeserverUrl.text)),
hasError.value = false; onChanged: (newVal) {
} if (hasError.value) {
}, hasError.value = false;
controller: homeserverUrl, }
decoration: InputDecoration( },
labelText: "Homeserver URL (e.g. matrix.org)", controller: homeserverUrl,
hintText: "e.g. matrix.org", decoration: InputDecoration(
focusedBorder: OutlineInputBorder( labelText: "Homeserver URL (e.g. matrix.org)",
borderSide: BorderSide( hintText: "e.g. matrix.org",
width: hasError.value ? 4 : 2, focusedBorder: OutlineInputBorder(
color: hasError.value borderSide: BorderSide(
? theme.colorScheme.error width: hasError.value ? 4 : 2,
: theme.colorScheme.primary, color: hasError.value
), ? theme.colorScheme.error
), : theme.colorScheme.primary
enabledBorder: OutlineInputBorder( )
borderSide: BorderSide(
width: hasError.value ? 4 : 2,
color: hasError.value
? theme.colorScheme.error
: theme.colorScheme.primary,
),
),
),
),
), ),
IconButton.filled( enabledBorder: OutlineInputBorder(
tooltip: "Confirm homeserver choice", borderSide: BorderSide(
onPressed: isLoading.value width: hasError.value ? 4 : 2,
? null color: hasError.value
: () => setHomeserver( ? theme.colorScheme.error
Uri.tryParse(homeserverUrl.text), : theme.colorScheme.primary
), )
icon: Icon(Icons.check),
), ),
],
),
Expanded(
child: ListView(
padding: EdgeInsets.only(top: 12),
children: [
DividerText(
"Or, choose from some popular homeservers:",
),
...(<Homeserver>[
Homeserver(
name: "Matrix.org",
description:
"The Matrix.org Foundation offers the matrix.org homeserver as an easy entry point for anyone wanting to try out Matrix.",
url: Uri.https("matrix.org"),
iconUrl:
"https://raw.githubusercontent.com/element-hq/logos/refs/heads/master/matrix/matrix-favicon${Theme.brightnessOf(context) == Brightness.dark ? "-white" : ""}.png",
),
Homeserver(
name: "Federated Nexus",
description:
"Federated Nexus is a community resource hosting multiple FOSS (especially federated) services, including Matrix and Forgejo. By the same developers who made Nexus client.",
url: Uri.https("federated.nexus"),
iconUrl:
"https://federated.nexus/images/icon.png",
),
Homeserver(
name: "Unredacted",
description:
"Unredacted is a 501(c)(3) non-profit organization that builds Internet infrastructure and services to help people evade censorship and protect their right to privacy.",
url: Uri.https(
"unredacted.org",
"services/si/matrix",
),
iconUrl: "https://unredacted.org/favicon.ico",
),
Homeserver(
name: "Lorem ipsum",
description:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
url: Uri.https("loremipsum.io"),
iconUrl: "https://loremipsum.io/favicon.ico",
),
].map(
(homeserver) => Card(
child: ListTile(
title: Text(homeserver.name),
leading: Image.network(
homeserver.iconUrl,
errorBuilder: (_, _, _) => SizedBox.shrink(),
height: 32,
),
subtitle: Text(homeserver.description),
onTap: isLoading.value
? null
: () => setHomeserver(homeserver.url),
trailing: IconButton(
tooltip: "Launch homeserver info page",
onPressed: () => launch(homeserver.url),
icon: Icon(Icons.info_outline),
),
),
),
)),
],
), ),
), ),
SizedBox(height: 5), ),
TextButton( IconButton.filled(
onPressed: () => tooltip: "Confirm homeserver choice",
launch(Uri.https("servers.joinmatrix.org")), onPressed: isLoading.value
child: Text("See more homeservers..."), ? null
), : () => setHomeserver(Uri.tryParse(homeserverUrl.text)),
icon: Icon(Icons.check),
),
],
),
Expanded(
child: ListView(
padding: EdgeInsets.only(top: 12),
children: [
DividerText("Or, choose from some popular homeservers:"),
...(<Homeserver>[
Homeserver(
name: "Matrix.org",
description:
"The Matrix.org Foundation offers the matrix.org homeserver as an easy entry point for anyone wanting to try out Matrix.",
url: Uri.https("matrix.org"),
iconUrl:
"https://raw.githubusercontent.com/element-hq/logos/refs/heads/master/matrix/matrix-favicon${Theme.brightnessOf(context) == Brightness.dark ? "-white" : ""}.png",
),
Homeserver(
name: "Federated Nexus",
description:
"Federated Nexus is a community resource hosting multiple FOSS (especially federated) services, including Matrix and Forgejo. By the same developers who made Nexus client.",
url: Uri.https("federated.nexus"),
iconUrl: "https://federated.nexus/images/icon.png",
),
Homeserver(
name: "Unredacted",
description:
"Unredacted is a 501(c)(3) non-profit organization that builds Internet infrastructure and services to help people evade censorship and protect their right to privacy.",
url: Uri.https("unredacted.org", "services/si/matrix"),
iconUrl: "https://unredacted.org/favicon.ico",
),
Homeserver(
name: "Lorem ipsum",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
url: Uri.https("loremipsum.io"),
iconUrl: "https://loremipsum.io/favicon.ico"
),
].map(
(homeserver) => Card(
child: ListTile(
title: Text(homeserver.name),
leading: Image.network(
homeserver.iconUrl,
errorBuilder: (_, _, _) => SizedBox.shrink(),
height: 32,
),
subtitle: Text(homeserver.description),
onTap: isLoading.value
? null
: () => setHomeserver(homeserver.url),
trailing: IconButton(
tooltip: "Launch homeserver info page",
onPressed: () => launch(homeserver.url),
icon: Icon(Icons.info_outline),
),
),
),
)),
], ],
), ),
), ),
), SizedBox(height: 5),
TextButton(
onPressed: () => launch(Uri.https("servers.joinmatrix.org")),
child: Text("See more homeservers..."),
),
],
),
),
),
); );
} }
} }