feat: adopt smolmail protocol 1.1, adaptive nav shell, and server mail retention
This commit is contained in:
parent
eaaa3f2ede
commit
c693e6fcb9
27 changed files with 1190 additions and 592 deletions
|
|
@ -24,6 +24,34 @@ class ContactDetailScreen extends ConsumerStatefulWidget {
|
|||
|
||||
class _ContactDetailScreenState extends ConsumerState<ContactDetailScreen> {
|
||||
bool resolving = false;
|
||||
bool tokenBusy = false;
|
||||
|
||||
// §5.8: admitting or withdrawing a contact's accept token pushes the
|
||||
// change to the server right away, since it only takes effect once the
|
||||
// server holds the changed set.
|
||||
Future<void> _toggleAccepted(bool currentlyAccepted) async {
|
||||
final client = ref.read(clientProvider);
|
||||
setState(() => tokenBusy = true);
|
||||
try {
|
||||
if (currentlyAccepted) {
|
||||
await client.blockContact(widget.address);
|
||||
} else {
|
||||
await client.acceptContact(widget.address);
|
||||
}
|
||||
ref.read(revisionProvider.notifier).bump();
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(currentlyAccepted
|
||||
? "${widget.address} blocked; their mail now lands in requests"
|
||||
: "${widget.address} accepted; their mail now lands in your main tier")),
|
||||
);
|
||||
}
|
||||
} on SmolError catch (err) {
|
||||
if (mounted) showErrorSnackBar(context, err.message);
|
||||
} finally {
|
||||
if (mounted) setState(() => tokenBusy = false);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _reResolve() async {
|
||||
final client = ref.read(clientProvider);
|
||||
|
|
@ -54,7 +82,11 @@ class _ContactDetailScreenState extends ConsumerState<ContactDetailScreen> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final contact = ref.watch(storeProvider).contact(widget.address);
|
||||
ref.watch(revisionProvider);
|
||||
final store = ref.read(storeProvider);
|
||||
final contact = store.contact(widget.address);
|
||||
final accepted = store.accepted(widget.address);
|
||||
final isAccepted = accepted != null && accepted.active;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(widget.address)),
|
||||
|
|
@ -69,6 +101,29 @@ class _ContactDetailScreenState extends ConsumerState<ContactDetailScreen> {
|
|||
? "verified key"
|
||||
: "key pinned on first use"),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
"Accept token (SPEC.md §5.8)",
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
isAccepted
|
||||
? "This contact's mail lands in your main tier."
|
||||
: "This contact's mail lands in requests until accepted.",
|
||||
style: Theme.of(context).textTheme.labelSmall,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
tokenBusy
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: TextButton.icon(
|
||||
onPressed: () => _toggleAccepted(isAccepted),
|
||||
icon: Icon(isAccepted ? Icons.block : Icons.check_circle_outline,
|
||||
size: 18),
|
||||
label: Text(isAccepted ? "Block" : "Accept"),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const Divider(height: 1),
|
||||
const SizedBox(height: 20),
|
||||
_field(context, "fingerprint", fingerprint(contact.key)),
|
||||
_field(context, "public key", b32encode(contact.key)),
|
||||
Row(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue