mirror of
https://github.com/block/buzz.git
synced 2026-11-13 21:01:53 +00:00
fix(mobile): keep hosted setup on the existing welcome surface
Co-authored-by: Brain <1a02c72794dcd0f07058a353bc3a81f4028b8c77c92c87fce6d5c8b85970a20b@buzz.block.builderlab.xyz> Signed-off-by: Wes <wesbillman@users.noreply.github.com>
This commit is contained in:
@@ -55,17 +55,14 @@ class HostedConnectPage extends HookConsumerWidget {
|
||||
onPopInvokedWithResult: (didPop, _) {
|
||||
if (!didPop) close();
|
||||
},
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Connect to Buzz'),
|
||||
leading: BackButton(onPressed: close),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(Grid.sm),
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 440),
|
||||
child: Stack(
|
||||
children: [
|
||||
ModalBarrier(color: Colors.black54, dismissible: false),
|
||||
AlertDialog(
|
||||
title: const Text('Connect to Builderlab'),
|
||||
content: SizedBox(
|
||||
width: 440,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
@@ -134,7 +131,7 @@ class HostedConnectPage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -185,7 +185,9 @@ class HostedAppGate extends HookConsumerWidget {
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
child,
|
||||
if (flow.stage == HostedFlowStage.signIn)
|
||||
if (flow.stage == HostedFlowStage.signIn &&
|
||||
authenticated &&
|
||||
!inlineEntry)
|
||||
Positioned(
|
||||
left: Grid.sm,
|
||||
right: Grid.sm,
|
||||
|
||||
@@ -27,6 +27,7 @@ class OnboardingAppEntry extends HookConsumerWidget {
|
||||
unawaited(restore());
|
||||
return null;
|
||||
}, [flow]);
|
||||
final signingIn = flow.stage == HostedFlowStage.signIn;
|
||||
final busy =
|
||||
flow.accountOnly &&
|
||||
(flow.stage == HostedFlowStage.working ||
|
||||
@@ -63,9 +64,15 @@ class OnboardingAppEntry extends HookConsumerWidget {
|
||||
existingUser: existingUser.value,
|
||||
enteringCommunityLink: enteringCommunityLink.value,
|
||||
onCommunityLink: () => enteringCommunityLink.value = true,
|
||||
onBuilderlab: busy ? null : () => unawaited(flow.signInAccount()),
|
||||
onBuilderlab: signingIn
|
||||
? () => unawaited(flow.leave())
|
||||
: busy
|
||||
? null
|
||||
: () => unawaited(flow.signInAccount()),
|
||||
accountStatus: status,
|
||||
builderlabLabel: connected
|
||||
builderlabLabel: signingIn
|
||||
? 'Cancel sign-in'
|
||||
: connected
|
||||
? 'Continue with Builderlab'
|
||||
: busy
|
||||
? 'Signing in…'
|
||||
|
||||
@@ -257,6 +257,11 @@ class BuilderlabAccountClient {
|
||||
final response = await _http
|
||||
.send(request)
|
||||
.timeout(const Duration(seconds: 30));
|
||||
if (kDebugMode && response.statusCode >= 400) {
|
||||
debugPrint(
|
||||
'Builderlab ${request.method} $path: HTTP ${response.statusCode}',
|
||||
);
|
||||
}
|
||||
if (response.statusCode == 401) throw AccountFailure.signedOut;
|
||||
if (response.statusCode == 403) throw AccountFailure.denied;
|
||||
if (const {400, 404, 409, 422}.contains(response.statusCode)) {
|
||||
|
||||
@@ -479,6 +479,10 @@ class HostedFlow extends ChangeNotifier {
|
||||
if (!current()) return;
|
||||
communities = listed;
|
||||
selectedRelays = Set.unmodifiable(listed.map((item) => item.relayUrl));
|
||||
if (listed.isEmpty) {
|
||||
await leave();
|
||||
return;
|
||||
}
|
||||
if (listed.length != 1) {
|
||||
_set(HostedFlowStage.chooseCommunities);
|
||||
return;
|
||||
@@ -510,7 +514,7 @@ class HostedFlow extends ChangeNotifier {
|
||||
communities = List.unmodifiable(
|
||||
communities.where((item) => selectedRelays.contains(item.relayUrl)),
|
||||
);
|
||||
_set(HostedFlowStage.chooseCommunities);
|
||||
await leave();
|
||||
return;
|
||||
}
|
||||
await browser.clearIntent();
|
||||
|
||||
@@ -6,8 +6,6 @@ import 'package:buzz/shared/hosted/hosted_flow.dart';
|
||||
import 'package:buzz/shared/hosted/builderlab_account.dart';
|
||||
import 'package:buzz/shared/hosted/hosted_community_connection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:buzz/shared/deeplink/deep_link.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
@@ -48,11 +46,10 @@ void main() {
|
||||
expect(f.flow.stage, HostedFlowStage.signIn);
|
||||
expect(f.browser.startedIntent, {'type': 'account'});
|
||||
expect(find.byType(HostedConnectPage), findsNothing);
|
||||
expect(find.text('Signing in…').hitTestable(), findsOneWidget);
|
||||
expect(find.text('Cancel sign-in').hitTestable(), findsOneWidget);
|
||||
expect(find.text('Enter a community link'), findsOneWidget);
|
||||
expect(find.text('Start login again').hitTestable(), findsOneWidget);
|
||||
f.expired = false;
|
||||
await tester.tap(find.text('Leave setup').hitTestable());
|
||||
await tester.tap(find.text('Cancel sign-in').hitTestable());
|
||||
await tester.pumpAndSettle();
|
||||
expect(f.flow.stage, HostedFlowStage.idle);
|
||||
await tester.pumpAndSettle();
|
||||
@@ -113,38 +110,33 @@ void main() {
|
||||
PendingDeepLinkNotifier.debugUriStreamOverride = null;
|
||||
await f.dispose();
|
||||
});
|
||||
await f.flow.signInAccount();
|
||||
await tester.pumpWidget(
|
||||
WidgetHelpers.testable(
|
||||
disableAnimations: true,
|
||||
child: HostedConnectPage(flow: f.flow, onClose: f.flow.leave),
|
||||
child: const HostedAppGate(
|
||||
authenticated: false,
|
||||
child: OnboardingAppEntry(),
|
||||
),
|
||||
overrides: [hostedFlowProvider.overrideWithValue(f.flow)],
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Create a community in Builderlab'), findsOneWidget);
|
||||
await tester.tap(find.text('Create a community in Builderlab'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(f.opened?.path, '/buzz');
|
||||
expect(find.text('Refresh communities'), findsOneWidget);
|
||||
await tester.enterText(find.byType(TextField), 'not a valid url');
|
||||
await tester.tap(find.text('Open community link'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
find.text('Enter a valid community address or invite link.'),
|
||||
findsOneWidget,
|
||||
);
|
||||
await tester.enterText(
|
||||
find.byType(TextField),
|
||||
'https://one.communities.buzz.xyz/invite/code',
|
||||
);
|
||||
await tester.testTextInput.receiveAction(TextInputAction.go);
|
||||
await tester.tap(find.text('Continue with Builderlab'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(f.flow.stage, HostedFlowStage.idle);
|
||||
final container = ProviderScope.containerOf(
|
||||
tester.element(find.byType(HostedConnectPage)),
|
||||
listen: false,
|
||||
);
|
||||
expect(container.read(pendingDeepLinkProvider), isA<InviteDeepLink>());
|
||||
expect(find.byType(HostedConnectPage), findsNothing);
|
||||
expect(find.text('Scan a QR code'), findsOneWidget);
|
||||
expect(find.text('Use pairing code'), findsOneWidget);
|
||||
f.rows = [f.row('denied')];
|
||||
f.beforeAdmission = (_) async => throw AccountFailure.denied;
|
||||
await tester.tap(find.text('Continue with Builderlab'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(AlertDialog), findsOneWidget);
|
||||
expect(find.text('Scan a QR code'), findsOneWidget);
|
||||
await tester.tap(find.text('Leave setup').hitTestable());
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(AlertDialog), findsNothing);
|
||||
expect(find.text('Scan a QR code'), findsOneWidget);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -154,19 +154,15 @@ void main() {
|
||||
expect(f.flow.selectedRelays, isEmpty);
|
||||
});
|
||||
|
||||
test(
|
||||
'zero communities can open creation and refresh into single entry',
|
||||
() async {
|
||||
final f = await fixture();
|
||||
await f.flow.signInAccount();
|
||||
expect(f.flow.stage, HostedFlowStage.chooseCommunities);
|
||||
await f.flow.openCommunityCreation();
|
||||
expect(f.opened, Uri.parse('https://app.builderlab.xyz/buzz'));
|
||||
f.rows = [f.row('created')];
|
||||
await f.flow.refreshCommunities();
|
||||
expect(f.admitted, ['created']);
|
||||
},
|
||||
);
|
||||
test('zero communities return to welcome and allow reconnect', () async {
|
||||
final f = await fixture();
|
||||
await f.flow.signInAccount();
|
||||
expect(f.flow.stage, HostedFlowStage.idle);
|
||||
expect(f.account.account, isNotNull);
|
||||
f.rows = [f.row('created')];
|
||||
await f.flow.signInAccount();
|
||||
expect(f.admitted, ['created']);
|
||||
});
|
||||
|
||||
test('identity mismatch blocks list and preserves saved key', () async {
|
||||
final f = await fixture();
|
||||
@@ -283,23 +279,20 @@ void main() {
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'single conflicting community can be skipped then creation refreshed',
|
||||
() async {
|
||||
final f = await fixture();
|
||||
f.rows = [f.row('conflict')];
|
||||
f.beforeAdmission = (_) async =>
|
||||
throw const SavedCommunityIdentityConflict();
|
||||
await f.flow.signInAccount();
|
||||
await f.flow.skipConflictingCommunity();
|
||||
expect(f.flow.stage, HostedFlowStage.chooseCommunities);
|
||||
expect(f.flow.communities, isEmpty);
|
||||
f.rows = [f.row('created')];
|
||||
f.beforeAdmission = null;
|
||||
await f.flow.refreshCommunities();
|
||||
expect(f.admitted, ['created']);
|
||||
},
|
||||
);
|
||||
test('single conflicting community can be skipped back to welcome', () async {
|
||||
final f = await fixture();
|
||||
f.rows = [f.row('conflict')];
|
||||
f.beforeAdmission = (_) async =>
|
||||
throw const SavedCommunityIdentityConflict();
|
||||
await f.flow.signInAccount();
|
||||
await f.flow.skipConflictingCommunity();
|
||||
expect(f.flow.stage, HostedFlowStage.idle);
|
||||
expect(f.flow.communities, isEmpty);
|
||||
f.rows = [f.row('created')];
|
||||
f.beforeAdmission = null;
|
||||
await f.flow.signInAccount();
|
||||
expect(f.admitted, ['created']);
|
||||
});
|
||||
|
||||
test('unsupported rows are skipped and wss prefix canonicalized', () async {
|
||||
final f = await fixture();
|
||||
|
||||
Reference in New Issue
Block a user