Compare commits

..

No commits in common. "e4785525876d9e70d5648a3deacd9f374b095afd" and "799c15321b1deac7443089792207f58e1ac7972b" have entirely different histories.

8 changed files with 107 additions and 282 deletions

View File

@ -23,8 +23,6 @@ linter:
rules: rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule # avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
curly_braces_in_flow_control_structures: false
avoid_print: false
# Additional information about this file can be found at # Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options # https://dart.dev/guides/language/analysis-options

View File

@ -1,6 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application <application
android:label="zenith" android:label="zenith"
android:name="${applicationName}" android:name="${applicationName}"

57
lib/chats.dart Normal file
View File

@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:zenith/zenith_client_provider.dart';
class ChatsPage extends StatefulWidget {
const ChatsPage({super.key});
@override
State<ChatsPage> createState() => _ChatsPageState();
}
class _ChatsPageState extends State<ChatsPage> {
final serverController = TextEditingController();
final usernameController = TextEditingController();
final passwordController = TextEditingController();
late Client client;
bool roomsLoading = false;
@override
void initState() {
super.initState();
client = Provider.of<ZenithClientProvider>(context, listen: false).client;
client.onRoomState.stream.listen((event) {
print(event.type);
});
}
void sendMessage() {}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text("Zenith"),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: roomsLoading
? const CircularProgressIndicator()
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text("${client.rooms.length} Rooms.")],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: sendMessage,
tooltip: 'Send message',
child: const Icon(Icons.send),
),
);
}
}

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:zenith/rooms.dart'; import 'package:zenith/chats.dart';
import 'package:zenith/zenith_client_provider.dart'; import 'package:zenith/zenith_client_provider.dart';
class LoginPage extends StatefulWidget { class LoginPage extends StatefulWidget {
@ -15,36 +16,11 @@ class _LoginPageState extends State<LoginPage> {
final serverController = TextEditingController(); final serverController = TextEditingController();
final usernameController = TextEditingController(); final usernameController = TextEditingController();
final passwordController = TextEditingController(); final passwordController = TextEditingController();
bool loggingIn = false;
String failedMessage = '';
void connectMatrix() async { void connectMatrix() async {
setState(() { final provider = Provider.of<ZenithClientProvider>(context, listen: false);
loggingIn = true;
});
try {
final provider =
Provider.of<ZenithClientProvider>(context, listen: false);
await provider.initialize(serverController.text, usernameController.text, await provider.initialize(serverController.text, usernameController.text,
passwordController.text); passwordController.text);
} catch (error) {
print("Error signing in $error");
setState(() {
failedMessage = error.toString();
});
Future.delayed(const Duration(seconds: 10), () {
setState(() {
failedMessage = '';
});
});
return;
} finally {
setState(() {
loggingIn = false;
});
}
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
prefs.setString("homeserver", serverController.text); prefs.setString("homeserver", serverController.text);
@ -54,7 +30,7 @@ class _LoginPageState extends State<LoginPage> {
if (!mounted) return; if (!mounted) return;
Navigator.pushAndRemoveUntil( Navigator.pushAndRemoveUntil(
context, context,
MaterialPageRoute(builder: (context) => const RoomsPage()), MaterialPageRoute(builder: (context) => const ChatsPage()),
(route) => false); (route) => false);
} }
@ -65,8 +41,7 @@ class _LoginPageState extends State<LoginPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Consumer<ZenithClientProvider>( return Scaffold(
builder: (context, provider, child) => Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text("Login"), title: const Text("Login"),
@ -91,22 +66,16 @@ class _LoginPageState extends State<LoginPage> {
controller: passwordController, controller: passwordController,
decoration: const InputDecoration(labelText: 'Password'), decoration: const InputDecoration(labelText: 'Password'),
obscureText: true, obscureText: true,
onFieldSubmitted: (value) => connectMatrix(),
), ),
Text(failedMessage,
style: Theme.of(context).textTheme.headlineSmall)
], ],
), ),
), ),
), ),
floatingActionButton: loggingIn floatingActionButton: FloatingActionButton(
? const CircularProgressIndicator()
: FloatingActionButton(
onPressed: connectMatrix, onPressed: connectMatrix,
tooltip: 'Log in', tooltip: 'Log in',
child: const Icon(Icons.login), child: const Icon(Icons.login),
), ),
),
); );
} }
} }

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:zenith/rooms.dart'; import 'package:zenith/chats.dart';
import 'package:zenith/login.dart'; import 'package:zenith/login.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:zenith/zenith_client_provider.dart'; import 'package:zenith/zenith_client_provider.dart';
@ -66,7 +66,7 @@ class _MyHomePageState extends State<MyHomePage> {
if (!savedCreds) { if (!savedCreds) {
return const LoginPage(); return const LoginPage();
} else { } else {
return const RoomsPage(); return const ChatsPage();
} }
} }
} }

