getTimeline method
Creates a timeline from the store. Returns a Timeline object. If you
just want to update the whole timeline on every change, use the onUpdate
callback. For updating only the parts that have changed, use the
onChange, onRemove, onInsert and the onHistoryReceived callbacks.
This method can also retrieve the timeline at a specific point by setting
the eventContextId
Implementation
Future<Timeline> getTimeline({
void Function(int index)? onChange,
void Function(int index)? onRemove,
void Function(int insertID)? onInsert,
void Function()? onNewEvent,
void Function()? onUpdate,
String? eventContextId,
int? limit = Room.defaultHistoryCount,
}) async {
await postLoad();
var events = <Event>[];
if (!isArchived) {
await client.database.transaction(() async {
events = await client.database.getEventList(this, limit: limit);
});
} else {
final archive = client.getArchiveRoomFromCache(id);
events = archive?.timeline.events.toList() ?? [];
for (var i = 0; i < events.length; i++) {
// Try to decrypt encrypted events but don't update the database.
if (encrypted && client.encryptionEnabled) {
if (events[i].type == EventTypes.Encrypted) {
events[i] = await client.encryption!.decryptRoomEvent(events[i]);
}
}
}
}
var chunk = TimelineChunk(events: events);
// Load the timeline arround eventContextId if set
if (eventContextId != null) {
if (!events.any((Event event) => event.eventId == eventContextId)) {
chunk =
await getEventContext(eventContextId) ?? TimelineChunk(events: []);
}
}
final timeline = Timeline(
room: this,
chunk: chunk,
onChange: onChange,
onRemove: onRemove,
onInsert: onInsert,
onNewEvent: onNewEvent,
onUpdate: onUpdate,
);
// Fetch all users from database we have got here.
if (eventContextId == null) {
final userIds = events.map((event) => event.senderId).toSet();
for (final userId in userIds) {
if (getState(EventTypes.RoomMember, userId) != null) continue;
final dbUser = await client.database.getUser(userId, this);
if (dbUser != null) setState(dbUser);
}
}
// Try again to decrypt encrypted events and update the database.
if (encrypted && client.encryptionEnabled) {
// decrypt messages
for (var i = 0; i < chunk.events.length; i++) {
if (chunk.events[i].type == EventTypes.Encrypted) {
if (eventContextId != null) {
// for the fragmented timeline, we don't cache the decrypted
//message in the database
chunk.events[i] = await client.encryption!.decryptRoomEvent(
chunk.events[i],
);
} else {
// else, we need the database
await client.database.transaction(() async {
for (var i = 0; i < chunk.events.length; i++) {
if (chunk.events[i].content['can_request_session'] == true) {
chunk.events[i] = await client.encryption!.decryptRoomEvent(
chunk.events[i],
store: !isArchived,
updateType: EventUpdateType.history,
);
}
}
});
}
}
}
}
// Fetch poll responses for fragmented timelines after decryption so
// encrypted poll start events are visible as poll events.
if (eventContextId != null) {
await Future.wait(
chunk.events
.where((event) => event.type == PollEventContent.startType)
.map(
(event) => timeline.fetchAggregatedEvents(
event.eventId,
RelationshipTypes.reference,
),
),
);
}
return timeline;
}