kirakira/lib/presentation/screens/compose_screen.dart

128 lines
3.9 KiB
Dart
Raw Permalink Normal View History

import "package:auto_route/auto_route.dart";
import "package:flutter/material.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:smol_mail/data/providers/providers.dart";
import "package:smol_mail/presentation/widgets/primary_button.dart";
import "package:smol_mail/presentation/widgets/small_loading_spinner.dart";
import "package:smol_mail/presentation/theme/app_colors.dart";
import "package:smol_mail/shared/utils/snackbar.dart";
import "package:smol_mail/smol/errors.dart";
/// One tap seals one envelope; the button disables while the send is in
/// flight so a second tap cannot seal a second message.
@RoutePage()
class ComposeScreen extends ConsumerStatefulWidget {
final String? to;
final String? subject;
const ComposeScreen({
super.key,
this.to,
this.subject,
});
@override
ConsumerState<ComposeScreen> createState() => _ComposeScreenState();
}
class _ComposeScreenState extends ConsumerState<ComposeScreen> {
late final toController = TextEditingController(text: widget.to ?? "");
late final subjectController =
TextEditingController(text: widget.subject ?? "");
final bodyController = TextEditingController();
bool anonymous = false;
bool busy = false;
@override
void dispose() {
toController.dispose();
subjectController.dispose();
bodyController.dispose();
super.dispose();
}
Future<void> _send() async {
final client = ref.read(clientProvider);
setState(() => busy = true);
try {
final to = await client.send(
toController.text,
subjectController.text,
bodyController.text,
anonymous: anonymous,
);
ref.read(revisionProvider.notifier).bump();
if (mounted) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text("sent to $to")));
AutoRouter.of(context).pop();
}
} on SmolError catch (err) {
if (mounted) {
showErrorSnackBar(context, err.message);
}
} finally {
if (mounted) setState(() => busy = false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Write")),
body: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: toController,
decoration: InputDecoration(
labelText: "To",
hintText: "user@host[:1961] or smol://user@host/key",
filled: true,
fillColor: Theme.of(context).extension<AppColors>()!.cardFill,
),
),
const SizedBox(height: 15),
TextField(
controller: subjectController,
decoration: InputDecoration(
labelText: "Subject",
filled: true,
fillColor: Theme.of(context).extension<AppColors>()!.cardFill,
),
),
const SizedBox(height: 15),
TextField(
controller: bodyController,
maxLines: 12,
decoration: InputDecoration(
labelText: "Body",
filled: true,
fillColor: Theme.of(context).extension<AppColors>()!.cardFill,
),
),
SwitchListTile(
value: anonymous,
onChanged: (value) => setState(() => anonymous = value),
title: Text(
"Anonymous (omit the signed Reply-To)",
style: Theme.of(context).textTheme.bodySmall,
),
),
const SizedBox(height: 10),
Center(
child: PrimaryButton(
onPressed: busy ? () {} : _send,
child: busy ? const SmallLoadingSpinner() : const Text("Send"),
),
),
],
),
),
);
}
}