mirror of
https://github.com/block/buzz.git
synced 2026-11-07 01:41:06 +00:00
An empty Bestie slot cycles still posters from Fuzzies, Gloopies and Figgies every five seconds, without creating video players. The next poster is precached; reduced motion and hidden-screen pauses are preserved. The launcher uses the neutral “Bestie” label until a selection is available, then “Chat with …”. For supported communities with no owned agents, tapping opens Create agent directly. A successful first-agent setup saves that agent as Bestie without replacing an existing preference; existing-agent and unsupported-community paths keep the Agents list. Verified Builderlab registrations can show sleeping remote agents as Online. Owner-scoped saved runtime availability now prevents a known absent or deleted runtime from returning Online after Builderlab sign-out or a fresh load, even when another community retains its registration. Validation: mobile static checks; 4,274 Flutter tests passed with hosted flows enabled; launcher → cancel → create → Home “Chat with Scout”, including encrypted persisted selection; sign-out, fresh-load, owner isolation and deletion regressions. Restoring the old carousel produced three video-player requests; removing the saved-availability guard reproduced incorrect Online after sign-out. The earlier iPhone recording below shows the original carousel and entry flow and stops before creation. The new poster-only behavior and completed creation are covered by widget tests, not a new device recording. https://github.com/user-attachments/assets/94fcff72-967a-44c2-99d5-27c715e196a2 --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
71 lines
2.0 KiB
Dart
71 lines
2.0 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:nostr/nostr.dart' as nostr;
|
|
|
|
import '../crypto/nip44.dart';
|
|
import '../relay/relay.dart';
|
|
import 'default_agent.dart';
|
|
import 'me_sync.dart';
|
|
|
|
/// Publishes and confirms the owner's encrypted, community-scoped choice.
|
|
/// Failures propagate to the selection UI so the user can retry.
|
|
Future<void> saveDefaultAgent(
|
|
MeSync sync,
|
|
String? pubkey, {
|
|
bool onlyIfUnset = false,
|
|
}) async {
|
|
if (pubkey != null && !RegExp(r'^[0-9a-f]{64}$').hasMatch(pubkey)) {
|
|
throw const FormatException('Invalid agent public key');
|
|
}
|
|
sync.check();
|
|
final filter = NostrFilter(
|
|
kinds: const [30078],
|
|
authors: [sync.pubkey],
|
|
tags: {
|
|
'#d': [defaultAgentCoordinate(sync.community)],
|
|
},
|
|
limit: 1,
|
|
);
|
|
final previous = await sync.session.fetchHistory(filter);
|
|
sync.check();
|
|
final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
|
for (final event in previous) {
|
|
decodeDefaultAgent(event, sync);
|
|
if (onlyIfUnset) return;
|
|
if (event.createdAt >= now) {
|
|
throw StateError('Please wait a second before saving again.');
|
|
}
|
|
}
|
|
final event = NostrEvent.fromJson(
|
|
nostr.Event.from(
|
|
kind: 30078,
|
|
content: nip44Encrypt(
|
|
sync.key,
|
|
jsonEncode({'version': 1, 'agent': pubkey}),
|
|
),
|
|
secretKey: sync.privateHex,
|
|
createdAt: now,
|
|
tags: [
|
|
['d', defaultAgentCoordinate(sync.community)],
|
|
['t', defaultAgentTag],
|
|
],
|
|
verify: false,
|
|
).toMap(),
|
|
);
|
|
await sync.session.publish(event);
|
|
sync.check();
|
|
final confirmed = await sync.session.fetchHistory(filter);
|
|
sync.check();
|
|
if (confirmed.isEmpty || confirmed.first.id != event.id) {
|
|
throw StateError('The Bestie changed on another device. Try again.');
|
|
}
|
|
final prefs = sync.prefs;
|
|
if (prefs != null &&
|
|
!await prefs.setString(
|
|
'main-agent:${sync.community}:${sync.pubkey}',
|
|
jsonEncode(event.toJson()),
|
|
)) {
|
|
throw StateError('Bestie saved, but offline storage failed. Try again.');
|
|
}
|
|
}
|