From 74c39f91feb34c2537b079178d3571c18c6e487b Mon Sep 17 00:00:00 2001 From: OfficialDakari Date: Fri, 24 Oct 2025 21:21:20 +0500 Subject: [PATCH] did something --- lib/src/database/database_api.dart | 2 + lib/src/database/matrix_sdk_database.dart | 46 +++++++++++----- lib/src/room.dart | 52 ++++++++++++++++++ lib/src/room_timeline.dart | 15 +++--- lib/src/thread.dart | 4 +- lib/src/thread_timeline.dart | 66 +++++++++++++++-------- 6 files changed, 142 insertions(+), 43 deletions(-) diff --git a/lib/src/database/database_api.dart b/lib/src/database/database_api.dart index 1f18a071..509cf55c 100644 --- a/lib/src/database/database_api.dart +++ b/lib/src/database/database_api.dart @@ -61,6 +61,8 @@ abstract class DatabaseApi { Future> getThreadList(String roomId, Client client); + Future getThread(String roomId, String threadRootEventId, Client client); + Future storeThread( String roomId, Event threadRootEvent, diff --git a/lib/src/database/matrix_sdk_database.dart b/lib/src/database/matrix_sdk_database.dart index f2ae5740..295abf42 100644 --- a/lib/src/database/matrix_sdk_database.dart +++ b/lib/src/database/matrix_sdk_database.dart @@ -1368,18 +1368,34 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { @override Future> getThreadList(String roomId, Client client) async { final allThreadsKeys = await _threadsBox.getAllKeys(); - final threadsKeys = {}; - // TERRIBLE implementation. Better to create another box (String[roomId]->List[event ids]) - for (final key in allThreadsKeys) { - if (key.startsWith(roomId)) threadsKeys.add(key); - } final threads = {}; - + // TERRIBLE implementation. Better to create another box (String[roomId]->List[event ids]) + for (final key in allThreadsKeys) { + if (key.startsWith('$roomId|')) { + final thread = await getThread(roomId, key.split('|')[1], client); + if (thread != null) { + threads.add(thread); + } + } + } return threads.toList(); } + @override + Future getThread( + String roomId, + String threadRootEventId, + Client client, + ) async { + final key = TupleKey(roomId, threadRootEventId).toString(); + final thread = await _threadsBox.get(key); + if (thread == null) return null; + Logs().w(thread.toString()); + return Thread.fromJson(thread.cast(), client); + } + @override Future storeThread( String roomId, @@ -1392,14 +1408,16 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { final key = TupleKey(roomId, threadRootEvent.eventId).toString(); // final currentRawThread = await _threadsBox.get(key); await _threadsBox.put( - key, - Thread( - room: Room(id: roomId, client: client), - rootEvent: threadRootEvent, - client: client, - currentUserParticipated: currentUserParticipated, - count: count, - ).toJson()); + key, + Thread( + room: Room(id: roomId, client: client), + rootEvent: threadRootEvent, + lastEvent: lastEvent, + client: client, + currentUserParticipated: currentUserParticipated, + count: count, + ).toJson(), + ); } @override diff --git a/lib/src/room.dart b/lib/src/room.dart index b69f549c..275e18b8 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -130,9 +130,52 @@ class Room { for (final state in allStates) { setState(state); } + + await _loadThreadsFromServer(); + partial = false; } + Future _loadThreadsFromServer() async { + try { + final response = await client.getThreadRoots(id); + + for (final threadEvent in response.chunk) { + final event = Event.fromMatrixEvent(threadEvent, this); + // Store thread in database + await client.database.storeThread( + id, + event, + event, // lastEvent + false, // currentUserParticipated + 1, // count + client, + ); + } + } catch (e) { + Logs().w('Failed to load threads from server', e); + } + } + + Future handleThreadSync(Event event) async { + // This should be called from the client's sync handling + // when a thread-related event is received + + if (event.relationshipType == RelationshipTypes.thread && event.relationshipEventId != null) { + // Update thread metadata in database + final root = await getEventById(event.relationshipEventId!); + if (root == null) return; + await client.database.storeThread( + id, + root, + event, // update last event + event.senderId == client.userID, // currentUserParticipated + 1, // increment count - should be calculated properly + client, + ); + } + } + /// Returns the [Event] for the given [typeKey] and optional [stateKey]. /// If no [stateKey] is provided, it defaults to an empty string. /// This returns either a `StrippedStateEvent` for rooms with membership @@ -174,6 +217,15 @@ class Room { client.onRoomState.add((roomId: id, state: state)); } + Future> getThreads() async { + final dict = {}; + final list = await client.database.getThreadList(id, client); + for (final thread in list) { + dict[thread.rootEvent.eventId] = thread; + } + return dict; + } + /// ID of the fully read marker event. String get fullyRead => roomAccountData['m.fully_read']?.content.tryGet('event_id') ?? ''; diff --git a/lib/src/room_timeline.dart b/lib/src/room_timeline.dart index 2818d808..3229ae4c 100644 --- a/lib/src/room_timeline.dart +++ b/lib/src/room_timeline.dart @@ -36,7 +36,7 @@ class RoomTimeline extends Timeline { StreamSubscription? roomSub; StreamSubscription? sessionIdReceivedSub; StreamSubscription? cancelSendEventSub; - + bool isRequestingHistory = false; bool isRequestingFuture = false; bool allowNewEvent = true; @@ -417,7 +417,7 @@ class RoomTimeline extends Timeline { final searchNeedle = {}; if (event_id != null) searchNeedle.add(event_id); if (unsigned_txid != null) searchNeedle.add(unsigned_txid); - + int i; for (i = 0; i < events.length; i++) { final searchHaystack = {events[i].eventId}; @@ -436,13 +436,16 @@ class RoomTimeline extends Timeline { try { if (event.roomId != room.id) return; - // This will be handled by ThreadTimeline - if (event.relationshipType == RelationshipTypes.thread) return; - if (type != EventUpdateType.timeline && type != EventUpdateType.history) { return; } + // Skip thread events in main timeline - THEY SHOULD ONLY APPEAR IN THREAD TIMELINES + if (event.relationshipType == RelationshipTypes.thread && + event.relationshipEventId != null) { + unawaited(room.handleThreadSync(event)); + } + if (type == EventUpdateType.timeline) { onNewEvent?.call(); } @@ -641,4 +644,4 @@ class RoomTimeline extends Timeline { e.matchesEventOrTransactionId(event.transactionId), ); } -} \ No newline at end of file +} diff --git a/lib/src/thread.dart b/lib/src/thread.dart index 74c2a3c1..75f776a8 100644 --- a/lib/src/thread.dart +++ b/lib/src/thread.dart @@ -7,8 +7,8 @@ class Thread { final Event rootEvent; Event? lastEvent; String? prev_batch; - bool currentUserParticipated; - int count; + bool? currentUserParticipated; + int? count; final Client client; Thread({ diff --git a/lib/src/thread_timeline.dart b/lib/src/thread_timeline.dart index 8501dc2a..0d8e8e8e 100644 --- a/lib/src/thread_timeline.dart +++ b/lib/src/thread_timeline.dart @@ -33,6 +33,8 @@ class ThreadTimeline extends Timeline { bool allowNewEvent = true; bool _collectHistoryUpdates = false; + + bool isRequestingFuture = false; ThreadTimeline({ required this.thread, @@ -367,14 +369,6 @@ class ThreadTimeline extends Timeline { return i; } - @override - // TODO: implement canRequestFuture - bool get canRequestFuture => throw UnimplementedError(); - - @override - // TODO: implement canRequestHistory - bool get canRequestHistory => throw UnimplementedError(); - @override void cancelSubscriptions() { // TODO: implement cancelSubscriptions @@ -392,13 +386,6 @@ class ThreadTimeline extends Timeline { return _eventCache[id]; } - @override - Future requestFuture( - {int historyCount = Room.defaultHistoryCount, StateFilter? filter}) { - // TODO: implement requestFuture - throw UnimplementedError(); - } - @override Future requestHistory( {int historyCount = Room.defaultHistoryCount, StateFilter? filter}) async { @@ -412,12 +399,6 @@ class ThreadTimeline extends Timeline { isRequestingHistory = false; } - @override - void requestKeys( - {bool tryOnlineBackup = true, bool onlineKeyBackupOnly = true}) { - // TODO: implement requestKeys - } - @override Future setReadMarker({String? eventId, bool? public}) { return thread.setReadMarker( @@ -438,4 +419,47 @@ class ThreadTimeline extends Timeline { // TODO: implement startSearch throw UnimplementedError(); } + + @override +bool get canRequestFuture => chunk.nextBatch != null && chunk.nextBatch!.isNotEmpty; + +@override +bool get canRequestHistory => chunk.prevBatch != null && chunk.prevBatch!.isNotEmpty; + +@override +Future requestFuture({ + int historyCount = Room.defaultHistoryCount, + StateFilter? filter, +}) async { + if (isRequestingFuture || !canRequestFuture) return; + isRequestingFuture = true; + + try { + await getThreadEvents( + historyCount: historyCount, + direction: Direction.f, + filter: filter, + ); + } finally { + isRequestingFuture = false; + } +} + +@override +void requestKeys({ + bool tryOnlineBackup = true, + bool onlineKeyBackupOnly = true, +}) { + for (final event in events) { + if (event.type == EventTypes.Encrypted && + event.messageType == MessageTypes.BadEncrypted && + event.content['can_request_session'] == true) { + final sessionId = event.content.tryGet('session_id'); + final senderKey = event.content.tryGet('sender_key'); + if (sessionId != null && senderKey != null) { + thread.room.requestSessionKey(sessionId, senderKey); + } + } + } +} }