Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions commet/lib/client/matrix/matrix_room.dart
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,9 @@ class MatrixRoom extends Room {
await tl.setReadMarker(public: public);
}

@override
String get lastRead => _matrixRoom.fullyRead;

@override
RoomVisibility get visibility {
switch (_matrixRoom.joinRules) {
Expand Down
27 changes: 24 additions & 3 deletions commet/lib/client/matrix/matrix_timeline.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:commet/client/matrix/timeline_events/matrix_timeline_event.dart'
import 'package:commet/client/timeline_events/timeline_event.dart';
import 'package:commet/client/timeline_events/timeline_event_message.dart';
import 'package:commet/client/timeline_events/timeline_event_sticker.dart';
import 'package:commet/debug/log.dart';

import '../client.dart';
import 'package:matrix/matrix.dart' as matrix;
Expand Down Expand Up @@ -117,10 +118,30 @@ class MatrixTimeline extends Timeline {
@override
Future<void> loadMoreFuture() async {
if (canLoadFuture) {
var f = _matrixTimeline?.requestFuture();

int waitSec = 4;
_loadingStatusChangedController.add(null);
await f;
while (true) {
var f = _matrixTimeline?.requestFuture();

try {
await f;
_loadingStatusChangedController.add(null);
break;
} on matrix.MatrixException catch (e) {
if (e.error == matrix.MatrixError.M_LIMIT_EXCEEDED) {
Log.i("Rate limited, waiting $waitSec second(s)...");
await Future.delayed(Duration(seconds: waitSec));
waitSec *= 2;
continue;
} else {
Log.e(e);
break;
}
} catch (e) {
Log.e(e);
break;
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ abstract class MatrixTimelineEvent implements TimelineEvent {
String get source =>
const JsonEncoder.withIndent(' ').convert(event.toJson());

@override
bool get mentionsRoom => event.mentions.room;

@override
List<String> get mentions => event.mentions.userIds;

@override
TimelineEventStatus get status => switch (event.status) {
matrix.EventStatus.error => TimelineEventStatus.error,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ class MatrixBackgroundTimelineEventMessage implements TimelineEventMessage {
@override
String get source => throw UnimplementedError();

@override
bool get mentionsRoom => throw UnimplementedError();

@override
List<String> get mentions => throw UnimplementedError();

@override
TimelineEventStatus get status => throw UnimplementedError();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,9 @@ class MatrixBackgroundRoom implements Room {
throw UnimplementedError();
}

@override
String get lastRead => throw UnimplementedError();

@override
// TODO: implement visibility
RoomVisibility get visibility => throw UnimplementedError();
Expand Down
2 changes: 2 additions & 0 deletions commet/lib/client/room.dart
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ abstract class Room {

Future<void> markAsRead();

String get lastRead;

@override
bool operator ==(Object other) {
if (other is! Room) return false;
Expand Down
2 changes: 1 addition & 1 deletion commet/lib/client/timeline.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ abstract class Timeline {
final Map<String, TimelineEvent> _eventsDict = {};
late StreamController<int> onEventAdded =
StreamController.broadcast(sync: true);
late StreamController<int> onChange = StreamController.broadcast(sync: true);
late StreamController<int> onChange = StreamController.broadcast(sync: false);
late StreamController<int> onRemove = StreamController.broadcast(sync: true);
late Client client;
late Room room;
Expand Down
2 changes: 2 additions & 0 deletions commet/lib/client/timeline_events/timeline_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ abstract class TimelineEvent<T extends Client> {
String get senderId;
DateTime get originServerTs;
String get source;
bool get mentionsRoom;
List<String> get mentions;

bool get editable;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class RoomTimelineWidgetViewState extends State<RoomTimelineWidgetView> {
bool isLoadingHistory = false;

MessageEffectComponent? effects;
String? lastReadEventId;

@override
void initState() {
Expand All @@ -102,11 +103,24 @@ class RoomTimelineWidgetViewState extends State<RoomTimelineWidgetView> {
}
}

isLoadingFuture = false;
isLoadingHistory = false;
String? targetEventId = timeline.room.lastRead; // timeline.contextEventId;

this.timeline = timeline;
recentItemsCount = timeline.events.length;
var index = timeline.events.indexWhere((i) => i.eventId == targetEventId);

if (index > 0) {
lastReadEventId = targetEventId;

isLoadingFuture = false;
isLoadingHistory = false;

recentItemsCount = index;
} else {
if (timeline.events.length > 1) {
recentItemsCount = 1;
}
}

var receipts = timeline.room.getComponent<ReadReceiptComponent>();
subscriptions = [
timeline.onEventAdded.stream.listen(onEventAdded),
Expand Down Expand Up @@ -218,16 +232,16 @@ class RoomTimelineWidgetViewState extends State<RoomTimelineWidgetView> {
}

void onAfterFirstFrame(_) {
if (timeline.events.isNotEmpty) {
widget.markAsRead?.call(timeline.events.first);
}

if (controller.hasClients) {
double extent = controller.position.minScrollExtent;

if (controller.position.viewportDimension < -extent) {
extent = -controller.position.viewportDimension / 2;
}

controller = ScrollController(initialScrollOffset: extent);
scrollViewKey = GlobalKey();
controller.addListener(onScroll);
widget.onAttachedToBottom?.call();
setState(() {
firstFrame = false;
});
Expand Down Expand Up @@ -266,18 +280,24 @@ class RoomTimelineWidgetViewState extends State<RoomTimelineWidgetView> {
if (controller.offset >
controller.position.maxScrollExtent - loadingThreshold &&
!timeline.isLoadingHistory &&
timeline.canLoadHistory) {
timeline.loadMoreHistory().then((_) {
WidgetsBinding.instance.addPostFrameCallback((_) => onScroll());
timeline.canLoadHistory)
setState(() async {
await timeline.loadMoreHistory().then((_) {
WidgetsBinding.instance.addPostFrameCallback((_) => onScroll());
});
});
}

if (controller.offset <
(controller.position.minScrollExtent + loadingThreshold) &&
!timeline.isLoadingFuture &&
timeline.canLoadFuture) {
timeline.loadMoreFuture();
}
timeline.canLoadFuture)
setState(() async {
await timeline.loadMoreFuture();
eventKeys = List.from(
timeline.events
.map((e) => (GlobalKey(debugLabel: e.eventId), e.eventId)),
growable: true);
});
}

void animateAndSnapToBottom() {
Expand Down Expand Up @@ -379,6 +399,10 @@ class RoomTimelineWidgetViewState extends State<RoomTimelineWidgetView> {
),
);
})),
// The slivers are split into recent and history events and
// are rendered separately. This prevents the timeline from
// jumping around and allows jumping to specific indices
// with more reliability.
SliverList(
key: recentItemsKey,
// Recent Items
Expand All @@ -390,7 +414,10 @@ class RoomTimelineWidgetViewState extends State<RoomTimelineWidgetView> {
recentItemsCount - sliverIndex - 1;
numBuilds += 1;

var key = eventKeys[timelineIndex];
var key;

key = eventKeys[timelineIndex];

assert(
key.$2 == timeline.events[timelineIndex].eventId);

Expand All @@ -409,6 +436,7 @@ class RoomTimelineWidgetViewState extends State<RoomTimelineWidgetView> {
setReplyingEvent: widget.setReplyingEvent,
isThreadTimeline: widget.isThreadTimeline,
highlightedEventId: highlightedEventId,
lastReadEventId: lastReadEventId,
previewMedia:
widget.timeline.room.shouldPreviewMedia,
jumpToEvent: jumpToEvent,
Expand Down Expand Up @@ -439,7 +467,10 @@ class RoomTimelineWidgetViewState extends State<RoomTimelineWidgetView> {
// ignore: avoid_print
var timelineIndex = recentItemsCount + sliverIndex;

var key = eventKeys[timelineIndex];
var key;

key = eventKeys[timelineIndex];

assert(
key.$2 == timeline.events[timelineIndex].eventId);

Expand All @@ -460,6 +491,7 @@ class RoomTimelineWidgetViewState extends State<RoomTimelineWidgetView> {
highlightedEventId: highlightedEventId,
previewMedia:
widget.timeline.room.shouldPreviewMedia,
lastReadEventId: lastReadEventId,
jumpToEvent: jumpToEvent,
initialIndex: timelineIndex),
);
Expand Down Expand Up @@ -525,8 +557,8 @@ class RoomTimelineWidgetViewState extends State<RoomTimelineWidgetView> {
);
}

void jumpToEvent(String eventId) async {
if (highlightedEventState?.mounted == true) {
void jumpToEvent(String eventId, {bool highlight = true}) async {
if (highlight && highlightedEventState?.mounted == true) {
highlightedEventState!.setHighlighted(false);
highlightedEventState = null;
}
Expand Down Expand Up @@ -555,7 +587,7 @@ class RoomTimelineWidgetViewState extends State<RoomTimelineWidgetView> {
var key = eventKeys[index].$1;
final state = key.currentState;

if (state is TimelineViewEntryState) {
if (highlight && state is TimelineViewEntryState) {
state.setHighlighted(true);
highlightedEventState = state;
}
Expand Down Expand Up @@ -586,9 +618,11 @@ class RoomTimelineWidgetViewState extends State<RoomTimelineWidgetView> {

setState(() {
recentItemsCount = index;
highlightedEventId = timeline.events[index].eventId;
highlightedEventOffstageIndex = index;
highlightedEventOffstageKey = GlobalKey();
if (highlight) {
highlightedEventId = timeline.events[index].eventId;
highlightedEventOffstageIndex = index;
highlightedEventOffstageKey = GlobalKey();
}
loading = false;
});
}
Expand All @@ -612,7 +646,7 @@ class RoomTimelineWidgetViewState extends State<RoomTimelineWidgetView> {
var index = timeline.events.indexWhere((i) => i.eventId == event);

if (index == -1) {
print("Could not find the event in the timeline view");
Log.w("Could not find the event in the timeline view");
}

if (state is TimelineEventViewWidget) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class TimelineEventViewMessage extends StatefulWidget {
this.jumpToEvent,
this.readReceipts = const [],
this.onReadReceiptsTapped,
this.onDoubleTapMessage,
this.detailed = false,
this.previewMedia = false,
required this.initialIndex});
Expand All @@ -56,6 +57,7 @@ class TimelineEventViewMessage extends StatefulWidget {
final bool isThreadTimeline;
final bool previewMedia;
final Function()? onReadReceiptsTapped;
final Function()? onDoubleTapMessage;

@override
State<TimelineEventViewMessage> createState() =>
Expand All @@ -68,6 +70,9 @@ class _TimelineEventViewMessageState extends State<TimelineEventViewMessage>
late String senderId;
late Color senderColor;

late bool mentionsRoom;
late List<String> mentions;

String get messageFailedToDecrypt => Intl.message("Failed to decrypt event",
desc: "Placeholde text for when a message fails to decrypt",
name: "messageFailedToDecrypt");
Expand Down Expand Up @@ -131,6 +136,9 @@ class _TimelineEventViewMessageState extends State<TimelineEventViewMessage>
formattedContent: formattedContent,
timestamp: timestampToString(sentTime),
edited: edited,
isMentioningSelf: mentionsRoom ||
mentions.contains(widget.timeline!.client.self!.identifier),
onDoubleTapMessage: widget.onDoubleTapMessage,
avatarBuilder: (child) {
var room = widget.room ?? widget.timeline?.room;

Expand Down Expand Up @@ -220,6 +228,8 @@ class _TimelineEventViewMessageState extends State<TimelineEventViewMessage>
}

void loadStateFromEvent(TimelineEvent event) {
mentionsRoom = event.mentionsRoom;
mentions = event.mentions;
showSender = shouldShowSender(index);
var room = widget.room ?? widget.timeline?.room;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class _TimelineEventViewReplyState extends State<TimelineEventViewReply> {
text: body ?? "Unknown",
style: Theme.of(context)
.textTheme
.bodyMedium
.bodySmall
?.copyWith(
color: material.Theme.of(context)
.colorScheme
Expand Down
Loading
Loading