refactor: Remove unnecessary setState in ChatPage for better performance
This commit is contained in:
parent
a615de889e
commit
63d7bef515
3 changed files with 221 additions and 214 deletions
|
|
@ -187,8 +187,6 @@ class ChatController extends State<ChatPageWithRoom> {
|
||||||
|
|
||||||
final int _loadHistoryCount = 100;
|
final int _loadHistoryCount = 100;
|
||||||
|
|
||||||
String inputText = '';
|
|
||||||
|
|
||||||
String pendingText = '';
|
String pendingText = '';
|
||||||
|
|
||||||
bool showEmojiPicker = false;
|
bool showEmojiPicker = false;
|
||||||
|
|
@ -260,7 +258,6 @@ class ChatController extends State<ChatPageWithRoom> {
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setReadMarker();
|
|
||||||
if (!scrollController.hasClients) return;
|
if (!scrollController.hasClients) return;
|
||||||
if (timeline?.allowNewEvent == false ||
|
if (timeline?.allowNewEvent == false ||
|
||||||
scrollController.position.pixels > 0 && _scrolledUp == false) {
|
scrollController.position.pixels > 0 && _scrolledUp == false) {
|
||||||
|
|
@ -285,7 +282,6 @@ class ChatController extends State<ChatPageWithRoom> {
|
||||||
final draft = prefs.getString('draft_$roomId');
|
final draft = prefs.getString('draft_$roomId');
|
||||||
if (draft != null && draft.isNotEmpty) {
|
if (draft != null && draft.isNotEmpty) {
|
||||||
sendController.text = draft;
|
sendController.text = draft;
|
||||||
setState(() => inputText = draft);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -472,7 +468,7 @@ class ChatController extends State<ChatPageWithRoom> {
|
||||||
);
|
);
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
inputText = pendingText;
|
sendController.text = pendingText;
|
||||||
replyEvent = null;
|
replyEvent = null;
|
||||||
editEvent = null;
|
editEvent = null;
|
||||||
pendingText = '';
|
pendingText = '';
|
||||||
|
|
@ -938,7 +934,7 @@ class ChatController extends State<ChatPageWithRoom> {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
await loadTimelineFuture;
|
await loadTimelineFuture;
|
||||||
setReadMarker(eventId: timeline!.events.first.eventId);
|
setReadMarker();
|
||||||
}
|
}
|
||||||
scrollController.jumpTo(0);
|
scrollController.jumpTo(0);
|
||||||
}
|
}
|
||||||
|
|
@ -1040,7 +1036,7 @@ class ChatController extends State<ChatPageWithRoom> {
|
||||||
setState(() {
|
setState(() {
|
||||||
pendingText = sendController.text;
|
pendingText = sendController.text;
|
||||||
editEvent = selectedEvents.first;
|
editEvent = selectedEvents.first;
|
||||||
inputText = sendController.text =
|
sendController.text =
|
||||||
editEvent!.getDisplayEvent(timeline!).calcLocalizedBodyFallback(
|
editEvent!.getDisplayEvent(timeline!).calcLocalizedBodyFallback(
|
||||||
MatrixLocals(L10n.of(context)!),
|
MatrixLocals(L10n.of(context)!),
|
||||||
withSenderNamePrefix: false,
|
withSenderNamePrefix: false,
|
||||||
|
|
@ -1187,7 +1183,6 @@ class ChatController extends State<ChatPageWithRoom> {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
await prefs.setString('draft_$roomId', text);
|
await prefs.setString('draft_$roomId', text);
|
||||||
});
|
});
|
||||||
setReadMarker();
|
|
||||||
if (text.endsWith(' ') && Matrix.of(context).hasComplexBundles) {
|
if (text.endsWith(' ') && Matrix.of(context).hasComplexBundles) {
|
||||||
final clients = currentRoomBundle;
|
final clients = currentRoomBundle;
|
||||||
for (final client in clients) {
|
for (final client in clients) {
|
||||||
|
|
@ -1196,7 +1191,6 @@ class ChatController extends State<ChatPageWithRoom> {
|
||||||
text.toLowerCase() == '${prefix.toLowerCase()} ') {
|
text.toLowerCase() == '${prefix.toLowerCase()} ') {
|
||||||
setSendingClient(client);
|
setSendingClient(client);
|
||||||
setState(() {
|
setState(() {
|
||||||
inputText = '';
|
|
||||||
sendController.text = '';
|
sendController.text = '';
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
|
@ -1222,9 +1216,15 @@ class ChatController extends State<ChatPageWithRoom> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setState(() => inputText = text);
|
if (_inputTextIsEmpty != text.isEmpty) {
|
||||||
|
setState(() {
|
||||||
|
_inputTextIsEmpty = text.isEmpty;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _inputTextIsEmpty = true;
|
||||||
|
|
||||||
bool get isArchived =>
|
bool get isArchived =>
|
||||||
{Membership.leave, Membership.ban}.contains(room.membership);
|
{Membership.leave, Membership.ban}.contains(room.membership);
|
||||||
|
|
||||||
|
|
@ -1291,7 +1291,7 @@ class ChatController extends State<ChatPageWithRoom> {
|
||||||
|
|
||||||
void cancelReplyEventAction() => setState(() {
|
void cancelReplyEventAction() => setState(() {
|
||||||
if (editEvent != null) {
|
if (editEvent != null) {
|
||||||
inputText = sendController.text = pendingText;
|
sendController.text = pendingText;
|
||||||
pendingText = '';
|
pendingText = '';
|
||||||
}
|
}
|
||||||
replyEvent = null;
|
replyEvent = null;
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ class ChatInputRow extends StatelessWidget {
|
||||||
duration: FluffyThemes.animationDuration,
|
duration: FluffyThemes.animationDuration,
|
||||||
curve: FluffyThemes.animationCurve,
|
curve: FluffyThemes.animationCurve,
|
||||||
height: 56,
|
height: 56,
|
||||||
width: controller.inputText.isEmpty ? 56 : 0,
|
width: controller.sendController.text.isEmpty ? 56 : 0,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
decoration: const BoxDecoration(),
|
decoration: const BoxDecoration(),
|
||||||
|
|
@ -268,7 +268,7 @@ class ChatInputRow extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (PlatformInfos.platformCanRecord &&
|
if (PlatformInfos.platformCanRecord &&
|
||||||
controller.inputText.isEmpty)
|
controller.sendController.text.isEmpty)
|
||||||
Container(
|
Container(
|
||||||
height: 56,
|
height: 56,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
|
|
@ -278,7 +278,8 @@ class ChatInputRow extends StatelessWidget {
|
||||||
onPressed: controller.voiceMessageAction,
|
onPressed: controller.voiceMessageAction,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (!PlatformInfos.isMobile || controller.inputText.isNotEmpty)
|
if (!PlatformInfos.isMobile ||
|
||||||
|
controller.sendController.text.isNotEmpty)
|
||||||
Container(
|
Container(
|
||||||
height: 56,
|
height: 56,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
|
|
|
||||||
|
|
@ -149,222 +149,228 @@ class ChatView extends StatelessWidget {
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTapDown: (_) => controller.setReadMarker(),
|
onTapDown: (_) => controller.setReadMarker(),
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
child: StreamBuilder(
|
child: MouseRegion(
|
||||||
stream: controller.room.onUpdate.stream
|
onEnter: (_) => controller.setReadMarker(),
|
||||||
.rateLimit(const Duration(seconds: 1)),
|
child: StreamBuilder(
|
||||||
builder: (context, snapshot) => FutureBuilder(
|
stream: controller.room.onUpdate.stream
|
||||||
future: controller.loadTimelineFuture,
|
.rateLimit(const Duration(seconds: 1)),
|
||||||
builder: (BuildContext context, snapshot) {
|
builder: (context, snapshot) => FutureBuilder(
|
||||||
return Scaffold(
|
future: controller.loadTimelineFuture,
|
||||||
appBar: AppBar(
|
builder: (BuildContext context, snapshot) {
|
||||||
actionsIconTheme: IconThemeData(
|
return Scaffold(
|
||||||
color: controller.selectedEvents.isEmpty
|
appBar: AppBar(
|
||||||
? null
|
actionsIconTheme: IconThemeData(
|
||||||
: Theme.of(context).colorScheme.primary,
|
color: controller.selectedEvents.isEmpty
|
||||||
|
? null
|
||||||
|
: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
leading: controller.selectMode
|
||||||
|
? IconButton(
|
||||||
|
icon: const Icon(Icons.close),
|
||||||
|
onPressed: controller.clearSelectedEvents,
|
||||||
|
tooltip: L10n.of(context)!.close,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
)
|
||||||
|
: UnreadRoomsBadge(
|
||||||
|
filter: (r) => r.id != controller.roomId,
|
||||||
|
badgePosition: BadgePosition.topEnd(end: 8, top: 4),
|
||||||
|
child: const Center(child: BackButton()),
|
||||||
|
),
|
||||||
|
titleSpacing: 0,
|
||||||
|
title: ChatAppBarTitle(controller),
|
||||||
|
actions: _appBarActions(context),
|
||||||
),
|
),
|
||||||
leading: controller.selectMode
|
floatingActionButton: controller.showScrollDownButton &&
|
||||||
? IconButton(
|
controller.selectedEvents.isEmpty
|
||||||
icon: const Icon(Icons.close),
|
? Padding(
|
||||||
onPressed: controller.clearSelectedEvents,
|
padding: const EdgeInsets.only(bottom: 56.0),
|
||||||
tooltip: L10n.of(context)!.close,
|
child: FloatingActionButton(
|
||||||
color: Theme.of(context).colorScheme.primary,
|
onPressed: controller.scrollDown,
|
||||||
|
heroTag: null,
|
||||||
|
mini: true,
|
||||||
|
child: const Icon(Icons.arrow_downward_outlined),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: UnreadRoomsBadge(
|
: null,
|
||||||
filter: (r) => r.id != controller.roomId,
|
body: DropTarget(
|
||||||
badgePosition: BadgePosition.topEnd(end: 8, top: 4),
|
onDragDone: controller.onDragDone,
|
||||||
child: const Center(child: BackButton()),
|
onDragEntered: controller.onDragEntered,
|
||||||
),
|
onDragExited: controller.onDragExited,
|
||||||
titleSpacing: 0,
|
child: Stack(
|
||||||
title: ChatAppBarTitle(controller),
|
children: <Widget>[
|
||||||
actions: _appBarActions(context),
|
if (Matrix.of(context).wallpaper != null)
|
||||||
),
|
Image.file(
|
||||||
floatingActionButton: controller.showScrollDownButton &&
|
Matrix.of(context).wallpaper!,
|
||||||
controller.selectedEvents.isEmpty
|
width: double.infinity,
|
||||||
? Padding(
|
height: double.infinity,
|
||||||
padding: const EdgeInsets.only(bottom: 56.0),
|
fit: BoxFit.cover,
|
||||||
child: FloatingActionButton(
|
filterQuality: FilterQuality.medium,
|
||||||
onPressed: controller.scrollDown,
|
),
|
||||||
heroTag: null,
|
SafeArea(
|
||||||
mini: true,
|
child: Column(
|
||||||
child: const Icon(Icons.arrow_downward_outlined),
|
children: <Widget>[
|
||||||
),
|
TombstoneDisplay(controller),
|
||||||
)
|
if (scrollUpBannerEventId != null)
|
||||||
: null,
|
Material(
|
||||||
body: DropTarget(
|
color: Theme.of(context)
|
||||||
onDragDone: controller.onDragDone,
|
.colorScheme
|
||||||
onDragEntered: controller.onDragEntered,
|
.surfaceVariant,
|
||||||
onDragExited: controller.onDragExited,
|
shape: Border(
|
||||||
child: Stack(
|
bottom: BorderSide(
|
||||||
children: <Widget>[
|
width: 1,
|
||||||
if (Matrix.of(context).wallpaper != null)
|
color: Theme.of(context).dividerColor,
|
||||||
Image.file(
|
),
|
||||||
Matrix.of(context).wallpaper!,
|
),
|
||||||
width: double.infinity,
|
child: ListTile(
|
||||||
height: double.infinity,
|
leading: IconButton(
|
||||||
fit: BoxFit.cover,
|
color: Theme.of(context)
|
||||||
filterQuality: FilterQuality.medium,
|
.colorScheme
|
||||||
),
|
.onSurfaceVariant,
|
||||||
SafeArea(
|
icon: const Icon(Icons.close),
|
||||||
child: Column(
|
tooltip: L10n.of(context)!.close,
|
||||||
children: <Widget>[
|
onPressed: () {
|
||||||
TombstoneDisplay(controller),
|
controller
|
||||||
if (scrollUpBannerEventId != null)
|
.discardScrollUpBannerEventId();
|
||||||
Material(
|
controller.setReadMarker();
|
||||||
color: Theme.of(context)
|
},
|
||||||
.colorScheme
|
),
|
||||||
.surfaceVariant,
|
title: Text(
|
||||||
shape: Border(
|
L10n.of(context)!.jumpToLastReadMessage,
|
||||||
bottom: BorderSide(
|
),
|
||||||
width: 1,
|
contentPadding:
|
||||||
color: Theme.of(context).dividerColor,
|
const EdgeInsets.only(left: 8),
|
||||||
|
trailing: TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
controller.scrollToEventId(
|
||||||
|
scrollUpBannerEventId,
|
||||||
|
);
|
||||||
|
controller
|
||||||
|
.discardScrollUpBannerEventId();
|
||||||
|
},
|
||||||
|
child: Text(L10n.of(context)!.jump),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: ListTile(
|
PinnedEvents(controller),
|
||||||
leading: IconButton(
|
Expanded(
|
||||||
color: Theme.of(context)
|
child: GestureDetector(
|
||||||
.colorScheme
|
onTap: controller.clearSingleSelectedEvent,
|
||||||
.onSurfaceVariant,
|
child: Builder(
|
||||||
icon: const Icon(Icons.close),
|
builder: (context) {
|
||||||
tooltip: L10n.of(context)!.close,
|
if (controller.timeline == null) {
|
||||||
onPressed: () {
|
return const Center(
|
||||||
controller.discardScrollUpBannerEventId();
|
child: CircularProgressIndicator
|
||||||
controller.setReadMarker();
|
.adaptive(
|
||||||
|
strokeWidth: 2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ChatEventList(
|
||||||
|
controller: controller,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
title: Text(
|
|
||||||
L10n.of(context)!.jumpToLastReadMessage,
|
|
||||||
),
|
|
||||||
contentPadding:
|
|
||||||
const EdgeInsets.only(left: 8),
|
|
||||||
trailing: TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
controller.scrollToEventId(
|
|
||||||
scrollUpBannerEventId,
|
|
||||||
);
|
|
||||||
controller.discardScrollUpBannerEventId();
|
|
||||||
},
|
|
||||||
child: Text(L10n.of(context)!.jump),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PinnedEvents(controller),
|
if (controller.room.canSendDefaultMessages &&
|
||||||
Expanded(
|
controller.room.membership == Membership.join)
|
||||||
child: GestureDetector(
|
Container(
|
||||||
onTap: controller.clearSingleSelectedEvent,
|
margin: EdgeInsets.only(
|
||||||
child: Builder(
|
bottom: bottomSheetPadding,
|
||||||
builder: (context) {
|
left: bottomSheetPadding,
|
||||||
if (controller.timeline == null) {
|
right: bottomSheetPadding,
|
||||||
return const Center(
|
|
||||||
child:
|
|
||||||
CircularProgressIndicator.adaptive(
|
|
||||||
strokeWidth: 2,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ChatEventList(
|
|
||||||
controller: controller,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (controller.room.canSendDefaultMessages &&
|
|
||||||
controller.room.membership == Membership.join)
|
|
||||||
Container(
|
|
||||||
margin: EdgeInsets.only(
|
|
||||||
bottom: bottomSheetPadding,
|
|
||||||
left: bottomSheetPadding,
|
|
||||||
right: bottomSheetPadding,
|
|
||||||
),
|
|
||||||
constraints: const BoxConstraints(
|
|
||||||
maxWidth: FluffyThemes.columnWidth * 2.5,
|
|
||||||
),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: Material(
|
|
||||||
borderRadius: const BorderRadius.only(
|
|
||||||
bottomLeft:
|
|
||||||
Radius.circular(AppConfig.borderRadius),
|
|
||||||
bottomRight:
|
|
||||||
Radius.circular(AppConfig.borderRadius),
|
|
||||||
),
|
),
|
||||||
elevation: 4,
|
constraints: const BoxConstraints(
|
||||||
shadowColor: Colors.black.withAlpha(64),
|
maxWidth: FluffyThemes.columnWidth * 2.5,
|
||||||
clipBehavior: Clip.hardEdge,
|
),
|
||||||
color: Theme.of(context).brightness ==
|
alignment: Alignment.center,
|
||||||
Brightness.light
|
child: Material(
|
||||||
? Colors.white
|
borderRadius: const BorderRadius.only(
|
||||||
: Colors.black,
|
bottomLeft: Radius.circular(
|
||||||
child: controller.room.isAbandonedDMRoom ==
|
AppConfig.borderRadius,
|
||||||
true
|
),
|
||||||
? Row(
|
bottomRight: Radius.circular(
|
||||||
mainAxisAlignment:
|
AppConfig.borderRadius,
|
||||||
MainAxisAlignment.spaceEvenly,
|
),
|
||||||
children: [
|
),
|
||||||
TextButton.icon(
|
elevation: 4,
|
||||||
style: TextButton.styleFrom(
|
shadowColor: Colors.black.withAlpha(64),
|
||||||
padding:
|
clipBehavior: Clip.hardEdge,
|
||||||
const EdgeInsets.all(16),
|
color: Theme.of(context).brightness ==
|
||||||
foregroundColor:
|
Brightness.light
|
||||||
Theme.of(context)
|
? Colors.white
|
||||||
.colorScheme
|
: Colors.black,
|
||||||
.error,
|
child: controller.room.isAbandonedDMRoom ==
|
||||||
|
true
|
||||||
|
? Row(
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
TextButton.icon(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.all(16),
|
||||||
|
foregroundColor:
|
||||||
|
Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.error,
|
||||||
|
),
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.archive_outlined,
|
||||||
|
),
|
||||||
|
onPressed: controller.leaveChat,
|
||||||
|
label: Text(
|
||||||
|
L10n.of(context)!.leave,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
icon: const Icon(
|
TextButton.icon(
|
||||||
Icons.archive_outlined,
|
style: TextButton.styleFrom(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.all(16),
|
||||||
|
),
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.forum_outlined,
|
||||||
|
),
|
||||||
|
onPressed:
|
||||||
|
controller.recreateChat,
|
||||||
|
label: Text(
|
||||||
|
L10n.of(context)!.reopenChat,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
onPressed: controller.leaveChat,
|
],
|
||||||
label: Text(
|
)
|
||||||
L10n.of(context)!.leave,
|
: Column(
|
||||||
),
|
mainAxisSize: MainAxisSize.min,
|
||||||
),
|
children: [
|
||||||
TextButton.icon(
|
const ConnectionStatusHeader(),
|
||||||
style: TextButton.styleFrom(
|
ReactionsPicker(controller),
|
||||||
padding:
|
ReplyDisplay(controller),
|
||||||
const EdgeInsets.all(16),
|
ChatInputRow(controller),
|
||||||
),
|
ChatEmojiPicker(controller),
|
||||||
icon: const Icon(
|
],
|
||||||
Icons.forum_outlined,
|
),
|
||||||
),
|
),
|
||||||
onPressed:
|
|
||||||
controller.recreateChat,
|
|
||||||
label: Text(
|
|
||||||
L10n.of(context)!.reopenChat,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const ConnectionStatusHeader(),
|
|
||||||
ReactionsPicker(controller),
|
|
||||||
ReplyDisplay(controller),
|
|
||||||
ChatInputRow(controller),
|
|
||||||
ChatEmojiPicker(controller),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (controller.dragging)
|
|
||||||
Container(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.scaffoldBackgroundColor
|
|
||||||
.withOpacity(0.9),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: const Icon(
|
|
||||||
Icons.upload_outlined,
|
|
||||||
size: 100,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
if (controller.dragging)
|
||||||
|
Container(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.scaffoldBackgroundColor
|
||||||
|
.withOpacity(0.9),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: const Icon(
|
||||||
|
Icons.upload_outlined,
|
||||||
|
size: 100,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
},
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue