mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
Animate compact agent updates without tap expansion
Signed-off-by: Tom Brow <tomb@block.xyz>
This commit is contained in:
@@ -39,7 +39,6 @@ class _MessageBubble extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final coordinationExpanded = useState(false);
|
||||
final messageSnapshotKey = useMemoized(GlobalKey.new, const []);
|
||||
final swipeContentKey = useMemoized(GlobalKey.new, const []);
|
||||
final mediaOffset = useMessageMediaOffset(message.content, message.tags);
|
||||
@@ -141,7 +140,7 @@ class _MessageBubble extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
if (message.isAgentCoordination && !coordinationExpanded.value) {
|
||||
if (message.isAgentCoordination) {
|
||||
final collapsed = Padding(
|
||||
key: swipeContentKey,
|
||||
padding: const EdgeInsets.symmetric(vertical: Grid.xxs),
|
||||
@@ -151,13 +150,10 @@ class _MessageBubble extends HookConsumerWidget {
|
||||
key: ValueKey('message-row-${message.id}'),
|
||||
snapshotKey: messageSnapshotKey,
|
||||
onLongPressDetails: openMessageActions,
|
||||
onTap: () => coordinationExpanded.value = true,
|
||||
child: RepaintBoundary(
|
||||
key: messageSnapshotKey,
|
||||
child: Semantics(
|
||||
button: true,
|
||||
expanded: false,
|
||||
label: 'Agent update from $displayName. Show message',
|
||||
label: 'Agent update from $displayName',
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
@@ -208,7 +204,11 @@ class _MessageBubble extends HookConsumerWidget {
|
||||
currentPubkey: currentPubkey,
|
||||
isMember: isMember,
|
||||
isArchived: isArchived,
|
||||
child: collapsed,
|
||||
child: AgentReplyHandoff(
|
||||
key: ValueKey('reply-handoff-${message.id}'),
|
||||
animate: typingHandoff,
|
||||
child: collapsed,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -334,17 +334,6 @@ class _MessageBubble extends HookConsumerWidget {
|
||||
? CrossAxisAlignment.end
|
||||
: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (message.isAgentCoordination)
|
||||
TextButton(
|
||||
key: ValueKey(
|
||||
'collapse-update-${message.id}',
|
||||
),
|
||||
onPressed: () =>
|
||||
coordinationExpanded.value = false,
|
||||
child: const Text(
|
||||
'Collapse agent update',
|
||||
),
|
||||
),
|
||||
if (showAuthor)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
|
||||
@@ -11078,6 +11078,71 @@ void main() {
|
||||
expect(findRichText('Here is the reply'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Me compact reply consumes typing and animates', (
|
||||
tester,
|
||||
) async {
|
||||
final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
final prompt = _textMsg(
|
||||
id: threadIdRoot,
|
||||
pubkey: 'self',
|
||||
content: 'Hello',
|
||||
createdAt: now - 3,
|
||||
);
|
||||
final typing = _FakeTypingNotifier(const []);
|
||||
await tester.pumpWidget(
|
||||
_buildTestable(
|
||||
personalChat: true,
|
||||
messages: [prompt],
|
||||
typingNotifier: typing,
|
||||
users: const {
|
||||
'alice': UserProfile(pubkey: 'alice', displayName: 'Alice'),
|
||||
},
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
typing.setEntries([
|
||||
TypingEntry(
|
||||
pubkey: 'alice',
|
||||
threadHeadId: threadIdRoot,
|
||||
expiresAtMs: (now - 1) * 1000 + 8000,
|
||||
),
|
||||
]);
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 250));
|
||||
expect(find.byKey(const ValueKey('agent-typing-bubble')), findsOneWidget);
|
||||
final container = ProviderScope.containerOf(
|
||||
tester.element(find.byType(ChannelDetailPage)),
|
||||
);
|
||||
final messages =
|
||||
container.read(meMessagesProvider(_channelId).notifier)
|
||||
as _FakeMessagesNotifier;
|
||||
messages.setMessages([
|
||||
prompt,
|
||||
_textMsg(
|
||||
id: 'reply',
|
||||
pubkey: 'alice',
|
||||
content: 'Here is the reply',
|
||||
createdAt: now,
|
||||
extraTags: [
|
||||
['e', threadIdRoot, '', 'reply'],
|
||||
['audience', 'agents'],
|
||||
],
|
||||
),
|
||||
]);
|
||||
await tester.pump();
|
||||
expect(
|
||||
find.byKey(const ValueKey('inline-agent-typing-alice')),
|
||||
findsNothing,
|
||||
);
|
||||
final handoff = tester.widget<AgentReplyHandoff>(
|
||||
find.byKey(const ValueKey('reply-handoff-reply')),
|
||||
);
|
||||
expect(handoff.animate, isTrue);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byKey(const ValueKey('agent-typing-bubble')), findsNothing);
|
||||
expect(find.text('Here is the reply'), findsOneWidget);
|
||||
});
|
||||
|
||||
for (final personal in [true, false]) {
|
||||
testWidgets('reply-scoped typing is visible only in flat Me: $personal', (
|
||||
tester,
|
||||
|
||||
@@ -307,7 +307,7 @@ void bubbleLayoutTests() {
|
||||
}
|
||||
|
||||
testWidgets(
|
||||
'agent audience updates collapse without a bubble and expand on tap',
|
||||
'agent audience updates stay compact on tap and preserve swipe reply',
|
||||
(tester) async {
|
||||
await tester.binding.setSurfaceSize(const Size(390, 844));
|
||||
addTearDown(() => tester.binding.setSurfaceSize(null));
|
||||
@@ -362,10 +362,6 @@ void bubbleLayoutTests() {
|
||||
}
|
||||
await tester.tap(row);
|
||||
await tester.pumpAndSettle();
|
||||
expect(tester.widget<MessageContent>(body()).bubble, isFalse);
|
||||
expect(tester.widget<MessageContent>(body()).content, update);
|
||||
await tester.tap(find.byKey(const ValueKey('collapse-update-update')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(body(), findsNothing);
|
||||
await tester.drag(row, const Offset(150, 0));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
Reference in New Issue
Block a user