fix(webmail): acertos de interface e mover mensagens entre pastas
Cc/Cco. Os dois rótulos do compositor eram o último texto em inglês que restava, e "Bcc" tem nome próprio em português - quem não conhece a abreviatura inglesa não adivinha o que o campo faz. Larguras. A barra lateral tinha 244px fixos e nela não cabia "Console de administração", que transbordava no telemóvel e no computador: o botão passa a mostrar um rótulo curto e guarda o nome completo no tooltip. O endereço da conta usava break-words e partia-se a meio do domínio, deixando um "br" sozinho na linha de baixo; passa a truncar com o valor inteiro no tooltip. A lista tinha 404px fixos e o leitor 760px centrados, portanto num ecrã largo os assuntos eram espremidos numa coluna estreita enquanto o leitor ficava entre duas margens enormes. Ambos crescem agora com a janela. O assunto ganha overflow-wrap: os Message-ID dos relatórios do Google são uma única "palavra" sem espaços, mais longa que a coluna, e sem sítio onde quebrar saíam da linha. Imagens remotas. A escolha era reposta sempre que uma mensagem era aberta, por isso a mesma newsletter tinha de ser desbloqueada em cada leitura. A decisão passa a ser lembrada por remetente, com um segundo botão para a retirar. Bloqueado continua a ser o que acontece por omissão: uma imagem remota é um aviso de leitura que o remetente não pediu licença para receber. Assinatura. Era escrita num textarea - texto simples com quebras de linha reais - e injetada no compositor com innerHTML, onde uma quebra de linha é espaço em branco: todas desapareciam. Passa a ser um campo rico, com negrito, itálico, links e imagens, guardado como HTML e sanitizado nos dois sentidos, já que este valor é injetado como marcação na origem do webmail. Assinaturas guardadas pelo campo antigo continuam a funcionar: as suas quebras de linha são convertidas. Mover entre pastas. Faltava a única ação corrente que o leitor não tinha: arquivar uma mensagem para fora da caixa de entrada era impossível. Enviados e Rascunhos nunca são destino - descrevem como a mensagem veio a existir, não onde o leitor a arrumou - mas sair deles é permitido. Pastas criadas pelo utilizador, que não têm papel special-use, são destino como qualquer outra. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ac0521cb9b
commit
f179cb1f27
20 files changed
+911
-37
No files matched your search
@@ -299,6 +299,28 @@ func (r *WebmailRepository) Expunge(
|
||||
// Trash itself is Expunge's job, not this one's.
|
||||
func (r *WebmailRepository) MoveToTrash(
|
||||
ctx context.Context, mailboxID, folderName string, uid uint32,
|
||||
) (uint32, error) {
|
||||
return r.moveInto(ctx, mailboxID, folderName, uid, trashTarget)
|
||||
}
|
||||
|
||||
// MoveToFolder files one message into a named folder.
|
||||
//
|
||||
// The target is matched on its special-use role or its name, the same way the
|
||||
// rest of this repository addresses folders, so "archive" and "Archive" both
|
||||
// reach the same place and a folder the user created themselves - which has no
|
||||
// role - is reachable by name.
|
||||
func (r *WebmailRepository) MoveToFolder(
|
||||
ctx context.Context, mailboxID, folderName string, uid uint32, target string,
|
||||
) (uint32, error) {
|
||||
return r.moveInto(ctx, mailboxID, folderName, uid, target)
|
||||
}
|
||||
|
||||
// trashTarget is what MoveToTrash resolves; kept as a constant so the two
|
||||
// callers cannot drift.
|
||||
const trashTarget = "trash"
|
||||
|
||||
func (r *WebmailRepository) moveInto(
|
||||
ctx context.Context, mailboxID, folderName string, uid uint32, target string,
|
||||
) (uint32, error) {
|
||||
tx, err := r.pool.Begin(ctx)
|
||||
if err != nil {
|
||||
@@ -333,11 +355,11 @@ func (r *WebmailRepository) MoveToTrash(
|
||||
var trashUID int64
|
||||
err = tx.QueryRow(ctx, `
|
||||
SELECT id::text, uid_next FROM folders
|
||||
WHERE mailbox_id = $1 AND (special_use = 'trash' OR LOWER(name) = 'trash')
|
||||
WHERE mailbox_id = $1 AND (special_use = LOWER($2) OR LOWER(name) = LOWER($2))
|
||||
ORDER BY special_use IS NOT NULL DESC
|
||||
LIMIT 1
|
||||
FOR UPDATE
|
||||
`, mailboxID).Scan(&trashFolderID, &trashUID)
|
||||
`, mailboxID, target).Scan(&trashFolderID, &trashUID)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return 0, port.ErrNoTrashFolder
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user