Files
buzz/mobile/lib/features/onboarding/hosted_community_selection.dart
klopez4212 f3a7bab098 Clarify mobile community onboarding and saved identity selection (#8729)
## Summary

Make community onboarding easier to scan: shorten hosted hostname
fallbacks, clarify Add Community instructions, and replace raw saved-key
buttons with identity cards. Use white pages, grey cards, ghost
secondary actions, standard page transitions, and a “Getting ready”
screen during joining. Native profile setup keeps a horizontal modal
transition. Identity cards load signed name/avatar metadata from saved
communities without switching accounts; unavailable profiles use a
neutral fallback. Cards show only the avatar, a 16px medium-weight name,
and muted npub; community lists are omitted. Retiring profile setup
closes its avatar editor too.

### Related issue

Follow-up to #8687 and #8599; no duplicate PR found.

### Testing

- Formatting and analysis passed. Full suite with hosted-auth defines:
4,019 passed, 5 skipped; includes profile-fetch and cancellation
regressions.
- Prior iOS device preview and native profile tests passed; hosted CI
native Swift checks passed on the preceding head. Latest Dart fixes are
not yet reinstalled on the phone.
- Simulator walkthrough and owner-tested iPhone preview use sample
identities and simulated admission.

Sample-data previews; the identity snapshot shows the
unavailable-profile fallback rendered by Flutter. Media is attached here
only.

| Community picker | Add Community | Saved identity |
| --- | --- | --- |
| ![Shortened community
name](https://github.com/user-attachments/assets/cf0af6aa-3a3d-4ffe-b469-e26f9bb92c30)
| ![White Add Community
page](https://github.com/user-attachments/assets/b2f71c77-1aa0-465a-b302-7eb5f1e92819)
| ![Saved identity card and ghost import
action](https://github.com/user-attachments/assets/47f20f34-f047-4ce5-befa-f56834096e95)
|

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
2026-10-30 19:00:42 +00:00

96 lines
2.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../../shared/hosted/hosted_flow.dart';
import '../../shared/hosted/hosted_environment.dart';
import '../../shared/theme/grid.dart';
import '../../shared/community/community_grid.dart';
import '../../shared/community/community_grid_option.dart';
import '../../shared/community/community_grid_add.dart';
import '../../onboarding_app_entry.dart';
import '../../shared/widgets/onboarding_step_page.dart';
/// Community selection is completed before admission reveals Home.
class HostedCommunitySelection extends HookConsumerWidget {
const HostedCommunitySelection({
super.key,
required this.flow,
this.onAddCommunity,
this.fullScreen = false,
this.onBack,
});
final HostedFlow flow;
final VoidCallback? onAddCommunity;
final bool fullScreen;
final VoidCallback? onBack;
@override
Widget build(BuildContext context, WidgetRef ref) {
useListenable(flow);
Future<void> addCommunity() async {
final navigator = Navigator.of(context, rootNavigator: true);
if (!await flow.prepareToAddCommunity() || !navigator.mounted) return;
late final MaterialPageRoute<void> route;
var exiting = false;
Future<void> exit() async {
if (exiting) return;
exiting = true;
await flow.signInAccount();
if (flow.stage == HostedFlowStage.signIn ||
flow.stage == HostedFlowStage.idle) {
exiting = false;
return;
}
if (route.isActive) route.navigator?.removeRoute(route);
}
route = MaterialPageRoute<void>(
builder: (_) => OnboardingAppEntry(
addingCommunity: true,
restoreAccount: false,
onExit: exit,
),
);
navigator.push(route);
// Mount the opaque entry route before retiring the setup cover.
WidgetsBinding.instance.addPostFrameCallback((_) {
if (route.isActive) flow.leave();
});
}
final choices = CommunityGrid(
children: [
for (final community in flow.communities)
CommunityGridOption(
id: community.relayUrl,
name: HostedEnvironment.communityDisplayName(
community.name,
community.relayUrl,
),
relayUrl: community.relayUrl,
onSelect: (_) async {
flow.chooseLandingCommunity(community.relayUrl);
await flow.addSelectedCommunities();
},
),
CommunityGridAdd(onTap: onAddCommunity ?? addCommunity),
],
);
if (fullScreen) {
return OnboardingStepPage(
title: 'Select a community',
onBack: onBack ?? () {},
content: choices,
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
choices,
const SizedBox(height: Grid.sm),
],
);
}
}