Skip to content

Commit 4a768b0

Browse files
committed
v6.0.4
1 parent 8e4890a commit 4a768b0

40 files changed

Lines changed: 2851 additions & 552 deletions

examples/ai_sample_app/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
name: ai_sample_app
22
description: "CometChat Flutter AI Sample App - AI Agents Chat Experience"
33
publish_to: 'none'
4-
version: 6.0.3
4+
version: 6.0.4
55

66
environment:
77
sdk: ^3.6.0
88

99
dependencies:
1010
flutter:
1111
sdk: flutter
12-
cometchat_chat_uikit: ^6.0.3
12+
cometchat_chat_uikit: ^6.0.4
1313
http: ^1.2.0
1414
intl: ^0.20.2
1515
shared_preferences: ^2.2.0

examples/sample_app/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
name: sample_app
22
description: "A sample app"
33
publish_to: 'none'
4-
version: 6.0.3
4+
version: 6.0.4
55

66
environment:
77
sdk: ^3.6.0
88

99
dependencies:
1010
flutter:
1111
sdk: flutter
12-
cometchat_chat_uikit: ^6.0.3
12+
cometchat_chat_uikit: ^6.0.4
1313

1414
dev_dependencies:
1515
flutter_test:

packages/cometchat_uikit/CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
# 6.0.4
2+
3+
## New
4+
- Added `CometChatCardBubble` for rendering developer card messages (category `card`).
5+
- Added support for rendering agent card messages within the AI Assistant bubble.
6+
- Added streaming support for cards that arrive during a streamed agent response.
7+
- Added the `ccCardActionClicked` event, which forwards raw card actions to the app layer for both developer and agent cards.
8+
- Added `MessageCategoryConstants.card` for card messages.
9+
10+
## Enhancements
11+
- Enhanced the AI Assistant bubble to render ordered text and card blocks, falling back to `getText()` for older messages.
12+
- Enhanced conversation previews for developer card messages to show `getText()` when available, or `Card Message` otherwise.
13+
- Updated developer card options to exclude `edit` and `copy`.
14+
15+
## Fixes
16+
- None
17+
118
# 6.0.3
219

320
## New

