Compare commits

...

8 Commits
main ... 5.42.x

Author SHA1 Message Date
Josh Perez 2fa9735f7f v5.42.0 2022-05-04 19:04:06 -04:00
Josh Perez 8fe0f4576e Updates translations 2022-05-04 19:03:53 -04:00
automated-signal 282b43e28b
On database error: Escape copies error and quits, additional logging
Co-authored-by: Scott Nonnenberg <scott@signal.org>
2022-05-03 13:25:25 -07:00
automated-signal 8c8a1c19f9
MediaGallery: Localize Media and Documents tab headers
Co-authored-by: Scott Nonnenberg <scott@signal.org>
2022-05-02 17:06:14 -07:00
automated-signal 325c12b076
Ensure that seenStatus is always updated along with readStatus
Co-authored-by: Scott Nonnenberg <scott@signal.org>
2022-05-02 09:24:54 -07:00
automated-signal 8dd2a1a467
Log better errors when unable to show attachments
Co-authored-by: Josh Perez <60019601+josh-signal@users.noreply.github.com>
2022-04-29 12:34:46 -07:00
automated-signal f235d48e99
Increment unprocessed attempts when fetching
Co-authored-by: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com>
2022-04-28 16:51:38 -07:00
automated-signal 93b26b972b
Confirm group call update messages
Co-authored-by: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com>
2022-04-28 16:51:10 -07:00
54 changed files with 433 additions and 148 deletions

View File

@ -7047,6 +7047,10 @@
"message": "‫يُرجى كتابة رد...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "لا جواب حاليا",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "المشاهدات",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Bir cavab yazın...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Hələ ki, cavab yoxdur",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Baxış",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Escriviu una resposta...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Encara no té respostes",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Visualitzacions",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Napište odpověď...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Zatím žádné odpovědi",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Zobrazení",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Teipiwch ateb...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Dim atebion eto",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Golygon",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Indtast et svar...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Ingen svar endnu",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Visninger",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Schreibe eine Antwort ...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Bisher keine Antworten",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Aufrufe",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Γράψε μια απάντηση...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Καμία απάντηση ακόμα",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Προβολές",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Entajpi respondon...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Neniu respondo",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Vidoj",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Escribe una respuesta …",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Sin respuestas por ahora",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Vistas",
"description": "Title for views tab"

View File

