fix(webmail): corrige os contadores das pastas e as ações do leitor
Todos estes foram reproduzidos na interface real, com a conta em
mail.lucasrguerra.dev.br, e não em teste.
Contadores das pastas. O badge caía para o total sempre que não havia
nada por ler, no mesmo lugar e no mesmo estilo do contador de não
lidas: uma caixa de entrada com catorze mensagens lidas mostrava "14",
e ler mais uma nunca movia o número - ele nunca esteve contando o que
o leitor pensava. Os dois números agora são visualmente distintos e o
de não lidas só aparece quando existe mensagem por ler.
Em Rascunhos o badge e o total são o mesmo número por definição, então
o sidebar imprimia-o duas vezes: "Rascunhos 1 1". A regra passou para
folderBadge, onde é testada, e o tamanho da pasta é renderizado num
único sítio.
Ações do leitor. Todas as mensagens ofereciam os mesmos quatro botões,
onde a pasta devia decidir. Um e-mail que o próprio utilizador enviou
oferecia "marcar como não lida" - uma flag que nada lê numa pasta onde
nada é entregue - e um rascunho por acabar oferecia Responder e
Encaminhar, mas nenhuma forma de o terminar. Abrir um rascunho agora
abre o compositor com o que estava escrito e adota o seu UID, para que
continuar a escrever substitua esse rascunho em vez de deixar o antigo
para trás. Em Lixeira o botão de apagar diz que apaga em definitivo,
porque é o que faz.
Anexos. t("mail.attachments") era chamado sem o parâmetro que a string
declara, e a interface mostrava literalmente "Anexos ({count})" nos
três idiomas. Na lista, que sabe que há anexos mas não quantos, passa a
nomear o facto em vez de imprimir uma contagem que não tem.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1dc87a06c8
commit
203316f2c3
9 files changed
+320
-46
No files matched your search
@@ -120,3 +120,77 @@ export function listHeaderCount(input: {
|
||||
export function applySeen<T extends MessageSummary>(messages: T[], uid: number, seen: boolean): T[] {
|
||||
return messages.map((m) => (m.uid === uid ? { ...m, seen } : m));
|
||||
}
|
||||
|
||||
/** What the sidebar shows beside one folder. */
|
||||
export interface FolderBadge {
|
||||
/** How many unread messages the folder holds. */
|
||||
unread: number;
|
||||
/** How many messages it holds in total. */
|
||||
total: number;
|
||||
/**
|
||||
* Whether the unread number is worth showing at all.
|
||||
*
|
||||
* Kept apart from the number itself because the sidebar used to fall back to
|
||||
* printing the total in the unread badge's place whenever nothing was
|
||||
* unread. An inbox holding fourteen read messages therefore showed "14", and
|
||||
* reading another message never moved it - the number was never counting
|
||||
* what the user thought it was.
|
||||
*/
|
||||
showsUnread: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* The two numbers a sidebar folder has, and which of them to emphasise.
|
||||
*
|
||||
* Drafts deliberately has no unread badge: nothing marks a draft read, so its
|
||||
* unread count is always zero and the number that matters there is how many
|
||||
* unfinished messages are waiting - which is the total. Returning showsUnread
|
||||
* false for it is what stops the sidebar rendering that same total twice, once
|
||||
* as the size and once as the badge, which is where "Rascunhos 1 1" came from.
|
||||
*/
|
||||
export function folderBadge(
|
||||
folders: FolderSummary[] | undefined | null,
|
||||
route: string,
|
||||
): FolderBadge {
|
||||
const { unread, total } = folderMetrics(folders, route);
|
||||
const isDrafts = route.trim().toLowerCase() === "drafts";
|
||||
return { unread, total, showsUnread: !isDrafts && unread > 0 };
|
||||
}
|
||||
|
||||
/** Which actions a message offers, decided by the folder it is in. */
|
||||
export interface ReaderActions {
|
||||
canReply: boolean;
|
||||
canForward: boolean;
|
||||
/** Reopen an unfinished message in the composer. */
|
||||
canEdit: boolean;
|
||||
canMarkUnread: boolean;
|
||||
canDelete: boolean;
|
||||
/** True in Trash, where delete destroys rather than moves. */
|
||||
deleteIsPermanent: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* The actions that make sense for a message, given where it lives.
|
||||
*
|
||||
* The reader offered the same four buttons everywhere, so a message the user
|
||||
* had sent themselves could be "marked unread" - a flag nothing reads on a
|
||||
* folder nothing delivers to - and a half-written draft offered Reply and
|
||||
* Forward but no way to carry on writing it. Meanwhile delete was offered
|
||||
* nowhere at all.
|
||||
*/
|
||||
export function readerActions(folder: string): ReaderActions {
|
||||
const role = folder.trim().toLowerCase();
|
||||
const isDrafts = role === "drafts";
|
||||
const isOwnCopy = isDrafts || role === "sent";
|
||||
|
||||
return {
|
||||
canReply: !isDrafts,
|
||||
canForward: !isDrafts,
|
||||
canEdit: isDrafts,
|
||||
// Sent mail and drafts are the user's own copies. Nothing ever delivers
|
||||
// to those folders, so an unread flag there conveys nothing.
|
||||
canMarkUnread: !isOwnCopy,
|
||||
canDelete: true,
|
||||
deleteIsPermanent: role === "trash",
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user