feat(app): redesigned interface, polls, drafts, forwarding and mentions

A new look across the app and a set of everyday messaging features.

Interface
- Home screen with Chats, Contacts and Settings tabs, a large title,
  search across chats and message text (diacritics-insensitive), filters
  and pinned chats.
- Conversation view with a translucent header and composer, grouped
  bubbles, author avatars and colours in groups, swipe to reply, an
  "N new messages" marker, a jump-to-latest button and in-chat search.
- Contact and group profiles: verification, shared photos, groups in
  common, disappearing messages, mute, block; members, admin badges,
  adding people, rename and leave for groups.
- Full-screen photo viewer that pages through the whole conversation.
- A stable avatar colour per person and a clearly visible read state.

Messaging
- Polls in chats and groups, single or multiple choice, with votes that
  can be changed or withdrawn; older clients see them as text.
- Drafts survive leaving a chat and show in the chat list.
- Forwarding of messages, photos, files and voice messages.
- Notes to self: a local-only, encrypted chat that never reaches the
  relay.
- @mentions in groups that notify even when the group is muted.
- Typing indicators sent as ephemeral messages, opt-in and mutual like
  read receipts.
- Delivery and read times per message and per group member.
- Profile photos, cropped, resized and stripped of metadata, delivered
  encrypted to contacts.
- Voice message playback at 1.5x and 2x.
- Call buttons in 1:1 chats and call log entries.

Fixes
- Muted groups no longer notify: muting was checked against the
  author's chat instead of the group.
- A blocked contact could unblock itself by changing its delivery
  address.
- Read receipts no longer regress to delivered when a late delivery
  receipt arrives.