View File

@ -1,90 +0,0 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
class RoomPage extends StatefulWidget {
const RoomPage({super.key, required this.room});
final Room room;
@override
State<RoomPage> createState() => _RoomPageState();
}
class _RoomPageState extends State<RoomPage> {
Timeline? timeline;
final chatController = TextEditingController();
StreamSubscription? updateListener;
void updateTimeline() async {
final newTimeline =
await widget.room.getTimeline(eventContextId: widget.room.fullyRead);
setState(() {
timeline = newTimeline;
});
}
@override
void initState() {
super.initState();
updateTimeline();
updateListener = widget.room.onUpdate.stream.listen((event) {
updateTimeline();
});
}
@override
void dispose() {
super.dispose();
updateListener?.cancel();
}
void sendMessage() {}
List<Widget> getChildren() {
if (timeline == null) return [const CircularProgressIndicator()];
return [
Expanded(
child: ListView.builder(
itemCount: timeline?.events.length,
reverse: true,
itemBuilder: (context, index) => ListTile(
title: Text(timeline!.events[index].senderFromMemoryOrFallback
.displayName ??
""),
subtitle: Text(timeline!.events[index].body),
)),
),
TextFormField(
controller: chatController,
decoration: const InputDecoration(hintText: 'Message'),
onFieldSubmitted: (value) async {
print("Sending text event $value to room ${widget.room.id}...");
await widget.room.sendTextEvent(value);
chatController.text = '';
},
),
];
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.room.getLocalizedDisplayname()),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: getChildren(),
)),
floatingActionButton: FloatingActionButton(
onPressed: sendMessage,
tooltip: 'Send message',
child: const Icon(Icons.send),
),
);
}
}

View File

@ -1,102 +0,0 @@
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:zenith/login.dart';
import 'package:zenith/room.dart';
import 'package:zenith/zenith_client_provider.dart';
class RoomsPage extends StatefulWidget {
const RoomsPage({super.key});
@override
State<RoomsPage> createState() => _RoomsPageState();
}
class _RoomsPageState extends State<RoomsPage> {
@override
void initState() {
super.initState();
}
void sendMessage() {}
void viewRoom(Room room) {
Navigator.push(
context, MaterialPageRoute(builder: (context) => RoomPage(room: room)));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text("Zenith"),
actions: [
IconButton(
onPressed: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text("Log out?"),
actions: [
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("Cancel")),
ElevatedButton(
child: const Text("Confirm"),
onPressed: () async {
print("Logging out...");
final prefs = await SharedPreferences.getInstance();
prefs.remove("homeserver");
prefs.remove("username");
prefs.remove("password");
if (!mounted) return;
final provider = Provider.of<ZenithClientProvider>(
context,
listen: false);
await provider.client.logout();
if (!mounted) return;
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => const LoginPage()),
(route) => false);
},
)
],
),
);
},
icon: const Icon(Icons.logout))
],
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(child: Consumer<ZenithClientProvider>(
builder: (context, provider, child) {
if (provider.loading)
return const CircularProgressIndicator();
else
return ListView.builder(
itemCount: provider.client.rooms.length,
itemBuilder: (context, index) => ListTile(
title: Text(provider.client.rooms[index]
.getLocalizedDisplayname()),
onTap: () => viewRoom(provider.client.rooms[index]),
));
},
)),
),
floatingActionButton: FloatingActionButton(
onPressed: sendMessage,
tooltip: 'Send message',
child: const Icon(Icons.send),
),
);
}
}

View File

@ -3,28 +3,23 @@ import 'package:matrix/matrix.dart';
class ZenithClientProvider extends ChangeNotifier { class ZenithClientProvider extends ChangeNotifier {
late Client _client; late Client _client;
bool _loading = true;
Client get client => _client; Client get client => _client;
bool get loading => _loading; void setClient(Client newClient) {
_client = newClient;
notifyListeners();
}
Future<void> initialize( Future<void> initialize(
String homeserver, String username, String password) async { String homeserver, String username, String password) async {
_loading = true;
_client = Client("zenith"); _client = Client("zenith");
try {
print("Checking homeserver..."); print("Checking homeserver...");
await client.checkHomeserver(Uri.parse(homeserver)); await client.checkHomeserver(Uri.parse(homeserver));
print("Logging in..."); print("Logging in...");
await client.login(LoginType.mLoginPassword, await client.login(LoginType.mLoginPassword,
identifier: AuthenticationUserIdentifier(user: username), identifier: AuthenticationUserIdentifier(user: username),
password: password); password: password);
await client.roomsLoading;
await client.accountDataLoading;
} finally {
_loading = false;
}
notifyListeners(); notifyListeners();
} }
} }