Compare commits
73 Commits
Author | SHA1 | Date |
---|---|---|
Scott Nonnenberg | e72db5cd08 | |
Scott Nonnenberg | 84943997a6 | |
Scott Nonnenberg | 326bda9595 | |
Scott Nonnenberg | e06b9878ba | |
Scott Nonnenberg | b8a59507c2 | |
Scott Nonnenberg | 0378cdff82 | |
Scott Nonnenberg | af6f32ee05 | |
Scott Nonnenberg | acf90adb62 | |
Scott Nonnenberg | 4515ad3db1 | |
Scott Nonnenberg | 4ca502dea0 | |
Scott Nonnenberg | 260c6f7da0 | |
Scott Nonnenberg | 38b43b350f | |
Scott Nonnenberg | c744a81d42 | |
Scott Nonnenberg | 6c8cff56bb | |
Scott Nonnenberg | d1d7ab61e0 | |
Scott Nonnenberg | da29423e48 | |
Scott Nonnenberg | d4bf4e189c | |
Scott Nonnenberg | 30745676e8 | |
Scott Nonnenberg | 04cb67fb71 | |
Scott Nonnenberg | 4309938861 | |
Scott Nonnenberg | b0cfb5a264 | |
Scott Nonnenberg | fae282cf8e | |
Scott Nonnenberg | 327fc7b215 | |
Daniel Gasienica | c8fb28c3d9 | |
Daniel Gasienica | eb4865b74c | |
Scott Nonnenberg | 74c393e893 | |
Scott Nonnenberg | 06bcae0fce | |
Scott Nonnenberg | 53101148a4 | |
Daniel Gasienica | b8a237bb37 | |
Scott Nonnenberg | ba02fe6bbe | |
Daniel Gasienica | 206196011f | |
Daniel Gasienica | e99a81d56a | |
Daniel Gasienica | 2256fed715 | |
Daniel Gasienica | 6621b8ac69 | |
Daniel Gasienica | 3180b6ca8d | |
Daniel Gasienica | 549e9c8d60 | |
Scott Nonnenberg | e9772b66ec | |
Scott Nonnenberg | a02612cd11 | |
Scott Nonnenberg | 636fae1857 | |
Scott Nonnenberg | 16a613066f | |
Scott Nonnenberg | 246f958616 | |
Scott Nonnenberg | f158220053 | |
Scott Nonnenberg | a246e1a614 | |
Scott Nonnenberg | 00ca91bc9b | |
Scott Nonnenberg | 10ad8b4008 | |
Scott Nonnenberg | 55614c245f | |
Scott Nonnenberg | 8bde6ec1cd | |
Scott Nonnenberg | 30c26cf210 | |
Scott Nonnenberg | 5e186cb50d | |
Scott Nonnenberg | b168dab826 | |
Scott Nonnenberg | 86598f2a6a | |
Scott Nonnenberg | 539c13871e | |
Scott Nonnenberg | 3b7248c09a | |
Scott Nonnenberg | a375d132dd | |
Scott Nonnenberg | 9942499230 | |
Scott Nonnenberg | 727be306ea | |
Scott Nonnenberg | bf02375212 | |
Scott Nonnenberg | 16a3763030 | |
Scott Nonnenberg | adb30e6490 | |
Scott Nonnenberg | 9585f14f4e | |
Scott Nonnenberg | 702ace15ec | |
Scott Nonnenberg | ac7a346412 | |
Scott Nonnenberg | cc5230f0b0 | |
Scott Nonnenberg | f047d36fc1 | |
Scott Nonnenberg | b3091e77f6 | |
Scott Nonnenberg | d4a77bded2 | |
Scott Nonnenberg | e9e4f168a3 | |
Scott Nonnenberg | 46e9ad3317 | |
Scott Nonnenberg | 16a09a8f94 | |
Scott Nonnenberg | d22e678d18 | |
Scott Nonnenberg | 3cbf67ceb0 | |
Scott Nonnenberg | e9d2694f8f | |
Scott Nonnenberg | a064e4b9b2 |
|
@ -1,6 +1,6 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- '0.10'
|
||||
- '6'
|
||||
install:
|
||||
- npm install
|
||||
script:
|
||||
|
|
|
@ -142,7 +142,7 @@
|
|||
"description": "Explain the purpose of the notification settings"
|
||||
},
|
||||
"timestamp_h": {
|
||||
"message": "1 hour",
|
||||
"message": "1 time",
|
||||
"description": "Brief timestamp for messages sent about one minute ago. Displayed in the conversation list and message bubble."
|
||||
},
|
||||
"timerOption_0_seconds_abbreviated": {
|
||||
|
@ -174,7 +174,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"delete": {
|
||||
"message": "Delete",
|
||||
"message": "Slet",
|
||||
"description": ""
|
||||
},
|
||||
"verified": {
|
||||
|
@ -246,7 +246,7 @@
|
|||
}
|
||||
},
|
||||
"timestamp_s": {
|
||||
"message": "now",
|
||||
"message": "nu",
|
||||
"description": "Brief timestamp for messages sent less than a minute ago. Displayed in the conversation list and message bubble."
|
||||
},
|
||||
"changedRecentlyMultiple": {
|
||||
|
@ -486,7 +486,7 @@
|
|||
"description": "Very short format indicating current timer setting in the conversation header"
|
||||
},
|
||||
"sync": {
|
||||
"message": "Contacts",
|
||||
"message": "Kontakter",
|
||||
"description": "Label for contact and group sync settings"
|
||||
},
|
||||
"timerOption_1_week": {
|
||||
|
@ -550,7 +550,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"deleteMessage": {
|
||||
"message": "Delete this message",
|
||||
"message": "Slet besked",
|
||||
"description": ""
|
||||
},
|
||||
"timerOption_0_seconds": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"scrollDown": {
|
||||
"message": "Scroll to bottom of conversation",
|
||||
"message": "Κύλιση στο τέρμα της συζήτησης",
|
||||
"description": "Alt text for button to take user down to bottom of conversation, shown when user scrolls up"
|
||||
},
|
||||
"timerOption_10_seconds": {
|
||||
|
@ -8,11 +8,11 @@
|
|||
"description": "Label for a selectable option in the message expiration timer menu"
|
||||
},
|
||||
"view": {
|
||||
"message": "View",
|
||||
"message": "Εμφάνιση",
|
||||
"description": "Used as a label on a button allowing user to see more information"
|
||||
},
|
||||
"upgradingDatabase": {
|
||||
"message": "Upgrading database. This may take some time...",
|
||||
"message": "Αναβάθμιση βάσης δεδομένων. Αυτό μπορεί να πάρει κάποια ώρα...",
|
||||
"description": "Message shown on the loading screen when we're changing database structure on first run of a new version"
|
||||
},
|
||||
"lastSynced": {
|
||||
|
@ -20,7 +20,7 @@
|
|||
"description": "Label for date and time of last sync operation"
|
||||
},
|
||||
"attemptingReconnection": {
|
||||
"message": "Attempting reconnect in $reconnect_duration_in_seconds$ seconds",
|
||||
"message": "Απόπειρα επανασύνδεσης σε $reconnect_duration_in_seconds$ δευτερόλεπτα",
|
||||
"description": "",
|
||||
"placeholders": {
|
||||
"reconnect_duration_in_seconds": {
|
||||
|
@ -30,15 +30,15 @@
|
|||
}
|
||||
},
|
||||
"unsupportedAttachment": {
|
||||
"message": "Μη υποστηριζόμενος τύπος συνημμένου. Πατήστε για αποθήκευση.",
|
||||
"message": "Μη υποστηριζόμενος τύπος συνημμένου. Κλικ για αποθήκευση.",
|
||||
"description": "Displayed for incoming unsupported attachment"
|
||||
},
|
||||
"showSafetyNumber": {
|
||||
"message": "Show safety number",
|
||||
"message": "Προβολή αριθμού ασφαλείας",
|
||||
"description": ""
|
||||
},
|
||||
"androidMessageLengthWarning": {
|
||||
"message": "Android clients will only receive the first 2000 characters of this message.",
|
||||
"message": "Οι παραλήπτες σε Android θα παραλάβουν μόνο τους πρώτους 2000 χαρακτήρες αυτού του μηνύματος.",
|
||||
"description": "Warning that long messages could not get received completely by Android clients."
|
||||
},
|
||||
"youChangedTheTimer": {
|
||||
|
@ -76,7 +76,7 @@
|
|||
"description": "Conversation menu option to enable disappearing messages"
|
||||
},
|
||||
"deleteWarning": {
|
||||
"message": "Are you sure? Clicking 'delete' will permanently remove this message from this device.",
|
||||
"message": "Είσαι σίγουρος/η; Πατώντας \"διαγραφή\", αυτό το μήνυμα θα αφαιρεθεί μόνιμα από αυτή την συσκευή.",
|
||||
"description": ""
|
||||
},
|
||||
"showMore": {
|
||||
|
@ -84,7 +84,7 @@
|
|||
"description": "Displays the details of a key change"
|
||||
},
|
||||
"verifyHelp": {
|
||||
"message": "If you wish to verify the security of your end-to-end encryption with $name$, compare the numbers above with the numbers on their device.",
|
||||
"message": "Αν επιθυμείς να επιβεβαιώσεις την ασφάλεια της κρυπτογράφηση από άκρο σε άκρο με τον/την $name$, σύγκρινε τους παραπάνω αριθμούς με τους αριθμούς στην συσκευή του/της.",
|
||||
"description": "",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -94,7 +94,7 @@
|
|||
}
|
||||
},
|
||||
"membersNeedingVerification": {
|
||||
"message": "Your safety numbers with these group members have changed since you last verified. Click a group member to see your new safety number with them.",
|
||||
"message": "Οι αριθμοί ασφαλείας με αυτά τα μέλη της ομάδας άλλαξαν από την τελευταία φορά που επιβεβαιώθηκαν. Κάνε κλικ σε ένα μέλος της ομάδας για να δεις τον νέο αριθμό ασφαλείας με αυτόν/ήν.",
|
||||
"description": "When there are multiple previously-verified group members with safety number changes, a banner will be shown. The list of contacts with safety number changes is shown, and this text introduces that list."
|
||||
},
|
||||
"timerOption_5_minutes_abbreviated": {
|
||||
|
@ -106,19 +106,19 @@
|
|||
"description": "Displayed for outgoing unsupported attachment"
|
||||
},
|
||||
"exportAgain": {
|
||||
"message": "Export again",
|
||||
"message": "Εξαγωγή ξανά",
|
||||
"description": "If user has already exported once, this button allows user to do it again if needed"
|
||||
},
|
||||
"clickToSave": {
|
||||
"message": "Click to save",
|
||||
"message": "Κλικ για αποθήκευση",
|
||||
"description": "Hover text for attachment filenames"
|
||||
},
|
||||
"messagesBelow": {
|
||||
"message": "New messages below",
|
||||
"message": "Νέα μηνύματα παρακάτω",
|
||||
"description": "Alt text for button to take user down to bottom of conversation with more than one message out of screen"
|
||||
},
|
||||
"installGeneratingKeys": {
|
||||
"message": "Δημιουργούνται Κλειδιά",
|
||||
"message": "Παράγονται Κλειδιά",
|
||||
"description": ""
|
||||
},
|
||||
"resetSession": {
|
||||
|
@ -126,15 +126,15 @@
|
|||
"description": "This is a menu item for resetting the session, using the imperative case, as in a command."
|
||||
},
|
||||
"offline": {
|
||||
"message": "Offline",
|
||||
"message": "Εκτός σύνδεσης",
|
||||
"description": "Displayed when the desktop client has no network connection."
|
||||
},
|
||||
"welcomeToSignal": {
|
||||
"message": "Καλώς ήρθατε στο Signal",
|
||||
"message": "Καλωσόρισες στο Signal",
|
||||
"description": ""
|
||||
},
|
||||
"checkNetworkConnection": {
|
||||
"message": "Check your network connection.",
|
||||
"message": "Έλεγξε την σύνδεση στο δίκτυο.",
|
||||
"description": "Obvious instructions for when a user's computer loses its network connection"
|
||||
},
|
||||
"notificationSettingsDialog": {
|
||||
|
@ -146,11 +146,11 @@
|
|||
"description": "Brief timestamp for messages sent about one minute ago. Displayed in the conversation list and message bubble."
|
||||
},
|
||||
"timerOption_0_seconds_abbreviated": {
|
||||
"message": "Σβησμένο",
|
||||
"message": "κλειστό",
|
||||
"description": "Short format indicating current timer setting in the conversation list snippet"
|
||||
},
|
||||
"syncExplanation": {
|
||||
"message": "Εισαγωγή όλων των Signal ομάδων και επαφών από την φορητή συσκευή σου.",
|
||||
"message": "Εισαγωγή όλων των ομάδων και επαφών Signal από την φορητή συσκευή σου.",
|
||||
"description": "Explanatory text for sync settings"
|
||||
},
|
||||
"restartSignal": {
|
||||
|
@ -158,7 +158,7 @@
|
|||
"description": "Menu item for restarting the program."
|
||||
},
|
||||
"youLeftTheGroup": {
|
||||
"message": "You left the group",
|
||||
"message": "Αποχώρησες από την ομάδα",
|
||||
"description": "Displayed when a user can't send a message because they have left the group"
|
||||
},
|
||||
"deleteMessages": {
|
||||
|
@ -166,7 +166,7 @@
|
|||
"description": "Menu item for deleting messages, title case."
|
||||
},
|
||||
"confirmMigration": {
|
||||
"message": "Start migration process? You will not be able to send or receive Signal messages from this application while the migration is in progress.",
|
||||
"message": "Εκκίνηση της διαδικασίας μεταφοράς; Δεν θα μπορείς να στείλεις ή να παραλάβεις μηνύματα Signal μέσω αυτής της εφαρμογής όσο η μεταφορά είναι σε εξέλιξη.",
|
||||
"description": "Confirmation dialogue when beginning migration"
|
||||
},
|
||||
"incomingError": {
|
||||
|
@ -174,19 +174,19 @@
|
|||
"description": ""
|
||||
},
|
||||
"delete": {
|
||||
"message": "Delete",
|
||||
"message": "Διαγραφή",
|
||||
"description": ""
|
||||
},
|
||||
"verified": {
|
||||
"message": "Verified",
|
||||
"message": "Επιβεβαιωμένο",
|
||||
"description": ""
|
||||
},
|
||||
"selectAContact": {
|
||||
"message": "Επιλέξτε μία επαφή ή ομάδα για να ξεκινήσετε συζήτηση.",
|
||||
"message": "Επέλεξε μια επαφή ή ομάδα για να ξεκινήσεις να συζητάς.",
|
||||
"description": ""
|
||||
},
|
||||
"exportError": {
|
||||
"message": "Unfortunately, something went wrong during the export. First, double-check your target empty directory for write access and enough space. Then, please submit a debug log so we can help you get migrated!",
|
||||
"message": "Δυστυχώς, κάτι πήγε στραβά κατά την εξαγωγή. Πρώτα, έλεγξε τα δικαιώματα εγγραφής και τον διαθέσιμο χώρο στον φάκελο που διάλεξες. Έπειτα, παρακαλώ καταχώρησε ένα αρχείο καταγραφής αποσφαλμάτωσης ώστε να σε βοηθήσουμε να μεταφερθείς!",
|
||||
"description": "Helper text if the user went forward on migrating the app, but ran into an error"
|
||||
},
|
||||
"installConnecting": {
|
||||
|
@ -194,11 +194,11 @@
|
|||
"description": "Displayed when waiting for the QR Code"
|
||||
},
|
||||
"selectedLocation": {
|
||||
"message": "your selected location",
|
||||
"message": "επιλεγμένη τοποθεσία σου",
|
||||
"description": "Message shown as the export location if we didn't capture the target directory"
|
||||
},
|
||||
"verifyContact": {
|
||||
"message": "Ίσως επιθυμείς να $tag_start$ επιβεβαιώσεις $tag_end$ τον κωδικό ασφαλείας σου με αυτή την επαφή.",
|
||||
"message": "Ίσως επιθυμείς να $tag_start$ επιβεβαιώσεις $tag_end$ τον αριθμό ασφαλείας σου με αυτή την επαφή.",
|
||||
"description": "Use $tag_start$ and $tag_end$ to wrap the word or phrase in this sentence that the user should click on in order to navigate to the verification screen. These placeholders will be replaced with appropriate HTML code.",
|
||||
"placeholders": {
|
||||
"tag_start": {
|
||||
|
@ -210,7 +210,7 @@
|
|||
}
|
||||
},
|
||||
"changedRecently": {
|
||||
"message": "Your safety number with $name$ has changed recently. This could mean that someone is trying to intercept your communication or that $name$ has simply reinstalled Signal.",
|
||||
"message": "Ο αριθμός ασφαλείας με τον/την $name$ άλλαξε πρόσφατα. Αυτό μπορεί να σημαίνει πως κάποιος προσπαθεί να υποκλέψει την επικοινωνία σου ή ότι απλά ο/η $name$ επανεγκατέστησε το Signal.",
|
||||
"description": "Shown on confirmation dialog when user attempts to send a message",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -224,15 +224,15 @@
|
|||
"description": "Label for the time a message was sent"
|
||||
},
|
||||
"migrationWarning": {
|
||||
"message": "The Signal Desktop Chrome app has been deprecated. Would you like to migrate to the new Signal Desktop now?",
|
||||
"message": "Η εφαρμογή Signal Desktop για τον Chrome είναι παρωχημένη. Θέλεις να μεταφερθείς στο νέο Signal Desktop τωρα;",
|
||||
"description": "Warning notification that this version of the app has been deprecated and the user must migrate"
|
||||
},
|
||||
"migrate": {
|
||||
"message": "Migrate",
|
||||
"message": "Μεταφορά",
|
||||
"description": "Button label to begin migrating this client to Electron"
|
||||
},
|
||||
"theyChangedTheTimer": {
|
||||
"message": "$name$ ρύθμισε την αντίστροφη μέτρηση ίση με $time$",
|
||||
"message": "Ο/Η $name$ ρύθμισε την αντίστροφη μέτρηση ίση με $time$",
|
||||
"description": "Message displayed when someone else changes the message expiration timer in a conversation.",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -250,7 +250,7 @@
|
|||
"description": "Brief timestamp for messages sent less than a minute ago. Displayed in the conversation list and message bubble."
|
||||
},
|
||||
"changedRecentlyMultiple": {
|
||||
"message": "Your safety numbers with multiple group members have changed recently. This could mean that someone is trying to intercept your communication or that they have simply reinstalled Signal.",
|
||||
"message": "Οι αριθμοί ασφαλείας με πολλαπλά μέλη της ομάδας άλλαξαν πρόσφατα. Αυτό μπορεί να σημαίνει πως κάποιος προσπαθεί να υποκλέψει την επικοινωνία σου ή ότι απλά επανεγκατέστησαν το Signal.",
|
||||
"description": "Shown on confirmation dialog when user attempts to send a message"
|
||||
},
|
||||
"timerOption_1_day": {
|
||||
|
@ -258,7 +258,7 @@
|
|||
"description": "Label for a selectable option in the message expiration timer menu"
|
||||
},
|
||||
"exporting": {
|
||||
"message": "Please wait while we export your data. It may take several minutes. You can still use Signal on your phone and other devices during this time.",
|
||||
"message": "Παρακαλώ περίμενε όσο εξάγουμε τα δεδομένα σου. Αυτό μπορεί να πάρει αρκετά λεπτά. Μπορείς ακόμα να χρησιμοποιείς το Signal στο κινητό σου και σε άλλες συσκευές.",
|
||||
"description": "Message shown on the migration screen while we export data"
|
||||
},
|
||||
"reportIssue": {
|
||||
|
@ -270,11 +270,11 @@
|
|||
"description": "Displayed in notifications for only 1 message"
|
||||
},
|
||||
"nameOnly": {
|
||||
"message": "Μόνο το όνομα του αποστολέα",
|
||||
"message": "Μόνο όνομα αποστολέα",
|
||||
"description": "Label for setting notifications to display sender name only"
|
||||
},
|
||||
"from": {
|
||||
"message": "Απο",
|
||||
"message": "Από",
|
||||
"description": "Label for the sender of a message"
|
||||
},
|
||||
"deleteConversationConfirmation": {
|
||||
|
@ -282,19 +282,19 @@
|
|||
"description": "Confirmation dialog text that asks the user if they really wish to delete the conversation. Answer buttons use the strings 'ok' and 'cancel'. The deletion is permanent, i.e. it cannot be undone."
|
||||
},
|
||||
"unlinkedWarning": {
|
||||
"message": "Relink Signal Desktop to your mobile device to continue messaging.",
|
||||
"message": "Επανασύνδεσε το Signal Desktop με την φορητή συσκευή σου για να συνεχίσεις να συνομιλείς.",
|
||||
"description": ""
|
||||
},
|
||||
"debugLogExplanation": {
|
||||
"message": "Αυτό το αρχείο καταγραφής συμβάντων θα δημοσιευτεί στο internet ώστε να το εξετάσουν εθελοντές. Μπορείς να το μελετήσεις και να το αλλάξεις πριν το υποβάλλεις.",
|
||||
"message": "Αυτό το αρχείο καταγραφής συμβάντων θα δημοσιευτεί στο διαδίκτυο ώστε να το εξετάσουν εθελοντές. Μπορείς να το μελετήσεις και να το αλλάξεις πριν το υποβάλλεις.",
|
||||
"description": ""
|
||||
},
|
||||
"newPhoneNumber": {
|
||||
"message": "Βάλτε νούμερο τηλεφώνου για να προσθέσετε επαφή",
|
||||
"message": "Βάλε αριθμό τηλεφώνου για να προσθέσεις μια επαφή",
|
||||
"description": "Placeholder for adding a new number to a contact"
|
||||
},
|
||||
"noLongerVerified": {
|
||||
"message": "Your safety number with $name$ has changed and is no longer verified. Click to show.",
|
||||
"message": "Ο αριθμός ασφαλείας με τον/την $name$ άλλαξε και δεν είναι πια επιβεβαιωμένος. Κλικ για εμφάνιση.",
|
||||
"description": "Shown in converation banner when user's safety number has changed, but they were previously verified.",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -308,11 +308,11 @@
|
|||
"description": "Placeholder text in the search input"
|
||||
},
|
||||
"someRecipientsFailed": {
|
||||
"message": "Some recipients failed.",
|
||||
"message": "Κάποιοι παραλήπτες απέτυχαν.",
|
||||
"description": "When you send to multiple recipients via a group, and the message went to some recipients but not others."
|
||||
},
|
||||
"noNameOrMessage": {
|
||||
"message": "Ούτε το όνομα αποστολέα, ούτε το μήνυμα",
|
||||
"message": "Ούτε όνομα αποστολέα, ούτε μήνυμα",
|
||||
"description": "Label for setting notifications to display no name and no message text"
|
||||
},
|
||||
"syncNow": {
|
||||
|
@ -324,7 +324,7 @@
|
|||
"description": "Informational text displayed if a sync operation times out."
|
||||
},
|
||||
"unlinked": {
|
||||
"message": "Unlinked",
|
||||
"message": "Αποσυνδεδεμένο",
|
||||
"description": ""
|
||||
},
|
||||
"installFollowUs": {
|
||||
|
@ -338,11 +338,11 @@
|
|||
}
|
||||
},
|
||||
"learnMore": {
|
||||
"message": "Πληροφορίες για την επαλήθευση κωδικών ασφαλείας",
|
||||
"message": "Μάθε περισσότερα για την επαλήθευση αριθμών ασφαλείας",
|
||||
"description": "Text that links to a support article on verifying safety numbers"
|
||||
},
|
||||
"unreadMessages": {
|
||||
"message": "$count$ Unread Messages",
|
||||
"message": "$count$ μη αναγνωσμένα μηνύματα",
|
||||
"description": "Text for unread message separator, with count",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
|
@ -352,7 +352,7 @@
|
|||
}
|
||||
},
|
||||
"installTooManyDevices": {
|
||||
"message": "Δυστυχώς έχετε ήδη πολλές συνδεδεμένες συσκευές. Προσπαθήστε να αφαιρέσετε μερικές.",
|
||||
"message": "Δυστυχώς έχεις ήδη πολλές συνδεδεμένες συσκευές. Προσπάθησε να αφαιρέσεις μερικές.",
|
||||
"description": ""
|
||||
},
|
||||
"installSignalLinks": {
|
||||
|
@ -386,7 +386,7 @@
|
|||
"description": "Very short format indicating current timer setting in the conversation header"
|
||||
},
|
||||
"loadingMessages": {
|
||||
"message": "Loading messages. $count$ so far...",
|
||||
"message": "Φόρτωση μηνυμάτων. $count$ μέχρι στιγμής...",
|
||||
"description": "Message shown on the loading screen when we're catching up on the backlog of messages",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
|
@ -396,7 +396,7 @@
|
|||
}
|
||||
},
|
||||
"changedSinceVerified": {
|
||||
"message": "Your safety number with $name$ has changed since you last verified. This could mean that someone is trying to intercept your communication or that $name$ has simply reinstalled Signal.",
|
||||
"message": "Ο αριθμός ασφαλείας με τον/την $name$ άλλαξε από την τελευταία φορά που επιβεβαιώθηκε. Αυτό μπορεί να σημαίνει πως κάποιος προσπαθεί να υποκλέψει την επικοινωνία σου ή ότι απλά ο/η $name$ επανεγκατέστησε το Signal.",
|
||||
"description": "Shown on confirmation dialog when user attempts to send a message",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -410,7 +410,7 @@
|
|||
"description": "Label text for button to upgrade the app to the latest version"
|
||||
},
|
||||
"yourSafetyNumberWith": {
|
||||
"message": "Your safety number with $name$:",
|
||||
"message": "Ο αριθμός ασφαλείας με τον/την $name$:",
|
||||
"description": "Heading for safety number view",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -428,7 +428,7 @@
|
|||
"description": "Header for theme settings"
|
||||
},
|
||||
"newIdentity": {
|
||||
"message": "Νέος κωδικός ασφαλείας",
|
||||
"message": "Νέος αριθμός ασφαλείας",
|
||||
"description": "Header for a key change dialog"
|
||||
},
|
||||
"installTagline": {
|
||||
|
@ -440,7 +440,7 @@
|
|||
"description": "Description for audio notification setting"
|
||||
},
|
||||
"isNotVerified": {
|
||||
"message": "You have not verified your safety number with $name$.",
|
||||
"message": "Δεν έχεις επιβεβαιώσει τον αριθμό ασφαλείας με τον/την $name$.",
|
||||
"description": "Summary state shown at top of the safety number screen if user has not verified contact.",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -458,7 +458,7 @@
|
|||
"description": "Label for a disabled sync button while sync is in progress."
|
||||
},
|
||||
"multipleNoLongerVerified": {
|
||||
"message": "Your safety numbers with multiple members of this group have changed and are no longer verified. Click to show.",
|
||||
"message": "Οι αριθμοί ασφαλείας με πολλαπλά μέλη αυτής της ομάδας άλλαξαν και δεν είναι πια επιβεβαιωμένοι. Κλικ για εμφάνιση.",
|
||||
"description": "Shown in conversation banner when more than one group member's safety number has changed, but they were previously verified."
|
||||
},
|
||||
"acceptNewKey": {
|
||||
|
@ -502,7 +502,7 @@
|
|||
"description": "Label for the time a message was received"
|
||||
},
|
||||
"youMarkedAsNotVerified": {
|
||||
"message": "You marked your safety number with $name$ as unverified.",
|
||||
"message": "Σημείωσες τον αριθμό ασφαλείας με τον/την $name$ ως μη επιβεβαιωμένο.",
|
||||
"description": "Shown in the conversation history when the user marks a contact as not verified, whether on the safety number screen or by dismissing a banner or dialog.",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -512,7 +512,7 @@
|
|||
}
|
||||
},
|
||||
"isVerified": {
|
||||
"message": "You have verified your safety number with $name$.",
|
||||
"message": "Επιβεβαίωσες τον αριθμό ασφαλείας με τον/την $name$.",
|
||||
"description": "Summary state shown at top of the safety number screen if user has verified contact.",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -526,11 +526,11 @@
|
|||
"description": "Label for a selectable option in the message expiration timer menu"
|
||||
},
|
||||
"sendMessage": {
|
||||
"message": "Νέο Μήνυμα",
|
||||
"message": "Στείλε ένα μήνυμα",
|
||||
"description": "Placeholder text in the message entry field"
|
||||
},
|
||||
"me": {
|
||||
"message": "Me",
|
||||
"message": "Εγώ",
|
||||
"description": "The label for yourself when shown in a group member list"
|
||||
},
|
||||
"mediaMessage": {
|
||||
|
@ -538,7 +538,7 @@
|
|||
"description": "Description of a message that has an attachment and no text, displayed in the conversation list as a preview."
|
||||
},
|
||||
"unreadMessage": {
|
||||
"message": "1 Unread Message",
|
||||
"message": "1 μη αναγνωσμένο μήνυμα",
|
||||
"description": "Text for unread message separator, just one message"
|
||||
},
|
||||
"expiredWarning": {
|
||||
|
@ -550,35 +550,35 @@
|
|||
"description": ""
|
||||
},
|
||||
"deleteMessage": {
|
||||
"message": "Delete this message",
|
||||
"message": "Διαγραφή αυτού του μηνύματος",
|
||||
"description": ""
|
||||
},
|
||||
"timerOption_0_seconds": {
|
||||
"message": "Σβησμένο",
|
||||
"message": "κλειστό",
|
||||
"description": "Label for option to turn off message expiration in the timer menu"
|
||||
},
|
||||
"installAndroidInstructions": {
|
||||
"message": "Ανοίξτε το Signal στο κινητό σας και πηγαίνετε στις Ρυθμίσεις > Συνδεδεμένες συσκευές. Πιέστε το πλήκτρο προσθήκης νέας συσκευής και σαρώστε το παραπάνω γραφικό.",
|
||||
"message": "Άνοιξε το Signal στο κινητό σου και πήγαινε στις Ρυθμίσεις > Συνδεδεμένες συσκευές. Πάτα το πλήκτρο προσθήκης νέας συσκευής και σάρωσε το παραπάνω γραφικό.",
|
||||
"description": ""
|
||||
},
|
||||
"migrationDisconnecting": {
|
||||
"message": "Disconnecting...",
|
||||
"message": "Αποσυνδέεται...",
|
||||
"description": "Displayed while we wait for pending incoming messages to process"
|
||||
},
|
||||
"invalidNumberError": {
|
||||
"message": "Λάθος νούμερο",
|
||||
"message": "Μη έγκυρος αριθμός",
|
||||
"description": "When a person inputs a number that is invalid"
|
||||
},
|
||||
"installWelcome": {
|
||||
"message": "Καλώς ήρθατε στο Signal Desktop",
|
||||
"message": "Καλωσόρισες στο Signal Desktop",
|
||||
"description": "Welcome title on the install page"
|
||||
},
|
||||
"messageBelow": {
|
||||
"message": "New message below",
|
||||
"message": "Νέο μήνυμα παρακάτω",
|
||||
"description": "Alt text for button to take user down to bottom of conversation with a new message out of screen"
|
||||
},
|
||||
"exportComplete": {
|
||||
"message": "Your data has been exported to: <p><b>$location$</b></p> You'll be able to import this data as you set up <a target='_blank' href='https://support.whispersystems.org/hc/en-us/articles/214507138'>the new Signal Desktop</a>.",
|
||||
"message": "Τα δεδομένα σου εξήχθησαν σε: <p><b>$location$</b></p> Θα μπορέσεις να εισάγεις αυτά τα δεδομένα κατά την εγκατάσταση του <a target='_blank' href='https://support.whispersystems.org/hc/en-us/articles/214507138'>νέου Signal Desktop</a>.",
|
||||
"description": "Message shown on the migration screen when we are done exporting data",
|
||||
"placeholders": {
|
||||
"location": {
|
||||
|
@ -592,7 +592,7 @@
|
|||
"description": "Displayed when we can't connect to the server."
|
||||
},
|
||||
"unverify": {
|
||||
"message": "Mark as not verified",
|
||||
"message": "Σημείωση ως μη επιβεβαιωμένο",
|
||||
"description": ""
|
||||
},
|
||||
"messageNotSent": {
|
||||
|
@ -608,7 +608,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"unblockToSend": {
|
||||
"message": "Ξεκλείδωσε αυτή την επαφή για να στείλεις ένα μήνυμα",
|
||||
"message": "Ξεμπλόκαρε αυτή την επαφή για να στείλεις ένα μήνυμα",
|
||||
"description": "Brief message shown when trying to message a blocked number"
|
||||
},
|
||||
"installIHaveSignalButton": {
|
||||
|
@ -616,15 +616,15 @@
|
|||
"description": "Button for the user to confirm that they have Signal for Android"
|
||||
},
|
||||
"unnamedFile": {
|
||||
"message": "Unnamed File",
|
||||
"message": "Ανώνυμο αρχείο",
|
||||
"description": "Hover text for attachment filenames"
|
||||
},
|
||||
"sendAnyway": {
|
||||
"message": "Send Anyway",
|
||||
"message": "Αποστολή όπως και να 'χει",
|
||||
"description": "Used on a warning dialog to make it clear that it might be risky to send the message."
|
||||
},
|
||||
"youMarkedAsVerified": {
|
||||
"message": "You marked your safety number with $name$ as verified.",
|
||||
"message": "Σημείωσες τον αριθμό ασφαλείας με τον/την $name$ ως επιβεβαιωμένο.",
|
||||
"description": "Shown in the conversation history when the user marks a contact as verified.",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -634,11 +634,11 @@
|
|||
}
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Connecting",
|
||||
"message": "Συνδέεται",
|
||||
"description": "Displayed when the desktop client is currently connecting to the server."
|
||||
},
|
||||
"sessionEnded": {
|
||||
"message": "Η ασφαλής σύνδεση επανεκκινήθηκε",
|
||||
"message": "Η ασφαλής συνεδρία επανεκκινήθηκε",
|
||||
"description": "This is a past tense, informational message. In other words, your secure session has been reset."
|
||||
},
|
||||
"installGetStartedButton": {
|
||||
|
@ -646,7 +646,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"relink": {
|
||||
"message": "Relink",
|
||||
"message": "Επανασύνδεση",
|
||||
"description": ""
|
||||
},
|
||||
"timerOption_1_week_abbreviated": {
|
||||
|
@ -658,11 +658,11 @@
|
|||
"description": "Label for a selectable option in the message expiration timer menu"
|
||||
},
|
||||
"showMembers": {
|
||||
"message": "Show members",
|
||||
"message": "Εμφάνιση μελών",
|
||||
"description": ""
|
||||
},
|
||||
"youMarkedAsNotVerifiedOtherDevice": {
|
||||
"message": "You marked your safety number with $name$ as not verified from another device.",
|
||||
"message": "Σημείωσες τον αριθμό ασφαλείας με τον/την $name$ ως μη επιβεβαιωμένο από άλλη συσκευή.",
|
||||
"description": "Shown in the conversation history when we discover that the user marked a contact as not verified on another device.",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -686,11 +686,11 @@
|
|||
"description": "Text displayed before the phone number that the user is in the process of linking with"
|
||||
},
|
||||
"groupMembers": {
|
||||
"message": "Group members",
|
||||
"message": "Μέλη ομάδας",
|
||||
"description": ""
|
||||
},
|
||||
"loading": {
|
||||
"message": "Loading...",
|
||||
"message": "Φορτώνει...",
|
||||
"description": "Message shown on the loading screen before we've loaded any messages"
|
||||
},
|
||||
"newMessages": {
|
||||
|
@ -698,15 +698,15 @@
|
|||
"description": "Displayed in notifications for multiple messages"
|
||||
},
|
||||
"newContact": {
|
||||
"message": "Πατήστε για να προσθέσετε καινούρια επαφή",
|
||||
"message": "Κλικ για προσθήκη νέας επαφής",
|
||||
"description": ""
|
||||
},
|
||||
"theirIdentityUnknown": {
|
||||
"message": "Δεν έχετε ανταλλάξει μηνύματα με αυτή την επαφή ακόμα. Ο κωδικός ασφαλείας σας με αυτή την επαφή θα είναι διαθέσιμος μετά το πρώτο μήνυμα.",
|
||||
"message": "Δεν έχεις ανταλλάξει ακόμα μηνύματα με αυτή την επαφή. Ο αριθμός ασφαλείας με αυτή την επαφή θα είναι διαθέσιμος μετά το πρώτο μήνυμα.",
|
||||
"description": ""
|
||||
},
|
||||
"voiceMessage": {
|
||||
"message": "Voice Message",
|
||||
"message": "Μήνυμα φωνής",
|
||||
"description": "Name for a voice message attachment"
|
||||
},
|
||||
"submit": {
|
||||
|
@ -714,7 +714,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"keychanged": {
|
||||
"message": "Your safety number with $name$ has changed. Click to show.",
|
||||
"message": "Ο αριθμός ασφαλείας με τον/την $name$ άλλαξε. Κλικ για εμφάνιση.",
|
||||
"description": "",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -724,7 +724,7 @@
|
|||
}
|
||||
},
|
||||
"identityKeyErrorOnSend": {
|
||||
"message": "Your safety number with $name$ has changed. This could either mean that someone is trying to intercept your communication or that $name$ has simply reinstalled Signal. You may wish to verify your saftey number with this contact.",
|
||||
"message": "Ο αριθμός ασφαλείας με τον/την $name$ έχει αλλάξει. Αυτό σημαίνει ότι είτε κάποιος προσπαθεί να υποκλέψει την επικοινωνία σου είτε απλά ο/η $name$ επανεγκατέστησε το Signal. Ίσως επιθυμείς να επαληθεύσεις τον αριθμό ασφαλείας με αυτή την επαφή.",
|
||||
"description": "Shown when user clicks on a failed recipient in the message detail view after an identity key change",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -750,11 +750,11 @@
|
|||
"description": ""
|
||||
},
|
||||
"installNewSignal": {
|
||||
"message": "Install new Signal Desktop",
|
||||
"message": "Εγκατάσταση του νέου Signal Desktop",
|
||||
"description": "When export is complete, a button shows which sends user to Signal Desktop install instructions"
|
||||
},
|
||||
"verify": {
|
||||
"message": "Mark as verified",
|
||||
"message": "Σημείωση ως επιβεβαιωμένο",
|
||||
"description": ""
|
||||
},
|
||||
"timerOption_10_seconds_abbreviated": {
|
||||
|
@ -766,7 +766,7 @@
|
|||
"description": "Label for setting notifications to display name and message text"
|
||||
},
|
||||
"failedToSend": {
|
||||
"message": "Αποτυχία αποστολής σε κάποιους παραλήπτες. Ελέγξτε την σύνδεση του δικτύου σας.",
|
||||
"message": "Αποτυχία αποστολής σε κάποιους παραλήπτες. Έλεγξε την σύνδεση του δικτύου.",
|
||||
"description": ""
|
||||
},
|
||||
"ok": {
|
||||
|
@ -774,19 +774,19 @@
|
|||
"description": ""
|
||||
},
|
||||
"identityChanged": {
|
||||
"message": "Ο κωδικός ασφαλείας με αυτή την επαφή έχει αλλάξει. Αυτό σημαίνει ότι είτε κάποιος προσπαθεί να υποκλέψει την συνομιλία σας είτε αυτή απλά επανεγκατέστησε το Signal. Ίσως επιθυμείτε να επαληθεύσετε το νέο κωδικό ασφαλείας παρακάτω.",
|
||||
"message": "Ο αριθμός ασφαλείας με αυτή την επαφή έχει αλλάξει. Αυτό σημαίνει ότι είτε κάποιος προσπαθεί να υποκλέψει την επικοινωνία σου είτε η επαφή απλά επανεγκατέστησε το Signal. Ίσως επιθυμείς να επαληθεύσεις το νέο αριθμό ασφαλείας παρακάτω.",
|
||||
"description": ""
|
||||
},
|
||||
"changedSinceVerifiedMultiple": {
|
||||
"message": "Your safety numbers with multiple group members have changed since you last verified. This could mean that someone is trying to intercept your communication or that they have simply reinstalled Signal.",
|
||||
"message": "Οι αριθμοί ασφαλείας με πολλαπλά μέλη αυτής της ομάδας άλλαξαν από την τελευταία φορά που επιβεβαιώθηκαν. Αυτό μπορεί να σημαίνει πως κάποιος προσπαθεί να υποκλέψει την επικοινωνία σου ή ότι απλά τα μέλη επανεγκατέστησαν το Signal.",
|
||||
"description": "Shown on confirmation dialog when user attempts to send a message"
|
||||
},
|
||||
"export": {
|
||||
"message": "Choose directory",
|
||||
"message": "Επιλογή φακέλου",
|
||||
"description": "Button to allow the user to export all data from app as part of migration process"
|
||||
},
|
||||
"submitDebugLog": {
|
||||
"message": "Υποβολή καταχωρήσεων αποσφαλμάτωσης",
|
||||
"message": "Υποβολή αρχείου καταγραφής αποσφαλμάτωσης",
|
||||
"description": "Menu item and header text for debug log modal, title case."
|
||||
},
|
||||
"error": {
|
||||
|
@ -794,7 +794,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"changedRightAfterVerify": {
|
||||
"message": "The safety number you are trying to verify has changed. Please review your new safety number with $name$. Remember, this change could mean that someone is trying to intercept your communication or that $name$ has simply reinstalled Signal.",
|
||||
"message": "Ο αριθμός ασφαλείας που προσπαθείς να επιβεβαιώσεις έχει αλλάξει. Παρακαλώ επιβεβαίωσε τον νέο αριθμό ασφαλείας με τον/την $name$. Θυμήσου, αυτή η αλλαγή μπορεί να σημαίνει πως κάποιος προσπαθεί να υποκλέψει την επικοινωνία σου ή ότι απλά ο/η $name$ επανεγκατέστησε το Signal.",
|
||||
"description": "Shown on the safety number screen when the user has selected to verify/unverify a contact's safety number, and we immediately discover a safety number change",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -804,7 +804,7 @@
|
|||
}
|
||||
},
|
||||
"timerSetTo": {
|
||||
"message": "Η ρύθμιση της αντίστροφης μέτρησης είναι ίση με $time$",
|
||||
"message": "Η ρύθμιση της αντίστροφης μέτρησης είναι $time$",
|
||||
"description": "Displayed in the conversation list when the timer is updated.",
|
||||
"placeholders": {
|
||||
"time": {
|
||||
|
@ -818,7 +818,7 @@
|
|||
"description": "Very short format indicating current timer setting in the conversation header"
|
||||
},
|
||||
"exportInstructions": {
|
||||
"message": "The first step is to choose a directory to store this application's exported data. It will contain your message history and sensitive cryptographic data, so be sure to save it somewhere private.",
|
||||
"message": "Το πρώτο βήμα είναι η επιλογή ενός φακέλου για την αποθήκευση των δεδομένων που θα εξαχθούν από αυτή την εφαρμογή. Αυτά περιέχουν το ιστορικό μηνυμάτων σου και ευαίσθητες κρυπτογραφικές πληροφορίες, γι' αυτό επιβεβαιώσου πως το αποθηκεύεις κάπου όπου θα είναι ασφαλές και μη δημόσιο.",
|
||||
"description": "Description of the export process"
|
||||
},
|
||||
"notifications": {
|
||||
|
@ -826,11 +826,11 @@
|
|||
"description": "Header for notification settings"
|
||||
},
|
||||
"resend": {
|
||||
"message": "Ξαναστείλτε",
|
||||
"message": "Επαναποστολή",
|
||||
"description": ""
|
||||
},
|
||||
"youMarkedAsVerifiedOtherDevice": {
|
||||
"message": "You marked your safety number with $name$ as verified from another device.",
|
||||
"message": "Σημείωσες τον αριθμό ασφαλείας με τον/την $name$ ως επιβεβαιωμένο από άλλη συσκευή.",
|
||||
"description": "Shown in the conversation history when we discover that the user marked a contact as verified on another device.",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
|
|
@ -1,57 +1,173 @@
|
|||
{
|
||||
"preLinkExpiredHeader": {
|
||||
"message": "Time to upgrade"
|
||||
},
|
||||
"uninstallHeader": {
|
||||
"message": "How to uninstall"
|
||||
},
|
||||
"uninstallStep1": {
|
||||
"message": "Open this location in your Chrome browser:"
|
||||
},
|
||||
"uninstallStep2": {
|
||||
"message": "Find 'Signal Private Messenger'"
|
||||
},
|
||||
"uninstallStep3": {
|
||||
"message": "Select 'Remove'"
|
||||
},
|
||||
"uninstallStep4": {
|
||||
"message": "Click 'Remove' to confirm"
|
||||
},
|
||||
"uninstallStep5": {
|
||||
"message": "Delete your previously exported data here:"
|
||||
},
|
||||
"uninstallStep6": {
|
||||
"message": "Remove the Signal Desktop Chrome App shortcut"
|
||||
},
|
||||
"linuxInstallInstructions": {
|
||||
"message": "Debian-based Linux install instructions",
|
||||
"description": "When exporting data for migration to Electron app, Linux instructions pop up."
|
||||
},
|
||||
"readOnlyMode": {
|
||||
"message": "This application is in read-only mode. Please upgrade to the latest Signal Desktop.",
|
||||
"description": "Shown when the user tries to send a message"
|
||||
},
|
||||
"close": {
|
||||
"message": "Close",
|
||||
"description": "Shown on a button to dismiss a dialog box"
|
||||
},
|
||||
"loading": {
|
||||
"message": "Loading...",
|
||||
"description": "Message shown on the loading screen before we've loaded any messages"
|
||||
},
|
||||
"migrationWarning": {
|
||||
"message": "The Signal Desktop Chrome app has been deprecated. Would you like to migrate to the new Signal Desktop now?",
|
||||
"description": "Warning notification that this version of the app has been deprecated and the user must migrate"
|
||||
"installComplete": {
|
||||
"message": "Install is complete",
|
||||
"description": "Button to click when user has installed the new Signal Desktop"
|
||||
},
|
||||
"exportInstructions": {
|
||||
"message": "The first step is to choose a directory to store this application's exported data. It will contain your message history and sensitive cryptographic data, so be sure to save it somewhere private.",
|
||||
"description": "Description of the export process"
|
||||
"upgradeBanner": {
|
||||
"message": "This legacy version of Signal Desktop will stop working in:",
|
||||
"description": "Shown at the top of the application alerting the user that this version of Signal Desktop will soon no longer be available."
|
||||
},
|
||||
"migrate": {
|
||||
"message": "Migrate",
|
||||
"description": "Button label to begin migrating this client to Electron"
|
||||
"upgradeBannerExpired": {
|
||||
"message": "This legacy version of Signal Desktop is in read-only mode",
|
||||
"description": "Shown at the top of the application alerting the user that they will need to find a new Signal Desktop application."
|
||||
},
|
||||
"export": {
|
||||
"message": "Choose directory",
|
||||
"description": "Button to allow the user to export all data from app as part of migration process"
|
||||
},
|
||||
"exportAgain": {
|
||||
"message": "Export again",
|
||||
"description": "If user has already exported once, this button allows user to do it again if needed"
|
||||
},
|
||||
"exportError": {
|
||||
"message": "Unfortunately, something went wrong during the export. First, double-check your target empty directory for write access and enough space. Then, please submit a debug log so we can help you get migrated!",
|
||||
"description": "Helper text if the user went forward on migrating the app, but ran into an error"
|
||||
},
|
||||
"confirmMigration": {
|
||||
"message": "Start migration process? You will not be able to send or receive Signal messages from this application while the migration is in progress.",
|
||||
"description": "Confirmation dialogue when beginning migration"
|
||||
},
|
||||
"migrationDisconnecting": {
|
||||
"message": "Disconnecting...",
|
||||
"description": "Displayed while we wait for pending incoming messages to process"
|
||||
},
|
||||
"exporting": {
|
||||
"message": "Please wait while we export your data. It may take several minutes. You can still use Signal on your phone and other devices during this time.",
|
||||
"description": "Message shown on the migration screen while we export data"
|
||||
},
|
||||
"exportComplete": {
|
||||
"message": "Your data has been exported to: <p><b>$location$</b></p> You'll be able to import this data as you set up <a target='_blank' href='https://support.whispersystems.org/hc/en-us/articles/214507138'>the new Signal Desktop</a>.",
|
||||
"description": "Message shown on the migration screen when we are done exporting data",
|
||||
"upgradeBannerTimespan": {
|
||||
"message": "$days$ days",
|
||||
"description": "Shown at the top of the application giving the user the amount of time left before this version of Signal Desktop stops working",
|
||||
"placeholders": {
|
||||
"location": {
|
||||
"days": {
|
||||
"content": "$1",
|
||||
"example": "/Users/someone/somewhere"
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"installNewSignal": {
|
||||
"message": "Install new Signal Desktop",
|
||||
"description": "When export is complete, a button shows which sends user to Signal Desktop install instructions"
|
||||
"upgradeNow": {
|
||||
"message": "Upgrade now",
|
||||
"description": "The button shown in the upgrade banner, starting the upgrade process."
|
||||
},
|
||||
"migrateToStandalone": {
|
||||
"message": "Migrate to standalone",
|
||||
"description": "Menu option to begin migrating this client to Electron"
|
||||
},
|
||||
"migrateInstallStep": {
|
||||
"message": "The first step is to install the new Signal Desktop.",
|
||||
"description": "The first step in the export process; installing the standalone desktop app, to ensure it is available for that platform."
|
||||
},
|
||||
"saveDataPrompt": {
|
||||
"message": "Your messages, contacts, groups, and other information can be seamlessly transferred to the standalone version of Signal Desktop. Select the folder where your exported Signal data should be saved.",
|
||||
"description": "Explanation of what will happen when the user presses the 'choose folder' button during the upgrade process"
|
||||
},
|
||||
"chooseFolder": {
|
||||
"message": "Choose folder",
|
||||
"description": "Button which pops up a directory selection dialog, and starts saving data to the selected folder"
|
||||
},
|
||||
"startExportHeader": {
|
||||
"message": "Upgrade to the new Signal Desktop",
|
||||
"description": "Header shown when starting the export and upgrade process"
|
||||
},
|
||||
"startExportIntroParagraph1": {
|
||||
"message": "You are currently using the legacy Chrome version of Signal that was officially deprecated on October 31, 2017. Since then, the new standalone version of Signal Desktop has received regular updates, numerous feature enhancements, and significant performance improvements.",
|
||||
"description": "Description of the why and the what of the export and upgrade process"
|
||||
},
|
||||
"startExportIntroParagraph2": {
|
||||
"message": "This migration process will help you switch to the latest release. All of your information will be saved, and the upgrade should only take a few minutes. ",
|
||||
"description": "Description of the why and the what of the export and upgrade process"
|
||||
},
|
||||
"startExportIntroParagraph3": {
|
||||
"message": "Note: Non-Debian-based Linux distributions, ChromeOS, and 32-bit Windows platforms are not currently supported.",
|
||||
"description": "Description of the why and the what of the export and upgrade process"
|
||||
},
|
||||
"moreInformation": {
|
||||
"message": "More information",
|
||||
"description": "Description of the why and the what of the export and upgrade process"
|
||||
},
|
||||
"upgradeLater": {
|
||||
"message": "Upgrade Later",
|
||||
"description": "Description of the why and the what of the export and upgrade process"
|
||||
},
|
||||
"imReady": {
|
||||
"message": "I'm ready",
|
||||
"description": "Button to kick off the export and upgrade process"
|
||||
},
|
||||
"installHeader": {
|
||||
"message": "Select your Operating System",
|
||||
"description": "Header text for the install step of the export and upgrade flow"
|
||||
},
|
||||
"installIntro": {
|
||||
"message": "Download and install the new Signal Desktop for your chosen OS.",
|
||||
"description": "Introduction text for the install step of the export and upgrade flow"
|
||||
},
|
||||
"macOS": {
|
||||
"message": "macOS",
|
||||
"description": "The name of Apple's desktop operating system"
|
||||
},
|
||||
"windows": {
|
||||
"message": "Windows",
|
||||
"description": "The name of Microsoft's desktop operating system"
|
||||
},
|
||||
"debianLinux": {
|
||||
"message": "Debian-based Linux",
|
||||
"description": "A succinct name for the flavors of linux based on Debian"
|
||||
},
|
||||
"installed": {
|
||||
"message": "It's installed",
|
||||
"description": "The button indicating that the user is done with the installation step"
|
||||
},
|
||||
"saveHeader": {
|
||||
"message": "Save your data",
|
||||
"description": "The header for the choose folder step of the export and upgrade flow"
|
||||
},
|
||||
"completeHeader": {
|
||||
"message": "Success!",
|
||||
"description": "The header for the finish step of the export and upgrade flow"
|
||||
},
|
||||
"completeIntro": {
|
||||
"message": "Your Signal Desktop migration data was successfully saved here. Remember this location:",
|
||||
"description": "A label for the location of the files we put on disk during the export"
|
||||
},
|
||||
"completeNextSteps": {
|
||||
"message": "You are almost done! Download and run the new version to import your data and finish the move.",
|
||||
"description": "A summary of the next steps to finish the export and upgrade process"
|
||||
},
|
||||
"getNewVersion": {
|
||||
"message": "Get New Version",
|
||||
"description": "The button to download the new Signal Desktop version after export is complete"
|
||||
},
|
||||
"exportErrorHeader": {
|
||||
"message": "Something went wrong!",
|
||||
"description": "Header shown at the top of the error screen if something goes wrong during data export"
|
||||
},
|
||||
"exportError": {
|
||||
"message": "Unfortunately, something went wrong during the export. First, check that you have enough space where you saved the file. Then, please submit a debug log so we can help you get migrated!",
|
||||
"description": "Helper text if the user went forward on migrating the app, but ran into an error"
|
||||
},
|
||||
"chooseFolderAndTryAgain": {
|
||||
"message": "Choose folder and try again",
|
||||
"description": "Button shown on the export error screen allowing user to try again"
|
||||
},
|
||||
"savingData": {
|
||||
"message": "Saving your data",
|
||||
"description": "Message shown during the upgrade process while we export data"
|
||||
},
|
||||
"selectedLocation": {
|
||||
"message": "your selected location",
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
"description": "Conversation menu option to enable disappearing messages"
|
||||
},
|
||||
"deleteWarning": {
|
||||
"message": "¿Estás seguro? Al hacer clic en 'eliminar' se eliminará permanentemente este mensaje de este dispositivo.",
|
||||
"message": "¿Estás seguro? Al hacer clic en «eliminar» se eliminará permanentemente este mensaje de este dispositivo.",
|
||||
"description": ""
|
||||
},
|
||||
"showMore": {
|
||||
|
@ -232,7 +232,7 @@
|
|||
"description": "Button label to begin migrating this client to Electron"
|
||||
},
|
||||
"theyChangedTheTimer": {
|
||||
"message": "$name$ ha fijado la caducidad a $time$.",
|
||||
"message": "$name$ ha fijado la caducidad de mensajes a $time$.",
|
||||
"description": "Message displayed when someone else changes the message expiration timer in a conversation.",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -258,7 +258,7 @@
|
|||
"description": "Label for a selectable option in the message expiration timer menu"
|
||||
},
|
||||
"exporting": {
|
||||
"message": "Por favor, espere mientras exportamos sus datos. Puede tomar varios minutos. Todavía puede utilizar Signal en su teléfono y otros dispositivos durante este tiempo.",
|
||||
"message": "Por favor, espera mientras exportamos tus datos. Puede tomar varios minutos. Mientras tanto puedes utilizar Signal en tu teléfono y otros dispositivos.",
|
||||
"description": "Message shown on the migration screen while we export data"
|
||||
},
|
||||
"reportIssue": {
|
||||
|
@ -320,7 +320,7 @@
|
|||
"description": "Label for a button that syncs contacts and groups from your phone"
|
||||
},
|
||||
"syncFailed": {
|
||||
"message": "Fallo al importar. Asegúrate de que ordenador y teléfono estan conectados a internet.",
|
||||
"message": "Fallo al importar. Asegúrate de que ordenador y teléfono están conectados a internet.",
|
||||
"description": "Informational text displayed if a sync operation times out."
|
||||
},
|
||||
"unlinked": {
|
||||
|
@ -578,7 +578,7 @@
|
|||
"description": "Alt text for button to take user down to bottom of conversation with a new message out of screen"
|
||||
},
|
||||
"exportComplete": {
|
||||
"message": "Sus datos se han exportado a: <p><b>$location$</b></p> Podrá importar estos datos al configurar <a target='_blank' href='https://support.whispersystems.org/hc/en-us/articles/214507138'>el nuevo Signal Desktop</a>.",
|
||||
"message": "Tus datos se han exportado a: <p><b>$location$</b></p> Podrás importar estos datos al configurar <a target='_blank' href='https://support.whispersystems.org/hc/en-us/articles/214507138'>el nuevo Signal Desktop</a>.",
|
||||
"description": "Message shown on the migration screen when we are done exporting data",
|
||||
"placeholders": {
|
||||
"location": {
|
||||
|
@ -750,7 +750,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"installNewSignal": {
|
||||
"message": "Instalar nuevo Signal Desktop",
|
||||
"message": "Instalar el nuevo Signal Desktop",
|
||||
"description": "When export is complete, a button shows which sends user to Signal Desktop install instructions"
|
||||
},
|
||||
"verify": {
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
"description": "Label for a selectable option in the message expiration timer menu"
|
||||
},
|
||||
"view": {
|
||||
"message": "View",
|
||||
"message": "نمایش",
|
||||
"description": "Used as a label on a button allowing user to see more information"
|
||||
},
|
||||
"upgradingDatabase": {
|
||||
"message": "Upgrading database. This may take some time...",
|
||||
"message": "در حال بهروزرسانی پابگاه داده. ممکن است کمی طول بکشد...",
|
||||
"description": "Message shown on the loading screen when we're changing database structure on first run of a new version"
|
||||
},
|
||||
"lastSynced": {
|
||||
|
@ -174,7 +174,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"delete": {
|
||||
"message": "Delete",
|
||||
"message": "پاک کن",
|
||||
"description": ""
|
||||
},
|
||||
"verified": {
|
||||
|
@ -308,7 +308,7 @@
|
|||
"description": "Placeholder text in the search input"
|
||||
},
|
||||
"someRecipientsFailed": {
|
||||
"message": "Some recipients failed.",
|
||||
"message": "برخی دریافتها با مشکل مواجه شد.",
|
||||
"description": "When you send to multiple recipients via a group, and the message went to some recipients but not others."
|
||||
},
|
||||
"noNameOrMessage": {
|
||||
|
@ -386,7 +386,7 @@
|
|||
"description": "Very short format indicating current timer setting in the conversation header"
|
||||
},
|
||||
"loadingMessages": {
|
||||
"message": "Loading messages. $count$ so far...",
|
||||
"message": "در حال بارگذاری پیامها. تاکنون $count$ ...",
|
||||
"description": "Message shown on the loading screen when we're catching up on the backlog of messages",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
|
@ -530,7 +530,7 @@
|
|||
"description": "Placeholder text in the message entry field"
|
||||
},
|
||||
"me": {
|
||||
"message": "Me",
|
||||
"message": "من",
|
||||
"description": "The label for yourself when shown in a group member list"
|
||||
},
|
||||
"mediaMessage": {
|
||||
|
@ -550,7 +550,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"deleteMessage": {
|
||||
"message": "Delete this message",
|
||||
"message": "پاک کردن این پیام",
|
||||
"description": ""
|
||||
},
|
||||
"timerOption_0_seconds": {
|
||||
|
@ -620,7 +620,7 @@
|
|||
"description": "Hover text for attachment filenames"
|
||||
},
|
||||
"sendAnyway": {
|
||||
"message": "Send Anyway",
|
||||
"message": "به هر حال بفرست",
|
||||
"description": "Used on a warning dialog to make it clear that it might be risky to send the message."
|
||||
},
|
||||
"youMarkedAsVerified": {
|
||||
|
@ -658,7 +658,7 @@
|
|||
"description": "Label for a selectable option in the message expiration timer menu"
|
||||
},
|
||||
"showMembers": {
|
||||
"message": "Show members",
|
||||
"message": "نمایش اعضا",
|
||||
"description": ""
|
||||
},
|
||||
"youMarkedAsNotVerifiedOtherDevice": {
|
||||
|
@ -686,11 +686,11 @@
|
|||
"description": "Text displayed before the phone number that the user is in the process of linking with"
|
||||
},
|
||||
"groupMembers": {
|
||||
"message": "Group members",
|
||||
"message": "اعضای گروه",
|
||||
"description": ""
|
||||
},
|
||||
"loading": {
|
||||
"message": "Loading...",
|
||||
"message": "در حال بارگذاری...",
|
||||
"description": "Message shown on the loading screen before we've loaded any messages"
|
||||
},
|
||||
"newMessages": {
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
"description": "Warning that long messages could not get received completely by Android clients."
|
||||
},
|
||||
"youChangedTheTimer": {
|
||||
"message": "You set the timer to $time$.",
|
||||
"message": "Postavili ste brojač na $time$.",
|
||||
"description": "Message displayed when you change the message expiration timer in a conversation.",
|
||||
"placeholders": {
|
||||
"time": {
|
||||
|
@ -662,7 +662,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"youMarkedAsNotVerifiedOtherDevice": {
|
||||
"message": "You marked your safety number with $name$ as not verified from another device.",
|
||||
"message": "Označili ste svoj sigurnosni broj s $name$ kao nepotvrđen s drugog uređaja.",
|
||||
"description": "Shown in the conversation history when we discover that the user marked a contact as not verified on another device.",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -804,7 +804,7 @@
|
|||
}
|
||||
},
|
||||
"timerSetTo": {
|
||||
"message": "Timer set to $time$",
|
||||
"message": "Brojač postavljen na $time$",
|
||||
"description": "Displayed in the conversation list when the timer is updated.",
|
||||
"placeholders": {
|
||||
"time": {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"description": "Label for a selectable option in the message expiration timer menu"
|
||||
},
|
||||
"view": {
|
||||
"message": "View",
|
||||
"message": "ತೋರಿಸು",
|
||||
"description": "Used as a label on a button allowing user to see more information"
|
||||
},
|
||||
"upgradingDatabase": {
|
||||
|
@ -34,7 +34,7 @@
|
|||
"description": "Displayed for incoming unsupported attachment"
|
||||
},
|
||||
"showSafetyNumber": {
|
||||
"message": "Show safety number",
|
||||
"message": "ಸುರಕ್ಷತಾ ಸಂಖ್ಯೆ ತೋರಿಸಿ",
|
||||
"description": ""
|
||||
},
|
||||
"androidMessageLengthWarning": {
|
||||
|
@ -72,7 +72,7 @@
|
|||
"description": "Label for a button that dismisses a dialog. The user clicks it to confirm that they understand the message in the dialog."
|
||||
},
|
||||
"disappearingMessages": {
|
||||
"message": "Disappearing messages",
|
||||
"message": "ಕಣ್ಮರೆಯಾಗುವ ಸಂದೇಶಗಳು",
|
||||
"description": "Conversation menu option to enable disappearing messages"
|
||||
},
|
||||
"deleteWarning": {
|
||||
|
@ -98,7 +98,7 @@
|
|||
"description": "When there are multiple previously-verified group members with safety number changes, a banner will be shown. The list of contacts with safety number changes is shown, and this text introduces that list."
|
||||
},
|
||||
"timerOption_5_minutes_abbreviated": {
|
||||
"message": "5m",
|
||||
"message": "5 ನಿಮಿಷಗಳು",
|
||||
"description": "Very short format indicating current timer setting in the conversation header"
|
||||
},
|
||||
"unsupportedFileType": {
|
||||
|
@ -110,11 +110,11 @@
|
|||
"description": "If user has already exported once, this button allows user to do it again if needed"
|
||||
},
|
||||
"clickToSave": {
|
||||
"message": "Click to save",
|
||||
"message": "ಉಳಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ",
|
||||
"description": "Hover text for attachment filenames"
|
||||
},
|
||||
"messagesBelow": {
|
||||
"message": "New messages below",
|
||||
"message": "ಕೆಳಗೆ ಹೊಸ ಸಂದೇಶಗಳು ಇವೆ",
|
||||
"description": "Alt text for button to take user down to bottom of conversation with more than one message out of screen"
|
||||
},
|
||||
"installGeneratingKeys": {
|
||||
|
@ -146,7 +146,7 @@
|
|||
"description": "Brief timestamp for messages sent about one minute ago. Displayed in the conversation list and message bubble."
|
||||
},
|
||||
"timerOption_0_seconds_abbreviated": {
|
||||
"message": "off",
|
||||
"message": "ಆರಿಸು",
|
||||
"description": "Short format indicating current timer setting in the conversation list snippet"
|
||||
},
|
||||
"syncExplanation": {
|
||||
|
@ -162,7 +162,7 @@
|
|||
"description": "Displayed when a user can't send a message because they have left the group"
|
||||
},
|
||||
"deleteMessages": {
|
||||
"message": "Delete messages",
|
||||
"message": "ಸಂದೇಶಗಳನ್ನು ಅಳಿಸಿ",
|
||||
"description": "Menu item for deleting messages, title case."
|
||||
},
|
||||
"confirmMigration": {
|
||||
|
@ -174,11 +174,11 @@
|
|||
"description": ""
|
||||
},
|
||||
"delete": {
|
||||
"message": "Delete",
|
||||
"message": "ಅಳಿಸು",
|
||||
"description": ""
|
||||
},
|
||||
"verified": {
|
||||
"message": "Verified",
|
||||
"message": "ಪರಿಶೀಲಿಸಲಾಗಿದೆ",
|
||||
"description": ""
|
||||
},
|
||||
"selectAContact": {
|
||||
|
@ -254,7 +254,7 @@
|
|||
"description": "Shown on confirmation dialog when user attempts to send a message"
|
||||
},
|
||||
"timerOption_1_day": {
|
||||
"message": "1 day",
|
||||
"message": "1 ದಿನ",
|
||||
"description": "Label for a selectable option in the message expiration timer menu"
|
||||
},
|
||||
"exporting": {
|
||||
|
@ -282,7 +282,7 @@
|
|||
"description": "Confirmation dialog text that asks the user if they really wish to delete the conversation. Answer buttons use the strings 'ok' and 'cancel'. The deletion is permanent, i.e. it cannot be undone."
|
||||
},
|
||||
"unlinkedWarning": {
|
||||
"message": "Relink Signal Desktop to your mobile device to continue messaging.",
|
||||
"message": "ಸಂದೇಶವನ್ನು ಮುಂದುವರಿಸಲು ಸಿಗ್ನಲ್ ಡೆಸ್ಕ್ಟಾಪ್ ಅನ್ನು ನಿಮ್ಮ ಮೊಬೈಲ್ ಸಾಧನಕ್ಕೆ ರಿಲೀಂಕ್ ಮಾಡಿ.",
|
||||
"description": ""
|
||||
},
|
||||
"debugLogExplanation": {
|
||||
|
@ -374,7 +374,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"timerOption_30_minutes_abbreviated": {
|
||||
"message": "30m",
|
||||
"message": "30 ನಿಮಿಷಗಳು",
|
||||
"description": "Very short format indicating current timer setting in the conversation header"
|
||||
},
|
||||
"themeAndroidDark": {
|
||||
|
@ -382,7 +382,7 @@
|
|||
"description": "Label text for dark Android theme"
|
||||
},
|
||||
"timerOption_1_minute_abbreviated": {
|
||||
"message": "1m",
|
||||
"message": "1 ನಿಮಿಷ",
|
||||
"description": "Very short format indicating current timer setting in the conversation header"
|
||||
},
|
||||
"loadingMessages": {
|
||||
|
@ -396,7 +396,7 @@
|
|||
}
|
||||
},
|
||||
"changedSinceVerified": {
|
||||
"message": "Your safety number with $name$ has changed since you last verified. This could mean that someone is trying to intercept your communication or that $name$ has simply reinstalled Signal.",
|
||||
"message": "ನೀವು ಕೊನೆಯದಾಗಿ ಪರಿಶೀಲಿಸಿದ ನಂತರ $name$ ನಿಮ್ಮ ಸುರಕ್ಷತೆ ಸಂಖ್ಯೆ ಬದಲಾಗಿದೆ. ನಿಮ್ಮ ಸಂವಹನವನ್ನು ಯಾರಾದರೂ ತಡೆಗಟ್ಟಲು ಪ್ರಯತ್ನಿಸುತ್ತಿದ್ದಾರೆ ಅಥವಾ $name$ ಸಿಗ್ನಲ್ ಅನ್ನು ಪುನಃ ಸ್ಥಾಪಿಸಿದ್ದಾರೆ",
|
||||
"description": "Shown on confirmation dialog when user attempts to send a message",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -428,7 +428,7 @@
|
|||
"description": "Header for theme settings"
|
||||
},
|
||||
"newIdentity": {
|
||||
"message": "New safety number",
|
||||
"message": "ಹೊಸ ಸುರಕ್ಷತೆ ಸಂಖ್ಯೆ",
|
||||
"description": "Header for a key change dialog"
|
||||
},
|
||||
"installTagline": {
|
||||
|
@ -466,7 +466,7 @@
|
|||
"description": "Label for a button to accept a new safety number"
|
||||
},
|
||||
"timerOption_12_hours_abbreviated": {
|
||||
"message": "12h",
|
||||
"message": "12 ಗಂಟೆ",
|
||||
"description": "Very short format indicating current timer setting in the conversation header"
|
||||
},
|
||||
"timestampFormat_M": {
|
||||
|
@ -474,7 +474,7 @@
|
|||
"description": "Timestamp format string for displaying month and day (but not the year) of a date within the current year, ex: use 'MMM D' for 'Aug 8', or 'D MMM' for '8 Aug'."
|
||||
},
|
||||
"timerOption_6_hours_abbreviated": {
|
||||
"message": "6h",
|
||||
"message": "6 ಗಂಟೆ",
|
||||
"description": "Very short format indicating current timer setting in the conversation header"
|
||||
},
|
||||
"unregisteredUser": {
|
||||
|
@ -482,7 +482,7 @@
|
|||
"description": "Error message displayed when sending to an unregistered user."
|
||||
},
|
||||
"timerOption_1_day_abbreviated": {
|
||||
"message": "1d",
|
||||
"message": "1 ದಿನ",
|
||||
"description": "Very short format indicating current timer setting in the conversation header"
|
||||
},
|
||||
"sync": {
|
||||
|
@ -490,7 +490,7 @@
|
|||
"description": "Label for contact and group sync settings"
|
||||
},
|
||||
"timerOption_1_week": {
|
||||
"message": "1 week",
|
||||
"message": "ಒಂದು ವಾರ",
|
||||
"description": "Label for a selectable option in the message expiration timer menu"
|
||||
},
|
||||
"installGotIt": {
|
||||
|
@ -530,7 +530,7 @@
|
|||
"description": "Placeholder text in the message entry field"
|
||||
},
|
||||
"me": {
|
||||
"message": "Me",
|
||||
"message": "ನಾನು",
|
||||
"description": "The label for yourself when shown in a group member list"
|
||||
},
|
||||
"mediaMessage": {
|
||||
|
@ -538,7 +538,7 @@
|
|||
"description": "Description of a message that has an attachment and no text, displayed in the conversation list as a preview."
|
||||
},
|
||||
"unreadMessage": {
|
||||
"message": "1 Unread Message",
|
||||
"message": "1 ಓದದಿರುವ ಸಂದೇಶ",
|
||||
"description": "Text for unread message separator, just one message"
|
||||
},
|
||||
"expiredWarning": {
|
||||
|
@ -550,11 +550,11 @@
|
|||
"description": ""
|
||||
},
|
||||
"deleteMessage": {
|
||||
"message": "Delete this message",
|
||||
"message": "ಈ ಸಂದೇಶವನ್ನು ಅಳಿಸಿ",
|
||||
"description": ""
|
||||
},
|
||||
"timerOption_0_seconds": {
|
||||
"message": "off",
|
||||
"message": "ಆರಿಸು",
|
||||
"description": "Label for option to turn off message expiration in the timer menu"
|
||||
},
|
||||
"installAndroidInstructions": {
|
||||
|
@ -566,7 +566,7 @@
|
|||
"description": "Displayed while we wait for pending incoming messages to process"
|
||||
},
|
||||
"invalidNumberError": {
|
||||
"message": "Invalid number",
|
||||
"message": "ತಪ್ಪಾದ ಸಂಖ್ಯೆ",
|
||||
"description": "When a person inputs a number that is invalid"
|
||||
},
|
||||
"installWelcome": {
|
||||
|
@ -634,7 +634,7 @@
|
|||
}
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Connecting",
|
||||
"message": "ಸಂಪರ್ಕಿಸಲಾಗುತ್ತಿದೆ",
|
||||
"description": "Displayed when the desktop client is currently connecting to the server."
|
||||
},
|
||||
"sessionEnded": {
|
||||
|
@ -650,7 +650,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"timerOption_1_week_abbreviated": {
|
||||
"message": "1w",
|
||||
"message": "1 ವಾರ",
|
||||
"description": "Very short format indicating current timer setting in the conversation header"
|
||||
},
|
||||
"timerOption_5_seconds": {
|
||||
|
@ -658,7 +658,7 @@
|
|||
"description": "Label for a selectable option in the message expiration timer menu"
|
||||
},
|
||||
"showMembers": {
|
||||
"message": "Show members",
|
||||
"message": "ಸದಸ್ಯರನ್ನು ತೋರಿಸು",
|
||||
"description": ""
|
||||
},
|
||||
"youMarkedAsNotVerifiedOtherDevice": {
|
||||
|
@ -698,15 +698,15 @@
|
|||
"description": "Displayed in notifications for multiple messages"
|
||||
},
|
||||
"newContact": {
|
||||
"message": "Click to create new contact",
|
||||
"message": "ಹೊಸ ಸಂಪರ್ಕವನ್ನು ಉಳಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ",
|
||||
"description": ""
|
||||
},
|
||||
"theirIdentityUnknown": {
|
||||
"message": "You haven't exchanged any messages with this contact yet. Your safety number with them will be available after the first message.",
|
||||
"message": "ನೀವು ಇನ್ನೂ ಈ ಸಂಪರ್ಕದೊಂದಿಗೆ ಯಾವುದೇ ಸಂದೇಶಗಳನ್ನು ವಿನಿಮಯ ಮಾಡಿಲ್ಲ. ಅವರೊಂದಿಗೆ ನಿಮ್ಮ ಸುರಕ್ಷತಾ ಸಂಖ್ಯೆ ಮೊದಲ ಸಂದೇಶದ ನಂತರ ಲಭ್ಯವಿರುತ್ತದೆ.",
|
||||
"description": ""
|
||||
},
|
||||
"voiceMessage": {
|
||||
"message": "Voice Message",
|
||||
"message": "ಧ್ವನಿ ಸಂದೇಶ",
|
||||
"description": "Name for a voice message attachment"
|
||||
},
|
||||
"submit": {
|
||||
|
|
|
@ -84,7 +84,7 @@
|
|||
"description": "Displays the details of a key change"
|
||||
},
|
||||
"verifyHelp": {
|
||||
"message": "Se você desejar verificar a segurança da sua criptografia ponta-a-ponta com $name$, compare os números acima com os números no aparelho desse contato.",
|
||||
"message": "Se você desejar verificar a segurança da sua criptografia ponta-a-ponta com $name$, compare os números acima com os números no aparelho dessa pessoa.",
|
||||
"description": "",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -308,7 +308,7 @@
|
|||
"description": "Placeholder text in the search input"
|
||||
},
|
||||
"someRecipientsFailed": {
|
||||
"message": "Some recipients failed.",
|
||||
"message": "Falha em alguns envios.",
|
||||
"description": "When you send to multiple recipients via a group, and the message went to some recipients but not others."
|
||||
},
|
||||
"noNameOrMessage": {
|
||||
|
@ -324,7 +324,7 @@
|
|||
"description": "Informational text displayed if a sync operation times out."
|
||||
},
|
||||
"unlinked": {
|
||||
"message": "Não-religado",
|
||||
"message": "Não religado",
|
||||
"description": ""
|
||||
},
|
||||
"installFollowUs": {
|
||||
|
@ -574,7 +574,7 @@
|
|||
"description": "Welcome title on the install page"
|
||||
},
|
||||
"messageBelow": {
|
||||
"message": "Nova mensagem abaixo",
|
||||
"message": "Nova mensagem, abaixo",
|
||||
"description": "Alt text for button to take user down to bottom of conversation with a new message out of screen"
|
||||
},
|
||||
"exportComplete": {
|
||||
|
@ -724,7 +724,7 @@
|
|||
}
|
||||
},
|
||||
"identityKeyErrorOnSend": {
|
||||
"message": "O seu número de segurança com $name$mudou. Isso pode significar uma tentativa de interceptação das suas comunicações ou somente que $name$ reinstalou Signal. Recomendamos que você verifique o seu número de segurança com com esse contato.",
|
||||
"message": "O seu número de segurança com $name$mudou. Isso pode significar uma tentativa de interceptação das suas comunicações ou somente que $name$ reinstalou Signal. Recomendamos que você verifique o seu número de segurança com esse contato.",
|
||||
"description": "Shown when user clicks on a failed recipient in the message detail view after an identity key change",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
|
@ -778,7 +778,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"changedSinceVerifiedMultiple": {
|
||||
"message": "Os seus números de segurança com varios membros mudaram desde a última vez que você os verificou. Isso pode significar uma tentativa de interceptação das suas comunicações ou somente que esses contatos reinstalaram Signal.",
|
||||
"message": "Os seus números de segurança com vários membros mudaram desde a última vez que você os verificou. Isso pode significar uma tentativa de interceptação das suas comunicações ou somente que esses contatos reinstalaram Signal.",
|
||||
"description": "Shown on confirmation dialog when user attempts to send a message"
|
||||
},
|
||||
"export": {
|
||||
|
|
|
@ -186,7 +186,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"exportError": {
|
||||
"message": "Infelizmente, algo falhou durante a exportação. Primeiro, verifique que a pasta de destino tem acesso de escrita e espaço suficiente. Depois, por favor envie um debug log para que o(a) possamos ajudar a migrar!",
|
||||
"message": "Infelizmente, algo falhou durante a exportação. Primeiro, verifique que a pasta de destino tem acesso de escrita e espaço suficiente. Depois, por favor envie um relatório de erros para que o(a) possamos ajudar a migrar!",
|
||||
"description": "Helper text if the user went forward on migrating the app, but ran into an error"
|
||||
},
|
||||
"installConnecting": {
|
||||
|
@ -224,7 +224,7 @@
|
|||
"description": "Label for the time a message was sent"
|
||||
},
|
||||
"migrationWarning": {
|
||||
"message": "O Signal Desktop para o Chrome foi descontinuada. Pretende migrar para o novo Signal Desktop agora?",
|
||||
"message": "O Signal Desktop para o Chrome foi descontinuado. Pretende migrar para o novo Signal Desktop agora?",
|
||||
"description": "Warning notification that this version of the app has been deprecated and the user must migrate"
|
||||
},
|
||||
"migrate": {
|
||||
|
|
|
@ -750,7 +750,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"installNewSignal": {
|
||||
"message": "Install new Signal Desktop",
|
||||
"message": "Instalează noul Signal Desktop",
|
||||
"description": "When export is complete, a button shows which sends user to Signal Desktop install instructions"
|
||||
},
|
||||
"verify": {
|
||||
|
|
|
@ -106,7 +106,7 @@
|
|||
"description": "Displayed for outgoing unsupported attachment"
|
||||
},
|
||||
"exportAgain": {
|
||||
"message": "Export again",
|
||||
"message": "再次匯出",
|
||||
"description": "If user has already exported once, this button allows user to do it again if needed"
|
||||
},
|
||||
"clickToSave": {
|
||||
|
@ -258,7 +258,7 @@
|
|||
"description": "Label for a selectable option in the message expiration timer menu"
|
||||
},
|
||||
"exporting": {
|
||||
"message": "Please wait while we export your data. It may take several minutes. You can still use Signal on your phone and other devices during this time.",
|
||||
"message": "請耐心等待我們匯出你的資料,此過程將需數分鐘。你可以繼續在電話及其他裝置上使用Signal。",
|
||||
"description": "Message shown on the migration screen while we export data"
|
||||
},
|
||||
"reportIssue": {
|
||||
|
@ -750,7 +750,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"installNewSignal": {
|
||||
"message": "Install new Signal Desktop",
|
||||
"message": "安裝新的Signal Desktop",
|
||||
"description": "When export is complete, a button shows which sends user to Signal Desktop install instructions"
|
||||
},
|
||||
"verify": {
|
||||
|
|
267
background.html
|
@ -2,30 +2,6 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<script type='text/x-tmpl-mustache' id='app-migration-screen'>
|
||||
<div class='content'>
|
||||
<img src='/images/icon_128.png'>
|
||||
{{ ^hideProgress }}
|
||||
<div class='container'>
|
||||
<span class='dot'></span>
|
||||
<span class='dot'></span>
|
||||
<span class='dot'></span>
|
||||
</div>
|
||||
{{ /hideProgress }}
|
||||
<div class='message'>{{& message }}</div>
|
||||
<div>
|
||||
{{ #installButton }}
|
||||
<button class='install grey'>{{ installButton }}</button>
|
||||
{{ /installButton }}
|
||||
{{ #exportButton }}
|
||||
<button class='export grey'>{{ exportButton }}</button>
|
||||
{{ /exportButton }}
|
||||
{{ #debugLogButton }}
|
||||
<button class='debug-log grey'>{{ debugLogButton }}</button>
|
||||
{{ /debugLogButton }}
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='app-loading-screen'>
|
||||
<div class='content'>
|
||||
<img src='/images/icon_128.png'>
|
||||
|
@ -58,6 +34,7 @@
|
|||
<ul class='menu-list'>
|
||||
<li class='showSettings'>{{ settings }}</li>
|
||||
<li class='show-debug-log'>{{ submitDebugLog }}</li>
|
||||
<li class='migrate'>{{ migrate }}</li>
|
||||
<li class='restart-signal'>{{ restartSignal }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -103,12 +80,15 @@
|
|||
<a target='_blank' href='https://chrome.google.com/webstore/detail/bikioccmkafdpakkkcpdbppfkghcmihk'>
|
||||
<button class='upgrade'>{{ upgrade }}</button>
|
||||
</a>
|
||||
{{ expiredWarning }}
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='migration_alert'>
|
||||
<button class='migrate'>{{ migrate }}</button>
|
||||
<div class='message'>
|
||||
{{ migrationWarning }}
|
||||
{{ expiredWarning }}
|
||||
</div>
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='upgrade_banner'>
|
||||
<span class='x banner-close'></span>
|
||||
<button class='migrate'>{{ upgradeNow }}</button>
|
||||
<div class='message'>
|
||||
{{ upgradeMessage }} <span class='highlight'>{{ highlight }}</span>
|
||||
</div>
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='banner'>
|
||||
|
@ -147,7 +127,6 @@
|
|||
<div class='conversation-menu menu'>
|
||||
<button class='hamburger' alt='conversation menu'></button>
|
||||
<ul class='menu-list'>
|
||||
<li class='disappearing-messages'>{{ disappearing-messages }}</li>
|
||||
{{#group}}
|
||||
<li class='show-members'>{{ show-members }}</li>
|
||||
<!-- <li class='update-group'>Update group</li> -->
|
||||
|
@ -157,7 +136,6 @@
|
|||
{{ ^isMe }}
|
||||
<li class='show-identity'>{{ show-identity }}</li>
|
||||
{{ /isMe }}
|
||||
<li class='end-session'>{{ end-session }}</li>
|
||||
{{/group}}
|
||||
<li class='destroy'>{{ destroy }}</li>
|
||||
</ul>
|
||||
|
@ -612,92 +590,167 @@
|
|||
{{/action }}
|
||||
</script>
|
||||
|
||||
<script type='text/x-tmpl-mustache' id='linux-install-instructions'>
|
||||
<div class='confirmation-dialog modal'>
|
||||
<div class="content" tabindex="2">
|
||||
<div class='header'>{{ header }}</div>
|
||||
<pre class='instructions'>curl -s https://updates.signal.org/desktop/apt/keys.asc | sudo apt-key add -
|
||||
echo "deb [arch=amd64] https://updates.signal.org/desktop/apt xenial main" | sudo tee -a /etc/apt/sources.list.d/signal-xenial.list
|
||||
sudo apt update && sudo apt install signal-desktop
|
||||
/opt/Signal/signal-desktop --import</pre>
|
||||
<div class='buttons'>
|
||||
<button class='ok' tabindex='1'>{{ ok }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type='text/x-tmpl-mustache' id='migration-flow-template'>
|
||||
{{#isStep1}}
|
||||
<div id='step1' class='step'>
|
||||
<div class='inner'>
|
||||
<div class='step-body'>
|
||||
<img class='banner-image' src='/images/icon_128.png'>
|
||||
<div class='header'>{{ startHeader }}</div>
|
||||
<div class='body-text-wide'>{{ startParagraph1 }}</div>
|
||||
<div class='body-text-wide'>{{ startParagraph2 }}</div>
|
||||
<div class='body-text-wide red-text'>{{ startParagraph3 }} <a target='_blank' href='https://support.signal.org/hc/en-us/articles/360007320431'>{{ moreInformation }}</div>
|
||||
</div>
|
||||
<div class='nav'>
|
||||
<div>
|
||||
<a class='button start'>{{ startButton }}</a>
|
||||
</div>
|
||||
<div>
|
||||
<a class='link cancel'>{{ cancelButton }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/isStep1}}
|
||||
{{#isStep2}}
|
||||
<div id='step2' class='step'>
|
||||
<div class='inner'>
|
||||
<div class='step-body'>
|
||||
<span class='banner-icon folder-outline'></span>
|
||||
<div class='header'>{{ chooseHeader }}</div>
|
||||
<div class='body-text-wide'>{{ choose }}</div>
|
||||
</div>
|
||||
<div class='nav'>
|
||||
<div>
|
||||
<a class='button choose'>{{ chooseButton }}</a>
|
||||
</div>
|
||||
<div>
|
||||
<a class='link cancel'>{{ cancelButton }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/isStep2}}
|
||||
{{#isStep3}}
|
||||
<div id='step3' class='step'>
|
||||
<div class='inner'>
|
||||
<div class='step-body'>
|
||||
<span class='banner-icon export'></span>
|
||||
<div class='header'>{{ exportHeader }}</div>
|
||||
</div>
|
||||
<div class='progress'>
|
||||
<div class='bar-container'>
|
||||
<div class='bar progress-bar progress-bar-striped active'></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='nav'>
|
||||
<div>
|
||||
<a class='link submit-debug-log'>{{ debugLogButton }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/isStep3}}
|
||||
{{#isStep4}}
|
||||
<div id='step4' class='step'>
|
||||
<div class='inner'>
|
||||
<div class='step-body'>
|
||||
<span class='banner-icon check-circle-outline'></span>
|
||||
<div class='header'>{{ completeHeader }}</div>
|
||||
<div class='body-text-wide center'>{{ completeIntro }}</div>
|
||||
<div class='export-location'>{{ completeLocation }}</div>
|
||||
<div class='body-text-wide'>{{ completeNextSteps }}</div>
|
||||
<div class='body-text-wide'>{{ completeSignoff }}</div>
|
||||
</div>
|
||||
<div class='nav'>
|
||||
<div>
|
||||
<a class='button get-new-version' target='_blank' href='{{ downloadLocation }}'>{{ installButton }}</a>
|
||||
</div>
|
||||
<div>
|
||||
<a class='link submit-debug-log'>{{ debugLogButton }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/isStep4}}
|
||||
{{#isStep5}}
|
||||
<div id='step5' class='step'>
|
||||
<div class='inner'>
|
||||
<div class='step-body'>
|
||||
<span class='banner-icon check-circle-outline'></span>
|
||||
<div class='header'>{{ uninstallHeader }}</div>
|
||||
<div class='body-text-wide center'>
|
||||
<ol class='uninstall-steps'>
|
||||
<li>{{ uninstallStep5 }}</li>
|
||||
<div class='export-location'>{{ completeLocation }}</div>
|
||||
{{ #uninstallStep6 }}
|
||||
<li><a href="https://support.signal.org/hc/en-us/articles/360007320191" target="_blank">{{ uninstallStep6 }}</a></li>
|
||||
{{ /uninstallStep6 }}
|
||||
<li>{{ uninstallStep1 }} <div class='url'>chrome://extensions/</div></li>
|
||||
<li>{{ uninstallStep2 }}</li>
|
||||
<li>{{ uninstallStep3 }}</li>
|
||||
<li>{{ uninstallStep4 }}</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class='nav'>
|
||||
<div>
|
||||
<a class='link submit-debug-log'>{{ debugLogButton }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/isStep5}}
|
||||
{{#isError}}
|
||||
<div id='error' class='step'>
|
||||
<div class='inner error-dialog clearfix'>
|
||||
<div class='step-body'>
|
||||
<span class='banner-icon alert-outline'></span>
|
||||
<div class='header'>{{ errorHeader }}</div>
|
||||
<div class='body-text-wide'>{{ error }}</div>
|
||||
</div>
|
||||
<div class='nav'>
|
||||
<div>
|
||||
<a class='button choose'>{{ tryAgain }}</a>
|
||||
</div>
|
||||
<div>
|
||||
<a class='link submit-debug-log'>{{ debugLogButton }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/isError}}
|
||||
</script>
|
||||
|
||||
|
||||
<script type='text/x-tmpl-mustache' id='install_flow_template'>
|
||||
<div id='step1' class='step'>
|
||||
<div class='inner'>
|
||||
<div class='step-body'>
|
||||
<img id='signal-icon' src='/images/icon_250.png'/>
|
||||
<h1>{{ installWelcome }}</h1>
|
||||
<p>{{ installTagline }}</p>
|
||||
</div>
|
||||
<div class='nav'>
|
||||
<div> <a class='button step2'>{{ installGetStartedButton }}</a> </div>
|
||||
<span class='dot step1 selected'></span>
|
||||
<span class='dot step2'></span>
|
||||
<span class='dot step3'></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id='step2' class='step'>
|
||||
<div class='inner'>
|
||||
<div class='step-body'>
|
||||
<img id='signal-phone' src='/images/signal-phone.png'>
|
||||
<p>{{{ installSignalLink }}}</p>
|
||||
</div>
|
||||
<div class='nav'>
|
||||
<div> <a class='button step3'>{{ installIHaveSignalButton }}</a> </div>
|
||||
<span class='dot step1'></span>
|
||||
<span class='dot step2 selected'></span>
|
||||
<span class='dot step3'></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id='step3' class='step'>
|
||||
<div class='inner'>
|
||||
<div class='step-body'>
|
||||
<div id="qr"></div>
|
||||
<p>{{ installAndroidInstructions }}</p>
|
||||
</div>
|
||||
<div class='nav'>
|
||||
<span class='dot step1'></span>
|
||||
<span class='dot step2'></span>
|
||||
<span class='dot step3 selected'></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form id='step4' class='step'>
|
||||
<div class='inner'>
|
||||
<div class='step-body'>
|
||||
<p>{{ installLinkingWithNumber }}</p>
|
||||
<h2 class='number'></h2>
|
||||
<img id='signal-computer' src='/images/signal-laptop.png'>
|
||||
<p>{{ installComputerName }}</p>
|
||||
<div>
|
||||
<input type='text' id='device-name' spellcheck='false' maxlength='50' />
|
||||
</div>
|
||||
<img class='banner-image' src='/images/icon_128.png'>
|
||||
<div class='header'>{{ preLinkExpiredHeader }}</div>
|
||||
<div class='body-text-wide'>{{ startExportIntroParagraph1 }}</div>
|
||||
</div>
|
||||
<div class='nav'>
|
||||
<div>
|
||||
<input type='submit' class='button' id='sync' value='{{ installFinalButton }}' />
|
||||
<a class='button get-new-version' target='_blank' href='https://signal.org/download'>{{ getNewVersion }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id='step5' class='step'>
|
||||
<div class='inner'>
|
||||
<div class='step-body'>
|
||||
<img id='signal-icon' src='/images/icon_250.png'/>
|
||||
<div class='progress-dialog'>
|
||||
<p class='status'></p>
|
||||
<div class='bar-container'><div class='bar progress-bar'></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='nav'>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id='stepTooManyDevices' class='step'>
|
||||
<div class='inner error-dialog clearfix'>
|
||||
<div class='panel step-body'>{{ installTooManyDevices }}</div>
|
||||
<div class='nav'>
|
||||
<button class='ok step3'>{{ ok }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M12,2L1,21H23M12,6L19.53,19H4.47M11,10V14H13V10M11,16V18H13V16" /></svg>
|
After Width: | Height: | Size: 357 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M18.71,19.5C17.88,20.74 17,21.95 15.66,21.97C14.32,22 13.89,21.18 12.37,21.18C10.84,21.18 10.37,21.95 9.1,22C7.79,22.05 6.8,20.68 5.96,19.47C4.25,17 2.94,12.45 4.7,9.39C5.57,7.87 7.13,6.91 8.82,6.88C10.1,6.86 11.32,7.75 12.11,7.75C12.89,7.75 14.37,6.68 15.92,6.84C16.57,6.87 18.39,7.1 19.56,8.82C19.47,8.88 17.39,10.1 17.41,12.63C17.44,15.65 20.06,16.66 20.09,16.67C20.06,16.74 19.67,18.11 18.71,19.5M13,3.5C13.73,2.67 14.94,2.04 15.94,2C16.07,3.17 15.6,4.35 14.9,5.19C14.21,6.04 13.07,6.7 11.95,6.61C11.8,5.46 12.36,4.26 13,3.5Z" /></svg>
|
After Width: | Height: | Size: 824 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4M11,16.5L6.5,12L7.91,10.59L11,13.67L16.59,8.09L18,9.5L11,16.5Z" /></svg>
|
After Width: | Height: | Size: 499 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M23,12L19,8V11H10V13H19V16M1,18V6C1,4.89 1.9,4 3,4H15A2,2 0 0,1 17,6V9H15V6H3V18H15V15H17V18A2,2 0 0,1 15,20H3A2,2 0 0,1 1,18Z" /></svg>
|
After Width: | Height: | Size: 421 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M20,18H4V8H20M20,6H12L10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6Z" /></svg>
|
After Width: | Height: | Size: 401 B |
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M791 411q-11 1-15.5 10.5t-8.5 9.5q-5 1-5-5 0-12 19-15h10zm87 14q-4 1-11.5-6.5t-17.5-4.5q24-11 32 2 3 6-3 9zm-351 427q-4-1-6 3t-4.5 12.5-5.5 13.5-10 13q-10 11-1 12 4 1 12.5-7t12.5-18q1-3 2-7t2-6 1.5-4.5.5-4v-3l-1-2.5-3-2zm855 359q0-18-55-42 4-15 7.5-27.5t5-26 3-21.5.5-22.5-1-19.5-3.5-22-4-20.5-5-25-5.5-26.5q-10-48-47-103t-72-75q24 20 57 83 87 162 54 278-11 40-50 42-31 4-38.5-18.5t-8-83.5-11.5-107q-9-39-19.5-69t-19.5-45.5-15.5-24.5-13-15-7.5-7q-14-62-31-103t-29.5-56-23.5-33-15-40q-4-21 6-53.5t4.5-49.5-44.5-25q-15-3-44.5-18t-35.5-16q-8-1-11-26t8-51 36-27q37-3 51 30t4 58q-11 19-2 26.5t30 .5q13-4 13-36v-37q-5-30-13.5-50t-21-30.5-23.5-15-27-7.5q-107 8-89 134 0 15-1 15-9-9-29.5-10.5t-33 .5-15.5-5q1-57-16-90t-45-34q-27-1-41.5 27.5t-16.5 59.5q-1 15 3.5 37t13 37.5 15.5 13.5q10-3 16-14 4-9-7-8-7 0-15.5-14.5t-9.5-33.5q-1-22 9-37t34-14q17 0 27 21t9.5 39-1.5 22q-22 15-31 29-8 12-27.5 23.5t-20.5 12.5q-13 14-15.5 27t7.5 18q14 8 25 19.5t16 19 18.5 13 35.5 6.5q47 2 102-15 2-1 23-7t34.5-10.5 29.5-13 21-17.5q9-14 20-8 5 3 6.5 8.5t-3 12-16.5 9.5q-20 6-56.5 21.5t-45.5 19.5q-44 19-70 23-25 5-79-2-10-2-9 2t17 19q25 23 67 22 17-1 36-7t36-14 33.5-17.5 30-17 24.5-12 17.5-2.5 8.5 11q0 2-1 4.5t-4 5-6 4.5-8.5 5-9 4.5-10 5-9.5 4.5q-28 14-67.5 44t-66.5 43-49 1q-21-11-63-73-22-31-25-22-1 3-1 10 0 25-15 56.5t-29.5 55.5-21 58 11.5 63q-23 6-62.5 90t-47.5 141q-2 18-1.5 69t-5.5 59q-8 24-29 3-32-31-36-94-2-28 4-56 4-19-1-18-2 1-4 5-36 65 10 166 5 12 25 28t24 20q20 23 104 90.5t93 76.5q16 15 17.5 38t-14 43-45.5 23q8 15 29 44.5t28 54 7 70.5q46-24 7-92-4-8-10.5-16t-9.5-12-2-6q3-5 13-9.5t20 2.5q46 52 166 36 133-15 177-87 23-38 34-30 12 6 10 52-1 25-23 92-9 23-6 37.5t24 15.5q3-19 14.5-77t13.5-90q2-21-6.5-73.5t-7.5-97 23-70.5q15-18 51-18 1-37 34.5-53t72.5-10.5 60 22.5zm-628-827q3-17-2.5-30t-11.5-15q-9-2-9 7 2 5 5 6 10 0 7 15-3 20 8 20 3 0 3-3zm419 197q-2-8-6.5-11.5t-13-5-14.5-5.5q-5-3-9.5-8t-7-8-5.5-6.5-4-4-4 1.5q-14 16 7 43.5t39 31.5q9 1 14.5-8t3.5-20zm-178-213q0-11-5-19.5t-11-12.5-9-3q-6 0-8 2t0 4 5 3q14 4 18 31 0 3 8-2 2-2 2-3zm54-233q0-2-2.5-5t-9-7-9.5-6q-15-15-24-15-9 1-11.5 7.5t-1 13-.5 12.5q-1 4-6 10.5t-6 9 3 8.5q4 3 8 0t11-9 15-9q1-1 9-1t15-2 9-7zm565 1341q20 12 31 24.5t12 24-2.5 22.5-15.5 22-23.5 19.5-30 18.5-31.5 16.5-32 15.5-27 13q-38 19-85.5 56t-75.5 64q-17 16-68 19.5t-89-14.5q-18-9-29.5-23.5t-16.5-25.5-22-19.5-47-9.5q-44-1-130-1-19 0-57 1.5t-58 2.5q-44 1-79.5 15t-53.5 30-43.5 28.5-53.5 11.5q-29-1-111-31t-146-43q-19-4-51-9.5t-50-9-39.5-9.5-33.5-14.5-17-19.5q-10-23 7-66.5t18-54.5q1-16-4-40t-10-42.5-4.5-36.5 10.5-27q14-12 57-14t60-12q30-18 42-35t12-51q21 73-32 106-32 20-83 15-34-3-43 10-13 15 5 57 2 6 8 18t8.5 18 4.5 17 1 22q0 15-17 49t-14 48q3 17 37 26 20 6 84.5 18.5t99.5 20.5q24 6 74 22t82.5 23 55.5 4q43-6 64.5-28t23-48-7.5-58.5-19-52-20-36.5q-121-190-169-242-68-74-113-40-11 9-15-15-3-16-2-38 1-29 10-52t24-47 22-42q8-21 26.5-72t29.5-78 30-61 39-54q110-143 124-195-12-112-16-310-2-90 24-151.5t106-104.5q39-21 104-21 53-1 106 13.5t89 41.5q57 42 91.5 121.5t29.5 147.5q-5 95 30 214 34 113 133 218 55 59 99.5 163t59.5 191q8 49 5 84.5t-12 55.5-20 22q-10 2-23.5 19t-27 35.5-40.5 33.5-61 14q-18-1-31.5-5t-22.5-13.5-13.5-15.5-11.5-20.5-9-19.5q-22-37-41-30t-28 49 7 97q20 70 1 195-10 65 18 100.5t73 33 85-35.5q59-49 89.5-66.5t103.5-42.5q53-18 77-36.5t18.5-34.5-25-28.5-51.5-23.5q-33-11-49.5-48t-15-72.5 15.5-47.5q1 31 8 56.5t14.5 40.5 20.5 28.5 21 19 21.5 13 16.5 9.5z"/></svg>
|
After Width: | Height: | Size: 3.4 KiB |
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M746 1006v651l-682-94v-557h682zm0-743v659h-682v-565zm982 743v786l-907-125v-661h907zm0-878v794h-907v-669z"/></svg>
|
After Width: | Height: | Size: 252 B |
|
@ -69,6 +69,16 @@
|
|||
return accountManager;
|
||||
};
|
||||
|
||||
function isTimeToUpgrade(text) {
|
||||
var percentage = parseInt(text, 10);
|
||||
|
||||
if (isNaN(percentage)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var roll = _.random(1, 100);
|
||||
return roll <= percentage;
|
||||
}
|
||||
|
||||
storage.fetch();
|
||||
storage.onready(function() {
|
||||
|
@ -94,7 +104,6 @@
|
|||
}
|
||||
|
||||
Whisper.WallClockListener.init(Whisper.events);
|
||||
Whisper.RotateSignedPreKeyListener.init(Whisper.events);
|
||||
Whisper.ExpiringMessagesListener.init(Whisper.events);
|
||||
});
|
||||
|
||||
|
@ -103,6 +112,8 @@
|
|||
};
|
||||
|
||||
Whisper.events.on('start-shutdown', function() {
|
||||
Whisper.RotateSignedPreKeyListener.stop();
|
||||
|
||||
if (messageReceiver) {
|
||||
messageReceiver.close().then(function() {
|
||||
messageReceiver = null;
|
||||
|
@ -115,62 +126,6 @@
|
|||
|
||||
function init(firstRun) {
|
||||
window.removeEventListener('online', init);
|
||||
if (!Whisper.Registration.isDone()) { return; }
|
||||
if (Whisper.Migration.inProgress()) { return; }
|
||||
|
||||
if (messageReceiver) { messageReceiver.close(); }
|
||||
|
||||
var USERNAME = storage.get('number_id');
|
||||
var PASSWORD = storage.get('password');
|
||||
var mySignalingKey = storage.get('signaling_key');
|
||||
|
||||
// initialize the socket and start listening for messages
|
||||
messageReceiver = new textsecure.MessageReceiver(
|
||||
SERVER_URL, SERVER_PORTS, USERNAME, PASSWORD, mySignalingKey
|
||||
);
|
||||
messageReceiver.addEventListener('message', onMessageReceived);
|
||||
messageReceiver.addEventListener('receipt', onDeliveryReceipt);
|
||||
messageReceiver.addEventListener('contact', onContactReceived);
|
||||
messageReceiver.addEventListener('group', onGroupReceived);
|
||||
messageReceiver.addEventListener('sent', onSentMessage);
|
||||
messageReceiver.addEventListener('read', onReadReceipt);
|
||||
messageReceiver.addEventListener('verified', onVerified);
|
||||
messageReceiver.addEventListener('error', onError);
|
||||
messageReceiver.addEventListener('empty', onEmpty);
|
||||
messageReceiver.addEventListener('progress', onProgress);
|
||||
|
||||
window.textsecure.messaging = new textsecure.MessageSender(
|
||||
SERVER_URL, SERVER_PORTS, USERNAME, PASSWORD
|
||||
);
|
||||
|
||||
// Because v0.43.2 introduced a bug that lost contact details, v0.43.4 introduces
|
||||
// a one-time contact sync to restore all lost contact/group information. We
|
||||
// disable this checking if a user is first registering.
|
||||
var key = 'chrome-contact-sync-v0.43.4';
|
||||
if (!storage.get(key)) {
|
||||
storage.put(key, true);
|
||||
|
||||
if (!firstRun && textsecure.storage.user.getDeviceId() != '1') {
|
||||
window.getSyncRequest();
|
||||
}
|
||||
}
|
||||
|
||||
if (firstRun === true && textsecure.storage.user.getDeviceId() != '1') {
|
||||
if (!storage.get('theme-setting') && textsecure.storage.get('userAgent') === 'OWI') {
|
||||
storage.put('theme-setting', 'ios');
|
||||
}
|
||||
var syncRequest = new textsecure.SyncRequest(textsecure.messaging, messageReceiver);
|
||||
Whisper.events.trigger('contactsync:begin');
|
||||
syncRequest.addEventListener('success', function() {
|
||||
console.log('sync successful');
|
||||
storage.put('synced_at', Date.now());
|
||||
Whisper.events.trigger('contactsync');
|
||||
});
|
||||
syncRequest.addEventListener('timeout', function() {
|
||||
console.log('sync timed out');
|
||||
Whisper.events.trigger('contactsync');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function onEmpty() {
|
||||
|
|
202
js/backup.js
|
@ -3,6 +3,22 @@
|
|||
window.Whisper = window.Whisper || {};
|
||||
|
||||
function stringToBlob(string) {
|
||||
if (string === null || string === undefined) {
|
||||
console.log('stringToBlob: replacing null/undefined with empty string');
|
||||
string = '';
|
||||
}
|
||||
if (string.type === 'ArrayBuffer' && string.encoding === 'base64') {
|
||||
console.log('stringToBlob: Processing base64 attachment data');
|
||||
string = dcodeIO.ByteBuffer.wrap(string.data, 'base64').toArrayBuffer();
|
||||
}
|
||||
if (typeof string !== 'string' && !(string instanceof ArrayBuffer)) {
|
||||
// Not sure what this is, but perhaps we can make the right thing happen by sending
|
||||
// it to a Uint8Array, which the wrap() method below handles just fine. Uint8Array
|
||||
// can take an ArrayBuffer, so it will help if I'm right that the weird attachment
|
||||
// data is an ArrayBuffer-like thing, while not being technically an instanceof.
|
||||
console.log('stringToBlob: sending strange object to Uint8Array --', typeof string, JSON.stringify(string), string);
|
||||
string = new Uint8Array(string);
|
||||
}
|
||||
var buffer = dcodeIO.ByteBuffer.wrap(string).toArrayBuffer();
|
||||
return new Blob([buffer]);
|
||||
}
|
||||
|
@ -43,10 +59,8 @@
|
|||
|
||||
function createOutputStream(fileWriter) {
|
||||
var wait = Promise.resolve();
|
||||
var count = 0;
|
||||
return {
|
||||
write: function(string) {
|
||||
var i = count++;
|
||||
wait = wait.then(function() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
fileWriter.onwriteend = resolve;
|
||||
|
@ -60,9 +74,11 @@
|
|||
};
|
||||
}
|
||||
|
||||
function exportNonMessages(idb_db, parent) {
|
||||
return createFileAndWriter(parent, 'db.json').then(function(writer) {
|
||||
return exportToJsonFile(idb_db, writer);
|
||||
function exportNonMessages(idb_db, parent, options) {
|
||||
// We wouldn't want to overwrite another db file.
|
||||
var exclusive = true;
|
||||
return createFileAndWriter(parent, 'db.json', exclusive).then(function(writer) {
|
||||
return exportToJsonFile(idb_db, writer, options);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -70,10 +86,27 @@
|
|||
* Export all data from an IndexedDB database
|
||||
* @param {IDBDatabase} idb_db
|
||||
*/
|
||||
function exportToJsonFile(idb_db, fileWriter) {
|
||||
function exportToJsonFile(idb_db, fileWriter, options) {
|
||||
options = options || {};
|
||||
_.defaults(options, {excludeClientConfig: false});
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
var storeNames = idb_db.objectStoreNames;
|
||||
storeNames = _.without(storeNames, 'messages');
|
||||
storeNames = _.without(storeNames, 'messages', 'debug');
|
||||
|
||||
if (options.excludeClientConfig) {
|
||||
console.log('exportToJsonFile: excluding client config from export');
|
||||
storeNames = _.without(
|
||||
storeNames,
|
||||
'items',
|
||||
'signedPreKeys',
|
||||
'preKeys',
|
||||
'identityKeys',
|
||||
'sessions',
|
||||
'unprocessed' // since we won't be able to decrypt them anyway
|
||||
);
|
||||
}
|
||||
|
||||
var exportedStoreNames = [];
|
||||
if (storeNames.length === 0) {
|
||||
throw new Error('No stores to export');
|
||||
|
@ -132,6 +165,12 @@
|
|||
stream.write('}').then(function() {
|
||||
console.log('Finished writing all stores to disk');
|
||||
resolve();
|
||||
}, function(error) {
|
||||
console.log(
|
||||
'Failed to write db.json to disk',
|
||||
error && error.stack ? error.stack : error
|
||||
);
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -217,17 +256,19 @@
|
|||
});
|
||||
}
|
||||
|
||||
function createDirectory(parent, name) {
|
||||
function createDirectory(parent, name, exclusive) {
|
||||
var sanitized = sanitizeFileName(name);
|
||||
console._log('-- about to create directory', sanitized);
|
||||
return new Promise(function(resolve, reject) {
|
||||
parent.getDirectory(sanitized, {create: true, exclusive: true}, resolve, reject);
|
||||
parent.getDirectory(sanitized, {create: true, exclusive: exclusive}, resolve, reject);
|
||||
});
|
||||
}
|
||||
|
||||
function createFileAndWriter(parent, name) {
|
||||
function createFileAndWriter(parent, name, exclusive) {
|
||||
var sanitized = sanitizeFileName(name);
|
||||
console._log('-- about to create file', sanitized);
|
||||
return new Promise(function(resolve, reject) {
|
||||
parent.getFile(sanitized, {create: true, exclusive: true}, function(file) {
|
||||
parent.getFile(sanitized, {create: true, exclusive: exclusive}, function(file) {
|
||||
return file.createWriter(function(writer) {
|
||||
resolve(writer);
|
||||
}, reject);
|
||||
|
@ -312,16 +353,31 @@
|
|||
});
|
||||
}
|
||||
|
||||
var numAttachments = 0;
|
||||
var numFailedAttachments = 0;
|
||||
|
||||
function writeAttachment(dir, attachment) {
|
||||
numAttachments += 1;
|
||||
|
||||
var filename = getAttachmentFileName(attachment);
|
||||
return createFileAndWriter(dir, filename).then(function(writer) {
|
||||
// If attachments are in messages with the same received_at and the same name,
|
||||
// then we'll let that overwrite happen. It should be very uncommon.
|
||||
var exclusive = false;
|
||||
return createFileAndWriter(dir, filename, exclusive).then(function(writer) {
|
||||
var stream = createOutputStream(writer);
|
||||
return stream.write(attachment.data);
|
||||
}).catch(function(error) {
|
||||
numFailedAttachments += 1;
|
||||
console.log('writeAttachment error:', error && error.stack ? error.stack : error);
|
||||
});
|
||||
}
|
||||
|
||||
function writeAttachments(parentDir, name, messageId, attachments) {
|
||||
return createDirectory(parentDir, messageId).then(function(dir) {
|
||||
// We've had a lot of trouble with attachments, likely due to messages with the same
|
||||
// received_at in the same conversation. So we sacrifice one of the attachments in
|
||||
// this unusual case.
|
||||
var exclusive = false;
|
||||
return createDirectory(parentDir, messageId, exclusive).then(function(dir) {
|
||||
return Promise.all(_.map(attachments, function(attachment) {
|
||||
return writeAttachment(dir, attachment);
|
||||
}));
|
||||
|
@ -340,9 +396,54 @@
|
|||
return filename.toString().replace(/[^a-z0-9.,+()'#\- ]/gi, '_');
|
||||
}
|
||||
|
||||
var numConversations = 0;
|
||||
var numFailedConversations = 0;
|
||||
|
||||
function delay(ms) {
|
||||
console.log('Waiting', ms, 'milliseconds');
|
||||
return new Promise(function(resolve) {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
|
||||
// When we write files to disk, those writes sometimes take a while to appear on disk,
|
||||
// even if the Chrome file-writing APIs tell us that the write is complete. So, to
|
||||
// ensure that the export is really complete, we repeatedly check the messages.json
|
||||
// file for well-formed JSON, trying for up to five minutes.
|
||||
var CHECK_MAX = 60;
|
||||
function checkConversation(conversationId, dir, count) {
|
||||
return delay(2000).then(function() {
|
||||
console.log(
|
||||
'Verifying messages.json produced for conversation',
|
||||
conversationId,
|
||||
'attempt number',
|
||||
count
|
||||
);
|
||||
return readFileAsText(dir, 'messages.json');
|
||||
}).then(function(contents) {
|
||||
JSON.parse(contents);
|
||||
}).catch(function(error) {
|
||||
console.log(
|
||||
'Export of conversation',
|
||||
conversationId,
|
||||
'may be malformed:',
|
||||
error && error.stack ? error.stack : error
|
||||
);
|
||||
|
||||
if (count >= CHECK_MAX) {
|
||||
numFailedConversations += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
return checkConversation(conversationId, dir, count + 1);
|
||||
});
|
||||
}
|
||||
|
||||
function exportConversation(idb_db, name, conversation, dir) {
|
||||
console.log('exporting conversation', name);
|
||||
return createFileAndWriter(dir, 'messages.json').then(function(writer) {
|
||||
// We wouldn't want to overwrite the contents of a different conversation.
|
||||
var exclusive = true;
|
||||
return createFileAndWriter(dir, 'messages.json', exclusive).then(function(writer) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var transaction = idb_db.transaction('messages', "readwrite");
|
||||
transaction.onerror = function(e) {
|
||||
|
@ -381,23 +482,41 @@
|
|||
request.onsuccess = function(event) {
|
||||
var cursor = event.target.result;
|
||||
if (cursor) {
|
||||
if (count !== 0) {
|
||||
stream.write(',');
|
||||
}
|
||||
|
||||
var message = cursor.value;
|
||||
var messageId = message.received_at;
|
||||
var attachments = message.attachments;
|
||||
|
||||
// skip message if it is disappearing, no matter the amount of time left
|
||||
if (message.expireTimer) {
|
||||
cursor.continue();
|
||||
return;
|
||||
}
|
||||
|
||||
if (count !== 0) {
|
||||
stream.write(',');
|
||||
}
|
||||
|
||||
// eliminate attachment data from the JSON, since it will go to disk
|
||||
message.attachments = _.map(attachments, function(attachment) {
|
||||
return _.omit(attachment, ['data']);
|
||||
});
|
||||
// completely drop any attachments in messages cached in error objects
|
||||
message.errors = _.map(message.errors, function(error) {
|
||||
if (error && error.args && error.args[0] && error.args[0].attachments) {
|
||||
error.args[0].attachments = [];
|
||||
}
|
||||
return error;
|
||||
});
|
||||
|
||||
var jsonString = JSON.stringify(stringify(message));
|
||||
stream.write(jsonString);
|
||||
|
||||
if (attachments && attachments.length) {
|
||||
var process = function() {
|
||||
console._log('-- writing attachments for message', message.id);
|
||||
if (!message.received_at) {
|
||||
return Promise.reject(new Error('Message', message.id, 'had no received_at'));
|
||||
}
|
||||
return writeAttachments(dir, name, messageId, attachments);
|
||||
};
|
||||
promiseChain = promiseChain.then(process);
|
||||
|
@ -407,7 +526,12 @@
|
|||
cursor.continue();
|
||||
} else {
|
||||
var promise = stream.write(']}');
|
||||
promiseChain = promiseChain.then(promise);
|
||||
|
||||
numConversations += 1;
|
||||
|
||||
promiseChain = promiseChain
|
||||
.then(promise)
|
||||
.then(checkConversation.bind(null, name, dir, 0));
|
||||
|
||||
return promiseChain.then(function() {
|
||||
console.log('done exporting conversation', name);
|
||||
|
@ -429,7 +553,7 @@
|
|||
|
||||
// Goals for directory names:
|
||||
// 1. Human-readable, for easy use and verification by user (names not just ids)
|
||||
// 2. Sorted just like the list of conversations in the left-pan (active_at)
|
||||
// 2. Sorted just like the list of conversations in the left pane (active_at)
|
||||
// 3. Disambiguated from other directories (active_at, truncated name, id)
|
||||
function getConversationDirName(conversation) {
|
||||
var name = conversation.active_at || 'never';
|
||||
|
@ -487,7 +611,10 @@
|
|||
var name = getConversationLoggingName(conversation);
|
||||
|
||||
var process = function() {
|
||||
return createDirectory(parentDir, dir).then(function(dir) {
|
||||
// If we have a conversation directory collision, the user will lose the
|
||||
// contents of the first conversation. So we throw an error.
|
||||
var exclusive = true;
|
||||
return createDirectory(parentDir, dir, exclusive).then(function(dir) {
|
||||
return exportConversation(idb_db, name, conversation, dir);
|
||||
});
|
||||
};
|
||||
|
@ -650,27 +777,54 @@
|
|||
return moment().format('YYYY MMM Do [at] h.mm.ss a');
|
||||
}
|
||||
|
||||
function printAttachmentStats() {
|
||||
console.log('Total attachments:', numAttachments);
|
||||
console.log('Failed attachments:', numFailedAttachments);
|
||||
}
|
||||
|
||||
function printConversationStats() {
|
||||
console.log('Total conversations:', numConversations);
|
||||
console.log('Possibly malformed conversations:', numFailedConversations);
|
||||
}
|
||||
|
||||
Whisper.Backup = {
|
||||
backupToDirectory: function() {
|
||||
exportToDirectory: function(options) {
|
||||
return getDirectory().then(function(directoryEntry) {
|
||||
var idb;
|
||||
var dir;
|
||||
numConversations = 0;
|
||||
numFailedConversations = 0;
|
||||
numAttachments = 0;
|
||||
numFailedAttachments = 0;
|
||||
|
||||
return openDatabase().then(function(idb_db) {
|
||||
idb = idb_db;
|
||||
var name = 'Signal Export ' + getTimestamp();
|
||||
return createDirectory(directoryEntry, name);
|
||||
// We don't want to overwrite another signal export, so we set exclusive = true
|
||||
var exclusive = true;
|
||||
return createDirectory(directoryEntry, name, exclusive);
|
||||
}).then(function(directory) {
|
||||
dir = directory;
|
||||
return exportNonMessages(idb, dir);
|
||||
return exportNonMessages(idb, dir, options);
|
||||
}).then(function() {
|
||||
return exportConversations(idb, dir);
|
||||
}).then(function() {
|
||||
return getDisplayPath(dir);
|
||||
});
|
||||
}).then(function(path) {
|
||||
printAttachmentStats();
|
||||
printConversationStats();
|
||||
console.log('done backing up!');
|
||||
if (numFailedAttachments) {
|
||||
throw new Error('Export failed, one or more attachments failed');
|
||||
}
|
||||
if (numFailedConversations) {
|
||||
throw new Error('Export failed, one or more conversations failed');
|
||||
}
|
||||
return path;
|
||||
}, function(error) {
|
||||
printAttachmentStats();
|
||||
printConversationStats();
|
||||
console.log(
|
||||
'the backup went wrong:',
|
||||
error && error.stack ? error.stack : error
|
||||
|
|
|
@ -172,9 +172,12 @@
|
|||
extension.windows.open({
|
||||
id: id,
|
||||
url: url,
|
||||
bounds: { width: 800, height: 666, },
|
||||
minWidth: 800,
|
||||
minHeight: 666
|
||||
bounds: {
|
||||
width: 580,
|
||||
height: 440,
|
||||
},
|
||||
minWidth: 600,
|
||||
minHeight: 360
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -255,13 +258,15 @@
|
|||
|
||||
if (chrome.runtime.onInstalled) {
|
||||
chrome.runtime.onInstalled.addListener(function(options) {
|
||||
var version = chrome.runtime.getManifest().version;
|
||||
|
||||
if (options.reason === 'install') {
|
||||
console.log('new install');
|
||||
console.log('new install:', version);
|
||||
extension.install();
|
||||
} else if (options.reason === 'update') {
|
||||
console.log('new update. previous version:', options.previousVersion);
|
||||
console.log('new update:', version, '- previous version:', options.previousVersion);
|
||||
} else {
|
||||
console.log('onInstalled', options.reason);
|
||||
console.log('onInstalled', options.reason, 'version:', version);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
152
js/database.js
|
@ -9,6 +9,70 @@
|
|||
window.Whisper.Database.id = window.Whisper.Database.id || 'signal';
|
||||
window.Whisper.Database.nolog = true;
|
||||
|
||||
window.Whisper.Database.cleanMessageAttachments = function(message) {
|
||||
var attachments = message.attachments;
|
||||
var changed = false;
|
||||
|
||||
if (!attachments || !attachments.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0, max = attachments.length; i < max; i += 1) {
|
||||
var attachment = attachments[i];
|
||||
|
||||
// catch missing and non-string filenames
|
||||
if (typeof attachment.fileName !== 'string') {
|
||||
if (attachment.fileName) {
|
||||
delete attachment.fileName;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// if id is falsey, or neither number or string, we make our own id
|
||||
if (!attachment.id || (typeof attachment.id !== 'number' && typeof attachment.id !== 'string')) {
|
||||
attachment.id = _.uniqueId('attachment');
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
// if contentType is truthy, we ensure it's a string
|
||||
if (attachment.contentType && typeof attachment.contentType !== 'string') {
|
||||
delete attachment.contentType;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// elminate all attachments without data
|
||||
var attachmentsWithData = _.filter(attachments, function(attachment) {
|
||||
return attachment.data;
|
||||
});
|
||||
|
||||
if (attachments.length !== attachmentsWithData.length) {
|
||||
message.attachments = attachmentsWithData;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
};
|
||||
|
||||
window.Whisper.Database.dropZeroLengthAttachments = function(message) {
|
||||
var attachments = message.attachments;
|
||||
var changed = false;
|
||||
|
||||
if (!attachments || !attachments.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var attachmentsWithData = _.filter(attachments, function(attachment) {
|
||||
return attachment.data && (attachment.data.length || attachment.data.byteLength);
|
||||
});
|
||||
|
||||
if (attachments.length !== attachmentsWithData.length) {
|
||||
message.attachments = attachmentsWithData;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
};
|
||||
|
||||
Whisper.Database.migrations = [
|
||||
{
|
||||
version: "1.0",
|
||||
|
@ -274,6 +338,92 @@
|
|||
messages.createIndex('unique', ['source', 'sourceDevice', 'sent_at'], { unique: true });
|
||||
next();
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
version: "16.0",
|
||||
migrate: function(transaction, next) {
|
||||
console.log('migration 16.0');
|
||||
console.log('Cleaning up dirty attachment data');
|
||||
|
||||
var messages = transaction.objectStore('messages');
|
||||
var queryRequest = messages.openCursor();
|
||||
var promises = [];
|
||||
|
||||
queryRequest.onsuccess = function(event) {
|
||||
var cursor = event.target.result;
|
||||
if (!cursor) {
|
||||
return Promise.all(promises).then(function() {
|
||||
console.log('Fixed', promises.length, 'messages with unexpected attachment structure');
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
var message = cursor.value;
|
||||
var changed = window.Whisper.Database.cleanMessageAttachments(message);
|
||||
|
||||
if (!changed) {
|
||||
return cursor.continue();
|
||||
}
|
||||
|
||||
promises.push(new Promise(function(resolve, reject) {
|
||||
var putRequest = messages.put(message, message.id);
|
||||
putRequest.onsuccess = resolve;
|
||||
putRequest.onerror = function(e) {
|
||||
console.log(e);
|
||||
reject(e);
|
||||
};
|
||||
}));
|
||||
|
||||
return cursor.continue();
|
||||
};
|
||||
|
||||
queryRequest.onerror = function(event) {
|
||||
console.log(event);
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
version: "17.0",
|
||||
migrate: function(transaction, next) {
|
||||
console.log('migration 17.0');
|
||||
console.log('Removing attachments with zero-length data');
|
||||
|
||||
var messages = transaction.objectStore('messages');
|
||||
var queryRequest = messages.openCursor();
|
||||
var promises = [];
|
||||
|
||||
queryRequest.onsuccess = function(event) {
|
||||
var cursor = event.target.result;
|
||||
if (!cursor) {
|
||||
return Promise.all(promises).then(function() {
|
||||
console.log('Fixed', promises.length, 'messages with unexpected attachment structure');
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
var message = cursor.value;
|
||||
var changed = window.Whisper.Database.dropZeroLengthAttachments(message);
|
||||
|
||||
if (!changed) {
|
||||
return cursor.continue();
|
||||
}
|
||||
|
||||
promises.push(new Promise(function(resolve, reject) {
|
||||
var putRequest = messages.put(message, message.id);
|
||||
putRequest.onsuccess = resolve;
|
||||
putRequest.onerror = function(e) {
|
||||
console.log(e);
|
||||
reject(e);
|
||||
};
|
||||
}));
|
||||
|
||||
return cursor.continue();
|
||||
};
|
||||
|
||||
queryRequest.onerror = function(event) {
|
||||
console.log(event);
|
||||
};
|
||||
}
|
||||
},
|
||||
];
|
||||
}());
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
|
||||
var MAX_MESSAGES = 2000;
|
||||
var PHONE_REGEX = /\+\d{7,12}(\d{3})/g;
|
||||
var DEBUGLOGS_BASE_URL = 'https://debuglogs.org';
|
||||
var log = new DebugLog();
|
||||
if (window.console) {
|
||||
console._log = console.log;
|
||||
|
@ -60,18 +61,56 @@
|
|||
if (log === undefined) {
|
||||
log = console.get();
|
||||
}
|
||||
return new Promise(function(resolve) {
|
||||
$.post('https://api.github.com/gists', textsecure.utils.jsonThing({
|
||||
"files": { "debugLog.txt": { "content": log } }
|
||||
})).then(function(response) {
|
||||
console._log('Posted debug log to ', response.html_url);
|
||||
resolve(response.html_url);
|
||||
}).fail(resolve);
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
$.get(DEBUGLOGS_BASE_URL).then(function (signedForm) {
|
||||
var url = signedForm.url;
|
||||
var fields = signedForm.fields;
|
||||
|
||||
var formData = new FormData();
|
||||
|
||||
// NOTE: Service expects `key` to come first:
|
||||
formData.append('key', fields.key);
|
||||
formData.append('Content-Type', 'text/plain');
|
||||
for (var key in fields) {
|
||||
if (key === 'key') {
|
||||
continue;
|
||||
}
|
||||
var value = fields[key];
|
||||
formData.append(key, value);
|
||||
}
|
||||
|
||||
var contentBlob = new Blob([log], { type: 'text/plain' });
|
||||
formData.append('file', contentBlob);
|
||||
|
||||
var publishedLogURL = DEBUGLOGS_BASE_URL + '/' + fields.key;
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
request.open('POST', url);
|
||||
request.onreadystatechange = function (event) {
|
||||
if (request.readyState !== XMLHttpRequest.DONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.status !== 204) {
|
||||
return reject(
|
||||
new Error('Failed to publish debug log. Status: ' +
|
||||
request.statusText + ' (' + request.status + ')'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return resolve(publishedLogURL);
|
||||
};
|
||||
request.send(formData);
|
||||
}).fail(function () {
|
||||
reject(new Error('Failed to publish logs'));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
window.onerror = function(message, script, line, col, error) {
|
||||
console.log(error.stack);
|
||||
console.log(error && error.stack ? error.stack : error);
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -618,11 +618,16 @@
|
|||
updateExpirationTimer: function(expireTimer, source, received_at) {
|
||||
if (!expireTimer) { expireTimer = null; }
|
||||
source = source || textsecure.storage.user.getNumber();
|
||||
var timestamp = received_at || Date.now();
|
||||
// When we add a disappearing messages notification to the conversation, we want it
|
||||
// to be above the message that initiated that change, hence the subtraction.
|
||||
var timestamp = (received_at || Date.now()) - 1;
|
||||
this.save({ expireTimer: expireTimer });
|
||||
var message = this.messageCollection.add({
|
||||
// Even though this isn't reflected to the user, we want to place the last seen
|
||||
// indicator above it. We set it to 'unread' to trigger that placement.
|
||||
unread : 1,
|
||||
conversationId : this.id,
|
||||
type : received_at ? 'incoming' : 'outgoing',
|
||||
// No type; 'incoming' messages are specially treated by conversation.markRead()
|
||||
sent_at : timestamp,
|
||||
received_at : timestamp,
|
||||
flags : textsecure.protobuf.DataMessage.Flags.EXPIRATION_TIMER_UPDATE,
|
||||
|
@ -635,16 +640,21 @@
|
|||
message.set({destination: this.id});
|
||||
}
|
||||
message.save();
|
||||
if (message.isOutgoing()) { // outgoing update, send it to the number/group
|
||||
var sendFunc;
|
||||
if (this.get('type') == 'private') {
|
||||
sendFunc = textsecure.messaging.sendExpirationTimerUpdateToNumber;
|
||||
}
|
||||
else {
|
||||
sendFunc = textsecure.messaging.sendExpirationTimerUpdateToGroup;
|
||||
}
|
||||
message.send(sendFunc(this.get('id'), this.get('expireTimer'), message.get('sent_at')));
|
||||
|
||||
// if change was made remotely, don't send it to the number/group
|
||||
if (received_at) {
|
||||
return message;
|
||||
}
|
||||
|
||||
var sendFunc;
|
||||
if (this.get('type') == 'private') {
|
||||
sendFunc = textsecure.messaging.sendExpirationTimerUpdateToNumber;
|
||||
}
|
||||
else {
|
||||
sendFunc = textsecure.messaging.sendExpirationTimerUpdateToGroup;
|
||||
}
|
||||
message.send(sendFunc(this.get('id'), this.get('expireTimer'), message.get('sent_at')));
|
||||
|
||||
return message;
|
||||
},
|
||||
|
||||
|
@ -770,6 +780,10 @@
|
|||
},
|
||||
|
||||
getProfile: function(id) {
|
||||
if (!textsecure.messaging) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return textsecure.messaging.getProfile(id).then(function(profile) {
|
||||
var identityKey = dcodeIO.ByteBuffer.wrap(profile.identityKey, 'base64').toArrayBuffer();
|
||||
|
||||
|
|
|
@ -45,8 +45,15 @@
|
|||
timeout = setTimeout(runWhenOnline, waitTime);
|
||||
}
|
||||
|
||||
var started = false;
|
||||
Whisper.RotateSignedPreKeyListener = {
|
||||
init: function(events) {
|
||||
if (started) {
|
||||
console.log('Already started signed prekey listener');
|
||||
return;
|
||||
}
|
||||
started = true;
|
||||
|
||||
if (Whisper.Registration.isDone()) {
|
||||
setTimeoutForNextRun();
|
||||
}
|
||||
|
@ -59,6 +66,15 @@
|
|||
setTimeoutForNextRun();
|
||||
}
|
||||
});
|
||||
},
|
||||
stop: function() {
|
||||
console.log('Stopping signed prekey rotation');
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
},
|
||||
start: function() {
|
||||
console.log('Starting signed prekey rotation');
|
||||
setTimeoutForNextRun();
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
|
|
@ -44,8 +44,12 @@
|
|||
var item = items.get("" + key);
|
||||
if (item) {
|
||||
items.remove(item);
|
||||
item.destroy();
|
||||
return new Promise(function(resolve, reject) {
|
||||
item.destroy().then(resolve, reject);
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
onready: function(callback) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
/*
|
||||
* vim: ts=4:sw=4:expandtab
|
||||
*/
|
||||
(function () {
|
||||
|
@ -50,6 +50,9 @@
|
|||
this.cancel();
|
||||
}
|
||||
},
|
||||
focusOk: function() {
|
||||
this.$('.ok').focus();
|
||||
},
|
||||
focusCancel: function() {
|
||||
this.$('.cancel').focus();
|
||||
}
|
||||
|
|
|
@ -25,9 +25,12 @@
|
|||
this.listenTo(this.model.messageCollection, 'add remove', updateLastMessage);
|
||||
this.listenTo(this.model, 'newmessage', updateLastMessage);
|
||||
|
||||
extension.windows.onClosed(this.stopListening.bind(this));
|
||||
this.timeStampView = new Whisper.TimestampView({brief: true});
|
||||
this.model.updateLastMessage();
|
||||
|
||||
if (extension.windows) {
|
||||
extension.windows.onClosed(this.stopListening.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
markSelected: function() {
|
||||
|
|
|
@ -20,6 +20,11 @@
|
|||
return { toastMessage: i18n('youLeftTheGroup') };
|
||||
}
|
||||
});
|
||||
Whisper.ReadOnlyToast = Whisper.ToastView.extend({
|
||||
render_attributes: function() {
|
||||
return { toastMessage: i18n('readOnlyMode') };
|
||||
}
|
||||
});
|
||||
|
||||
var MenuView = Whisper.View.extend({
|
||||
toggleMenu: function() {
|
||||
|
@ -869,34 +874,10 @@
|
|||
|
||||
checkUnverifiedSendMessage: function(e, options) {
|
||||
e.preventDefault();
|
||||
this.sendStart = Date.now();
|
||||
this.$messageField.prop('disabled', true);
|
||||
|
||||
options = options || {};
|
||||
_.defaults(options, {force: false});
|
||||
|
||||
// This will go to the trust store for the latest identity key information,
|
||||
// and may result in the display of a new banner for this conversation.
|
||||
this.model.updateVerified().then(function() {
|
||||
var contacts = this.model.getUnverified();
|
||||
if (!contacts.length) {
|
||||
return this.checkUntrustedSendMessage(e, options);
|
||||
}
|
||||
|
||||
if (options.force) {
|
||||
return this.markAllAsVerifiedDefault(contacts).then(function() {
|
||||
this.checkUnverifiedSendMessage(e, options);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
this.showSendConfirmationDialog(e, contacts);
|
||||
}.bind(this)).catch(function(error) {
|
||||
this.focusMessageField();
|
||||
console.log(
|
||||
'checkUnverifiedSendMessage error:',
|
||||
error && error.stack ? error.stack : error
|
||||
);
|
||||
}.bind(this));
|
||||
var toast = new Whisper.ReadOnlyToast();
|
||||
toast.$el.insertAfter(this.$el);
|
||||
toast.render();
|
||||
},
|
||||
|
||||
checkUntrustedSendMessage: function(e, options) {
|
||||
|
|
|
@ -98,13 +98,6 @@
|
|||
model: { window: options.window }
|
||||
});
|
||||
|
||||
if (!options.initialLoadComplete) {
|
||||
this.appLoadingScreen = new Whisper.AppLoadingScreen();
|
||||
this.appLoadingScreen.render();
|
||||
this.appLoadingScreen.$el.prependTo(this.el);
|
||||
this.startConnectionListener();
|
||||
}
|
||||
|
||||
var inboxCollection = getInboxCollection();
|
||||
|
||||
inboxCollection.on('messageError', function() {
|
||||
|
@ -141,23 +134,13 @@
|
|||
this.networkStatusView = new Whisper.NetworkStatusView();
|
||||
this.$el.find('.network-status-container').append(this.networkStatusView.render().el);
|
||||
|
||||
extension.windows.onClosed(function() {
|
||||
this.inboxListView.stopListening();
|
||||
}.bind(this));
|
||||
this.showUpgradeScreen();
|
||||
this.showUpgradeBanner();
|
||||
|
||||
if (extension.expired()) {
|
||||
var banner = new Whisper.ExpiredAlertBanner().render();
|
||||
banner.$el.prependTo(this.$el);
|
||||
this.$el.addClass('expired');
|
||||
} else if (Whisper.Migration.inProgress()) {
|
||||
if (this.appLoadingScreen) {
|
||||
this.appLoadingScreen.remove();
|
||||
this.appLoadingScreen = null;
|
||||
}
|
||||
this.showMigrationScreen();
|
||||
} else if (storage.get('migrationEnabled')) {
|
||||
var migrationBanner = new Whisper.MigrationAlertBanner().render();
|
||||
migrationBanner.$el.prependTo(this.$el);
|
||||
if (extension.windows) {
|
||||
extension.windows.onClosed(function() {
|
||||
this.inboxListView.stopListening();
|
||||
}.bind(this));
|
||||
}
|
||||
},
|
||||
render_attributes: {
|
||||
|
@ -165,6 +148,7 @@
|
|||
selectAContact : i18n('selectAContact'),
|
||||
searchForPeopleOrGroups : i18n('searchForPeopleOrGroups'),
|
||||
submitDebugLog : i18n('submitDebugLog'),
|
||||
migrate : i18n('migrateToStandalone'),
|
||||
settings : i18n('settings'),
|
||||
restartSignal : i18n('restartSignal'),
|
||||
},
|
||||
|
@ -179,12 +163,32 @@
|
|||
'input input.search': 'filterContacts',
|
||||
'click .restart-signal': 'reloadBackgroundPage',
|
||||
'show .lightbox': 'showLightbox',
|
||||
'click .migrate': 'confirmMigration'
|
||||
'click .migrate': 'showUpgradeScreen',
|
||||
'click .banner-close': 'removeUpgradeBanner'
|
||||
},
|
||||
confirmMigration: function() {
|
||||
this.confirm(i18n('confirmMigration'), i18n('migrate')).then(this.showMigrationScreen.bind(this));
|
||||
showUpgradeBanner: function() {
|
||||
this.removeUpgradeBanner();
|
||||
|
||||
this.upgradeBanner = new Whisper.UpgradeBanner().render();
|
||||
this.upgradeBanner.$el.prependTo(this.$el);
|
||||
|
||||
// This class makes AppView have a height of 100% - 62px, so it doesn't push
|
||||
// our banner off-screen.
|
||||
this.$el.addClass('expired');
|
||||
},
|
||||
showMigrationScreen: function() {
|
||||
removeUpgradeBanner: function() {
|
||||
if (this.upgradeBanner) {
|
||||
this.upgradeBanner.remove();
|
||||
this.upgradeBanner = null;
|
||||
this.$el.removeClass('expired');
|
||||
}
|
||||
},
|
||||
showUpgradeScreen: function() {
|
||||
if (this.migrationScreen) {
|
||||
this.migrationScreen.remove();
|
||||
this.migrationScreen = null;
|
||||
}
|
||||
|
||||
this.migrationScreen = new Whisper.MigrationView();
|
||||
this.migrationScreen.render();
|
||||
this.migrationScreen.$el.prependTo(this.el);
|
||||
|
@ -307,13 +311,25 @@
|
|||
}
|
||||
});
|
||||
|
||||
Whisper.MigrationAlertBanner = Whisper.View.extend({
|
||||
templateName: 'migration_alert',
|
||||
className: 'expiredAlert clearfix',
|
||||
Whisper.UpgradeBanner = Whisper.View.extend({
|
||||
templateName: 'upgrade_banner',
|
||||
className: 'expiredAlert upgrade-banner clearfix',
|
||||
initializer: function() {
|
||||
var HOUR = 1000 * 60 * 60;
|
||||
this.interval = setInterval(this.render.bind(this), HOUR);
|
||||
},
|
||||
remove: function() {
|
||||
if (this.interval) {
|
||||
clearInterval(this.interval);
|
||||
this.interval = null;
|
||||
}
|
||||
|
||||
Backbone.View.prototype.remove.call(this);
|
||||
},
|
||||
render_attributes: function() {
|
||||
return {
|
||||
migrationWarning: i18n('migrationWarning'),
|
||||
migrate: i18n('migrate'),
|
||||
upgradeMessage: i18n('upgradeBannerExpired'),
|
||||
upgradeNow: i18n('upgradeNow'),
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
|
@ -7,138 +7,20 @@
|
|||
|
||||
Whisper.InstallView = Whisper.View.extend({
|
||||
templateName: 'install_flow_template',
|
||||
id: 'install',
|
||||
className: 'main',
|
||||
render_attributes: function() {
|
||||
var playStoreHref = 'https://play.google.com/store/apps/details?id=org.thoughtcrime.securesms';
|
||||
var appStoreHref = 'https://itunes.apple.com/us/app/signal-private-messenger/id874139669';
|
||||
var twitterHref = 'https://twitter.com/whispersystems';
|
||||
return {
|
||||
installWelcome: i18n('installWelcome'),
|
||||
installTagline: i18n('installTagline'),
|
||||
installGetStartedButton: i18n('installGetStartedButton'),
|
||||
installSignalLink: this.i18n_with_links('installSignalLinks', playStoreHref, appStoreHref),
|
||||
installIHaveSignalButton: i18n('installGotIt'),
|
||||
installFollowUs: this.i18n_with_links('installFollowUs', twitterHref),
|
||||
installAndroidInstructions: i18n('installAndroidInstructions'),
|
||||
installLinkingWithNumber: i18n('installLinkingWithNumber'),
|
||||
installComputerName: i18n('installComputerName'),
|
||||
installFinalButton: i18n('installFinalButton'),
|
||||
installTooManyDevices: i18n('installTooManyDevices'),
|
||||
ok: i18n('ok'),
|
||||
preLinkExpiredHeader: i18n('preLinkExpiredHeader'),
|
||||
startExportIntroParagraph1: i18n('startExportIntroParagraph1'),
|
||||
getNewVersion: i18n('getNewVersion'),
|
||||
};
|
||||
},
|
||||
initialize: function(options) {
|
||||
this.counter = 0;
|
||||
|
||||
console.log('initialize!', this);
|
||||
this.render();
|
||||
|
||||
var deviceName = textsecure.storage.user.getDeviceName();
|
||||
if (!deviceName) {
|
||||
deviceName = 'Chrome';
|
||||
if (navigator.userAgent.match('Mac OS')) {
|
||||
deviceName += ' on Mac';
|
||||
} else if (navigator.userAgent.match('Linux')) {
|
||||
deviceName += ' on Linux';
|
||||
} else if (navigator.userAgent.match('Windows')) {
|
||||
deviceName += ' on Windows';
|
||||
}
|
||||
}
|
||||
|
||||
this.$('#device-name').val(deviceName);
|
||||
this.$('#step1').show();
|
||||
this.connect();
|
||||
this.on('disconnected', this.reconnect);
|
||||
},
|
||||
connect: function() {
|
||||
this.clearQR();
|
||||
var accountManager = getAccountManager();
|
||||
accountManager.registerSecondDevice(
|
||||
this.setProvisioningUrl.bind(this),
|
||||
this.confirmNumber.bind(this),
|
||||
this.incrementCounter.bind(this)
|
||||
).catch(this.handleDisconnect.bind(this));
|
||||
},
|
||||
handleDisconnect: function(e) {
|
||||
if (e.message === 'websocket closed') {
|
||||
this.showConnectionError();
|
||||
this.trigger('disconnected');
|
||||
} else if (e.name === 'HTTPError' && e.code == 411) {
|
||||
this.showTooManyDevices();
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
reconnect: function() {
|
||||
setTimeout(this.connect.bind(this), 10000);
|
||||
},
|
||||
close: function() {
|
||||
this.remove();
|
||||
},
|
||||
events: function() {
|
||||
return {
|
||||
'click .error-dialog .ok': 'connect',
|
||||
'click .step1': this.selectStep.bind(this, 1),
|
||||
'click .step2': this.selectStep.bind(this, 2),
|
||||
'click .step3': this.selectStep.bind(this, 3)
|
||||
};
|
||||
},
|
||||
clearQR: function() {
|
||||
this.$('#qr').text(i18n("installConnecting"));
|
||||
},
|
||||
setProvisioningUrl: function(url) {
|
||||
this.$('#qr').html('');
|
||||
new QRCode(this.$('#qr')[0]).makeCode(url);
|
||||
},
|
||||
confirmNumber: function(number) {
|
||||
var parsed = libphonenumber.parse(number);
|
||||
if (!libphonenumber.isValidNumber(parsed)) {
|
||||
throw new Error('Invalid number ' + number);
|
||||
}
|
||||
this.$('#step4 .number').text(libphonenumber.format(
|
||||
parsed,
|
||||
libphonenumber.PhoneNumberFormat.INTERNATIONAL
|
||||
));
|
||||
this.selectStep(4);
|
||||
this.$('#device-name').focus();
|
||||
return new Promise(function(resolve, reject) {
|
||||
this.$('#step4 .cancel').click(function(e) {
|
||||
reject();
|
||||
});
|
||||
this.$('#step4').submit(function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
var name = this.$('#device-name').val();
|
||||
name = name.replace(/\0/g,''); // strip unicode null
|
||||
if (name.trim().length === 0) {
|
||||
this.$('#device-name').focus();
|
||||
return;
|
||||
}
|
||||
this.$('.progress-dialog .status').text(i18n('installGeneratingKeys'));
|
||||
this.selectStep(5);
|
||||
resolve(name);
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
},
|
||||
incrementCounter: function() {
|
||||
this.$('.progress-dialog .bar').css('width', (++this.counter * 100 / 100) + '%');
|
||||
},
|
||||
selectStep: function(step) {
|
||||
this.$('.step').hide();
|
||||
this.$('#step' + step).show();
|
||||
},
|
||||
showSync: function() {
|
||||
this.$('.progress-dialog .status').text(i18n('installSyncingGroupsAndContacts'));
|
||||
this.$('.progress-dialog .bar').addClass('progress-bar-striped active');
|
||||
},
|
||||
showTooManyDevices: function() {
|
||||
this.selectStep('TooManyDevices');
|
||||
},
|
||||
showConnectionError: function() {
|
||||
this.$('#qr').text(i18n("installConnectionFailed"));
|
||||
},
|
||||
hideDots: function() {
|
||||
this.$('#step3 .nav .dot').hide();
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
this.conversation = this.model.getExpirationTimerUpdateSource();
|
||||
this.listenTo(this.conversation, 'change', this.render);
|
||||
this.listenTo(this.model, 'unload', this.remove);
|
||||
this.listenTo(this.model, 'change', this.onChange);
|
||||
},
|
||||
render_attributes: function() {
|
||||
var seconds = this.model.get('expirationTimerUpdate').expireTimer;
|
||||
|
@ -91,6 +92,13 @@
|
|||
Whisper.ExpirationTimerOptions.getName(seconds)]);
|
||||
}
|
||||
return { content: timerMessage };
|
||||
},
|
||||
onChange: function() {
|
||||
this.addId();
|
||||
},
|
||||
addId: function() {
|
||||
// This is important to enable the lastSeenIndicator when it's just been added.
|
||||
this.$el.attr('id', this.id());
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -2,12 +2,86 @@
|
|||
'use strict';
|
||||
window.Whisper = window.Whisper || {};
|
||||
|
||||
var LinuxInstructionsView = Whisper.ConfirmationDialogView.extend({
|
||||
className: 'linux-install-instructions',
|
||||
templateName: 'linux-install-instructions',
|
||||
_super: Whisper.ConfirmationDialogView.prototype,
|
||||
initialize: function() {
|
||||
this._super.initialize.call(this, {
|
||||
okText: i18n('close'),
|
||||
});
|
||||
},
|
||||
events: {
|
||||
'keyup': 'onKeyup',
|
||||
'click .ok': 'ok',
|
||||
'click .modal': 'ok',
|
||||
},
|
||||
render_attributes: function() {
|
||||
var attributes = this._super.render_attributes.call(this);
|
||||
attributes.header = i18n('linuxInstallInstructions');
|
||||
return attributes;
|
||||
},
|
||||
ok: function(event) {
|
||||
// We have an event on .modal, which is the background div, darkening the screen.
|
||||
// This ensures that a click on the dialog will not fire that event, by checking
|
||||
// the actual thing clicked against the target.
|
||||
if (event.target !== event.currentTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._super.ok.call(this);
|
||||
}
|
||||
});
|
||||
|
||||
var State = {
|
||||
DISCONNECTING: 1,
|
||||
EXPORTING: 2,
|
||||
COMPLETE: 3
|
||||
COMPLETE: 3,
|
||||
};
|
||||
|
||||
var STEPS = {
|
||||
INTRODUCTION: 1,
|
||||
CHOOSE: 2,
|
||||
EXPORTING: 3,
|
||||
COMPLETE: 4,
|
||||
UNINSTALL: 5,
|
||||
};
|
||||
|
||||
var GET_YAML_PATH = /^path: (.+)$/m;
|
||||
var BASE_PATH = 'https://updates.signal.org/desktop/';
|
||||
|
||||
function getCacheBuster() {
|
||||
return Math.random().toString(36).substring(7);
|
||||
}
|
||||
|
||||
function getLink(file) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
$.get(BASE_PATH + file + '?b=' + getCacheBuster())
|
||||
.done(function(data) {
|
||||
var match = GET_YAML_PATH.exec(data);
|
||||
if (match && match[1]) {
|
||||
return resolve(BASE_PATH + match[1]);
|
||||
}
|
||||
|
||||
return reject(new Error('Link not found in YAML from ' + file + ': ' + data));
|
||||
})
|
||||
.fail(function(xhr) {
|
||||
return reject(new Error(
|
||||
'Problem pulling ' + file + '; Request Status: ' + xhr.status +
|
||||
' Status Text: ' + xhr.statusText + ' ' + xhr.responseText)
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getMacLink() {
|
||||
return getLink('latest-mac-import.yml');
|
||||
}
|
||||
|
||||
function getWindowsLink() {
|
||||
return getLink('latest-import.yml');
|
||||
}
|
||||
|
||||
Whisper.Migration = {
|
||||
isComplete: function() {
|
||||
return storage.get('migrationState') === State.COMPLETE;
|
||||
|
@ -23,16 +97,35 @@
|
|||
}
|
||||
},
|
||||
cancel: function() {
|
||||
storage.remove('migrationState');
|
||||
return Promise.all([
|
||||
storage.remove('migrationState'),
|
||||
storage.remove('migrationEverCompleted'),
|
||||
storage.remove('migrationStorageLocation')
|
||||
]);
|
||||
},
|
||||
beginExport: function() {
|
||||
storage.put('migrationState', State.EXPORTING);
|
||||
return Whisper.Backup.backupToDirectory();
|
||||
|
||||
var everAttempted = this.everAttempted();
|
||||
storage.put('migrationEverAttempted', true);
|
||||
|
||||
// If this is the second time the user is attempting to export, we'll exclude
|
||||
// client-specific encryption configuration. Yes, this will fire if the user
|
||||
// initially attempts to save to a read-only directory or something like that, but
|
||||
// it will prevent the horrible encryption errors which result from import to the
|
||||
// same client config more than once. They can import the same message history
|
||||
// more than once, so we preserve that.
|
||||
return Whisper.Backup.exportToDirectory({
|
||||
excludeClientConfig: everAttempted,
|
||||
});
|
||||
},
|
||||
init: function() {
|
||||
storage.put('migrationState', State.DISCONNECTING);
|
||||
Whisper.events.trigger('start-shutdown');
|
||||
},
|
||||
everAttempted: function() {
|
||||
return Boolean(storage.get('migrationEverAttempted'));
|
||||
},
|
||||
everComplete: function() {
|
||||
return Boolean(storage.get('migrationEverCompleted'));
|
||||
},
|
||||
|
@ -42,15 +135,39 @@
|
|||
};
|
||||
|
||||
Whisper.MigrationView = Whisper.View.extend({
|
||||
templateName: 'app-migration-screen',
|
||||
className: 'app-loading-screen',
|
||||
templateName: 'migration-flow-template',
|
||||
className: 'migration-flow',
|
||||
events: {
|
||||
'click .install': 'onClickInstall',
|
||||
'click .export': 'onClickExport',
|
||||
'click .debug-log': 'onClickDebugLog',
|
||||
'click .start': 'onClickStart',
|
||||
'click .choose': 'onClickChoose',
|
||||
'click .submit-debug-log': 'onClickDebugLog',
|
||||
'click .cancel': 'onClickCancel',
|
||||
'click .get-new-version': 'onGetNewVersion',
|
||||
},
|
||||
initialize: function() {
|
||||
this.step = STEPS.INTRODUCTION;
|
||||
|
||||
// init() tells MessageReceiver to disconnect and drain its queue, will fire
|
||||
// 'shutdown-complete' event when that is done. Might result in a synchronous
|
||||
// event, so call it after we register our callback.
|
||||
Whisper.events.once('shutdown-complete', function() {
|
||||
this.shutdownComplete = true;
|
||||
}.bind(this));
|
||||
Whisper.Migration.init();
|
||||
|
||||
Promise.all([getMacLink(), getWindowsLink()]).then(function(results) {
|
||||
this.macLink = results[0];
|
||||
this.windowsLink = results[1];
|
||||
this.render();
|
||||
}.bind(this), function(error) {
|
||||
console.log(
|
||||
'MigrationView: Ran into problem pulling Mac/Windows install links:',
|
||||
error.stack
|
||||
);
|
||||
});
|
||||
|
||||
if (!Whisper.Migration.inProgress()) {
|
||||
this.render();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -59,65 +176,133 @@
|
|||
if (Whisper.Migration.everComplete()) {
|
||||
// If the user has ever successfully exported before, we'll show the 'finished'
|
||||
// screen with the 'Export again' button.
|
||||
this.step = STEPS.UNINSTALL;
|
||||
Whisper.Migration.markComplete();
|
||||
} else if (!Whisper.Migration.isComplete()) {
|
||||
// This takes the user back to the very beginning of the process.
|
||||
Whisper.Migration.cancel();
|
||||
}
|
||||
|
||||
this.render();
|
||||
},
|
||||
render_attributes: function() {
|
||||
var message;
|
||||
var exportButton;
|
||||
var hideProgress = Whisper.Migration.isComplete();
|
||||
var debugLogButton = i18n('submitDebugLog');
|
||||
var installButton = i18n('installNewSignal');
|
||||
|
||||
if (this.error) {
|
||||
return {
|
||||
message: i18n('exportError'),
|
||||
hideProgress: true,
|
||||
exportButton: i18n('exportAgain'),
|
||||
isError: true,
|
||||
errorHeader: i18n('exportErrorHeader'),
|
||||
error: i18n('exportError'),
|
||||
tryAgain: i18n('chooseFolderAndTryAgain'),
|
||||
debugLogButton: i18n('submitDebugLog'),
|
||||
};
|
||||
}
|
||||
|
||||
switch (storage.get('migrationState')) {
|
||||
case State.COMPLETE:
|
||||
var location = Whisper.Migration.getExportLocation() || i18n('selectedLocation');
|
||||
message = i18n('exportComplete', location);
|
||||
exportButton = i18n('exportAgain');
|
||||
debugLogButton = null;
|
||||
break;
|
||||
case State.EXPORTING:
|
||||
message = i18n('exporting');
|
||||
break;
|
||||
case State.DISCONNECTING:
|
||||
message = i18n('migrationDisconnecting');
|
||||
installButton = null;
|
||||
break;
|
||||
default:
|
||||
hideProgress = true;
|
||||
message = i18n('exportInstructions');
|
||||
exportButton = i18n('export');
|
||||
debugLogButton = null;
|
||||
installButton = null;
|
||||
var location = Whisper.Migration.getExportLocation() || i18n('selectedLocation');
|
||||
|
||||
var userAgent = navigator.userAgent.toLowerCase();
|
||||
var downloadLocation = '#';
|
||||
if (userAgent.indexOf('windows') !== -1) {
|
||||
downloadLocation = this.windowsLink || 'https://signal.org/download';
|
||||
} else if (userAgent.indexOf('macintosh') !== -1) {
|
||||
downloadLocation = this.macLink || 'https://signal.org/download';
|
||||
} else {
|
||||
downloadLocation = 'https://signal.org/download';
|
||||
}
|
||||
|
||||
return {
|
||||
hideProgress: hideProgress,
|
||||
message: message,
|
||||
exportButton: exportButton,
|
||||
debugLogButton: debugLogButton,
|
||||
installButton: installButton,
|
||||
cancelButton: i18n('upgradeLater'),
|
||||
debugLogButton: i18n('submitDebugLog'),
|
||||
|
||||
isStep1: this.step === 1,
|
||||
startHeader: i18n('startExportHeader'),
|
||||
startParagraph1: i18n('startExportIntroParagraph1'),
|
||||
startParagraph2: i18n('startExportIntroParagraph2'),
|
||||
startParagraph3: i18n('startExportIntroParagraph3'),
|
||||
moreInformation: i18n('moreInformation'),
|
||||
startButton: i18n('imReady'),
|
||||
|
||||
isStep2: this.step === 2,
|
||||
chooseHeader: i18n('saveHeader'),
|
||||
choose: i18n('saveDataPrompt'),
|
||||
chooseButton: i18n('chooseFolder'),
|
||||
|
||||
isStep3: this.step === 3,
|
||||
exportHeader: i18n('savingData'),
|
||||
|
||||
isStep4: this.step === 4,
|
||||
completeHeader: i18n('completeHeader'),
|
||||
completeIntro: i18n('completeIntro'),
|
||||
completeLocation: location,
|
||||
completeNextSteps: i18n('completeNextSteps'),
|
||||
downloadLocation: downloadLocation,
|
||||
installButton: i18n('getNewVersion'),
|
||||
|
||||
isStep5: this.step === 5,
|
||||
uninstallHeader: i18n('uninstallHeader'),
|
||||
uninstallStep1: i18n('uninstallStep1'),
|
||||
uninstallStep2: i18n('uninstallStep2'),
|
||||
uninstallStep3: i18n('uninstallStep3'),
|
||||
uninstallStep4: i18n('uninstallStep4'),
|
||||
uninstallStep5: i18n('uninstallStep5'),
|
||||
uninstallStep6: userAgent.indexOf('macintosh') !== -1 ? i18n('uninstallStep6') : null,
|
||||
};
|
||||
},
|
||||
onClickInstall: function() {
|
||||
var url = 'https://support.whispersystems.org/hc/en-us/articles/214507138';
|
||||
window.open(url, '_blank');
|
||||
onGetNewVersion: function(e) {
|
||||
var userAgent = navigator.userAgent.toLowerCase();
|
||||
if (userAgent.indexOf('linux') !== -1) {
|
||||
e.preventDefault();
|
||||
|
||||
var dialog = this.linuxInstructionsView = new LinuxInstructionsView({});
|
||||
this.$el.prepend(dialog.el);
|
||||
dialog.focusOk();
|
||||
}
|
||||
},
|
||||
onClickStart: function() {
|
||||
this.selectStep(STEPS.CHOOSE);
|
||||
},
|
||||
onClickChoose: function() {
|
||||
this.error = null;
|
||||
|
||||
if (!this.shutdownComplete) {
|
||||
console.log("Preventing export start; we haven't disconnected from the server");
|
||||
this.error = true;
|
||||
this.render();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Whisper.Migration.everComplete()) {
|
||||
return this.beginMigration();
|
||||
}
|
||||
|
||||
// Different behavior for the user's second time through
|
||||
this.selectStep(STEPS.EXPORTING);
|
||||
Whisper.Migration.beginExport()
|
||||
.then(this.completeMigration.bind(this))
|
||||
.catch(function(error) {
|
||||
if (!error || error.name !== 'ChooseError') {
|
||||
this.error = error || new Error('in case we reject() null!');
|
||||
}
|
||||
// Even if we run into an error, we call this complete because the user has
|
||||
// completed the process once before.
|
||||
Whisper.Migration.markComplete();
|
||||
this.selectStep(STEPS.COMPLETE);
|
||||
}.bind(this));
|
||||
this.render();
|
||||
},
|
||||
onClickCancel: function() {
|
||||
this.cancel();
|
||||
},
|
||||
onClickDebugLog: function() {
|
||||
this.openDebugLog();
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
Whisper.Migration.cancel().then(function() {
|
||||
console.log('Removing migration view');
|
||||
this.remove();
|
||||
}.bind(this));
|
||||
},
|
||||
selectStep: function(step) {
|
||||
this.step = step;
|
||||
this.render();
|
||||
},
|
||||
|
||||
openDebugLog: function() {
|
||||
this.closeDebugLog();
|
||||
this.debugLogView = new Whisper.DebugLogView();
|
||||
|
@ -129,62 +314,33 @@
|
|||
this.debugLogView = null;
|
||||
}
|
||||
},
|
||||
onClickExport: function() {
|
||||
this.error = null;
|
||||
|
||||
if (!Whisper.Migration.everComplete()) {
|
||||
return this.beginMigration();
|
||||
}
|
||||
beginMigration: function() {
|
||||
this.selectStep(STEPS.EXPORTING);
|
||||
|
||||
// Different behavior for the user's second time through
|
||||
Whisper.Migration.beginExport()
|
||||
.then(this.completeMigration.bind(this))
|
||||
.catch(function(error) {
|
||||
if (error.name !== 'ChooseError') {
|
||||
this.error = error.message;
|
||||
// We ensure that a restart of the app acts as if the user never tried
|
||||
// to export.
|
||||
Whisper.Migration.cancel();
|
||||
|
||||
// We special-case the error we get when the user cancels out of the
|
||||
// filesystem choice dialog: it's not shown an error.
|
||||
if (!error || error.name !== 'ChooseError') {
|
||||
this.error = error || new Error('in case we reject() null!');
|
||||
}
|
||||
// Even if we run into an error, we call this complete because the user has
|
||||
// completed the process once before.
|
||||
Whisper.Migration.markComplete();
|
||||
this.render();
|
||||
|
||||
// Because this is our first time attempting to export, we go back to
|
||||
// the choice step. Compare this to the end of onClickChoose().
|
||||
this.selectStep(STEPS.CHOOSE);
|
||||
}.bind(this));
|
||||
this.render();
|
||||
},
|
||||
beginMigration: function() {
|
||||
Whisper.events.once('shutdown-complete', function() {
|
||||
Whisper.Migration.beginExport()
|
||||
.then(this.completeMigration.bind(this))
|
||||
.catch(this.onError.bind(this));
|
||||
|
||||
// Rendering because we're now in the 'exporting' state
|
||||
this.render();
|
||||
}.bind(this));
|
||||
|
||||
// tells MessageReceiver to disconnect and drain its queue, will fire
|
||||
// 'shutdown-complete' event when that is done. Might result in a synchronous
|
||||
// event, so call it after we register our callback.
|
||||
Whisper.Migration.init();
|
||||
|
||||
// Rendering because we're now in the 'disconnected' state
|
||||
this.render();
|
||||
},
|
||||
completeMigration: function(target) {
|
||||
// This will prevent connection to the server on future app launches
|
||||
Whisper.Migration.markComplete(target);
|
||||
this.render();
|
||||
this.selectStep(STEPS.COMPLETE);
|
||||
},
|
||||
onError: function(error) {
|
||||
if (error.name === 'ChooseError') {
|
||||
this.cancelMigration();
|
||||
} else {
|
||||
Whisper.Migration.cancel();
|
||||
this.error = error.message;
|
||||
this.render();
|
||||
}
|
||||
},
|
||||
cancelMigration: function() {
|
||||
Whisper.Migration.cancel();
|
||||
this.render();
|
||||
}
|
||||
});
|
||||
}());
|
||||
|
||||
|
|
|
@ -11,6 +11,10 @@
|
|||
initialize: function() {
|
||||
this.startTime = Date.now();
|
||||
this.interval = setInterval(this.updateTime.bind(this), 1000);
|
||||
|
||||
this.onSwitchAwayBound = this.onSwitchAway.bind(this);
|
||||
$(window).on('blur', this.onSwitchAwayBound);
|
||||
|
||||
this.start();
|
||||
},
|
||||
events: {
|
||||
|
@ -18,6 +22,9 @@
|
|||
'click .finish': 'finish',
|
||||
'close': 'close'
|
||||
},
|
||||
onSwitchAway: function() {
|
||||
this.close();
|
||||
},
|
||||
updateTime: function() {
|
||||
var duration = moment.duration(Date.now() - this.startTime, 'ms');
|
||||
var minutes = '' + Math.trunc(duration.asMinutes());
|
||||
|
@ -44,17 +51,23 @@
|
|||
}
|
||||
this.remove();
|
||||
this.trigger('closed');
|
||||
|
||||
$(window).off('blur', this.onSwitchAwayBound);
|
||||
},
|
||||
finish: function() {
|
||||
this.clickedFinish = true;
|
||||
this.recorder.finishRecording();
|
||||
this.close();
|
||||
},
|
||||
handleBlob: function(recorder, blob) {
|
||||
if (blob) {
|
||||
if (blob && this.clickedFinish) {
|
||||
this.trigger('send', blob);
|
||||
} else {
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
start: function() {
|
||||
this.clickedFinish = false;
|
||||
this.context = new AudioContext();
|
||||
this.input = this.context.createGain();
|
||||
this.recorder = new WebAudioRecorder(this.input, {
|
||||
|
@ -62,7 +75,7 @@
|
|||
workerDir: 'js/' // must end with slash
|
||||
});
|
||||
this.recorder.onComplete = this.handleBlob.bind(this);
|
||||
this.recorder.onError = this.onError;
|
||||
this.recorder.onError = this.onError.bind(this);
|
||||
navigator.webkitGetUserMedia({ audio: true }, function(stream) {
|
||||
this.source = this.context.createMediaStreamSource(stream);
|
||||
this.source.connect(this.input);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"name": "Signal Private Messenger",
|
||||
"short_name": "Signal",
|
||||
"description": "__MSG_installTagline__",
|
||||
"version": "0.43.4",
|
||||
"version": "0.48.1",
|
||||
"offline_enabled": false,
|
||||
"minimum_chrome_version": "37",
|
||||
"default_locale": "en",
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<link href='/images/icon_128.png' rel='shortcut icon'>
|
||||
</head>
|
||||
<body>
|
||||
<div id='install' class='main'>
|
||||
<div id='install' class='migration-flow'>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="js/components.js"></script>
|
||||
|
|
|
@ -218,7 +218,7 @@ button.hamburger {
|
|||
}
|
||||
|
||||
.dropoff {
|
||||
outline: solid 1px #2090ea;
|
||||
outline: solid 1px $blue;
|
||||
}
|
||||
|
||||
$avatar-size: 44px;
|
||||
|
@ -542,6 +542,51 @@ input[type=text], input[type=search], textarea {
|
|||
}
|
||||
}
|
||||
|
||||
.upgrade-banner {
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
rgb(213,213,213) 0%, // (1 - 0.41) * 255 + 0.41 * 213
|
||||
rgb(249,249,249) 35%, // (1 - 0.19) * 255 + 0.19 * 191
|
||||
rgb(255,255,255) 50%,
|
||||
rgb(249,249,249) 65%, // (1 - 0.19) * 255 + 0.19 * 222
|
||||
rgb(213,213,213) 100% // (1 - 0.27) * 255 + 0.27 * 98
|
||||
);
|
||||
padding: 10px;
|
||||
font-family: roboto-light;
|
||||
font-size: 14pt;
|
||||
|
||||
button {
|
||||
float: right;
|
||||
border: none;
|
||||
color: white;
|
||||
font-family: roboto-light;
|
||||
font-size: 14pt;
|
||||
border-radius: 0;
|
||||
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
line-height: 36px;
|
||||
padding: 0 2em;
|
||||
background: $blue;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.message {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
font-weight: bold;
|
||||
font-family: roboto;
|
||||
}
|
||||
|
||||
.x {
|
||||
float: right;
|
||||
margin-left: 0.5em;
|
||||
margin-top: 0.5em;
|
||||
background-color: $grey_l2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.inbox {
|
||||
position: relative;
|
||||
}
|
||||
|
@ -602,6 +647,289 @@ input[type=text], input[type=search], textarea {
|
|||
}
|
||||
}
|
||||
|
||||
.migration-flow {
|
||||
z-index: 1000;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
font-family: roboto-light;
|
||||
|
||||
color: black;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
rgb(238,238,238) 0%, // (1 - 0.41) * 255 + 0.41 * 213
|
||||
rgb(243,243,243) 12%, // (1 - 0.19) * 255 + 0.19 * 191
|
||||
rgb(255,255,255) 27%,
|
||||
rgb(255,255,255) 60%,
|
||||
rgb(249,249,249) 85%, // (1 - 0.19) * 255 + 0.19 * 222
|
||||
rgb(213,213,213) 100% // (1 - 0.27) * 255 + 0.27 * 98
|
||||
);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
font-size: 10pt;
|
||||
@media (min-height: 750px) and (min-width: 700px) {
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
.banner-image {
|
||||
margin: 1em;
|
||||
display: none;
|
||||
|
||||
@media (min-height: 550px) {
|
||||
display: inline-block;
|
||||
height: 10em;
|
||||
width: 10em;
|
||||
}
|
||||
}
|
||||
|
||||
.banner-icon {
|
||||
margin: 1em;
|
||||
display: none;
|
||||
|
||||
// 640px by 338px is the smallest the window can go
|
||||
@media (min-height: 550px) {
|
||||
display: inline-block;
|
||||
height: 10em;
|
||||
width: 10em;
|
||||
}
|
||||
|
||||
&.folder-outline {
|
||||
@include color-svg('../images/folder-outline.svg', #DEDEDE);
|
||||
}
|
||||
&.export {
|
||||
@include color-svg('../images/export.svg', #DEDEDE);
|
||||
}
|
||||
&.check-circle-outline {
|
||||
@include color-svg('../images/check-circle-outline.svg', #DEDEDE);
|
||||
}
|
||||
&.alert-outline {
|
||||
@include color-svg('../images/alert-outline.svg', #DEDEDE);
|
||||
}
|
||||
}
|
||||
|
||||
.os-header: {
|
||||
margin-top: 2em;
|
||||
}
|
||||
.header {
|
||||
font-weight: normal;
|
||||
margin-bottom: 1.5em;
|
||||
|
||||
font-size: 20pt;
|
||||
|
||||
@media (min-height: 750px) and (min-width: 700px) {
|
||||
font-size: 28pt;
|
||||
}
|
||||
}
|
||||
|
||||
.export-location {
|
||||
text-align: center;
|
||||
font-family: roboto;
|
||||
border: solid 1px $blue;
|
||||
color: $blue;
|
||||
margin-bottom: 1em;
|
||||
padding: 0.5em;
|
||||
-webkit-user-select: text;
|
||||
cursor: text;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
.body-text {
|
||||
margin-bottom: 1em;
|
||||
max-width: 22em;
|
||||
text-align: left;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.body-text-wide {
|
||||
margin-bottom: 1em;
|
||||
max-width: 30em;
|
||||
text-align: left;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
&.red-text {
|
||||
color: red;
|
||||
|
||||
a {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.step {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 70px 0 50px;
|
||||
}
|
||||
.step-body {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 35em;
|
||||
}
|
||||
.uninstall-steps {
|
||||
margin-bottom: 4em;
|
||||
li {
|
||||
margin: 0.5em;
|
||||
}
|
||||
}
|
||||
.url {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.linux-install-instructions .content {
|
||||
max-width: 1300px;
|
||||
.header {
|
||||
color: black;
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
pre {
|
||||
&::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: rgba(255,255,255,0.40);
|
||||
border-radius: 2;
|
||||
}
|
||||
|
||||
cursor: text;
|
||||
-webkit-user-select: text;
|
||||
background-color: black;
|
||||
color: white;
|
||||
text-align: left;
|
||||
padding: 0.5em;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
.inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.button {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
min-width: 300px;
|
||||
padding: 0.75em;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
color: white;
|
||||
background: $blue;
|
||||
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
|
||||
font-size: 12pt;
|
||||
|
||||
@media (min-height: 750px) and (min-width: 700px) {
|
||||
font-size: 20pt;
|
||||
}
|
||||
}
|
||||
|
||||
a.link {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
margin: 0.5em;
|
||||
color: $blue;
|
||||
}
|
||||
|
||||
.progress {
|
||||
text-align: center;
|
||||
padding: 1em;
|
||||
width: 80%;
|
||||
margin: auto;
|
||||
|
||||
.bar-container {
|
||||
height: 1em;
|
||||
margin: 1em;
|
||||
background-color: $grey_l;
|
||||
}
|
||||
.bar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: $blue_l;
|
||||
transition: width 0.25s;
|
||||
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.install-container {
|
||||
@media (min-height: 550px) {
|
||||
margin-top: 4em;
|
||||
}
|
||||
text-align: center;
|
||||
}
|
||||
.install {
|
||||
cursor: pointer;
|
||||
background-color: white;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em;
|
||||
display: inline-block;
|
||||
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
|
||||
a {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.install-icon {
|
||||
height: 7em;
|
||||
width: 7em;
|
||||
vertical-align: text-bottom;
|
||||
display: inline-block;
|
||||
margin: 1em;
|
||||
|
||||
@media (min-width: 800px) {
|
||||
margin-left: 2em;
|
||||
margin-right: 2em;
|
||||
}
|
||||
|
||||
|
||||
&.apple {
|
||||
@include color-svg('/images/apple.svg', $blue);
|
||||
}
|
||||
&.windows {
|
||||
@include color-svg('/images/windows.svg', $blue);
|
||||
}
|
||||
&.linux {
|
||||
@include color-svg('/images/linux.svg', $blue);
|
||||
}
|
||||
}
|
||||
|
||||
.nav {
|
||||
width: 100%;
|
||||
bottom: 50px;
|
||||
margin-top: auto;
|
||||
padding-bottom: 1em;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.installed {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//yellow border fix
|
||||
.inbox:focus {
|
||||
outline: none;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
.expired {
|
||||
.conversation-stack, .gutter {
|
||||
height: calc(100% - 56px);
|
||||
height: calc(100% - 62px);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,23 @@ $text-dark_l2: darken($text-dark, 30%);
|
|||
background-color: darken($button-dark, 8%);
|
||||
}
|
||||
}
|
||||
|
||||
.upgrade-banner {
|
||||
button {
|
||||
float: right;
|
||||
border: none;
|
||||
color: white;
|
||||
font-family: roboto-light;
|
||||
font-size: 14pt;
|
||||
border-radius: 0;
|
||||
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
line-height: 36px;
|
||||
padding: 0 2em;
|
||||
background: $blue;
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.message-detail, .message-container, .conversation,
|
||||
.discussion-container {
|
||||
background-color: $grey-dark_l3;
|
||||
|
|
|
@ -363,7 +363,7 @@ button.hamburger {
|
|||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
margin: 0 0 0 8px;
|
||||
width: calc(100% - 44px - 8px - 0.28571em);
|
||||
width: calc(100% - 44px - 8px - 0.2857142857em);
|
||||
text-align: left;
|
||||
cursor: pointer; }
|
||||
.contact-details p {
|
||||
|
@ -378,7 +378,7 @@ button.hamburger {
|
|||
text-align: left; }
|
||||
.contact-details .number {
|
||||
color: #616161;
|
||||
font-size: 0.92857em; }
|
||||
font-size: 0.9285714286em; }
|
||||
.contact-details.not-clickable {
|
||||
cursor: default; }
|
||||
.contact-details .verified-icon {
|
||||
|
@ -485,6 +485,34 @@ input[type=text]:active, input[type=text]:focus, input[type=search]:active, inpu
|
|||
.expiredAlert .message {
|
||||
padding: 10px 0; }
|
||||
|
||||
.upgrade-banner {
|
||||
background: linear-gradient(to bottom, #d5d5d5 0%, #f9f9f9 35%, white 50%, #f9f9f9 65%, #d5d5d5 100%);
|
||||
padding: 10px;
|
||||
font-family: roboto-light;
|
||||
font-size: 14pt; }
|
||||
.upgrade-banner button {
|
||||
float: right;
|
||||
border: none;
|
||||
color: white;
|
||||
font-family: roboto-light;
|
||||
font-size: 14pt;
|
||||
border-radius: 0;
|
||||
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
line-height: 36px;
|
||||
padding: 0 2em;
|
||||
background: #2090ea;
|
||||
margin-left: 20px; }
|
||||
.upgrade-banner .message {
|
||||
padding: 10px 0; }
|
||||
.upgrade-banner .highlight {
|
||||
font-weight: bold;
|
||||
font-family: roboto; }
|
||||
.upgrade-banner .x {
|
||||
float: right;
|
||||
margin-left: 0.5em;
|
||||
margin-top: 0.5em;
|
||||
background-color: #d9d9d9; }
|
||||
|
||||
.inbox {
|
||||
position: relative; }
|
||||
|
||||
|
@ -530,6 +558,217 @@ input[type=text]:active, input[type=text]:focus, input[type=search]:active, inpu
|
|||
.app-loading-screen .dot:nth-child(3) {
|
||||
animation: loading 1500ms ease infinite 666ms; }
|
||||
|
||||
.migration-flow {
|
||||
z-index: 1000;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
font-family: roboto-light;
|
||||
color: black;
|
||||
background: linear-gradient(to bottom, #eeeeee 0%, #f3f3f3 12%, white 27%, white 60%, #f9f9f9 85%, #d5d5d5 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
font-size: 10pt;
|
||||
.os-header-margin-top: 2em; }
|
||||
@media (min-height: 750px) and (min-width: 700px) {
|
||||
.migration-flow {
|
||||
font-size: 14pt; } }
|
||||
.migration-flow .banner-image {
|
||||
margin: 1em;
|
||||
display: none; }
|
||||
@media (min-height: 550px) {
|
||||
.migration-flow .banner-image {
|
||||
display: inline-block;
|
||||
height: 10em;
|
||||
width: 10em; } }
|
||||
.migration-flow .banner-icon {
|
||||
margin: 1em;
|
||||
display: none; }
|
||||
@media (min-height: 550px) {
|
||||
.migration-flow .banner-icon {
|
||||
display: inline-block;
|
||||
height: 10em;
|
||||
width: 10em; } }
|
||||
.migration-flow .banner-icon.folder-outline {
|
||||
-webkit-mask: url("../images/folder-outline.svg") no-repeat center;
|
||||
-webkit-mask-size: 100%;
|
||||
background-color: #DEDEDE; }
|
||||
.migration-flow .banner-icon.export {
|
||||
-webkit-mask: url("../images/export.svg") no-repeat center;
|
||||
-webkit-mask-size: 100%;
|
||||
background-color: #DEDEDE; }
|
||||
.migration-flow .banner-icon.check-circle-outline {
|
||||
-webkit-mask: url("../images/check-circle-outline.svg") no-repeat center;
|
||||
-webkit-mask-size: 100%;
|
||||
background-color: #DEDEDE; }
|
||||
.migration-flow .banner-icon.alert-outline {
|
||||
-webkit-mask: url("../images/alert-outline.svg") no-repeat center;
|
||||
-webkit-mask-size: 100%;
|
||||
background-color: #DEDEDE; }
|
||||
.migration-flow .header {
|
||||
font-weight: normal;
|
||||
margin-bottom: 1.5em;
|
||||
font-size: 20pt; }
|
||||
@media (min-height: 750px) and (min-width: 700px) {
|
||||
.migration-flow .header {
|
||||
font-size: 28pt; } }
|
||||
.migration-flow .export-location {
|
||||
text-align: center;
|
||||
font-family: roboto;
|
||||
border: solid 1px #2090ea;
|
||||
color: #2090ea;
|
||||
margin-bottom: 1em;
|
||||
padding: 0.5em;
|
||||
-webkit-user-select: text;
|
||||
cursor: text; }
|
||||
.migration-flow .center {
|
||||
text-align: center; }
|
||||
.migration-flow .body-text {
|
||||
margin-bottom: 1em;
|
||||
max-width: 22em;
|
||||
text-align: left;
|
||||
margin-left: auto;
|
||||
margin-right: auto; }
|
||||
.migration-flow .body-text-wide {
|
||||
margin-bottom: 1em;
|
||||
max-width: 30em;
|
||||
text-align: left;
|
||||
margin-left: auto;
|
||||
margin-right: auto; }
|
||||
.migration-flow .body-text-wide.red-text {
|
||||
color: red; }
|
||||
.migration-flow .body-text-wide.red-text a {
|
||||
color: red; }
|
||||
.migration-flow .step {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 70px 0 50px; }
|
||||
.migration-flow .step-body {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 35em; }
|
||||
.migration-flow .uninstall-steps {
|
||||
margin-bottom: 4em; }
|
||||
.migration-flow .uninstall-steps li {
|
||||
margin: 0.5em; }
|
||||
.migration-flow .url {
|
||||
font-weight: bold; }
|
||||
.migration-flow .linux-install-instructions .content {
|
||||
max-width: 1300px; }
|
||||
.migration-flow .linux-install-instructions .content .header {
|
||||
color: black;
|
||||
font-size: 120%; }
|
||||
.migration-flow .linux-install-instructions .content pre {
|
||||
cursor: text;
|
||||
-webkit-user-select: text;
|
||||
background-color: black;
|
||||
color: white;
|
||||
text-align: left;
|
||||
padding: 0.5em;
|
||||
overflow-x: scroll; }
|
||||
.migration-flow .linux-install-instructions .content pre::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
height: 5px; }
|
||||
.migration-flow .linux-install-instructions .content pre::-webkit-scrollbar-track {
|
||||
background: transparent; }
|
||||
.migration-flow .linux-install-instructions .content pre::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
border-radius: 2; }
|
||||
.migration-flow .inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
height: 100%; }
|
||||
.migration-flow .button {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
min-width: 300px;
|
||||
padding: 0.75em;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
color: white;
|
||||
background: #2090ea;
|
||||
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
font-size: 12pt; }
|
||||
@media (min-height: 750px) and (min-width: 700px) {
|
||||
.migration-flow .button {
|
||||
font-size: 20pt; } }
|
||||
.migration-flow a.link {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
margin: 0.5em;
|
||||
color: #2090ea; }
|
||||
.migration-flow .progress {
|
||||
text-align: center;
|
||||
padding: 1em;
|
||||
width: 80%;
|
||||
margin: auto; }
|
||||
.migration-flow .progress .bar-container {
|
||||
height: 1em;
|
||||
margin: 1em;
|
||||
background-color: #f3f3f3; }
|
||||
.migration-flow .progress .bar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #a2d2f4;
|
||||
transition: width 0.25s;
|
||||
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); }
|
||||
.migration-flow .install-container {
|
||||
text-align: center; }
|
||||
@media (min-height: 550px) {
|
||||
.migration-flow .install-container {
|
||||
margin-top: 4em; } }
|
||||
.migration-flow .install {
|
||||
cursor: pointer;
|
||||
background-color: white;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em;
|
||||
display: inline-block;
|
||||
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
margin-left: 1em;
|
||||
margin-right: 1em; }
|
||||
.migration-flow .install a {
|
||||
color: black;
|
||||
text-decoration: none; }
|
||||
.migration-flow .install-icon {
|
||||
height: 7em;
|
||||
width: 7em;
|
||||
vertical-align: text-bottom;
|
||||
display: inline-block;
|
||||
margin: 1em; }
|
||||
@media (min-width: 800px) {
|
||||
.migration-flow .install-icon {
|
||||
margin-left: 2em;
|
||||
margin-right: 2em; } }
|
||||
.migration-flow .install-icon.apple {
|
||||
-webkit-mask: url("/images/apple.svg") no-repeat center;
|
||||
-webkit-mask-size: 100%;
|
||||
background-color: #2090ea; }
|
||||
.migration-flow .install-icon.windows {
|
||||
-webkit-mask: url("/images/windows.svg") no-repeat center;
|
||||
-webkit-mask-size: 100%;
|
||||
background-color: #2090ea; }
|
||||
.migration-flow .install-icon.linux {
|
||||
-webkit-mask: url("/images/linux.svg") no-repeat center;
|
||||
-webkit-mask-size: 100%;
|
||||
background-color: #2090ea; }
|
||||
.migration-flow .nav {
|
||||
width: 100%;
|
||||
bottom: 50px;
|
||||
margin-top: auto;
|
||||
padding-bottom: 1em;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px; }
|
||||
.migration-flow .installed {
|
||||
visibility: hidden; }
|
||||
|
||||
.inbox:focus {
|
||||
outline: none; }
|
||||
|
||||
|
@ -841,11 +1080,11 @@ img.emoji.jumbo {
|
|||
padding: 0 20px;
|
||||
margin: 0 0 20px 20px; }
|
||||
.settings .syncSettings .synced_at {
|
||||
font-size: 0.92857em;
|
||||
font-size: 0.9285714286em;
|
||||
color: #616161; }
|
||||
.settings .syncSettings .sync_failed {
|
||||
display: none;
|
||||
font-size: 0.92857em;
|
||||
font-size: 0.9285714286em;
|
||||
color: red; }
|
||||
|
||||
.conversation-stack,
|
||||
|
@ -853,7 +1092,7 @@ img.emoji.jumbo {
|
|||
height: 100%; }
|
||||
|
||||
.expired .conversation-stack, .expired .gutter {
|
||||
height: calc(100% - 56px); }
|
||||
height: calc(100% - 62px); }
|
||||
|
||||
.scrollable {
|
||||
height: 100%;
|
||||
|
@ -994,7 +1233,7 @@ input.search {
|
|||
padding: 0.5em; }
|
||||
.index .last-message {
|
||||
margin: 6px 0 0;
|
||||
font-size: 0.92857em;
|
||||
font-size: 0.9285714286em;
|
||||
font-weight: 300; }
|
||||
.index .gutter .timestamp {
|
||||
position: absolute;
|
||||
|
@ -1166,7 +1405,7 @@ input.search {
|
|||
.key-verification label {
|
||||
display: block;
|
||||
margin: 10px 0;
|
||||
font-size: 0.92857em; }
|
||||
font-size: 0.9285714286em; }
|
||||
.key-verification .icon {
|
||||
height: 1.25em;
|
||||
width: 1.25em;
|
||||
|
@ -1272,7 +1511,7 @@ input.search {
|
|||
background-color: white; }
|
||||
.message-detail .contacts .contact-detail .error-message {
|
||||
margin: 6px 0 0;
|
||||
font-size: 0.92857em;
|
||||
font-size: 0.9285714286em;
|
||||
font-weight: bold;
|
||||
color: red; }
|
||||
.message-detail h3 {
|
||||
|
@ -1654,7 +1893,7 @@ li.entry .error-icon-container {
|
|||
color: white;
|
||||
box-shadow: 0 0 5px 0 black;
|
||||
border-radius: 5px;
|
||||
font-size: 0.92857em;
|
||||
font-size: 0.9285714286em;
|
||||
z-index: 100; }
|
||||
|
||||
.confirmation-dialog .content {
|
||||
|
@ -2137,13 +2376,25 @@ li.entry .error-icon-container {
|
|||
border: 1px solid #292929; }
|
||||
.android-dark button:hover, .android-dark .confirmation-dialog .content .buttons button:hover {
|
||||
background-color: #b8b8b8; }
|
||||
.android-dark .upgrade-banner button {
|
||||
float: right;
|
||||
border: none;
|
||||
color: white;
|
||||
font-family: roboto-light;
|
||||
font-size: 14pt;
|
||||
border-radius: 0;
|
||||
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
line-height: 36px;
|
||||
padding: 0 2em;
|
||||
background: #2090ea;
|
||||
margin-left: 20px; }
|
||||
.android-dark .message-detail, .android-dark .message-container, .android-dark .conversation,
|
||||
.android-dark .discussion-container {
|
||||
background-color: #171717; }
|
||||
.android-dark .modal .content {
|
||||
background-color: #333333; }
|
||||
.android-dark .lightbox .content {
|
||||
background-color: transparent; }
|
||||
background-color: rgba(0, 0, 0, 0); }
|
||||
.android-dark .key-verification .key {
|
||||
background-color: #030303;
|
||||
border-color: #292929; }
|
||||
|
|
|
@ -1,366 +1,7 @@
|
|||
@import 'variables';
|
||||
@import 'mixins';
|
||||
@import 'global';
|
||||
|
||||
@import 'intlTelInput';
|
||||
@import 'progress';
|
||||
.iti-flag {
|
||||
// override intlTelInput's flags image location
|
||||
background: url("/images/flags.png");
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,body {
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: $roboto;
|
||||
position: relative;
|
||||
background: #2090ea;
|
||||
color: white;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.clearfix:before,
|
||||
.clearfix:after {
|
||||
display: table;
|
||||
content: " ";
|
||||
}
|
||||
.clearfix:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
input, button, select, textarea {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
#install {
|
||||
display: none;
|
||||
height: 100%;
|
||||
|
||||
// 666px is the minimum window height in chromium.js
|
||||
@media screen and (max-height: 665px) {
|
||||
height: 666px;
|
||||
}
|
||||
}
|
||||
.main {
|
||||
padding: 70px 0 50px;
|
||||
}
|
||||
.step {
|
||||
display: none;
|
||||
height: 100%;
|
||||
}
|
||||
.inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.step-body {
|
||||
margin-top: auto;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
}
|
||||
}
|
||||
|
||||
#signal-computer,
|
||||
#signal-phone {
|
||||
max-width: 50%;
|
||||
max-height: 250px;
|
||||
}
|
||||
|
||||
p {
|
||||
max-width: 35em;
|
||||
margin: 1em auto;
|
||||
line-height: 1.5em;
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
&, &:visited, &:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
text-transform: uppercase;
|
||||
border: none;
|
||||
font-weight: bold;
|
||||
min-width: 300px;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
background: white;
|
||||
color: $blue;
|
||||
}
|
||||
|
||||
.nav {
|
||||
width: 100%;
|
||||
bottom: 50px;
|
||||
margin-top: auto;
|
||||
padding: 0 20px;
|
||||
|
||||
.button {
|
||||
margin-bottom: 3em;
|
||||
}
|
||||
|
||||
.dot {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
margin: 10px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 10px;
|
||||
background: white;
|
||||
border: solid 5px $blue;
|
||||
|
||||
&.selected {
|
||||
background: $blue_l;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
&:hover, &:focus {
|
||||
background: rgba(255,255,255,0.3);
|
||||
outline: none;
|
||||
}
|
||||
&, &:visited, &:hover {
|
||||
padding: 0 3px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
border-bottom: dashed 2px white;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
min-width: 650px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 30pt;
|
||||
font-weight: normal;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
h3.step {
|
||||
margin-top: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.help {
|
||||
border-top: 2px solid $grey_l;
|
||||
padding: 1.5em 0.1em;
|
||||
}
|
||||
|
||||
.install {
|
||||
display: inline-block;
|
||||
margin-top: 90px;
|
||||
}
|
||||
|
||||
#qr {
|
||||
display: inline-block;
|
||||
min-height: 266px;
|
||||
img {
|
||||
border: 5px solid white;
|
||||
}
|
||||
|
||||
canvas {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#device-name {
|
||||
border: none;
|
||||
border-bottom: 1px solid white;
|
||||
padding: 8px;
|
||||
background: transparent;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
&::selection, a::selection {
|
||||
color: $grey_d;
|
||||
background: white;
|
||||
}
|
||||
|
||||
&::-moz-selection, a::-moz-selection {
|
||||
color: $grey_d;
|
||||
background: white;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:hover, &:focus {
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#verifyCode,
|
||||
#code,
|
||||
#number {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
display: block;
|
||||
margin-bottom: 0.5em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#request-voice,
|
||||
#request-sms {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#request-sms {
|
||||
width: 57%;
|
||||
float: right;
|
||||
}
|
||||
#request-voice {
|
||||
width: 40%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.number-container {
|
||||
position: relative;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.number-container .intl-tel-input,
|
||||
.number-container .number {
|
||||
width: 100%;
|
||||
}
|
||||
.number-container::after {
|
||||
visibility: hidden;
|
||||
content: ' ';
|
||||
display: inline-block;
|
||||
border-radius: 1.5em;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
line-height: 1.5em;
|
||||
color: #ffffff;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 100%;
|
||||
margin: 3px 8px;
|
||||
text-align: center;
|
||||
}
|
||||
.number-container.valid::after {
|
||||
visibility: visible;
|
||||
content: '✓';
|
||||
background-color: #0f9d58;
|
||||
color: #ffffff;
|
||||
}
|
||||
.number-container.invalid::after {
|
||||
visibility: visible;
|
||||
content: '!';
|
||||
background-color: #f44336;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#error {
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
padding: 0.5em;
|
||||
text-align: center;
|
||||
}
|
||||
#error { background-color: #f44336; }
|
||||
#error:before {
|
||||
content: '\26a0';
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
.narrow {
|
||||
margin: auto;
|
||||
box-sizing: border-box;
|
||||
width: 275px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
ul.country-list {
|
||||
min-width: 197px !important;
|
||||
}
|
||||
|
||||
.confirmation-dialog, .progress-dialog, .error-dialog {
|
||||
padding: 1em;
|
||||
text-align: left;
|
||||
}
|
||||
.number { text-align: center; }
|
||||
.confirmation-dialog, .error-dialog {
|
||||
button {
|
||||
float: right;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
.progress-dialog {
|
||||
text-align: center;
|
||||
padding: 1em;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
margin: auto;
|
||||
|
||||
.status { padding: 1em; }
|
||||
|
||||
.bar-container {
|
||||
height: 1em;
|
||||
background-color: $grey_l;
|
||||
border: solid 1px white;
|
||||
}
|
||||
.bar {
|
||||
width: 0;
|
||||
height: 100%;
|
||||
background-color: $blue_l;
|
||||
transition: width 0.25s;
|
||||
|
||||
&.active {
|
||||
}
|
||||
}
|
||||
}
|
||||
.error-dialog {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.modal-container {
|
||||
display: none;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.1);
|
||||
top: 0;
|
||||
padding-top: 10em;
|
||||
text-align: center;
|
||||
|
||||
.modal-main {
|
||||
display: inline-block;
|
||||
width: 80%;
|
||||
max-width: 500px;
|
||||
border: solid 2px $blue;
|
||||
background: white;
|
||||
margin: 10% auto;
|
||||
box-shadow: 0 0 5px 3px rgba(darken($blue, 30%), 0.2);
|
||||
|
||||
h4 {
|
||||
background-color: $blue;
|
||||
color: white;
|
||||
padding: 1em;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.intl-tel-input .country-list {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.intl-tel-input .country-list .country .country-name {
|
||||
color: #000;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
'use strict';
|
||||
|
||||
describe('Database', function() {
|
||||
describe('cleanMessageAttachments', function() {
|
||||
it('does not modify a message with no attachments field', function() {
|
||||
const message = {};
|
||||
const actual = window.Whisper.Database.cleanMessageAttachments(message);
|
||||
|
||||
assert.strictEqual(actual, false);
|
||||
});
|
||||
|
||||
it('does not modify a message with no attachments', function() {
|
||||
const message = {
|
||||
attachments: [],
|
||||
};
|
||||
const actual = window.Whisper.Database.cleanMessageAttachments(message);
|
||||
|
||||
assert.strictEqual(actual, false);
|
||||
});
|
||||
|
||||
it('does not modify a message with an attachment with a string fileName', function() {
|
||||
const message = {
|
||||
attachments: [{
|
||||
fileName: 'blah.jpg',
|
||||
data: 'something',
|
||||
}],
|
||||
};
|
||||
const actual = window.Whisper.Database.cleanMessageAttachments(message);
|
||||
|
||||
assert.strictEqual(actual, false);
|
||||
});
|
||||
|
||||
it('does not modify a message with an attachment with no fileName and a number id', function() {
|
||||
const message = {
|
||||
attachments: [{
|
||||
id: 4,
|
||||
data: 'something',
|
||||
}],
|
||||
};
|
||||
const actual = window.Whisper.Database.cleanMessageAttachments(message);
|
||||
|
||||
console.log(message);
|
||||
assert.strictEqual(actual, false);
|
||||
});
|
||||
|
||||
it('does not modify a message with an attachment with no fileName and a string id', function() {
|
||||
const message = {
|
||||
attachments: [{
|
||||
id: '4',
|
||||
data: 'something',
|
||||
}],
|
||||
};
|
||||
const actual = window.Whisper.Database.cleanMessageAttachments(message);
|
||||
console.log(message);
|
||||
|
||||
assert.strictEqual(actual, false);
|
||||
});
|
||||
|
||||
it('eliminates non-string fileName', function() {
|
||||
const message = {
|
||||
attachments: [{
|
||||
fileName: 4,
|
||||
data: 'something',
|
||||
}],
|
||||
};
|
||||
const actual = window.Whisper.Database.cleanMessageAttachments(message);
|
||||
|
||||
assert.isUndefined(message.attachments[0].fileName);
|
||||
assert.strictEqual(actual, true);
|
||||
});
|
||||
|
||||
it('eliminates object id', function() {
|
||||
const message = {
|
||||
attachments: [{
|
||||
id: {
|
||||
info: 'yes',
|
||||
},
|
||||
data: 'something',
|
||||
}],
|
||||
};
|
||||
const actual = window.Whisper.Database.cleanMessageAttachments(message);
|
||||
|
||||
assert.strictEqual(typeof message.attachments[0].id, 'string');
|
||||
assert.strictEqual(actual, true);
|
||||
});
|
||||
|
||||
it('eliminates non-string contentType', function() {
|
||||
const message = {
|
||||
attachments: [{
|
||||
contentType: 4,
|
||||
data: 'something',
|
||||
}],
|
||||
};
|
||||
const actual = window.Whisper.Database.cleanMessageAttachments(message);
|
||||
|
||||
assert.isUndefined(message.attachments[0].contentType);
|
||||
assert.strictEqual(actual, true);
|
||||
});
|
||||
|
||||
it('drops an attachment with no data attribute', function() {
|
||||
const message = {
|
||||
attachments: [{
|
||||
id: 1,
|
||||
data: null,
|
||||
}, {
|
||||
id: 2,
|
||||
data: 'something'
|
||||
}],
|
||||
};
|
||||
const actual = window.Whisper.Database.cleanMessageAttachments(message);
|
||||
|
||||
assert.strictEqual(message.attachments.length, 1);
|
||||
assert.strictEqual(message.attachments[0].id, 2);
|
||||
assert.strictEqual(actual, true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('dropZeroLengthAttachments', function() {
|
||||
it('does not modify a message with no attachments field', function() {
|
||||
const message = {};
|
||||
const actual = window.Whisper.Database.dropZeroLengthAttachments(message);
|
||||
|
||||
assert.strictEqual(actual, false);
|
||||
});
|
||||
|
||||
it('does not modify a message with no attachments', function() {
|
||||
const message = {
|
||||
attachments: [],
|
||||
};
|
||||
const actual = window.Whisper.Database.dropZeroLengthAttachments(message);
|
||||
|
||||
assert.strictEqual(actual, false);
|
||||
});
|
||||
|
||||
it('does not modify a message with an attachment with a non-zero data field', function() {
|
||||
const message = {
|
||||
attachments: [{
|
||||
fileName: 'blah.jpg',
|
||||
data: 'something',
|
||||
}],
|
||||
};
|
||||
const actual = window.Whisper.Database.dropZeroLengthAttachments(message);
|
||||
|
||||
assert.strictEqual(actual, false);
|
||||
});
|
||||
|
||||
it('drops an attachment with null or zero-length data field', function() {
|
||||
const message = {
|
||||
attachments: [{
|
||||
id: 1,
|
||||
data: null,
|
||||
}, {
|
||||
id: 3,
|
||||
data: {
|
||||
byteLength: 1,
|
||||
},
|
||||
}],
|
||||
};
|
||||
const actual = window.Whisper.Database.dropZeroLengthAttachments(message);
|
||||
|
||||
assert.strictEqual(message.attachments.length, 1);
|
||||
assert.strictEqual(message.attachments[0].id, 3);
|
||||
assert.strictEqual(actual, true);
|
||||
});
|
||||
});
|
||||
});
|
2653
test/fixtures.js
|
@ -1,30 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
describe("Fixtures", function() {
|
||||
before(function(done) {
|
||||
// NetworkStatusView checks this method every five seconds while showing
|
||||
window.getSocketStatus = function() { return WebSocket.OPEN; };
|
||||
|
||||
Whisper.Fixtures.saveAll().then(function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders', function(done) {
|
||||
ConversationController.loadPromise().then(function() {
|
||||
var view = new Whisper.InboxView({window: window});
|
||||
view.onEmpty();
|
||||
view.$el.prependTo($('#render-android'));
|
||||
|
||||
var view = new Whisper.InboxView({window: window});
|
||||
view.$el.removeClass('android').addClass('ios');
|
||||
view.onEmpty();
|
||||
view.$el.prependTo($('#render-ios'));
|
||||
|
||||
var view = new Whisper.InboxView({window: window});
|
||||
view.$el.removeClass('android').addClass('android-dark');
|
||||
view.onEmpty();
|
||||
view.$el.prependTo($('#render-android-dark'));
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
|
@ -650,9 +650,7 @@
|
|||
<script type="text/javascript" src="emoji_util_test.js"></script>
|
||||
<script type="text/javascript" src="reliable_trigger_test.js"></script>
|
||||
<script type="text/javascript" src="backup_test.js"></script>
|
||||
|
||||
<script type="text/javascript" src="fixtures.js"></script>
|
||||
<script type="text/javascript" src="fixtures_test.js"></script>
|
||||
<script type="text/javascript" src="database_test.js"></script>
|
||||
|
||||
<!-- Comment out to turn off code coverage. Useful for getting real callstacks. -->
|
||||
<!-- <script type="text/javascript" src="blanket_mocha.js"></script> -->
|
||||
|
|