mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-12-25 15:02:33 +01:00
feat: Use fragmented timeline to jump to event
This commit is contained in:
parent
ede1e289ce
commit
a4a852ede8
@ -130,10 +130,6 @@ class ChatController extends State<Chat> {
|
|||||||
|
|
||||||
String pendingText = '';
|
String pendingText = '';
|
||||||
|
|
||||||
bool get canLoadMore =>
|
|
||||||
timeline!.events.isEmpty ||
|
|
||||||
timeline!.events.last.type != EventTypes.RoomCreate;
|
|
||||||
|
|
||||||
bool showEmojiPicker = false;
|
bool showEmojiPicker = false;
|
||||||
|
|
||||||
void recreateChat() async {
|
void recreateChat() async {
|
||||||
@ -178,7 +174,7 @@ class ChatController extends State<Chat> {
|
|||||||
EmojiPickerType emojiPickerType = EmojiPickerType.keyboard;
|
EmojiPickerType emojiPickerType = EmojiPickerType.keyboard;
|
||||||
|
|
||||||
void requestHistory() async {
|
void requestHistory() async {
|
||||||
if (canLoadMore) {
|
if (!timeline!.canRequestHistory) return;
|
||||||
try {
|
try {
|
||||||
await timeline!.requestHistory(historyCount: _loadHistoryCount);
|
await timeline!.requestHistory(historyCount: _loadHistoryCount);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -192,6 +188,21 @@ class ChatController extends State<Chat> {
|
|||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void requestFuture() async {
|
||||||
|
if (!timeline!.canRequestFuture) return;
|
||||||
|
try {
|
||||||
|
await timeline!.requestFuture(historyCount: _loadHistoryCount);
|
||||||
|
} catch (err) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(
|
||||||
|
(err).toLocalizedString(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateScrollController() {
|
void _updateScrollController() {
|
||||||
@ -201,13 +212,13 @@ class ChatController extends State<Chat> {
|
|||||||
setReadMarker();
|
setReadMarker();
|
||||||
if (!scrollController.hasClients) return;
|
if (!scrollController.hasClients) return;
|
||||||
if (scrollController.position.pixels ==
|
if (scrollController.position.pixels ==
|
||||||
scrollController.position.maxScrollExtent &&
|
scrollController.position.maxScrollExtent) {
|
||||||
timeline!.events.isNotEmpty &&
|
|
||||||
timeline!.events[timeline!.events.length - 1].type !=
|
|
||||||
EventTypes.RoomCreate) {
|
|
||||||
requestHistory();
|
requestHistory();
|
||||||
|
} else if (scrollController.position.pixels == 0) {
|
||||||
|
requestFuture();
|
||||||
}
|
}
|
||||||
if (scrollController.position.pixels > 0 && showScrollDownButton == false) {
|
if (timeline?.allowNewEvent == false ||
|
||||||
|
scrollController.position.pixels > 0 && showScrollDownButton == false) {
|
||||||
setState(() => showScrollDownButton = true);
|
setState(() => showScrollDownButton = true);
|
||||||
} else if (scrollController.position.pixels == 0 &&
|
} else if (scrollController.position.pixels == 0 &&
|
||||||
showScrollDownButton == true) {
|
showScrollDownButton == true) {
|
||||||
@ -237,11 +248,14 @@ class ChatController extends State<Chat> {
|
|||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> getTimeline() async {
|
Future<void> getTimeline([String? eventContextId]) async {
|
||||||
if (timeline == null) {
|
if (timeline == null) {
|
||||||
await Matrix.of(context).client.roomsLoading;
|
await Matrix.of(context).client.roomsLoading;
|
||||||
await Matrix.of(context).client.accountDataLoading;
|
await Matrix.of(context).client.accountDataLoading;
|
||||||
timeline = await room!.getTimeline(onUpdate: updateView);
|
timeline = await room!.getTimeline(
|
||||||
|
onUpdate: updateView,
|
||||||
|
eventContextId: eventContextId,
|
||||||
|
);
|
||||||
if (timeline!.events.isNotEmpty) {
|
if (timeline!.events.isNotEmpty) {
|
||||||
if (room!.markedUnread) room!.markUnread(false);
|
if (room!.markedUnread) room!.markUnread(false);
|
||||||
setReadMarker();
|
setReadMarker();
|
||||||
@ -261,7 +275,7 @@ class ChatController extends State<Chat> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
timeline!.requestKeys(onlineKeyBackupOnly: false);
|
timeline!.requestKeys(onlineKeyBackupOnly: false);
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void>? _setReadMarkerFuture;
|
Future<void>? _setReadMarkerFuture;
|
||||||
@ -723,45 +737,11 @@ class ChatController extends State<Chat> {
|
|||||||
void scrollToEventId(String eventId) async {
|
void scrollToEventId(String eventId) async {
|
||||||
var eventIndex = timeline!.events.indexWhere((e) => e.eventId == eventId);
|
var eventIndex = timeline!.events.indexWhere((e) => e.eventId == eventId);
|
||||||
if (eventIndex == -1) {
|
if (eventIndex == -1) {
|
||||||
// event id not found...maybe we can fetch it?
|
setState(() {
|
||||||
// the try...finally is here to start and close the loading dialog reliably
|
timeline = null;
|
||||||
await showFutureLoadingDialog(
|
});
|
||||||
context: context,
|
await getTimeline(eventId);
|
||||||
future: () async {
|
eventIndex = timeline!.events.indexWhere((e) => e.eventId == eventId);
|
||||||
// okay, we first have to fetch if the event is in the room
|
|
||||||
try {
|
|
||||||
final event = await timeline!.getEventById(eventId);
|
|
||||||
if (event == null) {
|
|
||||||
// event is null...meaning something is off
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
if (err is MatrixException && err.errcode == 'M_NOT_FOUND') {
|
|
||||||
// event wasn't found, as the server gave a 404 or something
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
// okay, we know that the event *is* in the room
|
|
||||||
while (eventIndex == -1) {
|
|
||||||
if (!canLoadMore) {
|
|
||||||
// we can't load any more events but still haven't found ours yet...better stop here
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await timeline!.requestHistory(historyCount: _loadHistoryCount);
|
|
||||||
} catch (err) {
|
|
||||||
if (err is TimeoutException) {
|
|
||||||
// loading the history timed out...so let's do nothing
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
eventIndex =
|
|
||||||
timeline!.events.indexWhere((e) => e.eventId == eventId);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return;
|
return;
|
||||||
@ -773,7 +753,15 @@ class ChatController extends State<Chat> {
|
|||||||
_updateScrollController();
|
_updateScrollController();
|
||||||
}
|
}
|
||||||
|
|
||||||
void scrollDown() => scrollController.jumpTo(0);
|
void scrollDown() async {
|
||||||
|
if (!timeline!.allowNewEvent) {
|
||||||
|
setState(() {
|
||||||
|
timeline = null;
|
||||||
|
});
|
||||||
|
await getTimeline();
|
||||||
|
}
|
||||||
|
scrollController.jumpTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
void onEmojiSelected(_, Emoji? emoji) {
|
void onEmojiSelected(_, Emoji? emoji) {
|
||||||
switch (emojiPickerType) {
|
switch (emojiPickerType) {
|
||||||
|
@ -48,6 +48,22 @@ class ChatEventList extends StatelessWidget {
|
|||||||
(BuildContext context, int i) {
|
(BuildContext context, int i) {
|
||||||
// Footer to display typing indicator and read receipts:
|
// Footer to display typing indicator and read receipts:
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
|
if (controller.timeline!.isRequestingFuture) {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (controller.timeline!.canRequestFuture) {
|
||||||
|
Center(
|
||||||
|
child: OutlinedButton(
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
|
),
|
||||||
|
onPressed: controller.requestFuture,
|
||||||
|
child: Text(L10n.of(context)!.loadMore),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@ -64,7 +80,7 @@ class ChatEventList extends StatelessWidget {
|
|||||||
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (controller.canLoadMore) {
|
if (controller.timeline!.canRequestHistory) {
|
||||||
Center(
|
Center(
|
||||||
child: OutlinedButton(
|
child: OutlinedButton(
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
|
@ -179,7 +179,7 @@ class ChatView extends StatelessWidget {
|
|||||||
child: StreamBuilder(
|
child: StreamBuilder(
|
||||||
stream: controller.room!.onUpdate.stream
|
stream: controller.room!.onUpdate.stream
|
||||||
.rateLimit(const Duration(seconds: 1)),
|
.rateLimit(const Duration(seconds: 1)),
|
||||||
builder: (context, snapshot) => FutureBuilder<bool>(
|
builder: (context, snapshot) => FutureBuilder(
|
||||||
future: controller.getTimeline(),
|
future: controller.getTimeline(),
|
||||||
builder: (BuildContext context, snapshot) {
|
builder: (BuildContext context, snapshot) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
Loading…
Reference in New Issue
Block a user