packages/cometchat_uikit/lib/chat_ui/src/message_list/bloc/message_list_bloc.dart

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,6 +1318,7 @@ class MessageListBloc extends Bloc<MessageListEvent, MessageListState>
13181318
/// The CometChatStreamBubble widget handles word-by-word rendering.
13191319
void _handleAIAssistantEvent(AIAssistantBaseEvent event) {
13201320
if (isClosed) return;
1321+
debugPrint('[MessageListBloc] AI event received: type=${event.type}, id=${event.id}');
13211322
final runId = event.id;
13221323
if (runId == null) return;
13231324

@@ -2468,6 +2469,11 @@ class MessageListBloc extends Bloc<MessageListEvent, MessageListState>
24682469
) async {
24692470
final message = event.message;
24702471

2472+
debugPrint('[MessageListBloc] Message received: id=${message.id}, '
2473+
'category=${message.category}, type=${message.type}, '
2474+
'sender=${message.sender?.uid}, '
2475+
'class=${message.runtimeType}');
2476+
24712477
// For AI assistant messages, replace the thinking/stream bubble
24722478
if (message is AIAssistantMessage && message.runId != null) {
24732479
final thinkingId = -(message.runId!);
@@ -2496,6 +2502,8 @@ class MessageListBloc extends Bloc<MessageListEvent, MessageListState>
24962502

24972503
// Check if message belongs to current conversation
24982504
if (!_isMessageForCurrentConversation(message)) {
2505+
debugPrint('[MessageListBloc] Message FILTERED: not for current conversation. '
2506+
'message.conversationId=${message.conversationId}');
24992507
return;
25002508
}
25012509

@@ -2526,6 +2534,9 @@ class MessageListBloc extends Bloc<MessageListEvent, MessageListState>
25262534

25272535
// Check message type/category filters
25282536
if (!_passesTypeAndCategoryFilters(message)) {
2537+
debugPrint('[MessageListBloc] Message FILTERED: type/category filter failed. '
2538+
'category=${message.category}, type=${message.type}, '
2539+
'allowedCategories=$categories');
25292540
return;
25302541
}
25312542

@@ -2569,6 +2580,7 @@ class MessageListBloc extends Bloc<MessageListEvent, MessageListState>
25692580
// If user has scrolled up (hasMoreNewer = true), don't append new messages
25702581
// This prevents messages from appearing while user is viewing older messages
25712582
if (state.hasMoreNewer) {
2583+
debugPrint('[MessageListBloc] Message FILTERED: hasMoreNewer=true (user scrolled up)');
25722584
return;
25732585
}
25742586

@@ -2580,6 +2592,10 @@ class MessageListBloc extends Bloc<MessageListEvent, MessageListState>
25802592
final insertIndex = state.messages.length;
25812593
final updatedMessages = [...state.messages, intercepted];
25822594

2595+
debugPrint('[MessageListBloc] Adding message to list: id=${intercepted.id}, '
2596+
'category=${intercepted.category}, type=${intercepted.type}, '
2597+
'insertIndex=$insertIndex');
2598+
25832599
// Update index maps incrementally
25842600
_addToIndexMaps(intercepted, insertIndex);
25852601

@@ -4115,26 +4131,44 @@ class _MessageListMessageListener with MessageListener {
41154131

41164132
@override
41174133
void onTextMessageReceived(TextMessage textMessage) {
4134+
debugPrint('[MessageListener] onTextMessageReceived: id=${textMessage.id}, sender=${textMessage.sender?.uid}');
41184135
onTextMessageReceivedCallback(textMessage);
41194136
}
41204137

41214138
@override
41224139
void onMediaMessageReceived(MediaMessage mediaMessage) {
4140+
debugPrint('[MessageListener] onMediaMessageReceived: id=${mediaMessage.id}, type=${mediaMessage.type}, sender=${mediaMessage.sender?.uid}');
41234141
onMediaMessageReceivedCallback(mediaMessage);
41244142
}
41254143

41264144
@override
41274145
void onCustomMessageReceived(CustomMessage customMessage) {
4146+
debugPrint('[MessageListener] onCustomMessageReceived: id=${customMessage.id}, type=${customMessage.type}, sender=${customMessage.sender?.uid}');
41284147
onCustomMessageReceivedCallback(customMessage);
41294148
}
41304149

41314150
@override
41324151
void onInteractiveMessageReceived(InteractiveMessage interactiveMessage) {
4152+
debugPrint('[MessageListener] onInteractiveMessageReceived: id=${interactiveMessage.id}, type=${interactiveMessage.type}, sender=${interactiveMessage.sender?.uid}');
41334153
onInteractiveMessageReceivedCallback(interactiveMessage);
41344154
}
41354155

4156+
@override
4157+
void onCardMessageReceived(CardMessage cardMessage) {
4158+
debugPrint('[MessageListener] onCardMessageReceived: id=${cardMessage.id}, '
4159+
'category=${cardMessage.category}, type=${cardMessage.type}, '
4160+
'hasCard=${cardMessage.getCard() != null}, '
4161+
'text=${cardMessage.getText()}');
4162+
// Route card messages through the same path as other messages
4163+
onTextMessageReceivedCallback(cardMessage);
4164+
}
4165+
41364166
@override
41374167
void onAIAssistantMessageReceived(AIAssistantMessage aiAssistantMessage) {
4168+
debugPrint('[MessageListener] onAIAssistantMessageReceived: id=${aiAssistantMessage.id}, '
4169+
'runId=${aiAssistantMessage.runId}, '
4170+
'hasElements=${aiAssistantMessage.getElements()?.isNotEmpty ?? false}, '
4171+
'elementsCount=${aiAssistantMessage.getElements()?.length ?? 0}');
41384172
onAIAssistantMessageReceivedCallback(aiAssistantMessage);
41394173
}
41404174

packages/cometchat_uikit/lib/chat_ui/src/message_list/utils/message_template_utils.dart

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import '../../extensions/polls/cometchat_polls_bubble.dart';
1313
import '../../extensions/stickers/cometchat_sticker_bubble.dart';
1414
import '../../extensions/collaborative/cometchat_collaborative_bubble.dart';
1515
import '../../extensions/link_preview/cometchat_link_preview_bubble.dart';
16+
import '../widgets/cometchat_card_bubble.dart';
1617
import '../../extensions/extension_moderator.dart';
1718
import 'package:url_launcher/url_launcher.dart';
1819

@@ -755,6 +756,7 @@ class MessageTemplateUtils {
755756
MessageTemplateUtils.getGroupActionTemplate(),
756757
MessageTemplateUtils.getFormMessageTemplate(),
757758
MessageTemplateUtils.getCardMessageTemplate(),
759+
MessageTemplateUtils.getCardBubbleTemplate(),
758760
MessageTemplateUtils.getSchedulerMessageTemplate(),
759761
MessageTemplateUtils.getPollMessageTemplate(),
760762
MessageTemplateUtils.getStickerMessageTemplate(),
@@ -1090,6 +1092,7 @@ class MessageTemplateUtils {
10901092
CometChatMessageCategory.action,
10911093
CometChatMessageCategory.interactive,
10921094
MessageCategoryConstants.custom,
1095+
MessageCategoryConstants.card,
10931096
CometChatMessageCategory.categoryAgentic,
10941097
CometChatMessageCategory.streamMessage,
10951098
];
@@ -1262,14 +1265,49 @@ class MessageTemplateUtils {
12621265
}
12631266

12641267

1265-
static Widget getCardMessageBubble(
1266-
{CardBubbleStyle? cardBubbleStyle, required CardMessage message}) {
1267-
return CometChatCardBubble(
1268-
cardMessage: message,
1269-
loggedInUser: CometChatUIKit.loggedInUser,
1268+
/// Returns the template for developer card messages (category: "card").
1269+
///
1270+
/// This is the NEW first-party card bubble (§2.9.7-A).
1271+
/// DO NOT confuse with [getCardMessageTemplate] below which is the
1272+
/// legacy interactive/card NOT_SUPPORTED stub (category: "interactive").
1273+
static CometChatMessageTemplate getCardBubbleTemplate() {
1274+
return CometChatMessageTemplate(
1275+
type: MessageTypeConstants.card,
1276+
category: MessageCategoryConstants.card,
1277+
contentView:
1278+
(BaseMessage message, BuildContext context, BubbleAlignment alignment,
1279+
{AdditionalConfigurations? additionalConfigurations}) {
1280+
if (message.deletedAt != null) {
1281+
return getDeleteMessageBubble(
1282+
message, context, additionalConfigurations?.deletedBubbleStyle);
1283+
}
1284+
1285+
final cardMessage = message as CardMessage;
1286+
return CometChatCardBubble(
1287+
message: cardMessage,
1288+
);
1289+
},
1290+
options: getCardBubbleOptions,
12701291
);
12711292
}
12721293

1294+
/// Options for developer card messages: text options minus edit and copy.
1295+
/// Preserves all conditional visibility logic (delete-only-for-own/admin, etc.)
1296+
static List<CometChatMessageOption> getCardBubbleOptions(
1297+
User loggedInUser,
1298+
BaseMessage messageObject,
1299+
BuildContext context,
1300+
Group? group,
1301+
AdditionalConfigurations? additionalConfigurations) {
1302+
// Get the full text options, then remove edit and copy
1303+
final textOptions = getTextMessageOptions(
1304+
loggedInUser, messageObject, context, group, additionalConfigurations);
1305+
textOptions.removeWhere((option) =>
1306+
option.id == MessageOptionConstants.editMessage ||
1307+
option.id == MessageOptionConstants.copyMessage);
1308+
return textOptions;
1309+
}
1310+
12731311

12741312
static CometChatMessageTemplate getCardMessageTemplate() {
12751313
return CometChatMessageTemplate(
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import 'dart:convert';
2+
3+
import 'package:flutter/material.dart';
4+
import 'package:cometchat_cards/cometchat_cards.dart';
5+
6+
import '../../../../shared_ui/cometchat_uikit_shared.dart';
7+
import '../../../../shared_ui/src/views/no_intrinsic_card_wrapper.dart';
8+
9+
/// Content view widget for developer card messages (category: "card").
10+
///
11+
/// Renders the raw card schema via [CometChatCardView] from the
12+
/// `cometchat_cards` renderer package. Mirrors the notification-feed
13+
/// card rendering pattern.
14+
///
15+
/// Handles:
16+
/// - Normal render: getCard() → jsonEncode → CometChatCardView
17+
/// - Null/empty card: shows getFallbackText() → getText() → "Card Message"
18+
/// - Deleted message: handled at the template level (not here)
19+
class CometChatCardBubble extends StatelessWidget {
20+
const CometChatCardBubble({
21+
super.key,
22+
required this.message,
23+
this.onCardAction,
24+
this.themeMode,
25+
this.themeOverride,
26+
});
27+
28+
/// The [CardMessage] from the SDK (category: "card").
29+
final CardMessage message;
30+
31+
/// Callback when the user taps an action within the card.
32+
/// Fires alongside the [CometChatUIEvents.ccCardActionClicked] event.
33+
final void Function(CardMessage message, CometChatCardActionEvent action)?
34+
onCardAction;
35+
36+
/// Theme mode for the card renderer.
37+
/// If null, auto-detected from the current [Brightness].
38+
final CometChatCardThemeMode? themeMode;
39+
40+
/// Theme override for the card renderer.
41+
final CometChatCardThemeOverride? themeOverride;
42+
43+
@override
44+
Widget build(BuildContext context) {
45+
final cardData = message.getCard();
46+
47+
// If card payload is null/empty, show fallback text
48+
if (cardData == null || cardData.isEmpty) {
49+
return _buildFallbackView(context);
50+
}
51+
52+
// Resolve theme mode from platform brightness if not provided
53+
final resolvedThemeMode = themeMode ?? _resolveThemeMode(context);
54+
55+
final String cardJson = jsonEncode(cardData);
56+
57+
final cardWidth = MediaQuery.sizeOf(context).width * 0.65;
58+
59+
return NoIntrinsicCardWrapper(
60+
width: cardWidth,
61+
child: CometChatCardView(
62+
cardJson: cardJson,
63+
themeMode: resolvedThemeMode,
64+
themeOverride: themeOverride,
65+
onAction: (CometChatCardActionEvent action) {
66+
// Fire the callback prop (for apps using the bubble directly)
67+
onCardAction?.call(message, action);
68+
69+
// Emit the UI event (for global subscription — covers nested agent cards too)
70+
CometChatUIEvents.ccCardActionClicked(message, action);
71+
},
72+
),
73+
);
74+
}
75+
76+
/// Fallback view when getCard() is null/empty.
77+
/// Shows getFallbackText() → getText() → "Card Message".
78+
Widget _buildFallbackView(BuildContext context) {
79+
final colorPalette = CometChatThemeHelper.getColorPalette(context);
80+
final typography = CometChatThemeHelper.getTypography(context);
81+
82+
final fallbackText = message.getFallbackText() ??
83+
message.getText() ??
84+
Translations.of(context).cardMessage;
85+
86+
return Padding(
87+
padding: const EdgeInsets.all(8.0),
88+
child: Text(
89+
fallbackText,
90+
style: TextStyle(
91+
color: colorPalette.textPrimary,
92+
fontWeight: typography.body?.regular?.fontWeight,
93+
fontSize: typography.body?.regular?.fontSize,
94+
fontFamily: typography.body?.regular?.fontFamily,
95+
),
96+
),
97+
);
98+
}
99+
100+
/// Resolves theme mode from the platform brightness.
101+
CometChatCardThemeMode _resolveThemeMode(BuildContext context) {
102+
final brightness = Theme.of(context).brightness;
103+
return brightness == Brightness.dark
104+
? CometChatCardThemeMode.dark
105+
: CometChatCardThemeMode.light;
106+
}
107+
}

0 commit comments

Comments
 (0)