feat: replace mail protocol with smolmail and rebuild the UI

This commit is contained in:
randogoth 2026-09-26 22:45:38 +03:00
parent cb24b68069
commit e94e4158cc
93 changed files with 6387 additions and 3479 deletions

View file

@ -1,77 +1,101 @@
import "package:auto_route/auto_route.dart";
import "package:flutter/material.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:flash_mail/data/models/message/message.dart";
import "package:flash_mail/presentation/routes/app_router.gr.dart";
import "package:flash_mail/shared/utils/random_color.dart";
import "package:smol_mail/data/providers/providers.dart";
import "package:smol_mail/presentation/routes/app_router.gr.dart";
import "package:smol_mail/presentation/theme/app_colors.dart";
import "package:smol_mail/shared/utils/avatar_color.dart";
import "package:smol_mail/shared/utils/format.dart";
import "package:smol_mail/smol/proto.dart";
import "package:smol_mail/smol/store.dart";
class MessageTile extends StatefulWidget {
final TMMessage message;
class MessageTile extends ConsumerWidget {
final MailRecord record;
const MessageTile({super.key, required this.message});
const MessageTile({super.key, required this.record});
@override
State<MessageTile> createState() => _MessageTileState();
}
Widget build(BuildContext context, WidgetRef ref) {
final folder = ref.watch(folderProvider);
final store = ref.watch(storeProvider);
final opened = ref.read(clientProvider).describe(record);
final appColors = Theme.of(context).extension<AppColors>()!;
class _MessageTileState extends State<MessageTile> {
late Color randomColor;
String who;
if (folder == "sent") {
who = record.recipient ?? "";
} else if (opened.sender != null) {
final known = store.addressForKey(opened.sender!);
who = known ?? "unknown · ${b32encode(opened.sender!).substring(0, 4)}";
} else {
who = "?";
}
final unread = folder == "inbox" && !store.isRead(record.id);
final errorText = opened.error != null ? "<unreadable>" : null;
final subject =
errorText ?? (opened.subject.isEmpty ? "(no subject)" : opened.subject);
final snippet = errorText == null
? opened.body.replaceAll(RegExp(r"\s+"), " ").trim()
: "";
final avatarColor = colorForKey(who, appColors.avatarPalette);
@override
void initState() {
super.initState();
randomColor = getRandomColor();
}
@override
Widget build(BuildContext context) {
return ListTile(
splashColor: const Color.fromRGBO(40, 70, 100, 1),
focusColor: const Color.fromRGBO(22, 39, 56, 1),
onTap: () => AutoRouter.of(context).push(
MessageDetailRoute(
id: widget.message.id,
subject: widget.message.subject,
),
),
splashColor: appColors.highlight,
focusColor: appColors.cardFill,
onTap: () {
if (folder == "inbox") {
store.markRead(record.id);
ref.read(revisionProvider.notifier).bump();
}
AutoRouter.of(context).push(
MessageDetailRoute(folder: folder, id: record.id),
);
},
leading: CircleAvatar(
radius: 25,
backgroundColor: randomColor,
backgroundColor: avatarColor,
child: Text(
widget.message.from["name"]!.toUpperCase()[0],
who.isEmpty ? "?" : who[0].toUpperCase(),
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
color: Colors.black,
color: contrastingTextColor(avatarColor),
fontWeight: FontWeight.w500,
),
),
),
title: Text(
widget.message.from["name"] as String,
who,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
fontWeight:
widget.message.seen ? FontWeight.w500 : FontWeight.bold,
fontWeight: unread ? FontWeight.bold : FontWeight.w500,
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.message.subject,
subject,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight:
widget.message.seen ? FontWeight.w300 : FontWeight.w500,
fontWeight: unread ? FontWeight.w500 : FontWeight.w300,
),
),
Text(
widget.message.intro ?? widget.message.subject,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: const Color.fromRGBO(193, 203, 213, 1),
),
)
if (snippet.isNotEmpty)
Text(
snippet.length > 140 ? snippet.substring(0, 140) : snippet,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: appColors.snippetText,
),
)
],
),
trailing: Text(
formatShortTime(folder == "sent" ? record.sentAt : opened.time),
style: Theme.of(context).textTheme.labelSmall,
),
);
}
}