refactor: Enhance logic when to mark room as read
This commit is contained in:
parent
03511a1e8d
commit
0cf6a1d74a
2 changed files with 229 additions and 225 deletions
|
|
@ -252,6 +252,7 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
setState(() => _scrolledUp = true);
|
setState(() => _scrolledUp = true);
|
||||||
} else if (scrollController.position.pixels <= 0 && _scrolledUp == true) {
|
} else if (scrollController.position.pixels <= 0 && _scrolledUp == true) {
|
||||||
setState(() => _scrolledUp = false);
|
setState(() => _scrolledUp = false);
|
||||||
|
setReadMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scrollController.position.pixels == 0 ||
|
if (scrollController.position.pixels == 0 ||
|
||||||
|
|
@ -275,6 +276,7 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
_loadDraft();
|
_loadDraft();
|
||||||
super.initState();
|
super.initState();
|
||||||
sendingClient = Matrix.of(context).client;
|
sendingClient = Matrix.of(context).client;
|
||||||
|
WidgetsBinding.instance.addObserver(this);
|
||||||
_tryLoadTimeline();
|
_tryLoadTimeline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -286,7 +288,6 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
if (fullyRead.isEmpty) return;
|
if (fullyRead.isEmpty) return;
|
||||||
if (timeline!.events.any((event) => event.eventId == fullyRead)) {
|
if (timeline!.events.any((event) => event.eventId == fullyRead)) {
|
||||||
Logs().v('Scroll up to visible event', fullyRead);
|
Logs().v('Scroll up to visible event', fullyRead);
|
||||||
setReadMarker();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
@ -317,6 +318,11 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
int? animateInEventIndex;
|
int? animateInEventIndex;
|
||||||
|
|
||||||
void onInsert(int i) {
|
void onInsert(int i) {
|
||||||
|
if (timeline?.events[i].status == EventStatus.synced) {
|
||||||
|
final index = timeline!.events.firstIndexWhereNotError;
|
||||||
|
if (i == index) setReadMarker(eventId: timeline?.events[i].eventId);
|
||||||
|
}
|
||||||
|
|
||||||
// setState will be called by updateView() anyway
|
// setState will be called by updateView() anyway
|
||||||
animateInEventIndex = i;
|
animateInEventIndex = i;
|
||||||
}
|
}
|
||||||
|
|
@ -350,6 +356,7 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
}
|
}
|
||||||
timeline!.requestKeys(onlineKeyBackupOnly: false);
|
timeline!.requestKeys(onlineKeyBackupOnly: false);
|
||||||
if (room.markedUnread) room.markUnread(false);
|
if (room.markedUnread) room.markUnread(false);
|
||||||
|
setReadMarker();
|
||||||
|
|
||||||
// when the scroll controller is attached we want to scroll to an event id, if specified
|
// when the scroll controller is attached we want to scroll to an event id, if specified
|
||||||
// and update the scroll controller...which will trigger a request history, if the
|
// and update the scroll controller...which will trigger a request history, if the
|
||||||
|
|
@ -371,7 +378,6 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
@override
|
@override
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
if (state != AppLifecycleState.resumed) return;
|
if (state != AppLifecycleState.resumed) return;
|
||||||
if (!_scrolledUp) return;
|
|
||||||
setReadMarker();
|
setReadMarker();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -379,13 +385,19 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
|
|
||||||
void setReadMarker({String? eventId}) {
|
void setReadMarker({String? eventId}) {
|
||||||
if (_setReadMarkerFuture != null) return;
|
if (_setReadMarkerFuture != null) return;
|
||||||
|
if (_scrolledUp) return;
|
||||||
if (scrollUpBannerEventId != null) return;
|
if (scrollUpBannerEventId != null) return;
|
||||||
if (eventId == null &&
|
if (eventId == null &&
|
||||||
!room.hasNewMessages &&
|
!room.hasNewMessages &&
|
||||||
room.notificationCount == 0) {
|
room.notificationCount == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!Matrix.of(context).webHasFocus) return;
|
|
||||||
|
// Do not send read markers when app is not in foreground
|
||||||
|
if (!Matrix.of(context).webHasFocus ||
|
||||||
|
WidgetsBinding.instance.lifecycleState != AppLifecycleState.resumed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final timeline = this.timeline;
|
final timeline = this.timeline;
|
||||||
if (timeline == null || timeline.events.isEmpty) return;
|
if (timeline == null || timeline.events.isEmpty) return;
|
||||||
|
|
@ -932,7 +944,6 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
await loadTimelineFuture;
|
await loadTimelineFuture;
|
||||||
setReadMarker();
|
|
||||||
}
|
}
|
||||||
scrollController.jumpTo(0);
|
scrollController.jumpTo(0);
|
||||||
}
|
}
|
||||||
|
|
@ -1174,7 +1185,6 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
|
|
||||||
void onInputBarChanged(String text) {
|
void onInputBarChanged(String text) {
|
||||||
if (_inputTextIsEmpty != text.isEmpty) {
|
if (_inputTextIsEmpty != text.isEmpty) {
|
||||||
setReadMarker();
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_inputTextIsEmpty = text.isEmpty;
|
_inputTextIsEmpty = text.isEmpty;
|
||||||
});
|
});
|
||||||
|
|
@ -1300,3 +1310,12 @@ class ChatController extends State<ChatPageWithRoom>
|
||||||
}
|
}
|
||||||
|
|
||||||
enum EmojiPickerType { reaction, keyboard }
|
enum EmojiPickerType { reaction, keyboard }
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -150,11 +150,6 @@ class ChatView extends StatelessWidget {
|
||||||
controller.emojiPickerAction();
|
controller.emojiPickerAction();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: GestureDetector(
|
|
||||||
onTapDown: (_) => controller.setReadMarker(),
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
child: MouseRegion(
|
|
||||||
onEnter: (_) => controller.setReadMarker(),
|
|
||||||
child: StreamBuilder(
|
child: StreamBuilder(
|
||||||
stream: controller.room.onUpdate.stream
|
stream: controller.room.onUpdate.stream
|
||||||
.rateLimit(const Duration(seconds: 1)),
|
.rateLimit(const Duration(seconds: 1)),
|
||||||
|
|
@ -220,9 +215,8 @@ class ChatView extends StatelessWidget {
|
||||||
TombstoneDisplay(controller),
|
TombstoneDisplay(controller),
|
||||||
if (scrollUpBannerEventId != null)
|
if (scrollUpBannerEventId != null)
|
||||||
Material(
|
Material(
|
||||||
color: Theme.of(context)
|
color:
|
||||||
.colorScheme
|
Theme.of(context).colorScheme.surfaceVariant,
|
||||||
.surfaceVariant,
|
|
||||||
shape: Border(
|
shape: Border(
|
||||||
bottom: BorderSide(
|
bottom: BorderSide(
|
||||||
width: 1,
|
width: 1,
|
||||||
|
|
@ -237,23 +231,20 @@ class ChatView extends StatelessWidget {
|
||||||
icon: const Icon(Icons.close),
|
icon: const Icon(Icons.close),
|
||||||
tooltip: L10n.of(context)!.close,
|
tooltip: L10n.of(context)!.close,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
controller
|
controller.discardScrollUpBannerEventId();
|
||||||
.discardScrollUpBannerEventId();
|
|
||||||
controller.setReadMarker();
|
controller.setReadMarker();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
L10n.of(context)!.jumpToLastReadMessage,
|
L10n.of(context)!.jumpToLastReadMessage,
|
||||||
),
|
),
|
||||||
contentPadding:
|
contentPadding: const EdgeInsets.only(left: 8),
|
||||||
const EdgeInsets.only(left: 8),
|
|
||||||
trailing: TextButton(
|
trailing: TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
controller.scrollToEventId(
|
controller.scrollToEventId(
|
||||||
scrollUpBannerEventId,
|
scrollUpBannerEventId,
|
||||||
);
|
);
|
||||||
controller
|
controller.discardScrollUpBannerEventId();
|
||||||
.discardScrollUpBannerEventId();
|
|
||||||
},
|
},
|
||||||
child: Text(L10n.of(context)!.jump),
|
child: Text(L10n.of(context)!.jump),
|
||||||
),
|
),
|
||||||
|
|
@ -267,8 +258,7 @@ class ChatView extends StatelessWidget {
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
if (controller.timeline == null) {
|
if (controller.timeline == null) {
|
||||||
return const Center(
|
return const Center(
|
||||||
child: CircularProgressIndicator
|
child: CircularProgressIndicator.adaptive(
|
||||||
.adaptive(
|
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -308,8 +298,7 @@ class ChatView extends StatelessWidget {
|
||||||
Brightness.light
|
Brightness.light
|
||||||
? Colors.white
|
? Colors.white
|
||||||
: Colors.black,
|
: Colors.black,
|
||||||
child: controller.room.isAbandonedDMRoom ==
|
child: controller.room.isAbandonedDMRoom == true
|
||||||
true
|
|
||||||
? Row(
|
? Row(
|
||||||
mainAxisAlignment:
|
mainAxisAlignment:
|
||||||
MainAxisAlignment.spaceEvenly,
|
MainAxisAlignment.spaceEvenly,
|
||||||
|
|
@ -319,8 +308,7 @@ class ChatView extends StatelessWidget {
|
||||||
padding: const EdgeInsets.all(
|
padding: const EdgeInsets.all(
|
||||||
16,
|
16,
|
||||||
),
|
),
|
||||||
foregroundColor:
|
foregroundColor: Theme.of(context)
|
||||||
Theme.of(context)
|
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.error,
|
.error,
|
||||||
),
|
),
|
||||||
|
|
@ -341,8 +329,7 @@ class ChatView extends StatelessWidget {
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.forum_outlined,
|
Icons.forum_outlined,
|
||||||
),
|
),
|
||||||
onPressed:
|
onPressed: controller.recreateChat,
|
||||||
controller.recreateChat,
|
|
||||||
label: Text(
|
label: Text(
|
||||||
L10n.of(context)!.reopenChat,
|
L10n.of(context)!.reopenChat,
|
||||||
),
|
),
|
||||||
|
|
@ -382,8 +369,6 @@ class ChatView extends StatelessWidget {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue