Share adaptive navigation and give Duo crop one cancel owner

Move conversation-pane infrastructure and tests into shared widgets. Keep photo-crop cancellation in the Duo rail without a duplicate AppBar action, with regression coverage for both rail sides and compact iPhone.

Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
Signed-off-by: kenny lopez <klopez4212@gmail.com>
This commit is contained in:
kenny lopez
2026-10-27 17:57:34 +00:00
parent 884a4863a2
commit 2b9c3ada4c
14 changed files with 132 additions and 32 deletions

View File

@@ -1,4 +1,4 @@
import '../home/adaptive_conversation_pane.dart';
import 'package:buzz/shared/widgets/adaptive_conversation_pane.dart';
import 'dart:async';
import 'package:flutter/foundation.dart';

View File

@@ -1,4 +1,4 @@
import '../home/adaptive_conversation_pane.dart';
import 'package:buzz/shared/widgets/adaptive_conversation_pane.dart';
import 'message_swipe_reply.dart';
import 'agent_reply_handoff.dart';
import 'huddle_timeline.dart';

View File

@@ -1,4 +1,4 @@
import '../home/adaptive_conversation_pane.dart';
import 'package:buzz/shared/widgets/adaptive_conversation_pane.dart';
import 'channel_unread.dart';
import '../../shared/widgets/collapsible_section_body.dart';
import '../../shared/widgets/compose_morph_icon.dart';

View File

@@ -1,4 +1,4 @@
import '../../home/adaptive_conversation_pane.dart';
import 'package:buzz/shared/widgets/adaptive_conversation_pane.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';

View File

@@ -1,4 +1,4 @@
import '../home/adaptive_conversation_pane.dart';
import 'package:buzz/shared/widgets/adaptive_conversation_pane.dart';
import 'message_swipe_reply.dart';
import '../../shared/widgets/ios_scroll_to_top.dart';
import 'message_footer_layout.dart';

View File

@@ -1,6 +1,6 @@
import 'dart:async';
import 'adaptive_conversation_pane.dart';
import 'package:buzz/shared/widgets/adaptive_conversation_pane.dart';
import '../../shared/community/community_provider.dart';
import '../channels/channel_detail_page.dart';
import 'dart:ui';

View File

@@ -98,11 +98,12 @@ class ProfileAvatarCropPage extends HookWidget {
return () => disposed = true;
}, [imageBytes]);
final strip = defaultTargetPlatform == TargetPlatform.iOS
? (DuoToolbarScope.maybeOf(context)?.strip ??
FoldGeometry.of(context).strip)
: null;
return DuoToolbarPortal(
strip: defaultTargetPlatform == TargetPlatform.iOS
? (DuoToolbarScope.maybeOf(context)?.strip ??
FoldGeometry.of(context).strip)
: null,
strip: strip,
active:
(DuoToolbarScope.maybeOf(context)?.active ?? true) &&
(ModalRoute.isCurrentOf(context) ?? true),
@@ -128,19 +129,22 @@ class ProfileAvatarCropPage extends HookWidget {
fontWeight: FontWeight.w600,
),
),
leadingWidth: 120,
leading: Padding(
padding: const EdgeInsets.only(left: Grid.gutter),
child: Align(
alignment: Alignment.centerLeft,
child: _CropHeaderButton(
label: 'Cancel',
onPressed: isCropping.value
? null
: () => Navigator.pop(context),
),
),
),
automaticallyImplyLeading: false,
leadingWidth: strip == null ? 120 : null,
leading: strip != null
? null
: Padding(
padding: const EdgeInsets.only(left: Grid.gutter),
child: Align(
alignment: Alignment.centerLeft,
child: _CropHeaderButton(
label: 'Cancel',
onPressed: isCropping.value
? null
: () => Navigator.pop(context),
),
),
),
actions: [
Padding(
padding: const EdgeInsets.only(right: Grid.gutter),

View File

@@ -1,4 +1,4 @@
import '../home/adaptive_conversation_pane.dart';
import 'package:buzz/shared/widgets/adaptive_conversation_pane.dart';
import '../../shared/widgets/duo_toolbar.dart';
import '../../shared/widgets/ios_navigation_bar.dart';
import 'package:flutter/foundation.dart';

View File

@@ -1,4 +1,4 @@
import '../home/adaptive_conversation_pane.dart';
import 'package:buzz/shared/widgets/adaptive_conversation_pane.dart';
import 'dart:async';
import 'package:flutter/foundation.dart';

View File

@@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:duo_kit/duo_kit.dart';
import '../../shared/widgets/duo_toolbar.dart';
import '../../shared/widgets/ios_navigation_metrics.dart';
import '../../shared/widgets/duo_workspace_scaffold.dart';
import 'duo_toolbar.dart';
import 'ios_navigation_metrics.dart';
import 'duo_workspace_scaffold.dart';
import '../../shared/theme/theme.dart';
import '../theme/theme.dart';
/// The active vertical fold, in window coordinates, when held like a book.
Rect? bookDivisionOf(BuildContext context) {

View File

@@ -1,4 +1,4 @@
import 'package:buzz/features/home/adaptive_conversation_pane.dart';
import 'package:buzz/shared/widgets/adaptive_conversation_pane.dart';
import 'package:buzz/features/activity/activity_page.dart';
import 'package:buzz/shared/widgets/frosted_scaffold.dart';
import 'package:buzz/shared/widgets/concentric_sheet_surface.dart';

View File

@@ -0,0 +1,96 @@
import 'dart:async';
import 'package:buzz/features/profile/profile_avatar_crop_page.dart';
import 'package:buzz/shared/widgets/duo_toolbar.dart';
import 'package:buzz/shared/widgets/ios_navigation_bar.dart';
import 'package:duo_kit/duo_kit.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
void main() {
for (final side in [null, FoldStripSide.left, FoldStripSide.right]) {
for (final failed in [false, true]) {
testWidgets('crop has one cancel owner: $side, failed=$failed', (
tester,
) async {
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
addTearDown(() => debugDefaultTargetPlatformOverride = null);
final semantics = tester.ensureSemantics();
final bytes = Completer<Uint8List?>();
final strip = side == null
? null
: FoldStrip(
side: side,
rect: Rect.fromLTWH(
side == FoldStripSide.left ? 0 : 724,
0,
76,
600,
),
topClearance: 110,
bottomClearance: 0,
isClearanceEstimated: false,
);
await tester.pumpWidget(
ProviderScope(
child: MaterialApp(
home: Builder(
builder: (context) => TextButton(
onPressed: () => Navigator.of(context).push<void>(
MaterialPageRoute<void>(
builder: (_) => DuoToolbarScope(
strip: strip,
active: true,
child: ProfileAvatarCropPage(imageBytes: bytes.future),
),
),
),
child: const Text('Open crop'),
),
),
),
),
);
await tester.tap(find.text('Open crop'));
await tester.pump();
await tester.pump(const Duration(milliseconds: 400));
if (failed) {
bytes.completeError(StateError('Cannot load image'));
await tester.pump();
await tester.pump();
expect(find.text("We couldn't prepare that photo."), findsOneWidget);
}
if (side == null) {
expect(find.bySemanticsLabel('Cancel'), findsOneWidget);
expect(find.byType(IosNavigationBar), findsNothing);
await tester.tap(find.text('Cancel'));
} else {
// UIKit owns the rail's semantics; its native action payload must
// have one cancel, with no competing Flutter cancel/back control.
expect(find.bySemanticsLabel(RegExp('Cancel|Back')), findsNothing);
final header = tester.widget<AppBar>(find.byType(AppBar));
expect(header.leading, isNull);
expect(header.automaticallyImplyLeading, isFalse);
final native = tester.widget<UiKitView>(find.byType(UiKitView));
final payload = native.creationParams! as Map<String, Object?>;
expect(
payload['leading'],
containsPair('label', 'Cancel photo positioning'),
);
final rail = tester.widget<IosNavigationBar>(
find.byType(IosNavigationBar),
);
rail.leading!.onPressed!();
}
await tester.pumpAndSettle();
expect(find.byType(ProfileAvatarCropPage), findsNothing);
expect(find.text('Open crop'), findsOneWidget);
expect(tester.takeException(), isNull);
semantics.dispose();
debugDefaultTargetPlatformOverride = null;
});
}
}
}

View File

@@ -5,7 +5,7 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:buzz/features/settings/settings_page.dart';
import 'package:buzz/features/home/adaptive_conversation_pane.dart';
import 'package:buzz/shared/widgets/adaptive_conversation_pane.dart';
import 'package:buzz/shared/community/community_membership_provider.dart';
import 'package:buzz/shared/community/community.dart';
import 'package:buzz/shared/community/community_provider.dart';

View File

@@ -7,7 +7,7 @@ import 'package:duo_kit/duo_kit.dart';
import 'package:buzz/shared/widgets/frosted_scaffold.dart';
import 'package:buzz/shared/widgets/frosted_app_bar.dart';
import 'package:buzz/shared/widgets/modal_presentation.dart';
import 'package:buzz/features/home/adaptive_conversation_pane.dart';
import 'package:buzz/shared/widgets/adaptive_conversation_pane.dart';
import 'package:buzz/shared/theme/theme.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';