simplify reconciliation

This commit is contained in:
Christien Rioux 2025-04-19 22:21:40 -04:00
parent bf38c2c44d
commit 4797184a1a
12 changed files with 512 additions and 345 deletions

View file

@ -77,8 +77,8 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
await _sentSubscription?.cancel();
await _rcvdSubscription?.cancel();
await _reconciledSubscription?.cancel();
await _sentMessagesCubit?.close();
await _rcvdMessagesCubit?.close();
await _sentMessagesDHTLog?.close();
await _rcvdMessagesDHTLog?.close();
await _reconciledMessagesCubit?.close();
// If the local conversation record is gone, then delete the reconciled
@ -111,10 +111,10 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
await _initReconciledMessagesCubit();
// Local messages key
await _initSentMessagesCubit();
await _initSentMessagesDHTLog();
// Remote messages key
await _initRcvdMessagesCubit();
await _initRcvdMessagesDHTLog();
// Command execution background process
_commandRunnerFut = Future.delayed(Duration.zero, _commandRunner);
@ -129,39 +129,40 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
}
// Open local messages key
Future<void> _initSentMessagesCubit() async {
Future<void> _initSentMessagesDHTLog() async {
final writer = _accountInfo.identityWriter;
_sentMessagesCubit = DHTLogCubit(
open: () async => DHTLog.openWrite(_localMessagesRecordKey, writer,
final sentMessagesDHTLog =
await DHTLog.openWrite(_localMessagesRecordKey, writer,
debugName: 'SingleContactMessagesCubit::_initSentMessagesCubit::'
'SentMessages',
parent: _localConversationRecordKey,
crypto: _conversationCrypto),
decodeElement: proto.Message.fromBuffer);
_sentSubscription =
_sentMessagesCubit!.stream.listen(_updateSentMessagesState);
_updateSentMessagesState(_sentMessagesCubit!.state);
crypto: _conversationCrypto);
_sentSubscription = await sentMessagesDHTLog.listen(_updateSentMessages);
_sentMessagesDHTLog = sentMessagesDHTLog;
_reconciliation.addInputSourceFromDHTLog(
_accountInfo.identityTypedPublicKey, sentMessagesDHTLog);
}
// Open remote messages key
Future<void> _initRcvdMessagesCubit() async {
Future<void> _initRcvdMessagesDHTLog() async {
// Don't bother if we don't have a remote messages record key yet
if (_remoteMessagesRecordKey == null) {
return;
}
// Open new cubit if one is desired
_rcvdMessagesCubit = DHTLogCubit(
open: () async => DHTLog.openRead(_remoteMessagesRecordKey!,
debugName: 'SingleContactMessagesCubit::_initRcvdMessagesCubit::'
'RcvdMessages',
parent: _remoteConversationRecordKey,
crypto: _conversationCrypto),
decodeElement: proto.Message.fromBuffer);
_rcvdSubscription =
_rcvdMessagesCubit!.stream.listen(_updateRcvdMessagesState);
_updateRcvdMessagesState(_rcvdMessagesCubit!.state);
final rcvdMessagesDHTLog = await DHTLog.openRead(_remoteMessagesRecordKey!,
debugName: 'SingleContactMessagesCubit::_initRcvdMessagesCubit::'
'RcvdMessages',
parent: _remoteConversationRecordKey,
crypto: _conversationCrypto);
_rcvdSubscription = await rcvdMessagesDHTLog.listen(_updateRcvdMessages);
_rcvdMessagesDHTLog = rcvdMessagesDHTLog;
_reconciliation.addInputSourceFromDHTLog(
_remoteIdentityPublicKey, rcvdMessagesDHTLog);
}
Future<void> updateRemoteMessagesRecordKey(
@ -175,17 +176,17 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
return;
}
// Close existing cubit if we have one
final rcvdMessagesCubit = _rcvdMessagesCubit;
_rcvdMessagesCubit = null;
// Close existing DHTLog if we have one
final rcvdMessagesDHTLog = _rcvdMessagesDHTLog;
_rcvdMessagesDHTLog = null;
_remoteMessagesRecordKey = null;
await _rcvdSubscription?.cancel();
_rcvdSubscription = null;
await rcvdMessagesCubit?.close();
await rcvdMessagesDHTLog?.close();
// Init the new cubit if we should
// Init the new DHTLog if we should
_remoteMessagesRecordKey = remoteMessagesRecordKey;
await _initRcvdMessagesCubit();
await _initRcvdMessagesDHTLog();
});
}
@ -275,30 +276,15 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
////////////////////////////////////////////////////////////////////////////
// Internal implementation
// Called when the sent messages cubit gets a change
// Called when the sent messages DHTLog gets a change
// This will re-render when messages are sent from another machine
void _updateSentMessagesState(DHTLogBusyState<proto.Message> avmessages) {
final sentMessages = avmessages.state.asData?.value;
if (sentMessages == null) {
return;
}
_reconciliation.reconcileMessages(
_accountInfo.identityTypedPublicKey, sentMessages, _sentMessagesCubit!);
// Update the view
_renderState();
void _updateSentMessages(DHTLogUpdate upd) {
_reconciliation.reconcileMessages(_accountInfo.identityTypedPublicKey);
}
// Called when the received messages cubit gets a change
void _updateRcvdMessagesState(DHTLogBusyState<proto.Message> avmessages) {
final rcvdMessages = avmessages.state.asData?.value;
if (rcvdMessages == null) {
return;
}
_reconciliation.reconcileMessages(
_remoteIdentityPublicKey, rcvdMessages, _rcvdMessagesCubit!);
// Called when the received messages DHTLog gets a change
void _updateRcvdMessages(DHTLogUpdate upd) {
_reconciliation.reconcileMessages(_remoteIdentityPublicKey);
}
// Called when the reconciled messages window gets a change
@ -327,7 +313,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
// _renderState();
try {
await _sentMessagesCubit!.operateAppendEventual((writer) async {
await _sentMessagesDHTLog!.operateAppendEventual((writer) async {
// Get the previous message if we have one
var previousMessage = writer.length == 0
? null
@ -357,16 +343,17 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
// Produce a state for this cubit from the input cubits and queues
void _renderState() {
// Get all reconciled messages
// Get all reconciled messages in the cubit window
final reconciledMessages =
_reconciledMessagesCubit?.state.state.asData?.value;
// Get all sent messages
final sentMessages = _sentMessagesCubit?.state.state.asData?.value;
// Get all sent messages that are still offline
//final sentMessages = _sentMessagesDHTLog.
//Get all items in the unsent queue
//final unsentMessages = _unsentMessagesQueue.queue;
// If we aren't ready to render a state, say we're loading
if (reconciledMessages == null || sentMessages == null) {
if (reconciledMessages == null) {
emit(const AsyncLoading());
return;
}
@ -377,11 +364,11 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
// keyMapper: (x) => x.content.authorUniqueIdString,
// values: reconciledMessages.windowElements,
// );
final sentMessagesMap =
IMap<String, OnlineElementState<proto.Message>>.fromValues(
keyMapper: (x) => x.value.authorUniqueIdString,
values: sentMessages.window,
);
// final sentMessagesMap =
// IMap<String, OnlineElementState<proto.Message>>.fromValues(
// keyMapper: (x) => x.value.authorUniqueIdString,
// values: sentMessages.window,
// );
// final unsentMessagesMap = IMap<String, proto.Message>.fromValues(
// keyMapper: (x) => x.authorUniqueIdString,
// values: unsentMessages,
@ -393,10 +380,12 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
final isLocal =
m.content.author.toVeilid() == _accountInfo.identityTypedPublicKey;
final reconciledTimestamp = Timestamp.fromInt64(m.reconciledTime);
final sm =
isLocal ? sentMessagesMap[m.content.authorUniqueIdString] : null;
final sent = isLocal && sm != null;
final sentOffline = isLocal && sm != null && sm.isOffline;
//final sm =
//isLocal ? sentMessagesMap[m.content.authorUniqueIdString] : null;
//final sent = isLocal && sm != null;
//final sentOffline = isLocal && sm != null && sm.isOffline;
final sent = isLocal;
final sentOffline = false; //
renderedElements.add(RenderStateElement(
message: m.content,
@ -491,16 +480,16 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
late final VeilidCrypto _conversationCrypto;
late final MessageIntegrity _senderMessageIntegrity;
DHTLogCubit<proto.Message>? _sentMessagesCubit;
DHTLogCubit<proto.Message>? _rcvdMessagesCubit;
DHTLog? _sentMessagesDHTLog;
DHTLog? _rcvdMessagesDHTLog;
TableDBArrayProtobufCubit<proto.ReconciledMessage>? _reconciledMessagesCubit;
late final MessageReconciliation _reconciliation;
late final PersistentQueue<proto.Message> _unsentMessagesQueue;
// IList<proto.Message> _sendingMessages = const IList.empty();
StreamSubscription<DHTLogBusyState<proto.Message>>? _sentSubscription;
StreamSubscription<DHTLogBusyState<proto.Message>>? _rcvdSubscription;
StreamSubscription<void>? _sentSubscription;
StreamSubscription<void>? _rcvdSubscription;
StreamSubscription<TableDBArrayProtobufBusyState<proto.ReconciledMessage>>?
_reconciledSubscription;
final StreamController<Future<void> Function()> _commandController;