Skip to content

Commit 8eb59a6

Browse files
committed
fix: fix various bugs & increase robustness
1 parent c6c646c commit 8eb59a6

16 files changed

Lines changed: 709 additions & 175 deletions

example/lib/main.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ class _InitializationScreenState extends State<InitializationScreen> {
6262
try {
6363
final dir =
6464
await getApplicationSupportDirectory(); // Best for persistent data
65+
logger.info("s5_messenger persisting @$dir");
6566
Hive.init(dir.path);
6667
// Initialize Hive
6768
setState(() => hiveInitialized = true);

example/lib/view/group_chat.dart

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import 'dart:convert';
22

3-
import 'package:adaptive_dialog/adaptive_dialog.dart';
43
import 'package:flutter/material.dart';
54
import 'package:flutter/services.dart';
6-
import 'package:lib5/util.dart';
75
import 'package:s5_messenger_example/main.dart';
86
import 'package:s5_messenger/s5_messenger.dart';
97
import 'package:uuid/uuid.dart';
@@ -111,44 +109,6 @@ class _GroupChatViewState extends State<GroupChatView> {
111109
padding: const EdgeInsets.all(8.0),
112110
child: Column(
113111
children: [
114-
ElevatedButton(
115-
onPressed: () async {
116-
final res = await showTextInputDialog(
117-
context: context,
118-
textFields: [
119-
DialogTextField(
120-
hintText: 's5-messenger-key-package:')
121-
],
122-
);
123-
if (res == null) return;
124-
final String kp = res.first;
125-
logger.info(kp);
126-
if (!kp.startsWith('s5-messenger-key-package:'))
127-
throw 'TODO1';
128-
final bytes = base64UrlNoPaddingDecode(
129-
kp.substring(25),
130-
);
131-
print(bytes);
132-
133-
final welcomeMessage =
134-
await group.addMemberToGroup(bytes);
135-
136-
print(welcomeMessage);
137-
138-
Clipboard.setData(
139-
ClipboardData(
140-
text: welcomeMessage,
141-
),
142-
);
143-
144-
/* final kp = await mls.createKeyPackage();
145-
146-
*/
147-
},
148-
child: Text(
149-
'Invite User',
150-
),
151-
),
152112
ElevatedButton(
153113
onPressed: () async {
154114
final String inviteToken =

lib/src/mls5/mls5.dart

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ import 'dart:typed_data';
55

66
import 'package:lib5/src/util/big_endian.dart';
77
import 'package:lib5/util.dart';
8+
// ignore: implementation_imports
9+
import 'package:lib5/src/node/logger/base.dart';
810
import 'package:ntp/ntp.dart';
911
import 'package:s5/s5.dart';
1012
import 'package:s5/src/hive_key_value_db.dart';
1113
import 'package:s5_messenger/src/mls5/state/messenger.dart';
14+
import 'package:s5_messenger/src/mls5/util/logger.dart';
1215
import 'package:s5_messenger/src/rust/api/simple.dart';
1316
import 'package:s5_messenger/src/rust/frb_generated.dart';
1417
import 'package:hive_ce/hive.dart';
@@ -42,7 +45,7 @@ class S5Messenger {
4245

4346
Future<void> init(S5 inputS5, String dbPath,
4447
[String prefix = 'default']) async {
45-
logger = SimpleLogger(prefix: "[s5_messenger]");
48+
logger = S5MessengerLogger(prefix: "[s5_messenger]");
4649
s5 = inputS5;
4750
dataBox = await Hive.openBox('s5-messenger-data');
4851
groupsBox = await Hive.openBox('s5-messenger-groups');
@@ -61,22 +64,34 @@ class S5Messenger {
6164
keystoreBox = /* HiveKeyValueDB( */ await Hive.openBox('$prefix-keystore');
6265
// groupStateDB = HiveKeyValueDB(await Hive.openBox('group_state'));
6366

67+
initLogging().listen((entry) {
68+
final rustLogger = S5MessengerLogger(prefix: "[s5_messenger:rust]");
69+
final msg = "${entry.tag}: ${entry.msg}";
70+
if (entry.level <= 1) {
71+
rustLogger.error(msg);
72+
} else if (entry.level == 2) {
73+
rustLogger.warn(msg);
74+
} else {
75+
rustLogger.info(msg);
76+
}
77+
});
78+
6479
config = await rust.crateApiSimpleOpenmlsInitConfig(dbPath: dbPath);
6580
logger.info('Initialized Rust!');
6681

6782
await setupIdentity();
6883

69-
Future.delayed(Duration(seconds: 1)).then((value) async {
70-
await recoverGroups();
71-
messengerState.update();
84+
await _setupTimeSync().timeout(const Duration(seconds: 2), onTimeout: () {
85+
logger.warn('NTP time sync timed out, using system clock.');
7286
});
7387

74-
_setupTimeSync();
88+
await recoverGroups();
89+
messengerState.update();
7590
}
7691

7792
Duration timeOffset = Duration.zero;
7893

79-
void _setupTimeSync() async {
94+
Future<void> _setupTimeSync() async {
8095
try {
8196
int offsetMillis = await NTP.getNtpOffset(localTime: DateTime.now());
8297
timeOffset = Duration(milliseconds: offsetMillis);
@@ -123,7 +138,7 @@ class S5Messenger {
123138
'identity': username,
124139
'publicKey': base64UrlNoPaddingEncode(publicKey),
125140
});
126-
print('$key created');
141+
logger.info('$key created');
127142
}
128143

129144
final groups = <String, GroupState>{};
@@ -310,7 +325,7 @@ class GroupState {
310325
).timeout(const Duration(minutes: 5))) {
311326
retryDelaySeconds = 1;
312327

313-
Logger logger = SimpleLogger(prefix: "[s5_messenger]");
328+
Logger logger = S5MessengerLogger(prefix: "[s5_messenger]");
314329
logger.info('debug1 incoming $groupId ${event.ts}');
315330
if (ignoreMessageIds.contains(event.ts)) {
316331
ignoreMessageIds.remove(event.ts);
@@ -363,15 +378,23 @@ class GroupState {
363378
}
364379
}
365380

381+
bool canLoadMore = true;
382+
List<MLSApplicationMessage> messagesMemory = <MLSApplicationMessage>[];
383+
366384
void _processNewMessage(MLSApplicationMessage msg) {
367-
messagesMemory.insert(0, msg);
385+
if (messagesMemory.any((m) => m.ts == msg.ts)) {
386+
return;
387+
}
388+
final newMessages = List<MLSApplicationMessage>.from(messagesMemory);
389+
newMessages.add(msg);
390+
newMessages.sort((a, b) => b.ts.compareTo(a.ts));
391+
392+
messagesMemory = newMessages;
393+
368394
mls.messageStoreBox.put(makeKey(msg), msg.serialize());
369395
messageListStateNotifier.update();
370396
}
371397

372-
bool canLoadMore = true;
373-
final messagesMemory = <MLSApplicationMessage>[];
374-
375398
void loadMoreMessages() {
376399
final anchorLow = String.fromCharCodes(base64UrlNoPaddingDecode(groupId));
377400
final anchorHigh = messagesMemory.isEmpty
@@ -383,13 +406,14 @@ class GroupState {
383406
keys.sort((a, b) => b.compareTo(a));
384407
// print(keys);
385408

386-
if (keys.length < 50) {
409+
if (keys.length <= 50) {
387410
canLoadMore = false;
388411
} else {
389412
keys.removeRange(50, keys.length);
390413
}
414+
final newMessages = List<MLSApplicationMessage>.from(messagesMemory);
391415
for (final String k in keys) {
392-
messagesMemory.add(
416+
newMessages.add(
393417
MLSApplicationMessage.deserialize(
394418
mls.messageStoreBox.get(k)!,
395419
decodeEndian(
@@ -398,8 +422,7 @@ class GroupState {
398422
),
399423
);
400424
}
401-
/* if (keys.isEmpty) {
402-
} */
425+
messagesMemory = newMessages;
403426

404427
messageListStateNotifier.update();
405428
}

lib/src/mls5/util/logger.dart

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import 'package:lib5/lib5.dart';
2+
// ignore: implementation_imports
3+
import 'package:lib5/src/node/logger/base.dart';
4+
5+
class S5MessengerLogger extends Logger {
6+
final String prefix;
7+
final bool showVerbose;
8+
9+
S5MessengerLogger({
10+
this.prefix = '',
11+
this.showVerbose = false,
12+
});
13+
14+
String _format(String level, String message, String color) {
15+
final now = DateTime.now();
16+
final timeStr =
17+
"${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}:${now.second.toString().padLeft(2, '0')}";
18+
19+
// ANSI Colors
20+
const reset = '\x1B[0m';
21+
22+
return "$timeStr $color[${level.padRight(5)}]$reset $prefix $message";
23+
}
24+
25+
@override
26+
void info(String s) {
27+
print(_format("INFO", s, '\x1B[32m')); // Green
28+
}
29+
30+
@override
31+
void warn(String s) {
32+
print(_format("WARN", s, '\x1B[33m')); // Yellow
33+
}
34+
35+
@override
36+
void error(String s) {
37+
print(_format("ERROR", s, '\x1B[31m')); // Red
38+
}
39+
40+
@override
41+
void verbose(String s) {
42+
if (!showVerbose) return;
43+
print(_format("VERB", s, '\x1B[34m')); // Blue
44+
}
45+
46+
@override
47+
void catched(e, st, [context]) {
48+
error("Caught Exception: $e\n$st");
49+
}
50+
}

lib/src/rust/api/simple.dart

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ import '../frb_generated.dart';
77
import '../lib.dart';
88
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
99

10-
// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `MyOpenMlsRustCrypto`
11-
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `crypto`, `from_slice`, `rand`, `storage`, `to_vec`
10+
// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `DartLogger`, `MyOpenMlsRustCrypto`
11+
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `crypto`, `enabled`, `flush`, `from_slice`, `log`, `rand`, `storage`, `to_vec`
1212
// These functions are ignored (category: IgnoreBecauseExplicitAttribute): `get_backend`
1313

14+
Stream<LogEntry> initLogging() =>
15+
RustLib.instance.api.crateApiSimpleInitLogging();
16+
1417
String greet({required String name}) =>
1518
RustLib.instance.api.crateApiSimpleGreet(name: name);
1619

@@ -203,6 +206,30 @@ class GroupMember {
203206
signatureKey == other.signatureKey;
204207
}
205208

209+
class LogEntry {
210+
final int level;
211+
final String tag;
212+
final String msg;
213+
214+
const LogEntry({
215+
required this.level,
216+
required this.tag,
217+
required this.msg,
218+
});
219+
220+
@override
221+
int get hashCode => level.hashCode ^ tag.hashCode ^ msg.hashCode;
222+
223+
@override
224+
bool operator ==(Object other) =>
225+
identical(this, other) ||
226+
other is LogEntry &&
227+
runtimeType == other.runtimeType &&
228+
level == other.level &&
229+
tag == other.tag &&
230+
msg == other.msg;
231+
}
232+
206233
class MLSGroupAddMembersResponse {
207234
final Uint8List mlsMessageOut;
208235
final Uint8List welcomeOut;

0 commit comments

Comments
 (0)