mirror of
https://github.com/block/buzz.git
synced 2026-11-02 15:31:27 +00:00
## 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 | | --- | --- | --- | |  |  |  | --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
101 lines
3.5 KiB
Dart
101 lines
3.5 KiB
Dart
/// Trusted build configuration, never supplied by a deep link or relay.
|
|
abstract final class HostedEnvironment {
|
|
/// Account UI and startup work are opt-in, independently of the backend URL.
|
|
static bool get enabled => authDomains.isNotEmpty;
|
|
|
|
static final Uri origin = _origin();
|
|
static const _communityAccounts = {
|
|
'communities.buzz.xyz': 'https://app.builderlab.xyz',
|
|
'communities.public.builderlabstaging.xyz':
|
|
'https://app.builderlabstaging.xyz',
|
|
};
|
|
|
|
/// Shortens hostname fallbacks for display without changing relay identity.
|
|
static String communityDisplayName(String name, String relayUrl) {
|
|
final relay = Uri.tryParse(relayUrl);
|
|
if (relay == null ||
|
|
name != relay.host ||
|
|
accountOriginFor(relay) == null) {
|
|
return name;
|
|
}
|
|
for (final domain in _communityAccounts.keys) {
|
|
final suffix = '.$domain';
|
|
if (name.endsWith(suffix)) {
|
|
return name.substring(0, name.length - suffix.length);
|
|
}
|
|
}
|
|
return name;
|
|
}
|
|
|
|
/// Creation and admission use the same trusted build-specific host mapping.
|
|
static String get communitySuffix => _communityAccounts.entries
|
|
.singleWhere((entry) => Uri.parse(entry.value) == origin)
|
|
.key;
|
|
|
|
/// Comma-separated DNS domains requiring Builderlab authentication.
|
|
/// Empty builds keep Nostr-only entry available, including hosted QR pairing.
|
|
static final List<String> authDomains = List.unmodifiable(
|
|
const String.fromEnvironment('BUZZ_AUTH_DOMAINS')
|
|
.split(',')
|
|
.map(
|
|
(domain) =>
|
|
domain.trim().toLowerCase().replaceFirst(RegExp(r'\.+$'), ''),
|
|
)
|
|
.where((domain) => domain.isNotEmpty)
|
|
.map((domain) {
|
|
if (!RegExp(
|
|
r'^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*$',
|
|
).hasMatch(domain)) {
|
|
throw StateError(
|
|
'BUZZ_AUTH_DOMAINS must contain DNS names, not URLs or wildcards',
|
|
);
|
|
}
|
|
return domain;
|
|
}),
|
|
);
|
|
|
|
/// Matches a configured domain and its subdomains, never a string suffix.
|
|
static bool requiresAuth(String host) {
|
|
final normalized = host.toLowerCase().replaceFirst(RegExp(r'\.+$'), '');
|
|
return authDomains.any(
|
|
(domain) => normalized == domain || normalized.endsWith('.$domain'),
|
|
);
|
|
}
|
|
|
|
/// Explicit community-to-account mapping. Never infer an account endpoint
|
|
/// from arbitrary relay metadata or send this build's session across realms.
|
|
static Uri? accountOriginFor(Uri relay) {
|
|
if (!['wss', 'https'].contains(relay.scheme) ||
|
|
relay.userInfo.isNotEmpty ||
|
|
relay.hasQuery ||
|
|
relay.hasFragment ||
|
|
(relay.path.isNotEmpty && relay.path != '/') ||
|
|
(relay.hasPort && relay.port != 443)) {
|
|
return null;
|
|
}
|
|
final host = relay.host.toLowerCase();
|
|
for (final entry in _communityAccounts.entries) {
|
|
if (host.endsWith('.${entry.key}')) return Uri.parse(entry.value);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// Whether this build can use its account session for this relay.
|
|
static bool supportsRelay(Uri relay) => accountOriginFor(relay) == origin;
|
|
|
|
static Uri _origin() {
|
|
const configured = String.fromEnvironment(
|
|
'BUZZ_BUILDERLAB_URL',
|
|
defaultValue: 'https://app.builderlab.xyz',
|
|
);
|
|
final uri = Uri.parse(configured);
|
|
if (!{
|
|
'https://app.builderlab.xyz',
|
|
'https://app.builderlabstaging.xyz',
|
|
}.contains(configured)) {
|
|
throw StateError('Unsupported hosted account environment');
|
|
}
|
|
return uri;
|
|
}
|
|
}
|