mirror of
https://github.com/Iconica-Development/flutter_chat.git
synced 2025-05-18 18:33:49 +02:00
feat: use getAllUsersForChat on ChatDetailScreen to avoid call to getUser for each user
This commit is contained in:
parent
cb1ceb456a
commit
756b99cfaa
3 changed files with 70 additions and 65 deletions
|
@ -121,12 +121,13 @@ typedef ContainerBuilder = Widget Function(
|
|||
/// If null is returned, the default chat message widget will be used so you can
|
||||
/// override for specific cases
|
||||
/// [previousMessage] is the previous message in the chat
|
||||
/// [sender] is the sender of the message and null if no user sent the message
|
||||
typedef ChatMessageBuilder = Widget? Function(
|
||||
BuildContext context,
|
||||
MessageModel message,
|
||||
MessageModel? previousMessage,
|
||||
UserModel user,
|
||||
Function(UserModel user) onPressUserProfile,
|
||||
UserModel? sender,
|
||||
Function(UserModel sender) onPressSender,
|
||||
);
|
||||
|
||||
/// The group avatar builder
|
||||
|
|
|
@ -60,23 +60,24 @@ class ChatDetailScreen extends HookWidget {
|
|||
var chatSnapshot = useStream(chatStream);
|
||||
var chat = chatSnapshot.data;
|
||||
|
||||
var allUsersStream = useMemoized(
|
||||
() => options.userRepository.getAllUsersForChat(chatId: chatId),
|
||||
[chatId],
|
||||
);
|
||||
var usersSnapshot = useStream(allUsersStream);
|
||||
var allUsers = usersSnapshot.data ?? [];
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
if (chat == null) return;
|
||||
if (chat.isGroupChat) {
|
||||
chatTitle.value = options.translations.groupNameEmpty;
|
||||
} else {
|
||||
unawaited(
|
||||
_computeChatTitle(
|
||||
chatScope: chatScope,
|
||||
chat: chat,
|
||||
onTitleComputed: (title) => chatTitle.value = title,
|
||||
),
|
||||
);
|
||||
}
|
||||
chatTitle.value = _getChatTitle(
|
||||
chatScope: chatScope,
|
||||
chat: chat,
|
||||
allUsers: allUsers,
|
||||
);
|
||||
return;
|
||||
},
|
||||
[chat],
|
||||
[chat, allUsers],
|
||||
);
|
||||
|
||||
useEffect(
|
||||
|
@ -98,6 +99,7 @@ class ChatDetailScreen extends HookWidget {
|
|||
var body = _Body(
|
||||
chatId: chatId,
|
||||
chat: chat,
|
||||
chatUsers: allUsers,
|
||||
onPressUserProfile: onPressUserProfile,
|
||||
onUploadImage: onUploadImage,
|
||||
onMessageSubmit: onMessageSubmit,
|
||||
|
@ -120,21 +122,26 @@ class ChatDetailScreen extends HookWidget {
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> _computeChatTitle({
|
||||
String? _getChatTitle({
|
||||
required ChatScope chatScope,
|
||||
required ChatModel chat,
|
||||
required void Function(String?) onTitleComputed,
|
||||
}) async {
|
||||
if (chatScope.options.chatTitleResolver != null) {
|
||||
onTitleComputed(chatScope.options.chatTitleResolver!.call(chat));
|
||||
return;
|
||||
required List<UserModel> allUsers,
|
||||
}) {
|
||||
if (chat.isGroupChat) {
|
||||
return chatScope.options.translations.groupNameEmpty;
|
||||
}
|
||||
|
||||
var userId = chat.users.firstWhere((user) => user != chatScope.userId);
|
||||
var user = await chatScope.service.getUser(userId: userId).first;
|
||||
onTitleComputed(
|
||||
user.fullname ?? chatScope.options.translations.anonymousUser,
|
||||
// For one-to-one, pick the 'other' user from the list
|
||||
var otherUser = allUsers.firstWhere(
|
||||
(u) => u.id != chatScope.userId,
|
||||
orElse: () => const UserModel(
|
||||
id: "",
|
||||
),
|
||||
);
|
||||
|
||||
return otherUser.fullname?.isNotEmpty ?? false
|
||||
? otherUser.fullname
|
||||
: chatScope.options.translations.anonymousUser;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,6 +205,7 @@ class _Body extends HookWidget {
|
|||
const _Body({
|
||||
required this.chatId,
|
||||
required this.chat,
|
||||
required this.chatUsers,
|
||||
required this.onPressUserProfile,
|
||||
required this.onUploadImage,
|
||||
required this.onMessageSubmit,
|
||||
|
@ -206,6 +214,7 @@ class _Body extends HookWidget {
|
|||
|
||||
final String chatId;
|
||||
final ChatModel? chat;
|
||||
final List<UserModel> chatUsers;
|
||||
final Function(UserModel) onPressUserProfile;
|
||||
final Function(Uint8List image) onUploadImage;
|
||||
final Function(String message) onMessageSubmit;
|
||||
|
@ -261,15 +270,21 @@ class _Body extends HookWidget {
|
|||
_ChatNoMessages(isGroupChat: chat!.isGroupChat),
|
||||
]
|
||||
: [
|
||||
for (var (index, message) in messages.indexed)
|
||||
for (var (index, message) in messages.indexed) ...[
|
||||
if (chat!.id == message.chatId)
|
||||
_ChatBubble(
|
||||
key: ValueKey(message.id),
|
||||
sender: chatUsers
|
||||
.where(
|
||||
(u) => u.id == message.senderId,
|
||||
)
|
||||
.firstOrNull,
|
||||
message: message,
|
||||
previousMessage:
|
||||
index < messages.length - 1 ? messages[index + 1] : null,
|
||||
onPressUserProfile: onPressUserProfile,
|
||||
onPressSender: onPressUserProfile,
|
||||
),
|
||||
],
|
||||
];
|
||||
|
||||
return Stack(
|
||||
|
@ -457,7 +472,8 @@ class _ChatBottom extends HookWidget {
|
|||
class _ChatBubble extends HookWidget {
|
||||
const _ChatBubble({
|
||||
required this.message,
|
||||
required this.onPressUserProfile,
|
||||
required this.sender,
|
||||
required this.onPressSender,
|
||||
this.previousMessage,
|
||||
super.key,
|
||||
});
|
||||
|
@ -465,45 +481,33 @@ class _ChatBubble extends HookWidget {
|
|||
/// The message to display.
|
||||
final MessageModel message;
|
||||
|
||||
/// The user who sent the message. This can be null because some messages are
|
||||
/// not from users
|
||||
final UserModel? sender;
|
||||
|
||||
/// The previous message in the list, if any.
|
||||
final MessageModel? previousMessage;
|
||||
|
||||
/// Callback function when the user's profile is pressed.
|
||||
final Function(UserModel user) onPressUserProfile;
|
||||
/// Callback function when a message sender is pressed.
|
||||
final Function(UserModel user) onPressSender;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var chatScope = ChatScope.of(context);
|
||||
var service = chatScope.service;
|
||||
var options = chatScope.options;
|
||||
|
||||
var userStream = useMemoized(
|
||||
() => service.getUser(userId: message.senderId),
|
||||
[message.senderId],
|
||||
);
|
||||
var userSnapshot = useStream(userStream);
|
||||
|
||||
if (userSnapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
var user = userSnapshot.data;
|
||||
if (user == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return options.builders.chatMessageBuilder.call(
|
||||
context,
|
||||
message,
|
||||
previousMessage,
|
||||
user,
|
||||
onPressUserProfile,
|
||||
sender,
|
||||
onPressSender,
|
||||
) ??
|
||||
DefaultChatMessageBuilder(
|
||||
message: message,
|
||||
previousMessage: previousMessage,
|
||||
user: user,
|
||||
onPressUserProfile: onPressUserProfile,
|
||||
sender: sender,
|
||||
onPressSender: onPressSender,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ class DefaultChatMessageBuilder extends StatelessWidget {
|
|||
const DefaultChatMessageBuilder({
|
||||
required this.message,
|
||||
required this.previousMessage,
|
||||
required this.user,
|
||||
required this.onPressUserProfile,
|
||||
required this.sender,
|
||||
required this.onPressSender,
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
@ -25,25 +25,25 @@ class DefaultChatMessageBuilder extends StatelessWidget {
|
|||
/// is from the same sender as the previous message.
|
||||
final MessageModel? previousMessage;
|
||||
|
||||
/// The user that sent the message
|
||||
final UserModel user;
|
||||
/// The user that sent the message, can be null if the message is an event
|
||||
final UserModel? sender;
|
||||
|
||||
/// The function that is called when the user profile is pressed
|
||||
final Function(UserModel user) onPressUserProfile;
|
||||
/// The function that is called when the sender is clicked
|
||||
final Function(UserModel user) onPressSender;
|
||||
|
||||
/// implements [ChatMessageBuilder]
|
||||
static Widget builder(
|
||||
BuildContext context,
|
||||
MessageModel message,
|
||||
MessageModel? previousMessage,
|
||||
UserModel user,
|
||||
Function(UserModel user) onPressUserProfile,
|
||||
UserModel? sender,
|
||||
Function(UserModel sender) onPressSender,
|
||||
) =>
|
||||
DefaultChatMessageBuilder(
|
||||
message: message,
|
||||
previousMessage: previousMessage,
|
||||
user: user,
|
||||
onPressUserProfile: onPressUserProfile,
|
||||
sender: sender,
|
||||
onPressSender: onPressSender,
|
||||
);
|
||||
|
||||
/// Merges the [MessageTheme] from the themeresolver with the [MessageTheme]
|
||||
|
@ -53,7 +53,7 @@ class DefaultChatMessageBuilder extends StatelessWidget {
|
|||
required BuildContext context,
|
||||
required ChatOptions options,
|
||||
required MessageModel message,
|
||||
required UserModel user,
|
||||
required UserModel? user,
|
||||
}) =>
|
||||
[
|
||||
options.messageThemeResolver(context, message, user),
|
||||
|
@ -71,7 +71,7 @@ class DefaultChatMessageBuilder extends StatelessWidget {
|
|||
context: context,
|
||||
options: options,
|
||||
message: message,
|
||||
user: user,
|
||||
user: sender,
|
||||
);
|
||||
|
||||
var isSameSender = previousMessage != null &&
|
||||
|
@ -84,7 +84,7 @@ class DefaultChatMessageBuilder extends StatelessWidget {
|
|||
isMessageFromSelf: isMessageFromSelf,
|
||||
message: message,
|
||||
messageTheme: messageTheme,
|
||||
user: user,
|
||||
sender: sender,
|
||||
);
|
||||
|
||||
var messagePadding = messageTheme.messageSidePadding!;
|
||||
|
@ -122,14 +122,14 @@ class _ChatMessageBubble extends StatelessWidget {
|
|||
required this.isMessageFromSelf,
|
||||
required this.message,
|
||||
required this.messageTheme,
|
||||
required this.user,
|
||||
required this.sender,
|
||||
});
|
||||
|
||||
final bool isSameSender;
|
||||
final bool isMessageFromSelf;
|
||||
final MessageModel message;
|
||||
final MessageTheme messageTheme;
|
||||
final UserModel user;
|
||||
final UserModel? sender;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -141,7 +141,7 @@ class _ChatMessageBubble extends StatelessWidget {
|
|||
var messageTime = dateFormatter.format(date: message.timestamp);
|
||||
|
||||
var senderTitle = Text(
|
||||
user.firstName ?? "",
|
||||
sender?.firstName ?? "",
|
||||
style: theme.textTheme.titleMedium,
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in a new issue