Stabilize frontend login. Make chat messages multiline.
This commit is contained in:
1 parent
5bbdfe8747
commit
3f37f727af
6 files changed
+71
-31
No files matched your search
@@ -24,7 +24,7 @@ class Backend {
|
||||
|
||||
// Authentication
|
||||
static ({String username, String password})? _credentials;
|
||||
static Completer? _loginCompleter;
|
||||
static Completer<bool>? _loginCompleter;
|
||||
static Completer? _credentialsCompleter;
|
||||
|
||||
static void setup(String url) {
|
||||
@@ -49,8 +49,11 @@ class Backend {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> auth(String username, String password) {
|
||||
_socket!.send(json.encode({'username': username, 'password': password}));
|
||||
static Future<bool> auth(String username, String password) {
|
||||
if (_credentials != null) {
|
||||
print("Cannot authenticate twice");
|
||||
return Future.value(false);
|
||||
}
|
||||
_credentials = (username: username, password: password);
|
||||
if (_credentialsCompleter != null) {
|
||||
_credentialsCompleter!.complete(_credentials);
|
||||
@@ -62,11 +65,13 @@ class Backend {
|
||||
|
||||
static void connect() {
|
||||
_socket = WebSocket(_url! + "/ws");
|
||||
_socket!.onOpen.listen((event) {
|
||||
_socket!.onOpen.listen((event) async {
|
||||
print('Connected to the WebSocket server');
|
||||
Backend.invalidateCache(chat: true);
|
||||
|
||||
print('Logging in...');
|
||||
// secure the listener for the response before sending credentials
|
||||
var responseFuture = nextIncomingMessage();
|
||||
if (_credentials != null) {
|
||||
_socket!.send(json.encode({'username': _credentials!.username, 'password': _credentials!.password}));
|
||||
} else {
|
||||
@@ -75,7 +80,14 @@ class Backend {
|
||||
_socket!.send(json.encode({'username': credentials.username, 'password': credentials.password}));
|
||||
});
|
||||
}
|
||||
_loginCompleter?.complete();
|
||||
var response = await responseFuture;
|
||||
if (response['error'] == null) {
|
||||
_loginCompleter?.complete(true);
|
||||
} else {
|
||||
_loginCompleter?.complete(false);
|
||||
_socket!.close(); // Retry
|
||||
}
|
||||
_loginCompleter = null;
|
||||
});
|
||||
_socket!.onError.listen((event) {
|
||||
print('Error: $event');
|
||||
@@ -92,11 +104,10 @@ class Backend {
|
||||
|
||||
static void disconnect() {
|
||||
_socket?.close();
|
||||
_loginCompleter = null;
|
||||
}
|
||||
|
||||
static void _reconnect() {
|
||||
Future.delayed(Duration(seconds: 5), () {
|
||||
Future.delayed(Duration(seconds: 1), () {
|
||||
connect();
|
||||
});
|
||||
}
|
||||
@@ -140,10 +151,17 @@ class Backend {
|
||||
print("No active listeners for chatId $chatId");
|
||||
}
|
||||
} else {
|
||||
if (jsonData['error'] != null) {
|
||||
print("Error: ${jsonData['error']}");
|
||||
}
|
||||
_packetStreamController.add(jsonData);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> nextIncomingMessage() {
|
||||
return _packetStreamController.stream.first;
|
||||
}
|
||||
|
||||
static Stream<Map<String, dynamic>> chatMessages(int chatId) {
|
||||
if (!_chatControllers.containsKey(chatId) || _chatControllers[chatId]!.isClosed) {
|
||||
_chatControllers[chatId] = StreamController<Map<String, dynamic>>.broadcast();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'app_menu.dart';
|
||||
import 'styles.dart';
|
||||
import 'backend.dart';
|
||||
@@ -36,6 +37,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
String? _title;
|
||||
List<Message> _messages = [];
|
||||
final TextEditingController _textController = TextEditingController();
|
||||
final FocusNode _inputFocusNode = FocusNode();
|
||||
StreamSubscription? _chatSubscription;
|
||||
|
||||
@override
|
||||
@@ -137,23 +139,36 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _textController,
|
||||
decoration: inputDecoration(),
|
||||
onSubmitted: sendMessage, // Send message on Enter key
|
||||
child: KeyboardListener(
|
||||
focusNode: _inputFocusNode,
|
||||
onKeyEvent: (event) {
|
||||
// TODO: Probably doing this in the input focus node would be cleaner
|
||||
if (HardwareKeyboard.instance.isControlPressed && HardwareKeyboard.instance.isLogicalKeyPressed(LogicalKeyboardKey.enter)) {
|
||||
sendMessage(_textController.text);
|
||||
_textController.clear();
|
||||
//return KeyEventResult.handled;
|
||||
}
|
||||
//return KeyEventResult.ignored;
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _textController,
|
||||
maxLines: null,
|
||||
decoration: inputDecoration(),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
sendMessage(_textController.text);
|
||||
},
|
||||
child: Text('Send'),
|
||||
),
|
||||
],
|
||||
SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
sendMessage(_textController.text);
|
||||
_textController.clear();
|
||||
},
|
||||
child: Text('Send'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -44,12 +44,18 @@ class _UserCheckerState extends State<UserChecker> {
|
||||
}
|
||||
|
||||
void _handleLogin(String user, String password) async {
|
||||
bool success = await Backend.auth(user, password);
|
||||
if (!success) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
username = user;
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
await Backend.auth(user, password);
|
||||
await _storage.write(key: 'username', value: user);
|
||||
await _storage.write(key: 'password', value: password);
|
||||
|
||||
|
||||
Reference in new issue
Block a user