- A message marked as failed corrects itself once it is delivered.
- Double ticks in groups now mean delivered to every member.
This commit is contained in:
Julius Joska
2026-10-03 22:44:53 +02:00
parent 933a74c900
commit 5246a97db2
56 changed files with 12028 additions and 1668 deletions
+128 -7
View File
@@ -18,9 +18,23 @@
"groupsRename": "Rinomina gruppo",
"groupsLeave": "Abbandona il gruppo",
"groupsLeftBanner": "Non sei più nel gruppo. La cronologia resta, i nuovi messaggi non arriveranno.",
"groupsMembersWithCount": "Membri ({count})",
"groupsYou": "Tu",
"groupsFounder": "Fondatore",
"groupsAdmin": "Amministratore",
"groupsMakeAdmin": "Rendi amministratore",
"groupsDismissAdmin": "Rimuovi ruolo di amministratore",
"groupsRemoveMember": "Rimuovi dal gruppo",
"groupsRemoveMemberConfirmTitle": "Rimuovere {name} dal gruppo?",
"groupsRemoveMemberConfirmBody": "Non riceverà più i nuovi messaggi del gruppo. La cronologia esistente resta.",
"groupsRemoveAction": "Rimuovi",
"groupsViewContact": "Vedi contatto",
"groupsAdminOnlyHint": "Solo gli amministratori possono cambiare nome, membri e amministratori del gruppo.",
"groupsAdminOnlyCompatHint": "Solo gli amministratori possono rinominare il gruppo o cambiare gli amministratori. Per ora chiunque può aggiungere persone, perché qualcuno nel gruppo ha una versione precedente di Šepot.",
"groupsAdminActionFailed": "Modifica non riuscita: {error}",
"groupsResyncNotice": "Il gruppo si sta allineando con l'amministratore. Le modifiche ai membri potrebbero arrivare in ritardo.",
"groupsRejectedChangeNotice": "Qualcuno senza permesso ha provato a modificare il gruppo. La modifica non è stata applicata.",
"errorGroupNotAdmin": "Solo un amministratore del gruppo può farlo.",
"errorGroupLastAdmin": "Sei l'unico amministratore. Prima rendi amministratore qualcun altro.",
"groupsNameHint": "Nome del gruppo",
"commonCancel": "Annulla",
"commonSave": "Salva",
@@ -51,6 +65,12 @@
"chatEditingMessage": "Modifica messaggio",
"chatReply": "Risposta",
"chatActionReply": "Rispondi",
"chatActionForward": "Inoltra",
"forwardTitle": "Inoltra a…",
"forwardSearchHint": "Cerca contatto o gruppo",
"forwardEmpty": "Nessun risultato",
"forwardDone": "Inoltrato a {name}",
"forwardFailed": "Inoltro non riuscito: {error}",
"commonCopy": "Copia",
"commonEdit": "Modifica",
"chatDeleteForMe": "Elimina per me",
@@ -68,6 +88,9 @@
"attachmentTakePhoto": "Scatta foto",
"attachmentDocument": "Documento",
"attachmentTooLarge": "Il file è troppo grande (max {maxMb} MB)",
"attachmentKeepsMetadataTitle": "La foto includerà la posizione",
"attachmentKeepsMetadataBody": "Non riesco a rimuovere i metadati da questo formato (ad es. HEIC). Verrebbe inviata in originale, compresi il luogo dello scatto e i dati del telefono. È più sicuro inviarla dalla Galleria, dove i metadati vengono rimossi.",
"attachmentSendOriginal": "Invia originale",
"attachmentPickFailed": "Selezione dell'allegato non riuscita: {error}",
"attachmentSendFailed": "Invio dell'allegato non riuscito: {error}",
"chatSendFailed": "Invio non riuscito: {error}",
@@ -79,6 +102,7 @@
"errorContactLinkIncomplete": "Questo link è incompleto — l'altra persona l'ha creato prima di connettersi. Chiedile un nuovo link sepot://.",
"errorAttachmentTooLarge": "L'allegato è troppo grande (max {maxMb} MB)",
"errorGroupNeedsMember": "Il gruppo ha bisogno di almeno 1 membro",
"errorAttachmentUnavailable": "L'allegato non è più su questo telefono",
"errorGroupMaxMembers": "Il gruppo può avere al massimo {maxMembers} membri",
"errorMemberNoDeliveryAddress": "{displayName} non ha un indirizzo di consegna — chiedi un nuovo link",
"errorContactNoDeliveryAddress": "Il contatto non ha un indirizzo di consegna — chiedigli un nuovo link sepot://",
@@ -163,9 +187,36 @@
"attachmentOpenFailed": "Impossibile aprire l'allegato: {error}",
"commonFile": "File",
"chatListNewConversation": "Nuova conversazione",
"chatListPhotoWithCaptionPreview": "📷 {caption}",
"chatListFilePreview": "📎 {fileName}",
"chatListReadReceiptPreview": "Letto",
"chatListDraft": "Bozza",
"notesTitle": "Note per me",
"notesSubtitle": "Solo su questo telefono, cifrato",
"callVoice": "Chiama",
"callVideo": "Videochiamata",
"callStatusRinging": "Squilla…",
"callIncomingVoice": "Chiamata in arrivo",
"callIncomingVideo": "Videochiamata in arrivo",
"callStatusConnecting": "Connessione…",
"callEnded": "Chiamata terminata",
"callEndedBusy": "Occupato",
"callEndedRejected": "Chiamata rifiutata",
"callEndedUnanswered": "Nessuna risposta",
"callEndedFailed": "Connessione non riuscita",
"callEncrypted": "Crittografia end-to-end",
"callAccept": "Accetta",
"callReject": "Rifiuta",
"callMute": "Disattiva microfono",
"callSpeaker": "Altoparlante",
"callCamera": "Disattiva fotocamera",
"callSwitchCamera": "Cambia fotocamera",
"callHangUp": "Riaggancia",
"callLogMissed": "Chiamata persa",
"callLogUnanswered": "Chiamata senza risposta",
"callLogRejected": "Chiamata rifiutata",
"callLogBusy": "Occupato",
"callLogFailed": "Chiamata non riuscita",
"callLogVoice": "Chiamata · {duration}",
"callLogVideo": "Videochiamata · {duration}",
"chatListTimeNow": "adesso",
"chatListTimeMinutes": "{minutes} min",
"qrScanTitle": "Scansiona codice QR",
@@ -266,9 +317,9 @@
"pinUnlockError": "Errore di sblocco",
"pushTitle": "Notifiche push",
"pushModeOffDesc": "I messaggi verranno scaricati solo all'apertura dell'app.",
"pushModeUp": "UnifiedPush (consigliato)",
"pushModeUp": "UnifiedPush (senza Google)",
"pushModeUpDesc": "Senza Google — richiede l'app ntfy (F-Droid/Play).",
"pushModeFcm": "Google (FCM)",
"pushModeFcm": "Google (FCM, predefinito)",
"pushModeFcmDesc": "Affidabile senza altre app. Il token push del dispositivo va a Google; il contenuto dei messaggi mai.",
"pushSetupFailed": "Configurazione delle notifiche push non riuscita: {error}",
"pushHintOff": "I messaggi arrivano solo quando apri Šepot — le notifiche non sono configurate.",
@@ -280,7 +331,7 @@
"pushPickDistributorTitle": "Scegli il distributore",
"pushSubtitleUnifiedPush": "UnifiedPush · {distributor}",
"pushNoDistributorFallback": "nessun distributore",
"pushSubtitleFcm": "Google (FCM) — veloce, opt-in",
"pushSubtitleFcm": "Google (FCM)",
"restoreErrorNameRequired": "Inserisci il nome visualizzato.",
"restoreErrorWordCount": "{wordCount, plural, one{La frase ha 1 parola, ne servono {requiredWords}.} other{La frase ha {wordCount} parole, ne servono {requiredWords}.}}",
"restoreErrorInvalidPhrase": "La frase non è valida — controlla le parole e il loro ordine.",
@@ -364,5 +415,75 @@
"photoPreviewSend": "Invia",
"photoPreviewTextDialogTitle": "Testo sulla foto",
"photoPreviewRemoveText": "Rimuovi",
"photoPreviewDecodeFailed": "Impossibile caricare la foto"
"photoPreviewDecodeFailed": "Impossibile caricare la foto",
"chatActionInfo": "Info messaggio",
"messageInfoSent": "Inviato",
"messageInfoDelivered": "Consegnato",
"messageInfoRead": "Letto",
"messageInfoPending": "non ancora",
"messageInfoReadReceiptsOff": "Vedi quando i tuoi messaggi vengono letti solo se condividi anche tu le conferme di lettura (Impostazioni).",
"chatStatusReadAt": "Letto {time}",
"chatStatusDeliveredAt": "Consegnato {time}",
"groupsAddMembers": "Aggiungi membri",
"groupsAddMembersAction": "Aggiungi",
"groupsAddMembersFailed": "Impossibile aggiungere: {error}",
"groupsNoContactsToAdd": "Tutti i tuoi contatti sono già nel gruppo. Aggiungi prima una nuova persona ai contatti tramite codice QR.",
"navChats": "Chat",
"chatListNewChat": "Nuova chat",
"chatListSearchHint": "Cerca in chat e messaggi",
"chatFilterAll": "Tutte",
"chatFilterUnread": "Non lette",
"chatFilterGroups": "Gruppi",
"chatFilterArchive": "Archivio",
"chatListNoResults": "Nessun risultato.",
"chatListFilterEmpty": "Ancora niente qui.",
"chatListPin": "Fissa",
"chatListUnpin": "Sblocca",
"chatOpenProfile": "Vedi profilo",
"groupsProfile": "Info gruppo",
"chatSearchHint": "Cerca nella chat",
"chatSearchNoMatches": "Nessuno",
"chatSearchCounter": "{current} di {total}",
"chatSearchOlder": "Risultato precedente",
"chatSearchNewer": "Risultato successivo",
"chatUnreadDivider": "{count, plural, one{1 nuovo messaggio} other{{count} nuovi messaggi}}",
"chatJumpToLatest": "Vai ai più recenti",
"chatAttach": "Allega",
"chatSend": "Invia",
"chatRecordVoice": "Registra messaggio vocale",
"profileSharedMedia": "Media condivisi",
"profileMessage": "Messaggio",
"profileSearch": "Cerca",
"profileUnmute": "Riattiva",
"profileDisappearing": "Effimeri",
"profileCommonGroups": "Gruppi in comune",
"profileSafetyNumber": "Numero di sicurezza",
"profileSafetyNumberHint": "Confronta di persona o con codice QR",
"profileVerifiedInPerson": "Verificato di persona",
"profileVerifyNow": "Verifica contatto",
"profileAdd": "Aggiungi",
"profileGroupSubtitle": "Gruppo · {members} · creato il {date}",
"profileMembersOfMax": "{count} su {max}",
"chatTyping": "sta scrivendo…",
"chatTypingNamed": "{name} sta scrivendo…",
"chatTypingSeveral": "Più persone stanno scrivendo…",
"settingsTypingTitle": "Indicatore di scrittura",
"settingsTypingSubtitle": "Reciproco — se non lo condividi, non lo vedi (disattivato di default)",
"pollAttach": "Sondaggio",
"pollCreateTitle": "Nuovo sondaggio",
"pollQuestion": "Domanda",
"pollOptionHint": "Opzione {number}",
"pollAddOption": "Aggiungi opzione",
"pollRemoveOption": "Rimuovi opzione",
"pollAllowMultiple": "Consenti più risposte",
"pollSingle": "Sondaggio",
"pollMulti": "Sondaggio, scelta multipla",
"pollVoters": "{count, plural, =0{ancora nessun voto} one{1 voto} other{{count} voti}}",
"pollFallback": "Sondaggio: {question}",
"pollFallbackUpdate": "Aggiorna Šepot per votare.",
"profilePhotoEdit": "Cambia foto profilo",
"profilePhotoPick": "Scegli foto",
"profilePhotoRemove": "Rimuovi foto",
"profilePhotoHint": "La vedranno solo le persone con cui chatti — cifrata come i messaggi. Posizione e metadati non vengono inviati.",
"profilePhotoFailed": "Impossibile impostare la foto: {error}"
}