refactor: Not nullable room in ChatPage

This commit is contained in:
Krille 2023-03-25 15:06:12 +01:00
parent 2f6799470c
commit 2acf49a12b
No known key found for this signature in database
10 changed files with 102 additions and 95 deletions

View File

@ -72,7 +72,7 @@ class AppRoutes {
), ),
VWidget( VWidget(
path: ':roomid', path: ':roomid',
widget: const Chat(), widget: const ChatPage(),
stackedRoutes: [ stackedRoutes: [
VWidget( VWidget(
path: 'encryption', path: 'encryption',
@ -100,7 +100,7 @@ class AppRoutes {
stackedRoutes: [ stackedRoutes: [
VWidget( VWidget(
path: ':roomid', path: ':roomid',
widget: const Chat(), widget: const ChatPage(),
buildTransition: _dynamicTransition, buildTransition: _dynamicTransition,
), ),
], ],
@ -174,14 +174,14 @@ class AppRoutes {
VNester( VNester(
path: ':roomid', path: ':roomid',
widgetBuilder: (child) => SideViewLayout( widgetBuilder: (child) => SideViewLayout(
mainView: const Chat(), mainView: const ChatPage(),
sideView: child, sideView: child,
), ),
buildTransition: _fadeTransition, buildTransition: _fadeTransition,
nestedRoutes: [ nestedRoutes: [
VWidget( VWidget(
path: '', path: '',
widget: const Chat(), widget: const ChatPage(),
buildTransition: _fadeTransition, buildTransition: _fadeTransition,
), ),
VWidget( VWidget(
@ -245,7 +245,7 @@ class AppRoutes {
), ),
VWidget( VWidget(
path: ':roomid', path: ':roomid',
widget: const Chat(), widget: const ChatPage(),
buildTransition: _dynamicTransition, buildTransition: _dynamicTransition,
), ),
], ],

View File

@ -35,17 +35,48 @@ import 'send_file_dialog.dart';
import 'send_location_dialog.dart'; import 'send_location_dialog.dart';
import 'sticker_picker_dialog.dart'; import 'sticker_picker_dialog.dart';
class Chat extends StatefulWidget { class ChatPage extends StatelessWidget {
final Widget? sideView; final Widget? sideView;
const Chat({Key? key, this.sideView}) : super(key: key); const ChatPage({Key? key, this.sideView}) : super(key: key);
@override
Widget build(BuildContext context) {
final roomId = context.vRouter.pathParameters['roomid'];
final room =
roomId == null ? null : Matrix.of(context).client.getRoomById(roomId);
if (room == null) {
return Scaffold(
appBar: AppBar(title: Text(L10n.of(context)!.oopsSomethingWentWrong)),
body: Center(
child: Padding(
padding: const EdgeInsets.all(16),
child:
Text(L10n.of(context)!.youAreNoLongerParticipatingInThisChat),
),
),
);
}
return ChatPageWithRoom(sideView: sideView, room: room);
}
}
class ChatPageWithRoom extends StatefulWidget {
final Widget? sideView;
final Room room;
const ChatPageWithRoom({
Key? key,
required this.sideView,
required this.room,
}) : super(key: key);
@override @override
ChatController createState() => ChatController(); ChatController createState() => ChatController();
} }
class ChatController extends State<Chat> { class ChatController extends State<ChatPageWithRoom> {
Room? room; Room get room => widget.room;
late Client sendingClient; late Client sendingClient;
@ -53,7 +84,7 @@ class ChatController extends State<Chat> {
String? readMarkerEventId; String? readMarkerEventId;
String? get roomId => context.vRouter.pathParameters['roomid']; String get roomId => widget.room.id;
final AutoScrollController scrollController = AutoScrollController(); final AutoScrollController scrollController = AutoScrollController();
@ -95,7 +126,7 @@ class ChatController extends State<Chat> {
useRootNavigator: false, useRootNavigator: false,
builder: (c) => SendFileDialog( builder: (c) => SendFileDialog(
files: matrixFiles, files: matrixFiles,
room: room!, room: room,
), ),
); );
} }
@ -134,13 +165,13 @@ class ChatController extends State<Chat> {
bool get lastReadEventVisible => bool get lastReadEventVisible =>
timeline == null || timeline == null ||
room!.fullyRead.isEmpty || room.fullyRead.isEmpty ||
timeline!.events.any((event) => event.eventId == room!.fullyRead); timeline!.events.any((event) => event.eventId == room.fullyRead);
void recreateChat() async { void recreateChat() async {
final room = this.room; final room = this.room;
final userId = room?.directChatMatrixID; final userId = room.directChatMatrixID;
if (room == null || userId == null) { if (userId == null) {
throw Exception( throw Exception(
'Try to recreate a room with is not a DM room. This should not be possible from the UI!', 'Try to recreate a room with is not a DM room. This should not be possible from the UI!',
); );
@ -162,12 +193,6 @@ class ChatController extends State<Chat> {
} }
void leaveChat() async { void leaveChat() async {
final room = this.room;
if (room == null) {
throw Exception(
'Leave room button clicked while room is null. This should not be possible from the UI!',
);
}
final success = await showFutureLoadingDialog( final success = await showFutureLoadingDialog(
context: context, context: context,
future: room.leave, future: room.leave,
@ -262,12 +287,12 @@ class ChatController extends State<Chat> {
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( timeline = await room.getTimeline(
onUpdate: updateView, onUpdate: updateView,
eventContextId: eventContextId, eventContextId: eventContextId,
); );
if (timeline!.events.isNotEmpty) { if (timeline!.events.isNotEmpty) {
if (room!.markedUnread) room!.markUnread(false); if (room.markedUnread) room.markUnread(false);
setReadMarker(); setReadMarker();
} }
@ -293,8 +318,8 @@ class ChatController extends State<Chat> {
void setReadMarker({String? eventId}) { void setReadMarker({String? eventId}) {
if (_setReadMarkerFuture != null) return; if (_setReadMarkerFuture != null) return;
if (lastReadEventVisible && if (lastReadEventVisible &&
!room!.hasNewMessages && !room.hasNewMessages &&
room!.notificationCount == 0) { room.notificationCount == 0) {
return; return;
} }
if (!Matrix.of(context).webHasFocus) return; if (!Matrix.of(context).webHasFocus) return;
@ -312,7 +337,7 @@ class ChatController extends State<Chat> {
_setReadMarkerFuture = timeline.setReadMarker(eventId).then((_) { _setReadMarkerFuture = timeline.setReadMarker(eventId).then((_) {
_setReadMarkerFuture = null; _setReadMarkerFuture = null;
}); });
room!.client.updateIosBadge(); room.client.updateIosBadge();
} }
@override @override
@ -331,7 +356,7 @@ class ChatController extends State<Chat> {
// no need to have the setting typing to false be blocking // no need to have the setting typing to false be blocking
typingCoolDown?.cancel(); typingCoolDown?.cancel();
typingCoolDown = null; typingCoolDown = null;
room!.setTyping(false); room.setTyping(false);
currentlyTyping = false; currentlyTyping = false;
} }
// then set the new sending client // then set the new sending client
@ -351,7 +376,7 @@ class ChatController extends State<Chat> {
final commandMatch = RegExp(r'^\/(\w+)').firstMatch(sendController.text); final commandMatch = RegExp(r'^\/(\w+)').firstMatch(sendController.text);
if (commandMatch != null && if (commandMatch != null &&
!room!.client.commands.keys.contains(commandMatch[1]!.toLowerCase())) { !room.client.commands.keys.contains(commandMatch[1]!.toLowerCase())) {
final l10n = L10n.of(context)!; final l10n = L10n.of(context)!;
final dialogResult = await showOkCancelAlertDialog( final dialogResult = await showOkCancelAlertDialog(
context: context, context: context,
@ -366,7 +391,7 @@ class ChatController extends State<Chat> {
} }
// ignore: unawaited_futures // ignore: unawaited_futures
room!.sendTextEvent( room.sendTextEvent(
sendController.text, sendController.text,
inReplyTo: replyEvent, inReplyTo: replyEvent,
editEventId: editEvent?.eventId, editEventId: editEvent?.eventId,
@ -403,7 +428,7 @@ class ChatController extends State<Chat> {
).detectFileType, ).detectFileType,
) )
.toList(), .toList(),
room: room!, room: room,
), ),
); );
} }
@ -428,7 +453,7 @@ class ChatController extends State<Chat> {
).detectFileType, ).detectFileType,
) )
.toList(), .toList(),
room: room!, room: room,
), ),
); );
} }
@ -449,7 +474,7 @@ class ChatController extends State<Chat> {
name: file.path, name: file.path,
) )
], ],
room: room!, room: room,
), ),
); );
} }
@ -470,7 +495,7 @@ class ChatController extends State<Chat> {
name: file.path, name: file.path,
) )
], ],
room: room!, room: room,
), ),
); );
} }
@ -478,7 +503,7 @@ class ChatController extends State<Chat> {
void sendStickerAction() async { void sendStickerAction() async {
final sticker = await showAdaptiveBottomSheet<ImagePackImageContent>( final sticker = await showAdaptiveBottomSheet<ImagePackImageContent>(
context: context, context: context,
builder: (c) => StickerPickerDialog(room: room!), builder: (c) => StickerPickerDialog(room: room),
); );
if (sticker == null) return; if (sticker == null) return;
final eventContent = <String, dynamic>{ final eventContent = <String, dynamic>{
@ -487,7 +512,7 @@ class ChatController extends State<Chat> {
'url': sticker.url.toString(), 'url': sticker.url.toString(),
}; };
// send the sticker // send the sticker
await room!.sendEvent( await room.sendEvent(
eventContent, eventContent,
type: EventTypes.Sticker, type: EventTypes.Sticker,
); );
@ -521,7 +546,7 @@ class ChatController extends State<Chat> {
bytes: audioFile.readAsBytesSync(), bytes: audioFile.readAsBytesSync(),
name: audioFile.path, name: audioFile.path,
); );
await room!.sendFileEvent( await room.sendFileEvent(
file, file,
inReplyTo: replyEvent, inReplyTo: replyEvent,
extraContent: { extraContent: {
@ -571,7 +596,7 @@ class ChatController extends State<Chat> {
await showDialog( await showDialog(
context: context, context: context,
useRootNavigator: false, useRootNavigator: false,
builder: (c) => SendLocationDialog(room: room!), builder: (c) => SendLocationDialog(room: room),
); );
} }
@ -677,7 +702,7 @@ class ChatController extends State<Chat> {
if (client == null) { if (client == null) {
return; return;
} }
final room = client.getRoomById(roomId!)!; final room = client.getRoomById(roomId)!;
await Event.fromJson(event.toJson(), room).redactEvent(); await Event.fromJson(event.toJson(), room).redactEvent();
} }
} else { } else {
@ -694,7 +719,7 @@ class ChatController extends State<Chat> {
List<Client?> get currentRoomBundle { List<Client?> get currentRoomBundle {
final clients = Matrix.of(context).currentBundle!; final clients = Matrix.of(context).currentBundle!;
clients.removeWhere((c) => c!.getRoomById(roomId!) == null); clients.removeWhere((c) => c!.getRoomById(roomId) == null);
return clients; return clients;
} }
@ -808,7 +833,7 @@ class ChatController extends State<Chat> {
void forgetRoom() async { void forgetRoom() async {
final result = await showFutureLoadingDialog( final result = await showFutureLoadingDialog(
context: context, context: context,
future: room!.forget, future: room.forget,
); );
if (result.error != null) return; if (result.error != null) return;
VRouter.of(context).to('/archive'); VRouter.of(context).to('/archive');
@ -857,7 +882,7 @@ class ChatController extends State<Chat> {
final events = List<Event>.from(selectedEvents); final events = List<Event>.from(selectedEvents);
setState(() => selectedEvents.clear()); setState(() => selectedEvents.clear());
for (final event in events) { for (final event in events) {
await room!.sendReaction( await room.sendReaction(
event.eventId, event.eventId,
emoji!, emoji!,
); );
@ -904,7 +929,7 @@ class ChatController extends State<Chat> {
useRootNavigator: false, useRootNavigator: false,
context: context, context: context,
title: L10n.of(context)!.goToTheNewRoom, title: L10n.of(context)!.goToTheNewRoom,
message: room! message: room
.getState(EventTypes.RoomTombstone)! .getState(EventTypes.RoomTombstone)!
.parsedTombstoneContent .parsedTombstoneContent
.body, .body,
@ -915,8 +940,8 @@ class ChatController extends State<Chat> {
} }
final result = await showFutureLoadingDialog( final result = await showFutureLoadingDialog(
context: context, context: context,
future: () => room!.client.joinRoom( future: () => room.client.joinRoom(
room! room
.getState(EventTypes.RoomTombstone)! .getState(EventTypes.RoomTombstone)!
.parsedTombstoneContent .parsedTombstoneContent
.replacementRoom, .replacementRoom,
@ -924,7 +949,7 @@ class ChatController extends State<Chat> {
); );
await showFutureLoadingDialog( await showFutureLoadingDialog(
context: context, context: context,
future: room!.leave, future: room.leave,
); );
if (result.error == null) { if (result.error == null) {
VRouter.of(context).toSegments(['rooms', result.result!]); VRouter.of(context).toSegments(['rooms', result.result!]);
@ -1001,18 +1026,16 @@ class ChatController extends State<Chat> {
cancelLabel: L10n.of(context)!.cancel, cancelLabel: L10n.of(context)!.cancel,
); );
if (response == OkCancelResult.ok) { if (response == OkCancelResult.ok) {
final events = room!.pinnedEventIds final events = room.pinnedEventIds
..removeWhere((oldEvent) => oldEvent == eventId); ..removeWhere((oldEvent) => oldEvent == eventId);
showFutureLoadingDialog( showFutureLoadingDialog(
context: context, context: context,
future: () => room!.setPinnedEvents(events), future: () => room.setPinnedEvents(events),
); );
} }
} }
void pinEvent() { void pinEvent() {
final room = this.room;
if (room == null) return;
final pinnedEventIds = room.pinnedEventIds; final pinnedEventIds = room.pinnedEventIds;
final selectedEventIds = selectedEvents.map((e) => e.eventId).toSet(); final selectedEventIds = selectedEvents.map((e) => e.eventId).toSet();
final unpin = selectedEventIds.length == 1 && final unpin = selectedEventIds.length == 1 &&
@ -1057,7 +1080,7 @@ class ChatController extends State<Chat> {
typingCoolDown = Timer(const Duration(seconds: 2), () { typingCoolDown = Timer(const Duration(seconds: 2), () {
typingCoolDown = null; typingCoolDown = null;
currentlyTyping = false; currentlyTyping = false;
room!.setTyping(false); room.setTyping(false);
}); });
typingTimeout ??= Timer(const Duration(seconds: 30), () { typingTimeout ??= Timer(const Duration(seconds: 30), () {
typingTimeout = null; typingTimeout = null;
@ -1065,14 +1088,13 @@ class ChatController extends State<Chat> {
}); });
if (!currentlyTyping) { if (!currentlyTyping) {
currentlyTyping = true; currentlyTyping = true;
room! room.setTyping(true, timeout: const Duration(seconds: 30).inMilliseconds);
.setTyping(true, timeout: const Duration(seconds: 30).inMilliseconds);
} }
setState(() => inputText = text); setState(() => inputText = text);
} }
bool get isArchived => bool get isArchived =>
{Membership.leave, Membership.ban}.contains(room?.membership); {Membership.leave, Membership.ban}.contains(room.membership);
void showEventInfo([Event? event]) => void showEventInfo([Event? event]) =>
(event ?? selectedEvents.single).showInfoDialog(context); (event ?? selectedEvents.single).showInfoDialog(context);
@ -1120,7 +1142,7 @@ class ChatController extends State<Chat> {
if (success.result != null) { if (success.result != null) {
final voipPlugin = Matrix.of(context).voipPlugin; final voipPlugin = Matrix.of(context).voipPlugin;
try { try {
await voipPlugin!.voip.inviteToCall(room!.id, callType); await voipPlugin!.voip.inviteToCall(room.id, callType);
} catch (e) { } catch (e) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toLocalizedString(context))), SnackBar(content: Text(e.toLocalizedString(context))),

View File

@ -16,9 +16,6 @@ class ChatAppBarTitle extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final room = controller.room; final room = controller.room;
if (room == null) {
return const SizedBox.shrink();
}
if (controller.selectedEvents.isNotEmpty) { if (controller.selectedEvents.isNotEmpty) {
return Text(controller.selectedEvents.length.toString()); return Text(controller.selectedEvents.length.toString());
} }

View File

@ -146,7 +146,7 @@ class ChatInputRow extends StatelessWidget {
contentPadding: const EdgeInsets.all(0), contentPadding: const EdgeInsets.all(0),
), ),
), ),
if (controller.room! if (controller.room
.getImagePacks(ImagePackUsage.sticker) .getImagePacks(ImagePackUsage.sticker)
.isNotEmpty) .isNotEmpty)
PopupMenuItem<String>( PopupMenuItem<String>(
@ -227,7 +227,7 @@ class ChatInputRow extends StatelessWidget {
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0), padding: const EdgeInsets.symmetric(vertical: 4.0),
child: InputBar( child: InputBar(
room: controller.room!, room: controller.room,
minLines: 1, minLines: 1,
maxLines: 8, maxLines: 8,
autofocus: !PlatformInfos.isMobile, autofocus: !PlatformInfos.isMobile,

View File

@ -125,37 +125,26 @@ class ChatView extends StatelessWidget {
} else { } else {
return [ return [
if (Matrix.of(context).voipPlugin != null && if (Matrix.of(context).voipPlugin != null &&
controller.room!.isDirectChat) controller.room.isDirectChat)
IconButton( IconButton(
onPressed: controller.onPhoneButtonTap, onPressed: controller.onPhoneButtonTap,
icon: const Icon(Icons.call_outlined), icon: const Icon(Icons.call_outlined),
tooltip: L10n.of(context)!.placeCall, tooltip: L10n.of(context)!.placeCall,
), ),
EncryptionButton(controller.room!), EncryptionButton(controller.room),
ChatSettingsPopupMenu(controller.room!, !controller.room!.isDirectChat), ChatSettingsPopupMenu(controller.room, !controller.room.isDirectChat),
]; ];
} }
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
controller.room = controller.sendingClient.getRoomById(controller.roomId!); controller.readMarkerEventId ??= controller.room.fullyRead;
controller.readMarkerEventId ??= controller.room!.fullyRead;
if (controller.room == null) {
return Scaffold(
appBar: AppBar(
title: Text(L10n.of(context)!.oopsSomethingWentWrong),
),
body: Center(
child: Text(L10n.of(context)!.youAreNoLongerParticipatingInThisChat),
),
);
}
if (controller.room!.membership == Membership.invite) { if (controller.room.membership == Membership.invite) {
showFutureLoadingDialog( showFutureLoadingDialog(
context: context, context: context,
future: () => controller.room!.join(), future: () => controller.room.join(),
); );
} }
final bottomSheetPadding = FluffyThemes.isColumnMode(context) ? 16.0 : 8.0; final bottomSheetPadding = FluffyThemes.isColumnMode(context) ? 16.0 : 8.0;
@ -175,7 +164,7 @@ class ChatView extends StatelessWidget {
onTapDown: (_) => controller.setReadMarker(), onTapDown: (_) => controller.setReadMarker(),
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
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( builder: (context, snapshot) => FutureBuilder(
future: controller.getTimeline(), future: controller.getTimeline(),
@ -196,7 +185,7 @@ class ChatView extends StatelessWidget {
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
) )
: UnreadRoomsBadge( : UnreadRoomsBadge(
filter: (r) => r.id != controller.roomId!, filter: (r) => r.id != controller.roomId,
badgePosition: BadgePosition.topEnd(end: 8, top: 4), badgePosition: BadgePosition.topEnd(end: 8, top: 4),
child: const Center(child: BackButton()), child: const Center(child: BackButton()),
), ),
@ -269,8 +258,8 @@ class ChatView extends StatelessWidget {
), ),
), ),
), ),
if (controller.room!.canSendDefaultMessages && if (controller.room.canSendDefaultMessages &&
controller.room!.membership == Membership.join) controller.room.membership == Membership.join)
Container( Container(
margin: EdgeInsets.only( margin: EdgeInsets.only(
bottom: bottomSheetPadding, bottom: bottomSheetPadding,
@ -295,7 +284,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:
@ -359,14 +348,14 @@ class ChatView extends StatelessWidget {
child: FloatingActionButton.extended( child: FloatingActionButton.extended(
icon: const Icon(Icons.arrow_upward_outlined), icon: const Icon(Icons.arrow_upward_outlined),
onPressed: () => controller onPressed: () => controller
.scrollToEventId(controller.room!.fullyRead), .scrollToEventId(controller.room.fullyRead),
label: Row( label: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text(L10n.of(context)!.jumpToLastReadMessage), Text(L10n.of(context)!.jumpToLastReadMessage),
IconButton( IconButton(
onPressed: () => controller.setReadMarker( onPressed: () => controller.setReadMarker(
eventId: controller.room!.fullyRead, eventId: controller.room.fullyRead,
), ),
icon: const Icon(Icons.close), icon: const Icon(Icons.close),
), ),

View File

@ -46,14 +46,14 @@ class PinnedEvents extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final pinnedEventIds = controller.room!.pinnedEventIds; final pinnedEventIds = controller.room.pinnedEventIds;
if (pinnedEventIds.isEmpty) { if (pinnedEventIds.isEmpty) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
final completers = pinnedEventIds.map<Completer<Event?>>((e) { final completers = pinnedEventIds.map<Completer<Event?>>((e) {
final completer = Completer<Event?>(); final completer = Completer<Event?>();
controller.room! controller.room
.getEventById(e) .getEventById(e)
.then((value) => completer.complete(value)); .then((value) => completer.complete(value));
return completer; return completer;
@ -86,11 +86,10 @@ class PinnedEvents extends StatelessWidget {
color: Theme.of(context).colorScheme.onSurfaceVariant, color: Theme.of(context).colorScheme.onSurfaceVariant,
icon: const Icon(Icons.push_pin), icon: const Icon(Icons.push_pin),
tooltip: L10n.of(context)!.unpin, tooltip: L10n.of(context)!.unpin,
onPressed: controller.room onPressed:
?.canSendEvent(EventTypes.RoomPinnedEvents) ?? controller.room.canSendEvent(EventTypes.RoomPinnedEvents)
false ? () => controller.unpinEvent(event.eventId)
? () => controller.unpinEvent(event.eventId) : null,
: null,
), ),
Expanded( Expanded(
child: Padding( child: Padding(

View File

@ -18,7 +18,7 @@ class ReactionsPicker extends StatelessWidget {
if (controller.showEmojiPicker) return const SizedBox.shrink(); if (controller.showEmojiPicker) return const SizedBox.shrink();
final display = controller.editEvent == null && final display = controller.editEvent == null &&
controller.replyEvent == null && controller.replyEvent == null &&
controller.room!.canSendDefaultMessages && controller.room.canSendDefaultMessages &&
controller.selectedEvents.isNotEmpty; controller.selectedEvents.isNotEmpty;
return AnimatedContainer( return AnimatedContainer(
duration: FluffyThemes.animationDuration, duration: FluffyThemes.animationDuration,

View File

@ -12,7 +12,7 @@ class SeenByRow extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final seenByUsers = controller.room!.getSeenByUsers(controller.timeline!); final seenByUsers = controller.room.getSeenByUsers(controller.timeline!);
const maxAvatars = 7; const maxAvatars = 7;
return Container( return Container(
width: double.infinity, width: double.infinity,

View File

@ -11,7 +11,7 @@ class TombstoneDisplay extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (controller.room!.getState(EventTypes.RoomTombstone) == null) { if (controller.room.getState(EventTypes.RoomTombstone) == null) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
return SizedBox( return SizedBox(
@ -26,7 +26,7 @@ class TombstoneDisplay extends StatelessWidget {
child: const Icon(Icons.upgrade_outlined), child: const Icon(Icons.upgrade_outlined),
), ),
title: Text( title: Text(
controller.room! controller.room
.getState(EventTypes.RoomTombstone)! .getState(EventTypes.RoomTombstone)!
.parsedTombstoneContent .parsedTombstoneContent
.body, .body,

View File

@ -12,7 +12,7 @@ class TypingIndicators extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final typingUsers = controller.room!.typingUsers final typingUsers = controller.room.typingUsers
..removeWhere((u) => u.stateKey == Matrix.of(context).client.userID); ..removeWhere((u) => u.stateKey == Matrix.of(context).client.userID);
const topPadding = 20.0; const topPadding = 20.0;
const bottomPadding = 4.0; const bottomPadding = 4.0;