@ -6102,11 +6102,11 @@
"description": "aria-label for the 'next' button in the forward a message modal dialog"
},
"TimelineDateHeader--date-in-last-6-months": {
"message": "ddd D. MMMMta",
"message": "ddd D. MMMM[ta]",
"description": "Moment.js format for date headers in the message timeline, for dates <6 months old. See https://momentjs.com/docs/#/displaying/format/."
},
"TimelineDateHeader--date-older-than-6-months": {
"message": "ddd D. MMMMta YYYY",
"message": "ddd D. MMMM[ta] YYYY",
"description": "Moment.js format for date headers in the message timeline, for dates >=6 months old. See https://momentjs.com/docs/#/displaying/format/."
},
"MessageRequestWarning__learn-more": {
@ -7047,6 +7047,10 @@
"message": "Kirjoita vastaus...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Ei vastauksia vielä",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Näytöt",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Tapez une réponse…",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Aucune réponse pour le moment",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Vues",
"description": "Title for views tab"
@ -7104,7 +7108,7 @@
"description": "Release notes for releases that only include bug fixes"
},
"WhatsNew__v5.40--1": {
"message": "Bug fixes including a fix to an issue that would sometimes make it difficult to click on menus. ",
"message": "Correction de bogues, notamment dun problème rendant parfois difficile de cliquer sur les menus.",
"description": "Release notes for v5.40"
},
"WhatsNew__v5.40--2": {

View File

@ -7047,6 +7047,10 @@
"message": "הקלד תשובה…",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "אין תשובות עדין",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "צפיות",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Küldj egy választ...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Még nem érkeztek reakciók",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Megtekintések",
"description": "Title for views tab"

View File

@ -221,6 +221,10 @@
"message": "Percakapan-percakapan ini diarsipkan dan hanya akan muncul di kotak masuk jika pesan baru diterima.",
"description": "Shown at the top of the archived conversations list in the left pane"
},
"noArchivedConversations": {
"message": "No archived conversations.",
"description": "Shown at the top of the archived conversations list in the left pane if there is no any archived conversation"
},
"archiveConversation": {
"message": "Arsip",
"description": "Shown in menu for conversation, and moves conversation out of main conversation list"
@ -5892,11 +5896,11 @@
"description": "Information shown below the invite list"
},
"PendingRequests--block--button": {
"message": "Block request",
"message": "Blokir permintaan",
"description": "Shown in timeline if users cancel their request to join a group via a group link"
},
"PendingRequests--block--title": {
"message": "Block request?",
"message": "Blokir permintaan?",
"description": "Title of dialog to block a user from requesting to join via the link again"
},
"PendingRequests--block--contents": {
@ -6353,6 +6357,14 @@
"message": "Waktu kustom",
"description": "Text for an option in Conversation Details Disappearing Messages setting when user previously selected custom time"
},
"DisappearingTimeDialog__label--value": {
"message": "Number",
"description": "aria-label for the number select box"
},
"DisappearingTimeDialog__label--units": {
"message": "Unit of time",
"description": "aria-label for the units of time select box"
},
"DisappearingTimeDialog__title": {
"message": "Waktu Kustom",
"description": "Title for the custom disappearing message timeout dialog"
@ -6799,6 +6811,24 @@
"message": "Mohon tutup secara manual dan klik Coba Lagi untuk melanjutkan.",
"description": "Second line of the dialog displayed when Windows installer can't close application automatically and needs user intervention to complete the installation."
},
"NSIS__appRunning": {
"message": "$appName$ is running.\nClick OK to close it.\nIf it doesn't close, try closing it manually.",
"description": "The contents of a dialog displayed when Windows installer detect that the application is running and asks user to close it. Note: please keep the line breaks so that the text occupies three separate lines",
"placeholders": {
"appName": {
"content": "$1",
"example": "Signal"
}
}
},
"NSIS__decompressionFailed": {
"message": "Failed to decompress files. Please try running the installer again.",
"description": "Displayed when Windows installer cannot decompress application files"
},
"NSIS__uninstallFailed": {
"message": "Failed to uninstall old application files. Please try running the installer again.",
"description": "Displayed when Windows installer cannot uninstall the old application"
},
"CrashReportDialog__title": {
"message": "Aplikasi macet",
"description": "A title of the dialog displayed when starting an application after a recent crash"
@ -6912,7 +6942,7 @@
"description": "Performs the crop"
},
"MyStories__title": {
"message": "My Stories",
"message": "Cerita saya",
"description": "Title for the my stories list"
},
"MyStories__story": {
@ -6976,13 +7006,17 @@
"description": "Title for the stories list"
},
"Stories__mine": {
"message": "My Stories",
"message": "Cerita saya",
"description": "Label for your stories"
},
"Stories__add": {
"message": "Add a story",
"description": "Description hint to add a story"
},
"Stories__hidden-stories": {
"message": "Hidden stories",
"description": "Button label to go to hidden stories pane"
},
"Stories__list-empty": {
"message": "No recent stories to show right now",
"description": "Description for when there are no stories to show"
@ -7013,6 +7047,10 @@
"message": "Type a reply...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "No replies yet",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Views",
"description": "Title for views tab"
@ -7033,6 +7071,10 @@
"message": "Story",
"description": "aria-label for the story list button"
},
"StoryListItem__unhide": {
"message": "Show stories",
"description": "Label for menu item to un-hide the story"
},
"StoryListItem__hide": {
"message": "Hide story",
"description": "Label for menu item to hide the story"
@ -7065,28 +7107,22 @@
"message": "Versi ini mengandung sejumlah perubahan kecil dan perbaikan bug agar Signal tetap berjalan lancar.",
"description": "Release notes for releases that only include bug fixes"
},
"WhatsNew__v5.36--1": {
"message": "Quickly scanning that group chat? There's more room for more messages on the screen at once. We now group sender's messages together if they're close together in time.",
"description": "Release notes for v5.36"
"WhatsNew__v5.40--1": {
"message": "Bug fixes including a fix to an issue that would sometimes make it difficult to click on menus. ",
"description": "Release notes for v5.40"
},
"WhatsNew__v5.36--2": {
"message": "When you perform a Delete for Everyone you'll now see a progress spinner letting you know whether it's been successfully sent or not. If it fails for some reason, you'll be able to retry too!",
"description": "Release notes for v5.36"
},
"WhatsNew__v5.37--1": {
"message": "We're keeping short messages short, by putting stuff like timestamps on the same line as the text. Now you've got more space on the screen for that quick 'hey' to check in on a friend.",
"description": "Release notes for v5.37"
},
"WhatsNew__v5.37--2": {
"message": "Missing sticker packs begone! Desktop should now be able to handle all sticker packs that your phone can!",
"description": "Release notes for v5.37"
},
"WhatsNew__v5.39--1": {
"message": "You can now add people to groups using just their phone number.",
"description": "Release notes for v5.39"
},
"WhatsNew__v5.39--2": {
"message": "Your favorite contacts are now just a few keystrokes away. Contact search now supports non-Latin alphabets like Cyrillic.",
"description": "Release notes for v5.39"
"WhatsNew__v5.40--2": {
"message": "Thanks to our open source contributors $dsanders11$ and $yusufsahinhamza$ for contributing to these improvements.",
"description": "Release notes for v5.40",
"placeholders": {
"dsanders11": {
"content": "$1",
"example": "dsanders11"
},
"yusufsahinhamza": {
"content": "$2",
"example": "yusufsahinhamza"
}
}
}
}

View File

@ -222,7 +222,7 @@
"description": "Shown at the top of the archived conversations list in the left pane"
},
"noArchivedConversations": {
"message": "No archived conversations.",
"message": "Engin samtöl í geymslu.",
"description": "Shown at the top of the archived conversations list in the left pane if there is no any archived conversation"
},
"archiveConversation": {
@ -6358,11 +6358,11 @@
"description": "Text for an option in Conversation Details Disappearing Messages setting when user previously selected custom time"
},
"DisappearingTimeDialog__label--value": {
"message": "Number",
"message": "Tala",
"description": "aria-label for the number select box"
},
"DisappearingTimeDialog__label--units": {
"message": "Unit of time",
"message": "Tímaeining",
"description": "aria-label for the units of time select box"
},
"DisappearingTimeDialog__title": {
@ -6812,7 +6812,7 @@
"description": "Second line of the dialog displayed when Windows installer can't close application automatically and needs user intervention to complete the installation."
},
"NSIS__appRunning": {
"message": "$appName$ is running.\nClick OK to close it.\nIf it doesn't close, try closing it manually.",
"message": "$appName$ er í gangi.\nSmelltu á 'Í lagi' til að loka því.\nEf það lokast ekki, geturðu reynt að loka því handvirkt..",
"description": "The contents of a dialog displayed when Windows installer detect that the application is running and asks user to close it. Note: please keep the line breaks so that the text occupies three separate lines",
"placeholders": {
"appName": {
@ -6822,11 +6822,11 @@
}
},
"NSIS__decompressionFailed": {
"message": "Failed to decompress files. Please try running the installer again.",
"message": "Mistókst að afþjappa skrám. Keyrðu uppsetningarforritið aftur.",
"description": "Displayed when Windows installer cannot decompress application files"
},
"NSIS__uninstallFailed": {
"message": "Failed to uninstall old application files. Please try running the installer again.",
"message": "Mistókst að hreinsa út gamlar forritsskrár. Keyrðu uppsetningarforritið aftur.",
"description": "Displayed when Windows installer cannot uninstall the old application"
},
"CrashReportDialog__title": {
@ -7047,6 +7047,10 @@
"message": "Skrifaðu svar...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Ennþá engin svör",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Skoðað",
"description": "Title for views tab"
@ -7068,7 +7072,7 @@
"description": "aria-label for the story list button"
},
"StoryListItem__unhide": {
"message": "Show stories",
"message": "Birta sögur",
"description": "Label for menu item to un-hide the story"
},
"StoryListItem__hide": {
@ -7104,11 +7108,11 @@
"description": "Release notes for releases that only include bug fixes"
},
"WhatsNew__v5.40--1": {
"message": "Bug fixes including a fix to an issue that would sometimes make it difficult to click on menus. ",
"message": "Villulagfæringar, meðal annars á vandamáli við að smella á valmyndir.",
"description": "Release notes for v5.40"
},
"WhatsNew__v5.40--2": {
"message": "Thanks to our open source contributors $dsanders11$ and $yusufsahinhamza$ for contributing to these improvements.",
"message": "Þökk sé sjálfboðaliðunum $dsanders11$ og $yusufsahinhamza$ sem áttu stóran hlut í þessum endurbótum.",
"description": "Release notes for v5.40",
"placeholders": {
"dsanders11": {

View File

@ -7047,6 +7047,10 @@
"message": "Scrivi una risposta...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Ancora nessuna risposta",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Visualizzazioni",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "返信を入力してください…",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "返信 なし",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "閲覧",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Rašykite atsakymą...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Atsakymų kol kas nėra",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Peržiūros",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Type a reply...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Vēl nav atbilžu",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Skatījumi",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Type a reply...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Сè уште нема одговори",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Прегледи",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Typ een reactie …",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Nog geen reacties",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Aantal weergaven",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Type a reply...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Ingen svar enno",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Visningar",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Napisz odpowiedź...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Brak odpowiedzi",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Odsłony",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Digitar uma resposta…",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Ainda não há respostas",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Visualizações",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Escreva uma resposta...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Ainda não existem respostas",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Visualizações",
"description": "Title for views tab"

View File

@ -6174,7 +6174,7 @@
"description": "Title for the contact name spoofing review dialog in groups"
},
"ContactSpoofingReviewDialog__group__description": {
"message": "$count$ membri ai grupului au nume similar, revizuiește membri de mai jos sau alege să iei o decizie.",
"message": "$count$ membri ai grupului au nume similar, revizuiește membri de mai jos sau ia măsuri.",
"description": "Description for the group contact spoofing review dialog"
},
"ContactSpoofingReviewDialog__group__members-header": {
@ -7047,6 +7047,10 @@
"message": "Scrie un răspuns...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Niciun răspuns încă",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Vizualizări",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Введите ответ…",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Ещё нет ответов",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Просмотры",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Napíšte odpoveď...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Zatiaľ žiadne odpovede",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Pozretia",
"description": "Title for views tab"

View File

@ -1114,7 +1114,7 @@
"description": ""
},
"failedToSend": {
"message": "Pošiljanje nekaterim prejemnikom ni uspelo. Preverite povezavo z omrežjem.",
"message": "Pošiljanje nekaterim prejemnikom_cam ni uspelo. Preverite povezavo z omrežjem.",
"description": ""
},
"error": {
@ -5598,7 +5598,7 @@
"description": "This is the label for the disappearing messages setting panel"
},
"ConversationDetails--disappearing-messages-info--group": {
"message": "Poslana in prejeta sporočila bodo izginila po tem, ko bodo videna s strani prejemnika.",
"message": "Poslana in prejeta sporočila bodo izginila po tem, ko bodo videna s strani prejemnika_ce.",
"description": "This is the info about the disappearing messages setting, in groups"
},
"ConversationDetails--disappearing-messages-info--direct": {
@ -6998,7 +6998,7 @@
}
},
"MyStories__delete": {
"message": "Želite izbrisati to zgodbo? Izbrisana bo tudi na napravah dugih prejemnikov.",
"message": "Želite izbrisati to zgodbo? Izbrisana bo tudi na napravah dugih prejemnikov_ic.",
"description": "Confirmation dialog description text for deleting a story"
},
"Stories__title": {
@ -7047,6 +7047,10 @@
"message": "Vnesi odgovor ...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Brez odgovorov",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Ogledi",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Shtypni një përgjigje…",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Ende pa përgjigje",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Parje",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Унесите одговор...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Без одговора",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Прегледа",
"description": "Title for views tab"
@ -7108,7 +7112,7 @@
"description": "Release notes for v5.40"
},
"WhatsNew__v5.40--2": {
"message": "Thanks to our open source contributors $dsanders11$ and $yusufsahinhamza$ for contributing to these improvements.",
"message": "Хвала нашим сарадницима отвореног кода $dsanders11$ и $yusufsahinhamza$ за допринос овим побољшањима.",
"description": "Release notes for v5.40",
"placeholders": {
"dsanders11": {

View File

@ -584,11 +584,11 @@
"description": "The string \"yesterday\""
},
"thisWeek": {
"message": "Denna vecka",
"message": "Den här veckan",
"description": "Section header in the media gallery"
},
"thisMonth": {
"message": "Denna månad",
"message": "Den här månaden",
"description": "Section header in the media gallery"
},
"unsupportedAttachment": {
@ -876,7 +876,7 @@
"description": ""
},
"typingAlt": {
"message": "Skrivanimation för denna konversation",
"message": "Skrivanimation för den här konversationen",
"description": "Used as the 'title' attribute for the typing animation"
},
"contactInAddressBook": {
@ -1150,7 +1150,7 @@
"description": "Label for when something is turned off"
},
"deleteWarning": {
"message": "Detta meddelande tas bort från denna enhet.",
"message": "Det här meddelandet tas bort från den här enheten.",
"description": "Text shown in the confirmation dialog for deleting a message locally"
},
"deleteForEveryoneWarning": {
@ -1208,7 +1208,7 @@
}
},
"theirIdentityUnknown": {
"message": "Du har inte utbytt några meddelanden med denna kontakt ännu. Ditt säkerhetsnummer med kontakten är tillgänglig efter det första meddelandet.",
"message": "Du har inte utbytt några meddelanden med den här kontakten ännu. Ditt säkerhetsnummer med kontakten är tillgänglig efter det första meddelandet.",
"description": ""
},
"back": {
@ -1264,7 +1264,7 @@
"description": "Shown in timeline when session is automatically reset, to provide access to a popup info dialog"
},
"ChatRefresh--summary": {
"message": "Signal använder end-to-end-kryptering och det kan behöva uppdatera din chattsession ibland. Detta påverkar inte din chatts säkerhet, men du kan ha missat ett meddelande från denna kontakt och du kan behöva be dem skicka det igen.",
"message": "Signal använder end-to-end-kryptering och det kan behöva uppdatera din chattsession ibland. Detta påverkar inte din chatts säkerhet, men du kan ha missat ett meddelande från den här kontakten och du kan behöva be dem skicka det igen.",
"description": "Shown on explainer dialog available from chat session refreshed timeline events"
},
"ChatRefresh--contactSupport": {
@ -1340,7 +1340,7 @@
"description": "Used in the alt tag for the image shown in a full-screen lightbox view"
},
"imageCaptionIconAlt": {
"message": "Ikon visandes att denna bild har en bildtext",
"message": "Ikon visandes att den här bilden har en bildtext",
"description": "Used for the icon layered on top of an image in message bubbles"
},
"save": {
@ -2170,11 +2170,11 @@
"description": "Shown if the user tries to send more than 64kb of text"
},
"unblockToSend": {
"message": "Sluta blockera denna kontakt för att skicka meddelanden.",
"message": "Sluta blockera den här kontakten för att skicka meddelanden.",
"description": "Brief message shown when trying to message a blocked number"
},
"unblockGroupToSend": {
"message": "Sluta blockera denna grupp för att skicka meddelanden.",
"message": "Sluta blockera den här gruppen för att skicka meddelanden.",
"description": "Brief message shown when trying to message a blocked group"
},
"youChangedTheTimer": {
@ -2362,7 +2362,7 @@
"description": "Label text for system theme"
},
"noteToSelf": {
"message": "Notering till mig själv",
"message": "Egen anteckning",
"description": "Name for the conversation with your own phone number"
},
"noteToSelfHero": {
@ -2508,7 +2508,7 @@
"description": "The header for the members list in the 'set group metadata' left pane screen"
},
"setGroupMetadata__error-message": {
"message": "Denna grupp kunde inte skapas. Kontrollera din anslutning och försök igen.",
"message": "Den här gruppen kunde inte skapas. Kontrollera din anslutning och försök igen.",
"description": "Shown in the modal when we can't create a group"
},
"updateGroupAttributes__title": {
@ -3482,11 +3482,11 @@
}
},
"MessageRequests--message-group": {
"message": "Gå med i denna grupp och dela ditt namn och foto med dess medlemmar? De vet inte att du har sett sina meddelanden förrän du accepterar det.",
"message": "Gå med i den här gruppen och dela ditt namn och foto med dess medlemmar? De vet inte att du har sett sina meddelanden förrän du accepterar det.",
"description": "Shown as the message for a message request in a group"
},
"MessageRequests--message-group-blocked": {
"message": "Ta bort blockeringen av denna grupp och dela ditt namn och foto med dess medlemmar? Du får inte några meddelanden förrän du har avblockerat gruppen.",
"message": "Ta bort blockeringen av den här gruppen och dela ditt namn och foto med dess medlemmar? Du får inte några meddelanden förrän du har avblockerat gruppen.",
"description": "Shown as the message for a message request in a blocked group"
},
"MessageRequests--block": {
@ -3512,7 +3512,7 @@
"description": "Shown as the body in the confirmation modal for unblocking a private message request"
},
"MessageRequests--unblock-group-confirm-body": {
"message": "Gruppmedlemmar kommer att kunna lägga till dig i denna grupp igen.",
"message": "Gruppmedlemmar kommer att kunna lägga till dig i den här gruppen igen.",
"description": "Shown as the body in the confirmation modal for unblocking a group message request"
},
"MessageRequests--block-and-report-spam": {
@ -3560,7 +3560,7 @@
"description": "Shown as the title in the confirmation modal for deleting a private message request"
},
"MessageRequests--delete-direct-confirm-body": {
"message": "Denna konversation tas bort från alla dina enheter.",
"message": "Den här konversationen tas bort från alla dina enheter.",
"description": "Shown as the body in the confirmation modal for deleting a private message request"
},
"MessageRequests--delete-group-confirm-title": {
@ -3582,7 +3582,7 @@
"description": "Shown as a button to let the user delete a group message request"
},
"MessageRequests--delete-group-confirm-body": {
"message": "Du kommer lämnar denna grupp och den tas bort från alla dina enheter.",
"message": "Du kommer lämnar den här gruppen och den tas bort från alla dina enheter.",
"description": "Shown as the body in the confirmation modal for deleting a group message request"
},
"MessageRequests--accept": {
@ -3604,7 +3604,7 @@
}
},
"MessageRequests--profile-sharing--direct": {
"message": "Fortsätt denna konversation med $firstName$ och dela ditt namn och foto med personen? $learnMore$",
"message": "Fortsätt den här konversationen med $firstName$ och dela ditt namn och foto med personen? $learnMore$",
"description": "Shown when user hasn't shared their profile in a 1:1 conversation yet",
"placeholders": {
"firstName": {
@ -4126,15 +4126,15 @@
"description": "Shown if we are unable to parse a group link"
},
"GroupV2--join--prompt": {
"message": "Vill du gå med i denna grupp och dela ditt namn och foto med dess medlemmar?",
"message": "Vill du gå med i den här gruppen och dela ditt namn och foto med dess medlemmar?",
"description": "Shown when you click on a group link to confirm"
},
"GroupV2--join--already-in-group": {
"message": "Du är redan i denna grupp.",
"message": "Du är redan i den här gruppen.",
"description": "Shown if you click a group link for a group where you're already a member"
},
"GroupV2--join--already-awaiting-approval": {
"message": "Du har redan begärt godkännande för att gå med i denna grupp.",
"message": "Du har redan begärt godkännande för att gå med i den här gruppen.",
"description": "Shown if you click a group link for a group where you've already requested approval'"
},
"GroupV2--join--unknown-link-version--title": {
@ -4150,7 +4150,7 @@
"description": "Shown if you click a group link and we can't get information about it"
},
"GroupV2--join--link-revoked": {
"message": "Denna grupplänk är inte längre giltig.",
"message": "Den här grupplänken är inte längre giltig.",
"description": "Shown if you click a group link and we can't get information about it"
},
"GroupV2--join--link-forbidden--title": {
@ -5364,7 +5364,7 @@
"description": "Shown in timeline or conversation preview when v2 group changes"
},
"GroupV1--Migration--disabled": {
"message": "Uppgradera den här gruppen för att aktivera nya funktioner som @omnämnanden och administratörer. Medlemmar som inte har delat sitt namn eller foto i denna grupp kommer att bjudas in att gå med. $learnMore$",
"message": "Uppgradera den här gruppen för att aktivera nya funktioner som @omnämnanden och administratörer. Medlemmar som inte har delat sitt namn eller foto i den här gruppen kommer att bjudas in att gå med. $learnMore$",
"description": "Shown instead of composition area when user is forced to migrate a legacy group (GV1).",
"placeholders": {
"learnMore": {
@ -5374,7 +5374,7 @@
}
},
"GroupV1--Migration--was-upgraded": {
"message": "Denna grupp uppgraderades till en Ny grupp.",
"message": "Den här gruppen uppgraderades till en Ny grupp.",
"description": "Shown in timeline when a legacy group (GV1) is upgraded to a new group (GV2)"
},
"GroupV1--Migration--learn-more": {
@ -5406,7 +5406,7 @@
"description": "Shown on Migration popup before GV1 migration"
},
"GroupV1--Migration--info--invited--you": {
"message": "Du måste acceptera en inbjudan för att gå med i denna grupp igen och kommer inte att få gruppmeddelanden förrän du accepterar.",
"message": "Du måste acceptera en inbjudan för att gå med i den här gruppen igen och kommer inte att få gruppmeddelanden förrän du accepterar.",
"description": "Shown on Learn More popup after GV1 migration"
},
"GroupV1--Migration--info--invited--many": {
@ -5414,7 +5414,7 @@
"description": "Shown on Learn More popup after or Migration popup before GV1 migration"
},
"GroupV1--Migration--info--invited--one": {
"message": "Denna medlem måste acceptera en inbjudan att gå med i denna grupp igen och kommer inte att få gruppmeddelanden förrän medlemmen accepterar:",
"message": "Den här medlemmen måste acceptera en inbjudan att gå med i den här gruppen igen och kommer inte att få gruppmeddelanden förrän medlemmen accepterar:",
"description": "Shown on Learn More popup after or Migration popup before GV1 migration"
},
"GroupV1--Migration--info--removed--before--many": {
@ -5422,7 +5422,7 @@
"description": "Shown on Learn More popup after or Migration popup before GV1 migration"
},
"GroupV1--Migration--info--removed--before--one": {
"message": "Denna medlem kan inte gå med i Nya grupper och kommer att tas bort från gruppen:",
"message": "Den här medlemmen kan inte gå med i Nya grupper och kommer att tas bort från gruppen:",
"description": "Shown on Learn More popup after or Migration popup before GV1 migration"
},
"GroupV1--Migration--info--removed--after--many": {
@ -5430,7 +5430,7 @@
"description": "Shown on Learn More popup after or Migration popup before GV1 migration"
},
"GroupV1--Migration--info--removed--after--one": {
"message": "Denna medlem kunde inte gå med i Nya grupper och togs bort från gruppen:",
"message": "Den här medlemmen kunde inte gå med i Nya grupper och togs bort från gruppen:",
"description": "Shown on Learn More popup after or Migration popup before GV1 migration"
},
"GroupV1--Migration--invited--you": {
@ -5646,7 +5646,7 @@
"description": "This is a button to block a group"
},
"ConversationDetailsActions--leave-group-must-choose-new-admin": {
"message": "Innan du lämnar måste du välja minst en ny administratör för denna grupp.",
"message": "Innan du lämnar måste du välja minst en ny administratör för den här gruppen.",
"description": "Shown if, before leaving a group, you need to choose an admin"
},
"ConversationDetailsActions--leave-group-modal-title": {
@ -5654,7 +5654,7 @@
"description": "This is the modal title for confirming leaving a group"
},
"ConversationDetailsActions--leave-group-modal-content": {
"message": "Du kommer inte längre att kunna skicka eller ta emot meddelanden i denna grupp.",
"message": "Du kommer inte längre att kunna skicka eller ta emot meddelanden i den här gruppen.",
"description": "This is the modal content for confirming leaving a group"
},
"ConversationDetailsActions--leave-group-modal-confirm": {
@ -7047,6 +7047,10 @@
"message": "Skriv ett svar...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Inga svar ännu",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Visningar",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Type a reply...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Henüz yanıt yok",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Görülmeler",
"description": "Title for views tab"

View File

@ -7047,6 +7047,10 @@
"message": "Відповісти...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "Ще немає відповідей",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "Перегляди",
"description": "Title for views tab"
@ -7068,7 +7072,7 @@
"description": "aria-label for the story list button"
},
"StoryListItem__unhide": {
"message": "Show stories",
"message": "Показати історії",
"description": "Label for menu item to un-hide the story"
},
"StoryListItem__hide": {

View File

@ -7047,6 +7047,10 @@
"message": "輸入回覆...",
"description": "Placeholder text for the story reply modal"
},
"StoryViewsNRepliesModal__no-replies": {
"message": "未有回覆",
"description": "Placeholder text for when there are no replies"
},
"StoryViewsNRepliesModal__tab--views": {
"message": "檢視",
"description": "Title for views tab"

View File

@ -1287,6 +1287,21 @@ function showPermissionsPopupWindow(forCalling: boolean, forCamera: boolean) {
});
}
const runSQLCorruptionHandler = async () => {
// This is a glorified event handler. Normally, this promise never resolves,
// but if there is a corruption error triggered by any query that we run
// against the database - the promise will resolve and we will call
// `onDatabaseError`.
const error = await sql.whenCorrupted();
getLogger().error(
'Detected sql corruption in main process. ' +
`Restarting the application immediately. Error: ${error.message}`
);
await onDatabaseError(error.stack || error.message);
};
async function initializeSQL(
userDataPath: string
): Promise<{ ok: true; error: undefined } | { ok: false; error: Error }> {
@ -1331,6 +1346,9 @@ async function initializeSQL(
sqlInitTimeEnd = Date.now();
}
// Only if we've initialized things successfully do we set up the corruption handler
runSQLCorruptionHandler();
return { ok: true, error: undefined };
}
@ -1346,44 +1364,32 @@ const onDatabaseError = async (error: string) => {
const buttonIndex = dialog.showMessageBoxSync({
buttons: [
getLocale().i18n('copyErrorAndQuit'),
getLocale().i18n('deleteAndRestart'),
getLocale().i18n('copyErrorAndQuit'),
],
defaultId: 0,
defaultId: 1,
cancelId: 1,
detail: redactAll(error),
message: getLocale().i18n('databaseError'),
noLink: true,
type: 'error',
});
if (buttonIndex === 0) {
if (buttonIndex === 1) {
clipboard.writeText(`Database startup error:\n\n${redactAll(error)}`);
} else {
await sql.removeDB();
userConfig.remove();
getLogger().error(
'onDatabaseError: Requesting immediate restart after quit'
);
app.relaunch();
}
getLogger().error('onDatabaseError: Quitting application');
app.exit(1);
};
const runSQLCorruptionHandler = async () => {
// This is a glorified event handler. Normally, this promise never resolves,
// but if there is a corruption error triggered by any query that we run
// against the database - the promise will resolve and we will call
// `onDatabaseError`.
const error = await sql.whenCorrupted();
getLogger().error(
'Detected sql corruption in main process. ' +
`Restarting the application immediately. Error: ${error.message}`
);
await onDatabaseError(error.stack || error.message);
};
runSQLCorruptionHandler();
let sqlInitPromise:
| Promise<{ ok: true; error: undefined } | { ok: false; error: Error }>
| undefined;

View File

@ -4,7 +4,7 @@
"description": "Private messaging from your desktop",
"desktopName": "signal.desktop",
"repository": "https://github.com/signalapp/Signal-Desktop.git",
"version": "5.42.0-beta.1",
"version": "5.42.0",
"license": "AGPL-3.0-only",
"author": {
"name": "Signal Messenger, LLC",

View File

@ -1859,9 +1859,9 @@ export class SignalProtocolStore extends EventsMixin {
});
}
getAllUnprocessed(): Promise<Array<UnprocessedType>> {
getAllUnprocessedAndIncrementAttempts(): Promise<Array<UnprocessedType>> {
return this.withZone(GLOBAL_ZONE, 'getAllUnprocessed', async () => {
return window.Signal.Data.getAllUnprocessed();
return window.Signal.Data.getAllUnprocessedAndIncrementAttempts();
});
}

View File

@ -2905,6 +2905,7 @@ export async function startApp(): Promise<void> {
}
if (handleGroupCallUpdateMessage(data.message, messageDescriptor)) {
confirm();
return Promise.resolve();
}

View File

@ -89,19 +89,20 @@ export class MediaGallery extends React.Component<Props, State> {
}
public override render(): JSX.Element {
const { i18n } = this.props;
const { selectedTab } = this.state;
return (
<div className="module-media-gallery" tabIndex={-1} ref={this.focusRef}>
<div className="module-media-gallery__tab-container">
<Tab
label="Media"
label={i18n('media')}
type="media"
isSelected={selectedTab === 'media'}
onSelect={this.handleTabSelect}
/>
<Tab
label="Documents"
label={i18n('documents')}
type="documents"
isSelected={selectedTab === 'documents'}
onSelect={this.handleTabSelect}

View File

@ -3383,6 +3383,7 @@ export class ConversationModel extends window.Backbone
return this.queueJob('onReadMessage', () =>
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.markRead(message.get('received_at')!, {
newestSentAt: message.get('sent_at'),
sendReadReceipts: false,
readAt,
})

View File

@ -209,7 +209,16 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
const readStatus = migrateLegacyReadStatus(this.attributes);
if (readStatus !== undefined) {
this.set('readStatus', readStatus, { silent: true });
this.set(
{
readStatus,
seenStatus:
readStatus === ReadStatus.Unread
? SeenStatus.Unseen
: SeenStatus.Seen,
},
{ silent: true }
);
}
const sendStateByConversationId = migrateLegacySendAttributes(

View File

@ -4,6 +4,7 @@
import type { MessageAttributesType } from '../model-types.d';
import { ReadStatus, maxReadStatus } from '../messages/MessageReadStatus';
import { notificationService } from './notifications';
import { SeenStatus } from '../MessageSeenStatus';
function markReadOrViewed(
messageAttrs: Readonly<MessageAttributesType>,
@ -17,6 +18,7 @@ function markReadOrViewed(
const nextMessageAttributes: MessageAttributesType = {
...messageAttrs,
readStatus: newReadStatus,
seenStatus: SeenStatus.Seen,
};
const { id: messageId, expireTimer, expirationStartTimestamp } = messageAttrs;

View File

@ -247,7 +247,7 @@ const dataInterface: ClientInterface = {
migrateConversationMessages,
getUnprocessedCount,
getAllUnprocessed,
getAllUnprocessedAndIncrementAttempts,
getUnprocessedById,
updateUnprocessedWithData,
updateUnprocessedsWithData,
@ -1443,8 +1443,8 @@ async function getUnprocessedCount() {
return channels.getUnprocessedCount();
}
async function getAllUnprocessed() {
return channels.getAllUnprocessed();
async function getAllUnprocessedAndIncrementAttempts() {
return channels.getAllUnprocessedAndIncrementAttempts();
}
async function getUnprocessedById(id: string) {

View File

@ -21,6 +21,7 @@ import type { UUIDStringType } from '../types/UUID';
import type { BadgeType } from '../badges/types';
import type { RemoveAllConfiguration } from '../types/RemoveAllConfiguration';
import type { LoggerType } from '../types/Logging';
import type { ReadStatus } from '../messages/MessageReadStatus';
export type AttachmentDownloadJobTypeType =
| 'long-message'
@ -397,7 +398,16 @@ export type DataInterface = {
storyId?: UUIDStringType;
}) => Promise<
Array<
Pick<MessageType, 'id' | 'source' | 'sourceUuid' | 'sent_at' | 'type'>
{ originalReadStatus: ReadStatus | undefined } & Pick<
MessageType,
| 'id'
| 'readStatus'
| 'seenStatus'
| 'sent_at'
| 'source'
| 'sourceUuid'
| 'type'
>
>
>;
getUnreadReactionsAndMarkRead: (options: {
@ -474,7 +484,7 @@ export type DataInterface = {
) => Promise<void>;
getUnprocessedCount: () => Promise<number>;
getAllUnprocessed: () => Promise<Array<UnprocessedType>>;
getAllUnprocessedAndIncrementAttempts: () => Promise<Array<UnprocessedType>>;
updateUnprocessedWithData: (
id: string,
data: UnprocessedUpdateType

View File

@ -244,7 +244,7 @@ const dataInterface: ServerInterface = {
migrateConversationMessages,
getUnprocessedCount,
getAllUnprocessed,
getAllUnprocessedAndIncrementAttempts,
updateUnprocessedWithData,
updateUnprocessedsWithData,
getUnprocessedById,
@ -595,6 +595,7 @@ async function removeDB(): Promise<void> {
);
}
logger.warn('removeDB: Removing all database files');
rimraf.sync(databaseFilePath);
rimraf.sync(`${databaseFilePath}-shm`);
rimraf.sync(`${databaseFilePath}-wal`);
@ -2075,7 +2076,18 @@ async function getUnreadByConversationAndMarkRead({
storyId?: UUIDStringType;
readAt?: number;
}): Promise<
Array<Pick<MessageType, 'id' | 'source' | 'sourceUuid' | 'sent_at' | 'type'>>
Array<
{ originalReadStatus: ReadStatus | undefined } & Pick<
MessageType,
| 'id'
| 'source'
| 'sourceUuid'
| 'sent_at'
| 'type'
| 'readStatus'
| 'seenStatus'
>
>
> {
const db = getInstance();
return db.transaction(() => {
@ -2109,10 +2121,10 @@ async function getUnreadByConversationAndMarkRead({
.prepare<Query>(
`
SELECT id, json FROM messages
INDEXED BY messages_unread
WHERE
readStatus = ${ReadStatus.Unread} AND
conversationId = $conversationId AND
seenStatus = ${SeenStatus.Unseen} AND
isStory = 0 AND
(${_storyIdPredicate(storyId, isGroup)}) AND
received_at <= $newestUnreadAt
ORDER BY received_at DESC, sent_at DESC;
@ -2151,7 +2163,9 @@ async function getUnreadByConversationAndMarkRead({
return rows.map(row => {
const json = jsonToObject<MessageType>(row.json);
return {
originalReadStatus: json.readStatus,
readStatus: ReadStatus.Read,
seenStatus: SeenStatus.Seen,
...pick(json, [
'expirationStartTimestamp',
'id',
@ -3181,32 +3195,58 @@ async function getUnprocessedCount(): Promise<number> {
return getCountFromTable(getInstance(), 'unprocessed');
}
async function getAllUnprocessed(): Promise<Array<UnprocessedType>> {
async function getAllUnprocessedAndIncrementAttempts(): Promise<
Array<UnprocessedType>
> {
const db = getInstance();
const { changes: deletedCount } = db
.prepare<Query>('DELETE FROM unprocessed WHERE timestamp < $monthAgo')
.run({
monthAgo: Date.now() - durations.MONTH,
});
return db.transaction(() => {
const { changes: deletedStaleCount } = db
.prepare<Query>('DELETE FROM unprocessed WHERE timestamp < $monthAgo')
.run({
monthAgo: Date.now() - durations.MONTH,
});
if (deletedCount !== 0) {
logger.warn(
`getAllUnprocessed: deleting ${deletedCount} old unprocessed envelopes`
);
}
if (deletedStaleCount !== 0) {
logger.warn(
'getAllUnprocessedAndIncrementAttempts: ' +
`deleting ${deletedStaleCount} old unprocessed envelopes`
);
}
const rows = db
.prepare<EmptyQuery>(
db.prepare<EmptyQuery>(
`
SELECT *
FROM unprocessed
ORDER BY timestamp ASC;
UPDATE unprocessed
SET attempts = attempts + 1
`
)
.all();
).run();
return rows;
const { changes: deletedInvalidCount } = db
.prepare<Query>(
`
DELETE FROM unprocessed
WHERE attempts >= $MAX_UNPROCESSED_ATTEMPTS
`
)
.run({ MAX_UNPROCESSED_ATTEMPTS });
if (deletedInvalidCount !== 0) {
logger.warn(
'getAllUnprocessedAndIncrementAttempts: ' +
`deleting ${deletedInvalidCount} invalid unprocessed envelopes`
);
}
return db
.prepare<EmptyQuery>(
`
SELECT *
FROM unprocessed
ORDER BY timestamp ASC;
`
)
.all();
})();
}
function removeUnprocessedsSync(ids: Array<string>): void {

View File

@ -29,7 +29,7 @@ describe('Errors', () => {
assert.isUndefined(error.stack);
const formattedError = Errors.toLogFormat(error);
assert.strictEqual(formattedError, 'Error: boom');
assert.strictEqual(formattedError, 'boom');
});
[0, false, null, undefined].forEach(value => {

View File

@ -1499,7 +1499,8 @@ describe('SignalProtocolStore', () => {
assert.equal(await store.loadSession(id), testSession);
assert.equal(await store.getSenderKey(id, distributionId), testSenderKey);
const allUnprocessed = await store.getAllUnprocessed();
const allUnprocessed =
await store.getAllUnprocessedAndIncrementAttempts();
assert.deepEqual(
allUnprocessed.map(({ envelope }) => envelope),
['second']
@ -1551,7 +1552,7 @@ describe('SignalProtocolStore', () => {
assert.equal(await store.loadSession(id), testSession);
assert.equal(await store.getSenderKey(id, distributionId), testSenderKey);
assert.deepEqual(await store.getAllUnprocessed(), []);
assert.deepEqual(await store.getAllUnprocessedAndIncrementAttempts(), []);
});
it('can be re-entered', async () => {
@ -1647,7 +1648,7 @@ describe('SignalProtocolStore', () => {
beforeEach(async () => {
await store.removeAllUnprocessed();
const items = await store.getAllUnprocessed();
const items = await store.getAllUnprocessedAndIncrementAttempts();
assert.strictEqual(items.length, 0);
});
@ -1687,7 +1688,7 @@ describe('SignalProtocolStore', () => {
}),
]);
const items = await store.getAllUnprocessed();
const items = await store.getAllUnprocessedAndIncrementAttempts();
assert.strictEqual(items.length, 3);
// they are in the proper order because the collection comparator is 'timestamp'
@ -1708,10 +1709,11 @@ describe('SignalProtocolStore', () => {
});
await store.updateUnprocessedWithData(id, { decrypted: 'updated' });
const items = await store.getAllUnprocessed();
const items = await store.getAllUnprocessedAndIncrementAttempts();
assert.strictEqual(items.length, 1);
assert.strictEqual(items[0].decrypted, 'updated');
assert.strictEqual(items[0].timestamp, NOW + 1);
assert.strictEqual(items[0].attempts, 1);
});
it('removeUnprocessed successfully deletes item', async () => {
@ -1726,7 +1728,21 @@ describe('SignalProtocolStore', () => {
});
await store.removeUnprocessed(id);
const items = await store.getAllUnprocessed();
const items = await store.getAllUnprocessedAndIncrementAttempts();
assert.strictEqual(items.length, 0);
});
it('getAllUnprocessedAndIncrementAttempts deletes items', async () => {
await store.addUnprocessed({
id: '1-one',
envelope: 'first',
timestamp: NOW + 1,
receivedAtCounter: 0,
version: 2,
attempts: 3,
});
const items = await store.getAllUnprocessedAndIncrementAttempts();
assert.strictEqual(items.length, 0);
});
});

View File

@ -166,7 +166,7 @@ describe('sql/markRead', () => {
readAt,
});
assert.lengthOf(markedRead2, 3, 'three messages marked read');
assert.lengthOf(markedRead2, 2, 'two messages marked read');
assert.strictEqual(markedRead2[0].id, message7.id, 'should be message7');
assert.strictEqual(

View File

@ -802,17 +802,11 @@ export default class MessageReceiver
return [];
}
const items = await this.storage.protocol.getAllUnprocessed();
const items =
await this.storage.protocol.getAllUnprocessedAndIncrementAttempts();
log.info('getAllFromCache loaded', items.length, 'saved envelopes');
return items.map(item => {
const { attempts = 0 } = item;
return {
...item,
attempts: attempts + 1,
};
});
return items;
}
private async decryptAndCacheBatch(

View File

@ -3,11 +3,17 @@
/* eslint-disable max-classes-per-file */
import { get, has } from 'lodash';
export function toLogFormat(error: unknown): string {
if (error instanceof Error && error.stack) {
return error.stack;
}
if (has(error, 'message')) {
return get(error, 'message');
}
return String(error);
}

View File

@ -1,6 +1,8 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { omit } from 'lodash';
import type { ConversationAttributesType } from '../model-types.d';
import { hasErrors } from '../state/selectors/message';
import { readReceiptsJobQueue } from '../jobs/readReceiptsJobQueue';
@ -9,6 +11,7 @@ import { notificationService } from '../services/notifications';
import { isGroup } from './whatTypeOfConversation';
import * as log from '../logging/log';
import { getConversationIdForLogging } from './idForLogging';
import { ReadStatus } from '../messages/MessageReadStatus';
export async function markConversationRead(
conversationAttrs: ConversationAttributesType,
@ -76,11 +79,12 @@ export async function markConversationRead(
const message = window.MessageController.getById(messageSyncData.id);
// we update the in-memory MessageModel with the fresh database call data
if (message) {
message.set(messageSyncData);
message.set(omit(messageSyncData, 'originalReadStatus'));
}
return {
messageId: messageSyncData.id,
originalReadStatus: messageSyncData.originalReadStatus,
senderE164: messageSyncData.source,
senderUuid: messageSyncData.sourceUuid,
senderId: window.ConversationController.ensureContactIds({
@ -92,14 +96,18 @@ export async function markConversationRead(
};
});
// Some messages we're marking read are local notifications with no sender
// If a message has errors, we don't want to send anything out about it.
// Some messages we're marking read are local notifications with no sender or were just
// unseen and not unread.
// Also, if a message has errors, we don't want to send anything out about it:
// read syncs - let's wait for a client that really understands the message
// to mark it read. we'll mark our local error read locally, though.
// read receipts - here we can run into infinite loops, where each time the
// conversation is viewed, another error message shows up for the contact
const unreadMessagesSyncData = allReadMessagesSync.filter(
item => Boolean(item.senderId) && !item.hasErrors
item =>
Boolean(item.senderId) &&
item.originalReadStatus === ReadStatus.Unread &&
!item.hasErrors
);
const readSyncs: Array<{

View File

@ -2138,6 +2138,21 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
getAbsoluteAttachmentPath(item.thumbnail?.path ?? ''),
}));
if (!media.length) {
log.error(
'showLightbox: unable to load attachment',
attachments.map(x => ({
contentType: x.contentType,
error: x.error,
flags: x.flags,
path: x.path,
size: x.size,
}))
);
showToast(ToastUnableToLoadAttachment);
return;
}
const selectedMedia =
media.find(item => attachment.path === item.path) || media[0];