I forgot what I changed sorry
This commit is contained in:
parent
c4df172416
commit
889477e07c
|
|
@ -598,13 +598,44 @@ class RoomTimeline extends Timeline {
|
|||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
extension on List<Event> {
|
||||
int get firstIndexWhereNotError {
|
||||
if (isEmpty) return 0;
|
||||
final index = indexWhere((event) => !event.status.isError);
|
||||
if (index == -1) return length;
|
||||
return index;
|
||||
/// Add an event to the aggregation tree
|
||||
void addAggregatedEvent(Event event) {
|
||||
final relationshipType = event.relationshipType;
|
||||
final relationshipEventId = event.relationshipEventId;
|
||||
if (relationshipType == null || relationshipEventId == null) {
|
||||
return;
|
||||
}
|
||||
final e = (aggregatedEvents[relationshipEventId] ??=
|
||||
<String, Set<Event>>{})[relationshipType] ??= <Event>{};
|
||||
_removeEventFromSet(e, event);
|
||||
e.add(event);
|
||||
if (onChange != null) {
|
||||
final index = _findEvent(event_id: relationshipEventId);
|
||||
onChange?.call(index);
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove an event from aggregation
|
||||
void removeAggregatedEvent(Event event) {
|
||||
aggregatedEvents.remove(event.eventId);
|
||||
if (event.transactionId != null) {
|
||||
aggregatedEvents.remove(event.transactionId);
|
||||
}
|
||||
for (final types in aggregatedEvents.values) {
|
||||
for (final e in types.values) {
|
||||
_removeEventFromSet(e, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove event from set based on event or transaction ID
|
||||
void _removeEventFromSet(Set<Event> eventSet, Event event) {
|
||||
eventSet.removeWhere(
|
||||
(e) =>
|
||||
e.matchesEventOrTransactionId(event.eventId) ||
|
||||
event.unsigned != null &&
|
||||
e.matchesEventOrTransactionId(event.transactionId),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,12 +4,14 @@ class Thread {
|
|||
final Room room;
|
||||
final Event rootEvent;
|
||||
Event? lastEvent;
|
||||
String? prev_batch;
|
||||
final Client client;
|
||||
|
||||
Thread({
|
||||
required this.room,
|
||||
required this.rootEvent,
|
||||
required this.client,
|
||||
this.prev_batch,
|
||||
this.lastEvent,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,163 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:matrix/src/models/timeline_chunk.dart';
|
||||
import 'package:matrix/src/thread.dart';
|
||||
|
||||
// ThreadTimeline: hey RoomTimeline can i copy your homework?
|
||||
// RoomTimeline: sure just don't make it too obvious
|
||||
// ThreadTimeline:
|
||||
|
||||
class ThreadTimeline extends Timeline {
|
||||
final Thread thread;
|
||||
|
||||
|
||||
@override
|
||||
List<Event> get events => chunk.events;
|
||||
|
||||
TimelineChunk chunk;
|
||||
|
||||
StreamSubscription<Event>? timelineSub;
|
||||
StreamSubscription<Event>? historySub;
|
||||
StreamSubscription<SyncUpdate>? roomSub;
|
||||
StreamSubscription<String>? sessionIdReceivedSub;
|
||||
StreamSubscription<String>? cancelSendEventSub;
|
||||
|
||||
ThreadTimeline({
|
||||
required this.thread,
|
||||
required this.chunk
|
||||
required this.chunk,
|
||||
super.onUpdate,
|
||||
super.onChange,
|
||||
super.onInsert,
|
||||
super.onRemove,
|
||||
super.onNewEvent,
|
||||
}) {
|
||||
|
||||
final room = thread.room;
|
||||
timelineSub = room.client.onTimelineEvent.stream.listen(
|
||||
(event) => _handleEventUpdate(event, EventUpdateType.timeline),
|
||||
);
|
||||
historySub = room.client.onHistoryEvent.stream.listen(
|
||||
(event) => _handleEventUpdate(event, EventUpdateType.history),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleEventUpdate(
|
||||
Event event,
|
||||
EventUpdateType type, {
|
||||
bool update = true,
|
||||
}) {
|
||||
try {
|
||||
if (event.roomId != thread.room.id) return;
|
||||
// Ignore events outside of this thread
|
||||
if (event.relationshipType != RelationshipTypes.thread ||
|
||||
event.relationshipEventId != thread.rootEvent.eventId) return;
|
||||
|
||||
if (type != EventUpdateType.timeline && type != EventUpdateType.history) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == EventUpdateType.timeline) {
|
||||
onNewEvent?.call();
|
||||
}
|
||||
|
||||
if (type == EventUpdateType.history &&
|
||||
events.indexWhere((e) => e.eventId == event.eventId) != -1) {
|
||||
return;
|
||||
}
|
||||
var index = events.length;
|
||||
if (type == EventUpdateType.history) {
|
||||
events.add(event);
|
||||
} else {
|
||||
index = events.firstIndexWhereNotError;
|
||||
events.insert(index, event);
|
||||
}
|
||||
onInsert?.call(index);
|
||||
|
||||
addAggregatedEvent(event);
|
||||
|
||||
// Handle redaction events
|
||||
if (event.type == EventTypes.Redaction) {
|
||||
final index = _findEvent(event_id: event.redacts);
|
||||
if (index < events.length) {
|
||||
removeAggregatedEvent(events[index]);
|
||||
|
||||
// Is the redacted event a reaction? Then update the event this
|
||||
// belongs to:
|
||||
if (onChange != null) {
|
||||
final relationshipEventId = events[index].relationshipEventId;
|
||||
if (relationshipEventId != null) {
|
||||
onChange?.call(_findEvent(event_id: relationshipEventId));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
events[index].setRedactionEvent(event);
|
||||
onChange?.call(index);
|
||||
}
|
||||
}
|
||||
|
||||
if (update) {
|
||||
onUpdate?.call();
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logs().w('Handle event update failed', e, s);
|
||||
}
|
||||
}
|
||||
|
||||
/// Add an event to the aggregation tree
|
||||
void addAggregatedEvent(Event event) {
|
||||
final relationshipType = event.relationshipType;
|
||||
final relationshipEventId = event.relationshipEventId;
|
||||
if (relationshipType == null || relationshipEventId == null) {
|
||||
return;
|
||||
}
|
||||
final e = (aggregatedEvents[relationshipEventId] ??=
|
||||
<String, Set<Event>>{})[relationshipType] ??= <Event>{};
|
||||
_removeEventFromSet(e, event);
|
||||
e.add(event);
|
||||
if (onChange != null) {
|
||||
final index = _findEvent(event_id: relationshipEventId);
|
||||
onChange?.call(index);
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove an event from aggregation
|
||||
void removeAggregatedEvent(Event event) {
|
||||
aggregatedEvents.remove(event.eventId);
|
||||
if (event.transactionId != null) {
|
||||
aggregatedEvents.remove(event.transactionId);
|
||||
}
|
||||
for (final types in aggregatedEvents.values) {
|
||||
for (final e in types.values) {
|
||||
_removeEventFromSet(e, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove event from set based on event or transaction ID
|
||||
void _removeEventFromSet(Set<Event> eventSet, Event event) {
|
||||
eventSet.removeWhere(
|
||||
(e) =>
|
||||
e.matchesEventOrTransactionId(event.eventId) ||
|
||||
event.unsigned != null &&
|
||||
e.matchesEventOrTransactionId(event.transactionId),
|
||||
);
|
||||
}
|
||||
|
||||
/// Find event index by event ID or transaction ID
|
||||
int _findEvent({String? event_id, String? unsigned_txid}) {
|
||||
final searchNeedle = <String>{};
|
||||
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 = <String>{events[i].eventId};
|
||||
final txnid = events[i].transactionId;
|
||||
if (txnid != null) searchHaystack.add(txnid);
|
||||
if (searchNeedle.intersection(searchHaystack).isNotEmpty) break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
@override
|
||||
// TODO: implement canRequestFuture
|
||||
|
|
@ -38,19 +179,22 @@ class ThreadTimeline extends Timeline {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<void> requestFuture({int historyCount = Room.defaultHistoryCount, StateFilter? filter}) {
|
||||
Future<void> requestFuture(
|
||||
{int historyCount = Room.defaultHistoryCount, StateFilter? filter}) {
|
||||
// TODO: implement requestFuture
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> requestHistory({int historyCount = Room.defaultHistoryCount, StateFilter? filter}) {
|
||||
Future<void> requestHistory(
|
||||
{int historyCount = Room.defaultHistoryCount, StateFilter? filter}) {
|
||||
// TODO: implement requestHistory
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
void requestKeys({bool tryOnlineBackup = true, bool onlineKeyBackupOnly = true}) {
|
||||
void requestKeys(
|
||||
{bool tryOnlineBackup = true, bool onlineKeyBackupOnly = true}) {
|
||||
// TODO: implement requestKeys
|
||||
}
|
||||
|
||||
|
|
@ -61,9 +205,15 @@ class ThreadTimeline extends Timeline {
|
|||
}
|
||||
|
||||
@override
|
||||
Stream<(List<Event>, String?)> startSearch({String? searchTerm, int requestHistoryCount = 100, int maxHistoryRequests = 10, String? prevBatch, String? sinceEventId, int? limit, bool Function(Event p1)? searchFunc}) {
|
||||
Stream<(List<Event>, String?)> startSearch(
|
||||
{String? searchTerm,
|
||||
int requestHistoryCount = 100,
|
||||
int maxHistoryRequests = 10,
|
||||
String? prevBatch,
|
||||
String? sinceEventId,
|
||||
int? limit,
|
||||
bool Function(Event p1)? searchFunc}) {
|
||||
// TODO: implement startSearch
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,11 +17,7 @@
|
|||
*/
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:matrix/src/models/timeline_chunk.dart';
|
||||
|
||||
/// Abstract base class for all timeline implementations.
|
||||
/// Provides common functionality for event management, aggregation, and search.
|
||||
|
|
@ -93,62 +89,6 @@ abstract class Timeline {
|
|||
bool Function(Event)? searchFunc,
|
||||
});
|
||||
|
||||
/// Add an event to the aggregation tree
|
||||
void addAggregatedEvent(Event event) {
|
||||
final relationshipType = event.relationshipType;
|
||||
final relationshipEventId = event.relationshipEventId;
|
||||
if (relationshipType == null || relationshipEventId == null) {
|
||||
return;
|
||||
}
|
||||
final e = (aggregatedEvents[relationshipEventId] ??=
|
||||
<String, Set<Event>>{})[relationshipType] ??= <Event>{};
|
||||
_removeEventFromSet(e, event);
|
||||
e.add(event);
|
||||
if (onChange != null) {
|
||||
final index = _findEvent(event_id: relationshipEventId);
|
||||
onChange?.call(index);
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove an event from aggregation
|
||||
void removeAggregatedEvent(Event event) {
|
||||
aggregatedEvents.remove(event.eventId);
|
||||
if (event.transactionId != null) {
|
||||
aggregatedEvents.remove(event.transactionId);
|
||||
}
|
||||
for (final types in aggregatedEvents.values) {
|
||||
for (final e in types.values) {
|
||||
_removeEventFromSet(e, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Find event index by event ID or transaction ID
|
||||
int _findEvent({String? event_id, String? unsigned_txid}) {
|
||||
final searchNeedle = <String>{};
|
||||
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 = <String>{events[i].eventId};
|
||||
final txnid = events[i].transactionId;
|
||||
if (txnid != null) searchHaystack.add(txnid);
|
||||
if (searchNeedle.intersection(searchHaystack).isNotEmpty) break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/// Remove event from set based on event or transaction ID
|
||||
void _removeEventFromSet(Set<Event> eventSet, Event event) {
|
||||
eventSet.removeWhere(
|
||||
(e) =>
|
||||
e.matchesEventOrTransactionId(event.eventId) ||
|
||||
event.unsigned != null &&
|
||||
e.matchesEventOrTransactionId(event.transactionId),
|
||||
);
|
||||
}
|
||||
|
||||
/// Handle event updates (to be implemented by subclasses)
|
||||
void _handleEventUpdate(Event event, EventUpdateType type, {bool update = true});
|
||||
|
||||
|
|
@ -172,4 +112,14 @@ abstract class Timeline {
|
|||
limit: limit,
|
||||
searchFunc: searchFunc,
|
||||
).map((result) => result.$1);
|
||||
}
|
||||
|
||||
// TODO: make up a better name
|
||||
extension TimelineExtension on List<Event> {
|
||||
int get firstIndexWhereNotError {
|
||||
if (isEmpty) return 0;
|
||||
final index = indexWhere((event) => !event.status.isError);
|
||||
if (index == -1) return length;
|
||||
return index;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue