Compare commits

..
17 Commits
Author SHA1 Message Date
brandon.presley 11de9ccfe8 Turn off autocorrect/capitalization for login 2023-12-30 16:17:05 +13:00
brandon.presley 128a1bf35f Add persistence to matrix Client 2023-12-30 16:11:20 +13:00
brandon.presley a552c30ba0 Remove logging of rooms with "Group with" 2023-12-30 16:00:54 +13:00
brandon.presley 2039b4e90d Remove custom code for renaming groups
Was something for my local matrix
environment, committed
accidentally.
2023-12-30 15:54:30 +13:00
brandon.presley 1aa73bbd7e Add failed attempt to fix all room names 2023-12-30 15:16:42 +13:00
brandon.presley 6ca8f646b2 Fix null being on chat messages author temporarily 2023-12-30 14:44:35 +13:00
brandon.presley a40dd74b96 Close chat faster on send 2023-12-30 12:49:14 +13:00
brandon.presley 10b84d1521 Hide "Group with" from room names 2023-12-30 12:47:48 +13:00
brandon.presley 8ca788ccdd Add button to change image of chat 2023-12-30 12:41:40 +13:00
brandon.presley 7c1e18c4e8 Add avatar to rooms 2023-12-30 11:47:27 +13:00
brandon.presley 76411aa827 Fix login flow and room name updates
1. Logging in with saved credentials should
just go straight to rooms page without
flickering the login page
2. Rooms should be updated if an event
fires for their names changing
2023-12-30 11:39:34 +13:00
brandon.presley 27b7f79d74 Only login if the client is logged out 2023-12-30 11:23:57 +13:00
brandon.presley f229c0371b Save username/password instead of tokens
The tokens never worked. I don't know what i'm doing wrong but fluffychat basically just logs in and never tries to save anything. Im not sure how im meant to do this.
2023-12-30 11:17:21 +13:00
brandon.presley 1cd8ba41ae Reduce nesting of login.dart 2023-12-30 11:16:38 +13:00
brandon.presley 3728172844 Tried to set up auth tokens
I always get invalid token error
when I try to login with mLoginToken type
2023-12-29 19:31:35 +13:00
brandon.presley ea7587fe2d Only show messages in room view 2023-12-29 17:19:40 +13:00
brandon.presley 1376580e7e Add profile images to chats 2023-12-29 17:10:47 +13:00
8 changed files with 167 additions and 66 deletions
+19 -9
View File
@@ -1,4 +1,5 @@
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/rooms.dart';
@@ -18,18 +19,23 @@ class _LoginPageState extends State<LoginPage> {
bool loggingIn = false; bool loggingIn = false;
String failedMessage = ''; String failedMessage = '';
void connectMatrix() async { void login() async {
setState(() { setState(() {
loggingIn = true; loggingIn = true;
}); });
final provider = Provider.of<ZenithClientProvider>(context, listen: false);
try { try {
final provider = await provider.client.checkHomeserver(Uri.parse(serverController.text));
Provider.of<ZenithClientProvider>(context, listen: false); await provider.client.login(
await provider.initialize(serverController.text, usernameController.text, LoginType.mLoginPassword,
passwordController.text); password: passwordController.text,
identifier: AuthenticationUserIdentifier(user: usernameController.text),
refreshToken: true,
);
} catch (error) { } catch (error) {
print("Error signing in $error"); print(error);
setState(() { setState(() {
failedMessage = error.toString(); failedMessage = error.toString();
loggingIn = false; loggingIn = false;
@@ -73,26 +79,30 @@ class _LoginPageState extends State<LoginPage> {
title: const Text("Login"), title: const Text("Login"),
), ),
body: Padding( body: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(8.0),
child: Center( child: Center(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
TextFormField( TextFormField(
controller: serverController, controller: serverController,
autocorrect: false,
textCapitalization: TextCapitalization.none,
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: 'Server', hintText: 'https://matrix.org'), labelText: 'Server', hintText: 'https://matrix.org'),
), ),
TextFormField( TextFormField(
controller: usernameController, controller: usernameController,
autocorrect: false,
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: 'Username', hintText: 'john'), labelText: 'Username', hintText: 'john'),
), ),
TextFormField( TextFormField(
controller: passwordController, controller: passwordController,
autocorrect: false,
decoration: const InputDecoration(labelText: 'Password'), decoration: const InputDecoration(labelText: 'Password'),
obscureText: true, obscureText: true,
onFieldSubmitted: (value) => connectMatrix(), onFieldSubmitted: (value) => login(),
), ),
Text(failedMessage, Text(failedMessage,
style: Theme.of(context).textTheme.headlineSmall) style: Theme.of(context).textTheme.headlineSmall)
@@ -101,7 +111,7 @@ class _LoginPageState extends State<LoginPage> {
), ),
), ),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
onPressed: loggingIn ? null : connectMatrix, onPressed: loggingIn ? null : login,
tooltip: 'Log in', tooltip: 'Log in',
child: loggingIn child: loggingIn
? const CircularProgressIndicator() ? const CircularProgressIndicator()
+34 -9
View File
@@ -37,7 +37,8 @@ class MyHomePage extends StatefulWidget {
} }
class _MyHomePageState extends State<MyHomePage> { class _MyHomePageState extends State<MyHomePage> {
bool savedCreds = false; bool showRooms = false;
String errorMessage = "";
void getCreds() async { void getCreds() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
@@ -46,13 +47,25 @@ class _MyHomePageState extends State<MyHomePage> {
final password = prefs.getString("password"); final password = prefs.getString("password");
if (homeserver == null || username == null || password == null) return; if (homeserver == null || username == null || password == null) return;
setState(() {
savedCreds = true;
});
if (!mounted) return; if (!mounted) return;
final provider = Provider.of<ZenithClientProvider>(context, listen: false); final provider = Provider.of<ZenithClientProvider>(context, listen: false);
await provider.initialize(homeserver, username, password); setState(() {
showRooms = true;
});
try {
await provider.client.checkHomeserver(Uri.parse(homeserver));
if (!provider.client.isLogged())
await provider.client.login(LoginType.mLoginPassword,
password: password,
identifier: AuthenticationUserIdentifier(user: username));
} catch (error) {
print(error);
setState(() {
errorMessage = error.toString();
});
}
} }
@override @override
@@ -63,10 +76,22 @@ class _MyHomePageState extends State<MyHomePage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (!savedCreds) { if (errorMessage.isNotEmpty)
return const LoginPage(); return Column(
} else { children: [
ElevatedButton(
onPressed: () {
setState(() {
errorMessage = "";
});
},
child: const Text("Clear")),
ErrorWidget(errorMessage),
],
);
if (showRooms)
return const RoomsPage(); return const RoomsPage();
} else
return const LoginPage();
} }
} }
+41 -13
View File
@@ -1,5 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
@@ -34,6 +36,16 @@ class _RoomPageState extends State<RoomPage> {
}); });
} }
void setAvatar() async {
final participants = widget.room.getParticipants();
final picked = await FilePicker.platform.pickFiles(type: FileType.image);
if (picked == null) return;
final file = File(picked.files.single.path!);
final bytes = await file.readAsBytes();
final uri = await widget.room.client.uploadContent(bytes);
await widget.room.client.setAvatarUrl(participants[0].id, uri);
}
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
@@ -42,27 +54,43 @@ class _RoomPageState extends State<RoomPage> {
void sendMessage() {} void sendMessage() {}
List<Event>? get messages => timeline?.events
.where((element) => element.type == EventTypes.Message)
.toList();
List<Widget> getChildren() { List<Widget> getChildren() {
if (timeline == null) return [const CircularProgressIndicator()]; if (timeline == null) return [const CircularProgressIndicator()];
return [ return [
Expanded( Expanded(
child: ListView.builder( child: ListView.builder(
itemCount: timeline?.events.length, itemCount: messages?.length,
reverse: true, reverse: true,
itemBuilder: (context, index) => ListTile( itemBuilder: (context, index) => ListTile(
title: Text(timeline!.events[index].senderFromMemoryOrFallback title: Text(
.displayName ?? messages![index].senderFromMemoryOrFallback.displayName ??
""), ""),
subtitle: Text(timeline!.events[index].body), subtitle: Text(messages![index].body),
leading:
messages![index].senderFromMemoryOrFallback.avatarUrl !=
null
? CircleAvatar(
foregroundImage: NetworkImage(messages![index]
.senderFromMemoryOrFallback
.avatarUrl!
.getThumbnail(widget.room.client,
width: 50, height: 50)
.toString()))
: null,
)), )),
), ),
TextFormField( TextFormField(
controller: chatController, controller: chatController,
textCapitalization: TextCapitalization.sentences,
textInputAction: TextInputAction.send,
decoration: const InputDecoration(hintText: 'Message'), decoration: const InputDecoration(hintText: 'Message'),
onFieldSubmitted: (value) async { onFieldSubmitted: (value) async {
print("Sending text event $value to room ${widget.room.id}...");
await widget.room.sendTextEvent(value);
chatController.text = ''; chatController.text = '';
await widget.room.sendTextEvent(value);
}, },
), ),
]; ];
@@ -73,18 +101,18 @@ class _RoomPageState extends State<RoomPage> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.room.getLocalizedDisplayname()), title: Text(widget.room
.getLocalizedDisplayname()
.replaceFirst(RegExp("Group with "), "")),
actions: [
IconButton(onPressed: setAvatar, icon: const Icon(Icons.image))
],
), ),
body: Padding( body: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(8.0),
child: Column( child: Column(
children: getChildren(), children: getChildren(),
)), )),
floatingActionButton: FloatingActionButton(
onPressed: sendMessage,
tooltip: 'Send message',
child: const Icon(Icons.send),
),
); );
} }
} }
+19 -8
View File
@@ -14,6 +14,8 @@ class RoomsPage extends StatefulWidget {
} }
class _RoomsPageState extends State<RoomsPage> { class _RoomsPageState extends State<RoomsPage> {
bool loadingRooms = true;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -76,27 +78,36 @@ class _RoomsPageState extends State<RoomsPage> {
], ],
), ),
body: Padding( body: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(8.0),
child: Center(child: Consumer<ZenithClientProvider>( child: Center(child: Consumer<ZenithClientProvider>(
builder: (context, provider, child) { builder: (context, provider, child) {
if (provider.loading) provider.client.onRoomState.stream.listen((event) {
setState(() {
loadingRooms = false;
});
});
if (provider.client.rooms.isEmpty)
return const CircularProgressIndicator(); return const CircularProgressIndicator();
else else
return ListView.builder( return ListView.builder(
itemCount: provider.client.rooms.length, itemCount: provider.client.rooms.length,
itemBuilder: (context, index) => ListTile( itemBuilder: (context, index) => ListTile(
title: Text(provider.client.rooms[index] title: Text(provider.client.rooms[index]
.getLocalizedDisplayname()), .getLocalizedDisplayname()
.replaceFirst(RegExp("Group with "), "")),
leading: CircleAvatar(
foregroundImage: NetworkImage(provider
.client.rooms[index].avatar
?.getThumbnail(provider.client,
width: 50, height: 50)
.toString() ??
"")),
onTap: () => viewRoom(provider.client.rooms[index]), onTap: () => viewRoom(provider.client.rooms[index]),
)); ));
}, },
)), )),
), ),
floatingActionButton: FloatingActionButton(
onPressed: sendMessage,
tooltip: 'Send message',
child: const Icon(Icons.send),
),
); );
} }
} }
+8 -25
View File
@@ -1,30 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:path_provider/path_provider.dart';
class ZenithClientProvider extends ChangeNotifier { class ZenithClientProvider extends ChangeNotifier {
late Client _client; final client = Client("zenith", databaseBuilder: (_) async {
bool _loading = true; final dir =
await getApplicationSupportDirectory(); // Recommend path_provider package
Client get client => _client; final db = HiveCollectionsDatabase('matrix_example_chat', dir.path);
await db.open();
bool get loading => _loading; return db;
});
Future<void> initialize(
String homeserver, String username, String password) async {
_loading = true;
_client = Client("zenith");
try {
print("Checking homeserver...");
await client.checkHomeserver(Uri.parse(homeserver));
print("Logging in...");
await client.login(LoginType.mLoginPassword,
identifier: AuthenticationUserIdentifier(user: username),
password: password);
await client.roomsLoading;
await client.accountDataLoading;
} finally {
_loading = false;
}
notifyListeners();
}
} }
@@ -5,8 +5,10 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import path_provider_foundation
import shared_preferences_foundation import shared_preferences_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
} }
+41 -1
View File
@@ -145,6 +145,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.0.0" version: "7.0.0"
file_picker:
dependency: "direct main"
description:
name: file_picker
sha256: "4e42aacde3b993c5947467ab640882c56947d9d27342a5b6f2895b23956954a6"
url: "https://pub.dev"
source: hosted
version: "6.1.1"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@@ -174,6 +182,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.0" version: "0.3.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da
url: "https://pub.dev"
source: hosted
version: "2.0.17"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@@ -328,6 +344,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.8.3" version: "1.8.3"
path_provider:
dependency: "direct main"
description:
name: path_provider
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
url: "https://pub.dev"
source: hosted
version: "2.1.1"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
url: "https://pub.dev"
source: hosted
version: "2.2.2"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
url: "https://pub.dev"
source: hosted
version: "2.3.1"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
description: description:
@@ -607,4 +647,4 @@ packages:
version: "6.3.0" version: "6.3.0"
sdks: sdks:
dart: ">=3.1.5 <4.0.0" dart: ">=3.1.5 <4.0.0"
flutter: ">=3.7.0" flutter: ">=3.10.0"
+2
View File
@@ -40,6 +40,8 @@ dependencies:
flutter_openssl_crypto: ^0.3.0 flutter_openssl_crypto: ^0.3.0
provider: ^6.1.1 provider: ^6.1.1
shared_preferences: ^2.2.2 shared_preferences: ^2.2.2
file_picker: ^6.1.1
path_provider: ^2.1.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: