Attach hidden connection action to labelled wordmark

Keep the developer entry without a keyboard path by product decision.

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-26 19:25:17 +00:00
parent f83702333d
commit 0b27e8c8ce
2 changed files with 121 additions and 88 deletions

View File

@@ -323,38 +323,42 @@ class _PairingWelcomeView extends HookWidget {
setupContent == null && setupContent == null &&
!signInPending && !signInPending &&
!isBusy; !isBusy;
final welcome = Semantics( final welcome = Padding(
customSemanticsActions: canRevealOptions padding: const EdgeInsets.symmetric(
? { horizontal: Grid.gutter,
const CustomSemanticsAction(label: 'More connection options'): vertical: Grid.sm,
revealOptions, ),
} child: CustomScrollView(
: null, // Stay fixed when the welcome content fits; allow only real overflow
child: Padding( // (such as the pairing form above the keyboard), with no rubber banding.
padding: const EdgeInsets.symmetric( physics: const ClampingScrollPhysics(),
horizontal: Grid.gutter, slivers: [
vertical: Grid.sm, SliverFillRemaining(
), hasScrollBody: false,
child: CustomScrollView( child: Column(
// Stay fixed when the welcome content fits; allow only real overflow children: [
// (such as the pairing form above the keyboard), with no rubber banding. Expanded(
physics: const ClampingScrollPhysics(), child: _TwoFingerDoubleTapSurface(
slivers: [ onDoubleTap: canRevealOptions ? revealOptions : null,
SliverFillRemaining( child: Center(
hasScrollBody: false, child: Column(
child: Column( mainAxisSize: MainAxisSize.min,
children: [ children: [
Expanded( ConstrainedBox(
child: _TwoFingerDoubleTapSurface( constraints: const BoxConstraints(maxWidth: 320),
onDoubleTap: canRevealOptions ? revealOptions : null, child: AspectRatio(
child: Center( aspectRatio: 777 / 326,
child: Column( child: Semantics(
mainAxisSize: MainAxisSize.min, // Hidden developer entry intentionally has no
children: [ // keyboard shortcut. Keep its assistive action
ConstrainedBox( // on the existing labelled wordmark node.
constraints: const BoxConstraints(maxWidth: 320), customSemanticsActions: canRevealOptions
child: AspectRatio( ? {
aspectRatio: 777 / 326, const CustomSemanticsAction(
label: 'More connection options',
): revealOptions,
}
: null,
child: OnboardingWordmark( child: OnboardingWordmark(
key: const Key('pairing-buzz-wordmark'), key: const Key('pairing-buzz-wordmark'),
color: context._onboardingIsDark color: context._onboardingIsDark
@@ -363,73 +367,71 @@ class _PairingWelcomeView extends HookWidget {
), ),
), ),
), ),
const SizedBox(height: Grid.xxs), ),
Text( const SizedBox(height: Grid.xxs),
'Your people, your agents, your projects —\nall in one place.', Text(
textAlign: TextAlign.center, 'Your people, your agents, your projects —\nall in one place.',
style: context.textTheme.bodyLarge?.copyWith( textAlign: TextAlign.center,
color: context._onboardingInk, style: context.textTheme.bodyLarge?.copyWith(
), color: context._onboardingInk,
), ),
], ),
), ],
), ),
), ),
), ),
const SizedBox(height: Grid.md), ),
ConstrainedBox( const SizedBox(height: Grid.md),
constraints: const BoxConstraints(maxWidth: 440), ConstrainedBox(
child: SizedBox( constraints: const BoxConstraints(maxWidth: 440),
width: double.infinity, child: SizedBox(
child: setupContent != null width: double.infinity,
? setupContent! child: setupContent != null
: !connectionLanding ? setupContent!
? actions(2) : !connectionLanding
: ClipRect( ? actions(2)
child: Stack( : ClipRect(
alignment: Alignment.bottomCenter, child: Stack(
children: [ alignment: Alignment.bottomCenter,
for (var page = 1; page < 5; page++) children: [
ExcludeFocus( for (var page = 1; page < 5; page++)
ExcludeFocus(
excluding: page != selectedPage,
child: ExcludeSemantics(
excluding: page != selectedPage, excluding: page != selectedPage,
child: ExcludeSemantics( child: IgnorePointer(
excluding: page != selectedPage, ignoring: page != selectedPage,
child: IgnorePointer( child: AnimatedSlide(
ignoring: page != selectedPage, key: ValueKey('welcome-panel-$page'),
child: AnimatedSlide( offset: Offset(
key: ValueKey( page == selectedPage
'welcome-panel-$page', ? 0
), : page == 1
offset: Offset( ? -1
page == selectedPage : 1,
? 0 0,
: page == 1 ),
? -1 duration: revealDuration,
: 1, curve: page == selectedPage
0, ? Curves.easeOutQuad
), : Curves.easeOutCubic.flipped,
duration: revealDuration, child: SizedBox(
curve: page == selectedPage width: double.infinity,
? Curves.easeOutQuad child: actions(page),
: Curves.easeOutCubic.flipped,
child: SizedBox(
width: double.infinity,
child: actions(page),
),
), ),
), ),
), ),
), ),
], ),
), ],
), ),
), ),
), ),
], ),
), ],
), ),
], ),
), ],
), ),
); );
return PopScope( return PopScope(

View File

@@ -1,3 +1,5 @@
import 'dart:ui' as ui;
import 'package:buzz/shared/hosted/hosted_community_creation.dart'; import 'package:buzz/shared/hosted/hosted_community_creation.dart';
import 'package:buzz/shared/hosted/hosted_environment.dart'; import 'package:buzz/shared/hosted/hosted_environment.dart';
import 'package:buzz/features/onboarding/hosted_connect_page.dart'; import 'package:buzz/features/onboarding/hosted_connect_page.dart';
@@ -10,6 +12,7 @@ import 'package:buzz/shared/hosted/builderlab_account.dart';
import 'package:buzz/shared/hosted/hosted_community_connection.dart'; import 'package:buzz/shared/hosted/hosted_community_connection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/semantics.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
@@ -46,7 +49,7 @@ void main() {
), ),
); );
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await revealOptions(tester); await revealOptionsWithSemantics(tester);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
tester.view.physicalSize = const Size(420, 900); tester.view.physicalSize = const Size(420, 900);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
@@ -322,3 +325,31 @@ Future<void> revealOptions(WidgetTester tester) async {
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
} }
} }
Future<void> revealOptionsWithSemantics(WidgetTester tester) async {
final handle = tester.ensureSemantics();
try {
await tester.pump();
final wordmark = tester.getSemantics(find.bySemanticsLabel('Buzz'));
final data = wordmark.getSemanticsData();
final actionIds = data.customSemanticsActionIds!;
expect(actionIds, hasLength(1));
expect(
CustomSemanticsAction.getAction(actionIds.single)!.label,
'More connection options',
);
// The action belongs to the wordmark, never an unlabelled page-sized stop.
expect(data.label, 'Buzz');
expect(wordmark.rect.height, lessThan(200));
wordmark.owner!.performAction(
wordmark.id,
ui.SemanticsAction.customAction,
actionIds.single,
);
await tester.pumpAndSettle();
expect(find.text('Enter a community link').hitTestable(), findsOneWidget);
expect(find.text('Use a pairing code').hitTestable(), findsOneWidget);
} finally {
handle.dispose();
}
}