feat: replace mail protocol with smolmail and rebuild the UI
This commit is contained in:
parent
cb24b68069
commit
e94e4158cc
93 changed files with 6387 additions and 3479 deletions
|
|
@ -1,69 +1,128 @@
|
|||
import "dart:typed_data";
|
||||
|
||||
import "package:auto_route/auto_route.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
|
||||
import "package:flash_mail/data/providers/providers.dart";
|
||||
import "package:flash_mail/presentation/widgets/image_banner.dart";
|
||||
import "package:flash_mail/presentation/widgets/message/message_view.dart";
|
||||
import "package:smol_mail/data/providers/providers.dart";
|
||||
import "package:smol_mail/presentation/widgets/image_banner.dart";
|
||||
import "package:smol_mail/presentation/widgets/message/message_view.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";
|
||||
|
||||
/// Fetched mail is acknowledged off the server during fetch, so deleting here
|
||||
/// removes only the local sealed copy.
|
||||
@RoutePage()
|
||||
class MessageDetailScreen extends ConsumerWidget {
|
||||
final String folder;
|
||||
final String id;
|
||||
final String subject;
|
||||
|
||||
const MessageDetailScreen({
|
||||
super.key,
|
||||
required this.folder,
|
||||
required this.id,
|
||||
required this.subject,
|
||||
});
|
||||
|
||||
void deleteMessage(BuildContext context, WidgetRef ref) async {
|
||||
await ref.read(clientProvider).deleteMessage(id);
|
||||
ref.invalidate(messagesProvider);
|
||||
Future<void> _delete(BuildContext context, WidgetRef ref) async {
|
||||
await ref.read(storeProvider).deleteMessage(folder, id);
|
||||
ref.read(revisionProvider.notifier).bump();
|
||||
if (context.mounted) {
|
||||
AutoRouter.of(context).pop();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _nameSender(BuildContext context, WidgetRef ref,
|
||||
Uint8List senderKey) async {
|
||||
final client = ref.read(clientProvider);
|
||||
final controller = TextEditingController();
|
||||
final address = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
backgroundColor: Theme.of(context).extension<AppColors>()!.cardFill,
|
||||
content: TextField(
|
||||
controller: controller,
|
||||
decoration: InputDecoration(
|
||||
hintText: "user@host or smol://…",
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(controller.text),
|
||||
child: const Text("Save"),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (address == null || address.trim().isEmpty) return;
|
||||
try {
|
||||
await client.nameSender(address, senderKey);
|
||||
ref.read(revisionProvider.notifier).bump();
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text("Address saved for this sender")),
|
||||
);
|
||||
}
|
||||
} on SmolError catch (err) {
|
||||
if (context.mounted) {
|
||||
showErrorSnackBar(context, err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final message = ref.watch(messageDetailProvider(id));
|
||||
final record = ref.watch(storeProvider).getMessage(folder, id);
|
||||
if (record == null) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: const ImageBanner(
|
||||
imgSrc: "assets/images/error.png",
|
||||
text: "Message not found!",
|
||||
),
|
||||
);
|
||||
}
|
||||
final client = ref.read(clientProvider);
|
||||
final opened = client.describe(record);
|
||||
final isSent = folder == "sent";
|
||||
final who = isSent
|
||||
? (record.recipient ?? "")
|
||||
: (opened.sender == null
|
||||
? ""
|
||||
: ref.watch(storeProvider).addressForKey(opened.sender!) ?? "");
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: who.isEmpty
|
||||
? null
|
||||
: Text(
|
||||
who,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontFamily: "Inter",
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 18,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () => deleteMessage(context, ref),
|
||||
icon: const Icon(Icons.delete, color: Colors.white),
|
||||
)
|
||||
tooltip: "Delete local copy",
|
||||
onPressed: () => _delete(context, ref),
|
||||
icon: const Icon(Icons.delete),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
subject,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyLarge!
|
||||
.copyWith(fontSize: 20),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
message.when(
|
||||
data: (data) => MessageView(message: data),
|
||||
error: (err, _) => const ImageBanner(
|
||||
imgSrc: "assets/images/error.png",
|
||||
text: "Something went wrong!",
|
||||
),
|
||||
loading: () => const SizedBox.shrink(),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
child: MessageView(
|
||||
record: record,
|
||||
opened: opened,
|
||||
onNameSender: opened.sender == null
|
||||
? null
|
||||
: (key) => _nameSender(context, ref, key),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue