horrible implementation of thread list in room

This commit is contained in:
OfficialDakari 2025-11-02 12:33:34 +05:00
parent 1757a7a4af
commit 0f5c19b0ce
8 changed files with 21 additions and 175 deletions

View File

@ -1,6 +1,5 @@
import 'dart:async';
import 'package:extera_next/pages/chat_thread/chat_threads.dart';
import 'package:extera_next/pages/chat_thread/thread.dart';
import 'package:flutter/material.dart';
@ -153,13 +152,6 @@ abstract class AppRoutes {
routes: [
GoRoute(
path: 'threads',
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
ChatThreads(
roomId: state.pathParameters['roomid']!,
),
),
redirect: loggedOutRedirect,
routes: [
GoRoute(
@ -377,6 +369,7 @@ abstract class AppRoutes {
roomId: state.pathParameters['roomid']!,
shareItems: shareItems,
eventId: state.uri.queryParameters['event'],
showThreadRoots: state.uri.queryParameters['threads'] == 'true',
),
);
},

View File

@ -51,12 +51,14 @@ class ChatPage extends StatelessWidget {
final String roomId;
final List<ShareItem>? shareItems;
final String? eventId;
final bool? showThreadRoots;
const ChatPage({
super.key,
required this.roomId,
this.eventId,
this.shareItems,
this.showThreadRoots,
});
@override
@ -79,6 +81,7 @@ class ChatPage extends StatelessWidget {
room: room,
shareItems: shareItems,
eventId: eventId,
showThreadRoots: showThreadRoots,
);
}
}
@ -88,6 +91,7 @@ class ChatPageWithRoom extends StatefulWidget {
final Thread? thread;
final List<ShareItem>? shareItems;
final String? eventId;
final bool? showThreadRoots;
const ChatPageWithRoom({
super.key,
@ -95,6 +99,7 @@ class ChatPageWithRoom extends StatefulWidget {
this.thread,
this.shareItems,
this.eventId,
this.showThreadRoots,
});
@override
@ -104,6 +109,7 @@ class ChatPageWithRoom extends StatefulWidget {
class ChatController extends State<ChatPageWithRoom>
with WidgetsBindingObserver {
Room get room => sendingClient.getRoomById(roomId) ?? widget.room;
bool get showThreadRoots => (widget.showThreadRoots ?? false);
Thread? get thread =>
sendingClient.getRoomById(roomId)?.threads[threadRootEventId] ??
widget.room.threads[threadRootEventId];

View File

@ -15,10 +15,12 @@ import 'package:extera_next/utils/platform_infos.dart';
class ChatEventList extends StatelessWidget {
final ChatController controller;
final bool showThreadRoots;
const ChatEventList({
super.key,
required this.controller,
this.showThreadRoots = false,
});
@override
@ -37,7 +39,14 @@ class ChatEventList extends StatelessWidget {
final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0;
final events = timeline.events.filterByVisibleInGui().filterByThreaded(controller.thread != null);
var events = timeline.events.filterByVisibleInGui();
if (showThreadRoots) {
events = events.filterThreadRoots();
} else {
events = events.filterByThreaded(controller.thread != null);
}
final animateInEventIndex = controller.animateInEventIndex;
final threads = controller.room.threads;

View File

@ -330,6 +330,7 @@ class ChatView extends StatelessWidget {
onTap: controller.clearSingleSelectedEvent,
child: ChatEventList(
controller: controller,
showThreadRoots: controller.showThreadRoots,
),
),
),
@ -349,7 +350,8 @@ class ChatView extends StatelessWidget {
),
)
else if (controller.room.canSendDefaultMessages &&
controller.room.membership == Membership.join)
controller.room.membership == Membership.join &&
!controller.showThreadRoots)
Container(
margin: EdgeInsets.all(bottomSheetPadding),
constraints: const BoxConstraints(

View File

@ -1,4 +1,3 @@
import 'package:extera_next/pages/chat_thread/chat_threads_view.dart';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';

View File

@ -271,7 +271,7 @@ class ChatDetailsView extends StatelessWidget {
subtitle:
Text(L10n.of(context).chatThreadsDescription),
onTap: () =>
context.push('/rooms/${room.id}/threads'),
context.push('/rooms/${room.id}?threads=true'),
trailing: const Icon(Icons.chevron_right_outlined),
),
ListTile(

View File

@ -1,53 +0,0 @@
import 'package:extera_next/pages/chat_thread/chat_threads_view.dart';
import 'package:extera_next/widgets/matrix.dart';
import 'package:flutter/cupertino.dart';
import 'package:matrix/matrix.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
class ChatThreads extends StatefulWidget {
final String roomId;
const ChatThreads({
super.key,
required this.roomId,
});
@override
ChatThreadsController createState() => ChatThreadsController();
}
class ChatThreadsController extends State<ChatThreads> {
String get roomId => widget.roomId;
Room? get room => Matrix.of(context).client.getRoomById(roomId);
bool isLoadingThreads = false;
final AutoScrollController scrollController = AutoScrollController();
@override
Widget build(BuildContext context) => ChatThreadsView(this);
void loadThreads([dynamic _]) async {
final room = Matrix.of(context).client.getRoomById(roomId);
if (room == null) {
return;
}
isLoadingThreads = true;
await room.loadThreadsFromServer();
isLoadingThreads = false;
}
List<Thread>? get threads => room?.threads.values.toList();
Stream get onChanged => Matrix.of(context).client.onSync.stream.where(
(e) =>
(e.rooms?.join?.containsKey(roomId) ?? false) &&
(e.rooms!.join![roomId]?.timeline?.events
?.any((s) => s.type == EventTypes.Message && s.content['m.relates_to'] != null) ??
false),
);
}

View File

@ -1,110 +0,0 @@
import 'package:extera_next/config/themes.dart';
import 'package:extera_next/generated/l10n/l10n.dart';
import 'package:extera_next/pages/chat_thread/chat_threads.dart';
import 'package:extera_next/utils/platform_infos.dart';
import 'package:extera_next/widgets/avatar.dart';
import 'package:extera_next/widgets/layouts/max_width_body.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
class ChatThreadsView extends StatelessWidget {
final ChatThreadsController controller;
const ChatThreadsView(this.controller, {super.key});
@override
Widget build(BuildContext context) {
final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0;
return Scaffold(
appBar: AppBar(
leading: const Center(child: BackButton()),
title: Text(L10n.of(context).chatThreads),
),
body: MaxWidthBody(
child: ListView.custom(
padding: EdgeInsets.only(
top: 16,
bottom: 8,
left: horizontalPadding,
right: horizontalPadding,
),
reverse: true,
controller: controller.scrollController,
keyboardDismissBehavior: PlatformInfos.isIOS
? ScrollViewKeyboardDismissBehavior.onDrag
: ScrollViewKeyboardDismissBehavior.manual,
childrenDelegate: SliverChildBuilderDelegate(
(BuildContext context, int i) {
if (i == (controller.threads?.length ?? 0) + 1) {
if (controller.isLoadingThreads) {
return const Center(
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
);
} else if (!(controller.room?.loadedAllThreads ?? false)) {
return Builder(
builder: (context) {
WidgetsBinding.instance
.addPostFrameCallback(controller.loadThreads);
return Center(
child: IconButton(
onPressed: controller.loadThreads,
icon: const Icon(Icons.refresh_outlined),
),
);
},
);
}
i--;
final thread = controller.threads![i];
return AutoScrollTag(
key: ValueKey(thread.rootEvent.eventId),
index: i,
controller: controller.scrollController,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
FutureBuilder<User?>(
future: thread.rootEvent.fetchSenderUser(),
builder: (context, snapshot) {
final user = snapshot.data ??
thread.rootEvent.senderFromMemoryOrFallback;
return Avatar(
mxContent: user.avatarUrl,
name: user.calcDisplayname(),
size: 48,
);
},
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(thread.rootEvent.senderFromMemoryOrFallback.calcDisplayname()),
],
),
),
const SizedBox(height: 4.0),
Text(thread.rootEvent.text),
],
),
),
);
}
},
childCount: (controller.threads?.length ?? 0) + 1,
),
),
),
);
}
}