mirror of
https://github.com/oxen-io/session-desktop.git
synced 2023-12-14 02:12:57 +01:00
Merge remote-tracking branch 'upstream/clearnet' into fix-integration-tests
This commit is contained in:
commit
58dd064e93
29 changed files with 414 additions and 225 deletions
1
.github/workflows/build-binaries.yml
vendored
1
.github/workflows/build-binaries.yml
vendored
|
@ -9,6 +9,7 @@ on:
|
|||
- clearnet
|
||||
- github-actions
|
||||
- refactor-ts-react
|
||||
- clean-en-translation
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
|
|
@ -83,10 +83,20 @@
|
|||
"message": "Löschen",
|
||||
"description": "Edit menu command to remove the selected text"
|
||||
},
|
||||
"editProfileModalTitle": {
|
||||
"message": "Profil",
|
||||
"description": "Title for the Edit Profile modal"
|
||||
},
|
||||
"editMenuSelectAll": {
|
||||
"message": "Alles auswählen",
|
||||
"description": "Edit menu comand to select all of the text in selected text box"
|
||||
},
|
||||
"newSession": {
|
||||
"message": "Neue Session"
|
||||
},
|
||||
"addContact": {
|
||||
"message": "Kontakt hinzufügen"
|
||||
},
|
||||
"editMenuStartSpeaking": {
|
||||
"message": "Sprachausgabe starten",
|
||||
"description": "Edit menu item under 'speech' to start dictation"
|
||||
|
@ -147,6 +157,9 @@
|
|||
"message": "Wird geladen …",
|
||||
"description": "Message shown on the loading screen before we've loaded any messages"
|
||||
},
|
||||
"or": {
|
||||
"message": "oder"
|
||||
},
|
||||
"optimizingApplication": {
|
||||
"message": "Anwendung wird optimiert …",
|
||||
"description": "Message shown on the loading screen while we are doing application optimizations"
|
||||
|
@ -549,10 +562,90 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"copiedPublicKey": {
|
||||
"message": "In Zwischenablage kopiert",
|
||||
"description": "A toast message telling the user that the key was copied"
|
||||
},
|
||||
"copiedChatId": {
|
||||
"message": "In Zwischenablage kopiert",
|
||||
"description": "A toast message telling the user that the key was copied"
|
||||
},
|
||||
"sessionResetOngoing": {
|
||||
"message": "Verschlüsselung neu gestartet",
|
||||
"description": "your secure session is currently being reset, waiting for the reset acknowledgment."
|
||||
},
|
||||
"blockUser": {
|
||||
"message": "Blockieren"
|
||||
},
|
||||
"unblockUser": {
|
||||
"message": "Freigeben"
|
||||
},
|
||||
"members": {
|
||||
"message": "$count$ mitglied",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"content": "$1",
|
||||
"example": "26"
|
||||
}
|
||||
}
|
||||
},
|
||||
"noContactsForGroup": {
|
||||
"message": "Sie haben noch keine Kontakte."
|
||||
},
|
||||
"getStarted": {
|
||||
"message": "Loslegen"
|
||||
},
|
||||
"createAccount": {
|
||||
"message": "Konto Erstellen"
|
||||
},
|
||||
"signIn": {
|
||||
"message": "Einloggen"
|
||||
},
|
||||
"beginYourSession": {
|
||||
"message": "Beginnen<br />Sie<br />Ihre<br />Session."
|
||||
},
|
||||
"continue": {
|
||||
"message": "Fortsetzen"
|
||||
},
|
||||
"welcomeToYourSession": {
|
||||
"message": "Willkommen bei Session"
|
||||
},
|
||||
"generateSessionID": {
|
||||
"message": "Session ID erstellen"
|
||||
},
|
||||
"yourUniqueSessionID": {
|
||||
"message": "Das ist Ihre Session ID."
|
||||
},
|
||||
"allUsersAreRandomly...": {
|
||||
"message": "Ihre Session ID ist die eindeutige Adresse, unter der Personen Sie über Session kontaktieren können. Ihre Session ID ist nicht mit Ihrer realen Identität verbunden, völlig anonym und von Natur aus privat."
|
||||
},
|
||||
"leaveOpenGroupConfirmation": {
|
||||
"message": "Möchtest du wirklich diese Gruppe verlassen??",
|
||||
"description": "Confirmation dialog text that tells the user what will happen if they leave the public channel."
|
||||
},
|
||||
"leaveClosedGroupConfirmation": {
|
||||
"message": "Möchtest du wirklich diese Gruppe verlassen?",
|
||||
"description": "Confirmation dialog text that tells the user what will happen if they leave the closed group."
|
||||
},
|
||||
"copyPublicKey": {
|
||||
"message": "Kopieren",
|
||||
"description": "Button action that the user can click to copy their public keys"
|
||||
},
|
||||
"copyChatId": {
|
||||
"message": "Kopieren"
|
||||
},
|
||||
"deleteContact": {
|
||||
"message": "Kontakt löschen",
|
||||
"description": "Confirmation dialog title that asks the user if they really wish to delete the contact. Answer buttons use the strings 'ok' and 'cancel'. The deletion is permanent, i.e. it cannot be undone."
|
||||
},
|
||||
"oneNonImageAtATimeToast": {
|
||||
"message": "Mehrere Anhänge je Nachricht sind ausschließlich bei Bildern erlaubt.",
|
||||
"description": "An error popup when the user has attempted to add an attachment"
|
||||
},
|
||||
"editGroupName": {
|
||||
"message": "Gruppe bearbeiten",
|
||||
"description": "Button action that the user can click to edit a group name (closed)"
|
||||
},
|
||||
"cannotMixImageAndNonImageAttachments": {
|
||||
"message": "Du kannst Bilder nicht gemeinsam mit anderen Anhängen in einer Nachricht kombinieren.",
|
||||
"description": "An error popup when the user has attempted to add an attachment"
|
||||
|
@ -739,6 +832,24 @@
|
|||
"message": "Auf Nachricht antworten",
|
||||
"description": "Shown in triple-dot menu next to message to allow user to start crafting a message with a quotation"
|
||||
},
|
||||
"enterOpenGroupURL": {
|
||||
"message": "Gruppen-URL öffnen"
|
||||
},
|
||||
"newClosedGroup": {
|
||||
"message": "Neue geschlossene Gruppe"
|
||||
},
|
||||
"createClosedGroupNamePrompt": {
|
||||
"message": "Gruppenname"
|
||||
},
|
||||
"createClosedGroupPlaceholder": {
|
||||
"message": "Geben Sie einen Gruppennamen ein."
|
||||
},
|
||||
"addChannelDescription": {
|
||||
"message": "Geben Sie eine offene Gruppen-URL ein."
|
||||
},
|
||||
"next": {
|
||||
"message": "Weiter"
|
||||
},
|
||||
"originalMessageNotFound": {
|
||||
"message": "Originalnachricht nicht gefunden",
|
||||
"description": "Shown in quote if reference message was not found as message was initially downloaded and processed"
|
||||
|
@ -802,7 +913,7 @@
|
|||
"description": "Shown on MacOS if running on a read-only volume and we cannot update"
|
||||
},
|
||||
"ok": {
|
||||
"message": "Okay",
|
||||
"message": "Ok",
|
||||
"description": ""
|
||||
},
|
||||
"cancel": {
|
||||
|
@ -907,6 +1018,14 @@
|
|||
"message": "Soll diese Unterhaltung unwiderruflich gelöscht werden?",
|
||||
"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."
|
||||
},
|
||||
"deleteAccount": {
|
||||
"message": "Alle Daten löschen",
|
||||
"description": "Text for button in settings view to delete account"
|
||||
},
|
||||
"deleteAccountWarningSub": {
|
||||
"message": "Dadurch werden Ihre Nachrichten, Sessions und Kontakte dauerhaft gelöscht.",
|
||||
"description": "Warning for account deletion in settings view"
|
||||
},
|
||||
"sessionEnded": {
|
||||
"message": "Verschlüsselung zurückgesetzt",
|
||||
"description": "This is a past tense, informational message. In other words, your secure session has been reset."
|
||||
|
@ -1117,10 +1236,60 @@
|
|||
"message": "Erfahre mehr über das Verifizieren von Sicherheitsnummern",
|
||||
"description": "Text that links to a support article on verifying safety numbers"
|
||||
},
|
||||
"appearanceSettingsTitle": {
|
||||
"message": "Darstellung"
|
||||
},
|
||||
"privacySettingsTitle": {
|
||||
"message": "Datenschutz"
|
||||
},
|
||||
"notificationsSettingsTitle": {
|
||||
"message": "Benachrichtigungen"
|
||||
},
|
||||
"devicesSettingsTitle": {
|
||||
"message": "Geräte"
|
||||
},
|
||||
"linkPreviewsTitle": {
|
||||
"message": "Link-Vorschauen senden",
|
||||
"description": "Option to control creation and send of link previews in setting screen"
|
||||
},
|
||||
"linkPreviewDescription": {
|
||||
"message": "Vorschauen werden unterstützt für Links von Imgur, Instagram, Pinterest, Reddit und YouTube",
|
||||
"description": "Description shown for the Link Preview option "
|
||||
},
|
||||
"expiredWarning": {
|
||||
"message": "Diese Version von Signal Desktop ist veraltet. Bitte führe eine Aktualisierung auf die aktuellste Version durch, um weiterhin Nachrichten austauschen zu können.",
|
||||
"description": "Warning notification that this version of the app has expired"
|
||||
},
|
||||
"readReceiptSettingDescription": {
|
||||
"message": "Lesebestätigungen aktivieren",
|
||||
"description": "Description of the read receipts setting"
|
||||
},
|
||||
"readReceiptSettingTitle": {
|
||||
"message": "Lesebestätigungen",
|
||||
"description": "Title of the read receipts setting"
|
||||
},
|
||||
"typingIndicatorsSettingDescription": {
|
||||
"message": "sehen und teilen, wann Nachrichten eingetippt werden",
|
||||
"description": "Description of the typing indicators setting"
|
||||
},
|
||||
"typingIndicatorsSettingTitle": {
|
||||
"message": "Tipp-Indikatoren",
|
||||
"description": "Title of the typing indicators setting"
|
||||
},
|
||||
"inviteContacts": {
|
||||
"message": "Freunde einladen"
|
||||
},
|
||||
"settingsHeader": {
|
||||
"message": "Einstellungen"
|
||||
},
|
||||
"leaveOpenGroup": {
|
||||
"message": "Gruppe öffnen verlassen",
|
||||
"description": "Button action that the user can click to leave the group"
|
||||
},
|
||||
"leaveClosedGroup": {
|
||||
"message": "Geschlossene Gruppe verlassen",
|
||||
"description": "Button action that the user can click to leave the group"
|
||||
},
|
||||
"upgrade": {
|
||||
"message": "Aktualisieren",
|
||||
"description": "Label text for button to upgrade the app to the latest version"
|
||||
|
@ -1513,6 +1682,24 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"joinOpenGroup": {
|
||||
"message": "Offener Gruppe beitreten"
|
||||
},
|
||||
"yourSessionID": {
|
||||
"message": "Ihre Session ID"
|
||||
},
|
||||
"createClosedGroup": {
|
||||
"message": "Neue geschlossene Gruppe"
|
||||
},
|
||||
"searchForAKeyPhrase": {
|
||||
"message": "Nach Unterhaltungen, Kontakten und Nachrichten suchen"
|
||||
},
|
||||
"enterSessionID": {
|
||||
"message": "Session ID eingeben"
|
||||
},
|
||||
"usersCanShareTheir...": {
|
||||
"message": "Benutzer können ihre Session ID freigeben, indem sie in ihren Einstellungen auf \"Session ID freigeben\" tippen oder ihren QR-Code freigeben."
|
||||
},
|
||||
"joinedTheGroup": {
|
||||
"message": "$name$ ist der Gruppe beigetreten",
|
||||
"description": "Shown in the conversation history when a single person joins the group",
|
||||
|
|
|
@ -596,8 +596,7 @@
|
|||
"description": "Shown to separate the types of search results"
|
||||
},
|
||||
"settingsHeader": {
|
||||
"message": "Settings",
|
||||
"description": "Shown to separate the types of search results"
|
||||
"message": "Settings"
|
||||
},
|
||||
"welcomeToSession": {
|
||||
"message": "Welcome to Session"
|
||||
|
@ -723,9 +722,6 @@
|
|||
"cancel": {
|
||||
"message": "Cancel"
|
||||
},
|
||||
"copy": {
|
||||
"message": "Copy"
|
||||
},
|
||||
"close": {
|
||||
"message": "Close"
|
||||
},
|
||||
|
@ -913,11 +909,7 @@
|
|||
"message": "Permanently delete the messages in this conversation?",
|
||||
"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."
|
||||
},
|
||||
"deletePublicChannel": {
|
||||
"message": "Leave Channel",
|
||||
"description": "Confirmation dialog title that asks the user if they really wish to delete a public channel. Answer buttons use the strings 'ok' and 'cancel'. The deletion is permanent, i.e. it cannot be undone."
|
||||
},
|
||||
"deletePublicChannelConfirmation": {
|
||||
"leaveOpenGroupConfirmation": {
|
||||
"message": "Leave this Open Group?",
|
||||
"description": "Confirmation dialog text that tells the user what will happen if they leave the public channel."
|
||||
},
|
||||
|
@ -1051,7 +1043,7 @@
|
|||
"description": "Header for general options on the settings screen"
|
||||
},
|
||||
"linkPreviewsTitle": {
|
||||
"message": "Link Previews",
|
||||
"message": "Send Link Previews",
|
||||
"description": "Option to control creation and send of link previews in setting screen"
|
||||
},
|
||||
"linkPreviewDescription": {
|
||||
|
@ -1116,22 +1108,6 @@
|
|||
"message": "Typing Indicators",
|
||||
"description": "Title of the typing indicators setting"
|
||||
},
|
||||
"multiDeviceDisabledTemporary": {
|
||||
"message": "MultiDevice disabled temporarily",
|
||||
"description": "Description of why multi device is disabled"
|
||||
},
|
||||
"multiDeviceDisabledTemporaryTitle": {
|
||||
"message": "Changes to Multi-device",
|
||||
"description": "Description of why multi device is disabled on app start"
|
||||
},
|
||||
"multiDeviceDisabledTemporaryDescriptionPrimary": {
|
||||
"message": "You’re seeing this because you have a secondary device linked to your Session ID. To improve reliability and stability, we’ve decided to temporarily disable Session’s multi-device functionality. Device linking has been disabled, and existing secondary clients will be erased on <span style=\"color:#00f782\">August 6th</span>.</br></br>To read more about this change, visit the Session FAQ at <a href=\"https://getsession.org/faq\">getsession.org/faq</a>.",
|
||||
"description": "Description of why multi device is disabled on app start for a primary device"
|
||||
},
|
||||
"multiDeviceDisabledTemporaryDescriptionSecondary": {
|
||||
"message": "You’re seeing this because this is a secondary device in a multi-device setup. To improve reliability and stability, we’ve decided to temporarily disable Session’s multi-device functionality. Device linking has been disabled, and existing secondary clients will be erased on <span style=\"color:#00f782\">August 6th</span>.</br></br>To read more about this change, visit the Session FAQ at <a href=\"https://getsession.org/faq\">getsession.org/faq</a>.",
|
||||
"description": "Description of why multi device is disabled on app start for a secondary device"
|
||||
},
|
||||
"messageTTL": {
|
||||
"message": "Message TTL",
|
||||
"description": "Title of the Message TTL setting"
|
||||
|
@ -1470,6 +1446,9 @@
|
|||
"unlinked": {
|
||||
"message": "Unlinked"
|
||||
},
|
||||
"successUnlinked": {
|
||||
"message": "Your device was unlinked successfully"
|
||||
},
|
||||
"relink": {
|
||||
"message": "Relink"
|
||||
},
|
||||
|
@ -1607,10 +1586,6 @@
|
|||
"copyChatId": {
|
||||
"message": "Copy Chat ID"
|
||||
},
|
||||
"leaveGroup": {
|
||||
"message": "Leave Group",
|
||||
"description": "Button action that the user can click to leave the group"
|
||||
},
|
||||
"leaveOpenGroup": {
|
||||
"message": "Leave Open Group",
|
||||
"description": "Button action that the user can click to leave the group"
|
||||
|
@ -2009,15 +1984,9 @@
|
|||
"enterSessionID": {
|
||||
"message": "Enter Session ID"
|
||||
},
|
||||
"pasteSessionIDRecipient": {
|
||||
"message": "Enter a Session ID"
|
||||
},
|
||||
"usersCanShareTheir...": {
|
||||
"message": "Users can share their Session ID from their account settings, or by sharing their QR code."
|
||||
},
|
||||
"searchByIDOrDisplayName": {
|
||||
"message": "Search by ID # or Display Name"
|
||||
},
|
||||
"message": {
|
||||
"message": "Message"
|
||||
},
|
||||
|
@ -2030,9 +1999,6 @@
|
|||
"createGroup": {
|
||||
"message": "Create Group"
|
||||
},
|
||||
"yourPublicKey": {
|
||||
"message": "Your Session ID"
|
||||
},
|
||||
"appearanceSettingsTitle": {
|
||||
"message": "Appearance"
|
||||
},
|
||||
|
@ -2042,7 +2008,7 @@
|
|||
"privacySettingsTitle": {
|
||||
"message": "Privacy"
|
||||
},
|
||||
"notificationSettingsTitle": {
|
||||
"notificationsSettingsTitle": {
|
||||
"message": "Notifications"
|
||||
},
|
||||
"devicesSettingsTitle": {
|
||||
|
@ -2063,9 +2029,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"addChannel": {
|
||||
"message": "Join Open Group"
|
||||
},
|
||||
"joinOpenGroup": {
|
||||
"message": "Join Open Group"
|
||||
},
|
||||
|
@ -2087,7 +2050,7 @@
|
|||
"closedGroupCreatedToastTitle": {
|
||||
"message": "Group created successfully"
|
||||
},
|
||||
"enterChannelURL": {
|
||||
"enterOpenGroupURL": {
|
||||
"message": "Enter Open Group URL"
|
||||
},
|
||||
"channelUrlPlaceholder": {
|
||||
|
|
25
app/sql.js
25
app/sql.js
|
@ -810,6 +810,7 @@ const LOKI_SCHEMA_VERSIONS = [
|
|||
updateToLokiSchemaVersion4,
|
||||
updateToLokiSchemaVersion5,
|
||||
updateToLokiSchemaVersion6,
|
||||
updateToLokiSchemaVersion7,
|
||||
];
|
||||
|
||||
async function updateToLokiSchemaVersion1(currentVersion, instance) {
|
||||
|
@ -1027,6 +1028,30 @@ async function updateToLokiSchemaVersion6(currentVersion, instance) {
|
|||
console.log('updateToLokiSchemaVersion6: success!');
|
||||
}
|
||||
|
||||
async function updateToLokiSchemaVersion7(currentVersion, instance) {
|
||||
if (currentVersion >= 7) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('updateToLokiSchemaVersion7: starting...');
|
||||
|
||||
await instance.run('BEGIN TRANSACTION;');
|
||||
|
||||
// Remove multi device data
|
||||
await instance.run('DELETE FROM pairingAuthorisations;');
|
||||
|
||||
await instance.run(
|
||||
`INSERT INTO loki_schema (
|
||||
version
|
||||
) values (
|
||||
7
|
||||
);`
|
||||
);
|
||||
|
||||
await instance.run('COMMIT TRANSACTION;');
|
||||
console.log('updateToLokiSchemaVersion7: success!');
|
||||
}
|
||||
|
||||
async function updateLokiSchema(instance) {
|
||||
const result = await instance.get(
|
||||
"SELECT name FROM sqlite_master WHERE type = 'table' AND name='loki_schema';"
|
||||
|
|
133
js/background.js
133
js/background.js
|
@ -129,6 +129,52 @@
|
|||
// of preload.js processing
|
||||
window.setImmediate = window.nodeSetImmediate;
|
||||
|
||||
window.toasts = new Map();
|
||||
window.pushToast = options => {
|
||||
// Setting toasts with the same ID can be used to prevent identical
|
||||
// toasts from appearing at once (stacking).
|
||||
// If toast already exists, it will be reloaded (updated)
|
||||
|
||||
const params = {
|
||||
title: options.title,
|
||||
id: options.id || window.generateID(),
|
||||
description: options.description || '',
|
||||
type: options.type || '',
|
||||
icon: options.icon || '',
|
||||
shouldFade: options.shouldFade,
|
||||
};
|
||||
|
||||
// Give all toasts an ID. User may define.
|
||||
let currentToast;
|
||||
const toastID = params.id;
|
||||
const toast = !!toastID && window.toasts.get(toastID);
|
||||
if (toast) {
|
||||
currentToast = window.toasts.get(toastID);
|
||||
currentToast.update(params);
|
||||
} else {
|
||||
// Make new Toast
|
||||
window.toasts.set(
|
||||
toastID,
|
||||
new Whisper.SessionToastView({
|
||||
el: $('body'),
|
||||
})
|
||||
);
|
||||
|
||||
currentToast = window.toasts.get(toastID);
|
||||
currentToast.render();
|
||||
currentToast.update(params);
|
||||
}
|
||||
|
||||
// Remove some toasts if too many exist
|
||||
const maxToasts = 6;
|
||||
while (window.toasts.size > maxToasts) {
|
||||
const finalToastID = window.toasts.keys().next().value;
|
||||
window.toasts.get(finalToastID).fadeToast();
|
||||
}
|
||||
|
||||
return toastID;
|
||||
};
|
||||
|
||||
const { IdleDetector, MessageDataMigrator } = Signal.Workflow;
|
||||
const {
|
||||
mandatoryMessageUpgrade,
|
||||
|
@ -151,6 +197,20 @@
|
|||
|
||||
window.log.info('background page reloaded');
|
||||
window.log.info('environment:', window.getEnvironment());
|
||||
const restartReason = localStorage.getItem('restart-reason');
|
||||
window.log.info('restartReason:', restartReason);
|
||||
|
||||
if (restartReason === 'unlink') {
|
||||
setTimeout(() => {
|
||||
localStorage.removeItem('restart-reason');
|
||||
|
||||
window.pushToast({
|
||||
title: window.i18n('successUnlinked'),
|
||||
type: 'info',
|
||||
id: '123',
|
||||
});
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
let idleDetector;
|
||||
let initialLoadComplete = false;
|
||||
|
@ -300,6 +360,12 @@
|
|||
storage.put('primaryDevicePubKey', textsecure.storage.user.getNumber());
|
||||
}
|
||||
|
||||
// 4th August 2020 - Force wipe of secondary devices as multi device is being disabled.
|
||||
if (storage.get('isSecondaryDevice')) {
|
||||
await window.deleteAccount('unlink');
|
||||
return;
|
||||
}
|
||||
|
||||
// These make key operations available to IPC handlers created in preload.js
|
||||
window.Events = {
|
||||
getThemeSetting: () => 'dark', // storage.get('theme-setting', 'dark')
|
||||
|
@ -754,76 +820,9 @@
|
|||
.toString(36)
|
||||
.substring(3);
|
||||
|
||||
window.toasts = new Map();
|
||||
window.pushToast = options => {
|
||||
// Setting toasts with the same ID can be used to prevent identical
|
||||
// toasts from appearing at once (stacking).
|
||||
// If toast already exists, it will be reloaded (updated)
|
||||
|
||||
const params = {
|
||||
title: options.title,
|
||||
id: options.id || window.generateID(),
|
||||
description: options.description || '',
|
||||
type: options.type || '',
|
||||
icon: options.icon || '',
|
||||
shouldFade: options.shouldFade,
|
||||
};
|
||||
|
||||
// Give all toasts an ID. User may define.
|
||||
let currentToast;
|
||||
const toastID = params.id;
|
||||
const toast = !!toastID && window.toasts.get(toastID);
|
||||
if (toast) {
|
||||
currentToast = window.toasts.get(toastID);
|
||||
currentToast.update(params);
|
||||
} else {
|
||||
// Make new Toast
|
||||
window.toasts.set(
|
||||
toastID,
|
||||
new Whisper.SessionToastView({
|
||||
el: $('body'),
|
||||
})
|
||||
);
|
||||
|
||||
currentToast = window.toasts.get(toastID);
|
||||
currentToast.render();
|
||||
currentToast.update(params);
|
||||
}
|
||||
|
||||
// Remove some toasts if too many exist
|
||||
const maxToasts = 6;
|
||||
while (window.toasts.size > maxToasts) {
|
||||
const finalToastID = window.toasts.keys().next().value;
|
||||
window.toasts.get(finalToastID).fadeToast();
|
||||
}
|
||||
|
||||
return toastID;
|
||||
};
|
||||
|
||||
// Get memberlist. This function is not accurate >>
|
||||
// window.getMemberList = window.lokiPublicChatAPI.getListOfMembers();
|
||||
|
||||
window.deleteAccount = async () => {
|
||||
try {
|
||||
window.log.info('Deleting everything!');
|
||||
|
||||
const { Logs } = window.Signal;
|
||||
await Logs.deleteAll();
|
||||
|
||||
await window.Signal.Data.removeAll();
|
||||
await window.Signal.Data.close();
|
||||
await window.Signal.Data.removeDB();
|
||||
|
||||
await window.Signal.Data.removeOtherData();
|
||||
} catch (error) {
|
||||
window.log.error(
|
||||
'Something went wrong deleting all data:',
|
||||
error && error.stack ? error.stack : error
|
||||
);
|
||||
}
|
||||
window.restart();
|
||||
};
|
||||
|
||||
window.toggleTheme = () => {
|
||||
const theme = window.Events.getThemeSetting();
|
||||
const updatedTheme = theme === 'dark' ? 'light' : 'dark';
|
||||
|
|
|
@ -137,6 +137,6 @@ window.onerror = (message, script, line, col, error) => {
|
|||
|
||||
window.addEventListener('unhandledrejection', rejectionEvent => {
|
||||
const error = rejectionEvent.reason;
|
||||
const errorInfo = error && error.stack ? error.stack : JSON.stringify(error);
|
||||
window.log.error(`Top-level unhandled promise rejection: ${errorInfo}`);
|
||||
const errorInfo = error && error.stack ? error.stack : error;
|
||||
window.log.error('Top-level unhandled promise rejection:', errorInfo);
|
||||
});
|
||||
|
|
|
@ -2210,16 +2210,20 @@
|
|||
return;
|
||||
}
|
||||
|
||||
const profileKeyBuffer = window.Signal.Crypto.base64ToArrayBuffer(
|
||||
profileKey
|
||||
);
|
||||
const accessKeyBuffer = await window.Signal.Crypto.deriveAccessKey(
|
||||
profileKeyBuffer
|
||||
);
|
||||
const accessKey = window.Signal.Crypto.arrayBufferToBase64(
|
||||
accessKeyBuffer
|
||||
);
|
||||
this.set({ accessKey });
|
||||
try {
|
||||
const profileKeyBuffer = window.Signal.Crypto.base64ToArrayBuffer(
|
||||
profileKey
|
||||
);
|
||||
const accessKeyBuffer = await window.Signal.Crypto.deriveAccessKey(
|
||||
profileKeyBuffer
|
||||
);
|
||||
const accessKey = window.Signal.Crypto.arrayBufferToBase64(
|
||||
accessKeyBuffer
|
||||
);
|
||||
this.set({ accessKey });
|
||||
} catch (e) {
|
||||
window.log.warn(`Failed to derive access key for ${this.id}`);
|
||||
}
|
||||
},
|
||||
|
||||
async upgradeMessages(messages) {
|
||||
|
@ -2327,8 +2331,8 @@
|
|||
let message = i18n('deleteContactConfirmation');
|
||||
|
||||
if (this.isPublic()) {
|
||||
title = i18n('deletePublicChannel');
|
||||
message = i18n('deletePublicChannelConfirmation');
|
||||
title = i18n('leaveOpenGroup');
|
||||
message = i18n('leaveOpenGroupConfirmation');
|
||||
} else if (this.isClosedGroup()) {
|
||||
title = i18n('leaveClosedGroup');
|
||||
message = i18n('leaveClosedGroupConfirmation');
|
||||
|
|
|
@ -1710,8 +1710,9 @@ class LokiPublicChannelAPI {
|
|||
sigString += [...attachmentAnnotations, ...previewAnnotations]
|
||||
.map(data => data.id || data.image.id)
|
||||
.sort()
|
||||
.join();
|
||||
.join('');
|
||||
sigString += sigVer;
|
||||
|
||||
return dcodeIO.ByteBuffer.wrap(sigString, 'utf8').toArrayBuffer();
|
||||
}
|
||||
|
||||
|
|
|
@ -218,6 +218,10 @@ class LokiHomeServerInstance extends LokiFileServerInstance {
|
|||
}
|
||||
|
||||
async updateOurDeviceMapping() {
|
||||
if (!window.lokiFeatureFlags.useMultiDevice) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const isPrimary = !storage.get('isSecondaryDevice');
|
||||
const authorisations = await window.libsession.Protocols.MultiDeviceProtocol.getPairingAuthorisations(
|
||||
this.ourKey
|
||||
|
|
|
@ -238,8 +238,8 @@
|
|||
let message = i18n('deleteContactConfirmation');
|
||||
|
||||
if (groupConvo.isPublic()) {
|
||||
title = i18n('deletePublicChannel');
|
||||
message = i18n('deletePublicChannelConfirmation');
|
||||
title = i18n('leaveOpenGroup');
|
||||
message = i18n('leaveOpenGroupConfirmation');
|
||||
} else if (groupConvo.isClosedGroup()) {
|
||||
title = i18n('leaveClosedGroup');
|
||||
message = i18n('leaveClosedGroupConfirmation');
|
||||
|
|
3
main.js
3
main.js
|
@ -763,9 +763,8 @@ app.on('ready', async () => {
|
|||
logger = logging.getLogger();
|
||||
logger.info('app ready');
|
||||
logger.info(`starting version ${packageJson.version}`);
|
||||
|
||||
if (!locale) {
|
||||
const appLocale = process.env.NODE_ENV === 'test' ? 'en' : 'en'; // app.getLocale(); // FIXME reenable once we have translated our files
|
||||
const appLocale = process.env.NODE_ENV === 'test' ? 'en' : app.getLocale();
|
||||
locale = loadLocale({ appLocale, logger });
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "session-messenger-desktop",
|
||||
"productName": "Session",
|
||||
"description": "Private messaging from your desktop",
|
||||
"version": "1.2.0",
|
||||
"version": "1.2.1",
|
||||
"license": "GPL-3.0",
|
||||
"author": {
|
||||
"name": "Loki Project",
|
||||
|
|
25
preload.js
25
preload.js
|
@ -456,6 +456,7 @@ window.lokiFeatureFlags = {
|
|||
enableSenderKeys: false,
|
||||
onionRequestHops: 3,
|
||||
debugMessageLogs: process.env.ENABLE_MESSAGE_LOGS,
|
||||
useMultiDevice: false,
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-extend-native,func-names
|
||||
|
@ -492,6 +493,7 @@ if (config.environment.includes('test-integration')) {
|
|||
useFileOnionRequests: false,
|
||||
debugMessageLogs: true,
|
||||
enableSenderKeys: true,
|
||||
useMultiDevice: false,
|
||||
};
|
||||
/* eslint-disable global-require, import/no-extraneous-dependencies */
|
||||
window.sinon = require('sinon');
|
||||
|
@ -505,3 +507,26 @@ const {
|
|||
} = require('./ts/util/blockedNumberController');
|
||||
|
||||
window.BlockedNumberController = BlockedNumberController;
|
||||
|
||||
window.deleteAccount = async reason => {
|
||||
try {
|
||||
window.log.info('Deleting everything!');
|
||||
|
||||
const { Logs } = window.Signal;
|
||||
await Logs.deleteAll();
|
||||
|
||||
await window.Signal.Data.removeAll();
|
||||
await window.Signal.Data.close();
|
||||
await window.Signal.Data.removeDB();
|
||||
|
||||
await window.Signal.Data.removeOtherData();
|
||||
// 'unlink' => toast will be shown on app restart
|
||||
window.localStorage.setItem('restart-reason', reason);
|
||||
} catch (error) {
|
||||
window.log.error(
|
||||
'Something went wrong deleting all data:',
|
||||
error && error.stack ? error.stack : error
|
||||
);
|
||||
}
|
||||
window.restart();
|
||||
};
|
||||
|
|
|
@ -209,7 +209,7 @@ textarea {
|
|||
border-radius: 2px;
|
||||
height: 33px;
|
||||
padding: 0px 18px;
|
||||
line-height: 33px;
|
||||
// line-height: 33px;
|
||||
font-size: $session-font-sm;
|
||||
}
|
||||
|
||||
|
|
|
@ -273,10 +273,6 @@ $session-compose-margin: 20px;
|
|||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.white-border {
|
||||
width: $session-left-pane-width;
|
||||
position: relative;
|
||||
|
@ -339,6 +335,12 @@ $session-compose-margin: 20px;
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
|
||||
.left-pane-contact-bottom-buttons .session-button {
|
||||
vertical-align: middle;
|
||||
white-space: normal;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.session-left-pane-section-content {
|
||||
|
@ -382,6 +384,7 @@ $session-compose-margin: 20px;
|
|||
flex-grow: 1;
|
||||
font-size: $session-font-sm;
|
||||
font-family: $session-font-default;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&:focus {
|
||||
outline: none !important;
|
||||
|
@ -424,7 +427,7 @@ $session-compose-margin: 20px;
|
|||
.session-button.square-outline.square.danger {
|
||||
flex-grow: 1;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
// line-height: 50px;
|
||||
|
||||
@at-root .light-theme #{&} {
|
||||
border: 1px solid $session-shade-15;
|
||||
|
@ -609,7 +612,7 @@ $session-compose-margin: 20px;
|
|||
flex-grow: 1;
|
||||
border: 1px solid $session-shade-8;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
// line-height: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,7 +122,7 @@ export class EditProfileDialog extends React.Component<Props, State> {
|
|||
|
||||
{viewDefault || viewQR ? (
|
||||
<SessionButton
|
||||
text={window.i18n('copy')}
|
||||
text={window.i18n('editMenuCopy')}
|
||||
buttonType={SessionButtonType.BrandOutline}
|
||||
buttonColor={SessionButtonColor.Green}
|
||||
onClick={() => {
|
||||
|
|
|
@ -2,12 +2,7 @@ import React from 'react';
|
|||
|
||||
import { Avatar } from '../Avatar';
|
||||
import { Colors, LocalizerType } from '../../types/Util';
|
||||
import {
|
||||
ContextMenu,
|
||||
ContextMenuTrigger,
|
||||
MenuItem,
|
||||
SubMenu,
|
||||
} from 'react-contextmenu';
|
||||
import { ContextMenu, ContextMenuTrigger, MenuItem } from 'react-contextmenu';
|
||||
|
||||
import {
|
||||
SessionIconButton,
|
||||
|
|
|
@ -65,13 +65,13 @@ export class MediaGallery extends React.Component<Props, State> {
|
|||
<div className="module-media-gallery">
|
||||
<div className="module-media-gallery__tab-container">
|
||||
<Tab
|
||||
label="Media"
|
||||
label={window.i18n('media')}
|
||||
type="media"
|
||||
isSelected={selectedTab === 'media'}
|
||||
onSelect={this.handleTabSelect}
|
||||
/>
|
||||
<Tab
|
||||
label="Documents"
|
||||
label={window.i18n('documents')}
|
||||
type="documents"
|
||||
isSelected={selectedTab === 'documents'}
|
||||
onSelect={this.handleTabSelect}
|
||||
|
|
|
@ -59,48 +59,6 @@ export class ActionsPanel extends React.Component<Props, State> {
|
|||
},
|
||||
'refreshAvatarCallback'
|
||||
);
|
||||
setTimeout(async () => {
|
||||
const disabledMultiDeviceCountDb = await getItemById(
|
||||
'disabledMultiDeviceCount'
|
||||
);
|
||||
const disabledMultiDeviceCount =
|
||||
Number(disabledMultiDeviceCountDb?.value) || 0;
|
||||
const data = {
|
||||
id: 'disabledMultiDeviceCount',
|
||||
value: String(disabledMultiDeviceCount + 1),
|
||||
};
|
||||
await createOrUpdateItem(data);
|
||||
if (disabledMultiDeviceCount % 5 !== 0) {
|
||||
return;
|
||||
}
|
||||
const currentDevice = await UserUtil.getCurrentDevicePubKey();
|
||||
if (!currentDevice) {
|
||||
return;
|
||||
}
|
||||
const secondaryDevices = await MultiDeviceProtocol.getSecondaryDevices(
|
||||
currentDevice
|
||||
);
|
||||
const isSecondary =
|
||||
secondaryDevices.find(s => s.key === currentDevice) ||
|
||||
!!window.textsecure.storage.get('isSecondaryDevice');
|
||||
|
||||
const hasMultipleDevices =
|
||||
(await MultiDeviceProtocol.getOurDevices()).length > 1;
|
||||
const primaryWithSecondary = !isSecondary && hasMultipleDevices;
|
||||
|
||||
if (!primaryWithSecondary && !isSecondary) {
|
||||
return;
|
||||
}
|
||||
|
||||
const opts = {
|
||||
hideCancel: true,
|
||||
title: window.i18n('multiDeviceDisabledTemporaryTitle'),
|
||||
message: primaryWithSecondary
|
||||
? window.i18n('multiDeviceDisabledTemporaryDescriptionPrimary')
|
||||
: window.i18n('multiDeviceDisabledTemporaryDescriptionSecondary'),
|
||||
};
|
||||
window.Whisper.events.trigger('showConfirmationDialog', opts);
|
||||
}, 1000);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -219,13 +219,13 @@ export class LeftPaneSettingSection extends React.Component<Props, State> {
|
|||
},
|
||||
{
|
||||
id: SessionSettingCategory.Notifications,
|
||||
title: window.i18n('notificationSettingsTitle'),
|
||||
title: window.i18n('notificationsSettingsTitle'),
|
||||
hidden: false,
|
||||
},
|
||||
{
|
||||
id: SessionSettingCategory.Devices,
|
||||
title: window.i18n('devicesSettingsTitle'),
|
||||
hidden: isSecondaryDevice,
|
||||
hidden: !window.lokiFeatureFlags.useMultiDevice || isSecondaryDevice,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
|
@ -557,9 +557,12 @@ export class RegistrationTabs extends React.Component<{}, State> {
|
|||
SessionButtonType.BrandOutline,
|
||||
SessionButtonColor.Green
|
||||
)}
|
||||
{/*<h4>{or}</h4>*/}
|
||||
{/* FIXME enable back to allow linking of device
|
||||
this.renderLinkDeviceToExistingAccountButton() */}
|
||||
{window.lokiFeatureFlags.useMultiDevice && (
|
||||
<>
|
||||
<h4>{or}</h4>
|
||||
{this.renderLinkDeviceToExistingAccountButton()}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -584,9 +587,12 @@ export class RegistrationTabs extends React.Component<{}, State> {
|
|||
return (
|
||||
<div>
|
||||
{this.renderContinueYourSessionButton()}
|
||||
{/*<h4>{or}</h4>*/}
|
||||
{/* FIXME enable back to allow linking of device
|
||||
this.renderLinkDeviceToExistingAccountButton()*/}
|
||||
{window.lokiFeatureFlags.useMultiDevice && (
|
||||
<>
|
||||
<h4>{or}</h4>
|
||||
{this.renderLinkDeviceToExistingAccountButton()}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -134,20 +134,20 @@ export class SessionClosableOverlay extends React.Component<Props, State> {
|
|||
buttonText = window.i18n('next');
|
||||
descriptionLong = window.i18n('usersCanShareTheir...');
|
||||
subtitle = window.i18n('enterSessionID');
|
||||
placeholder = window.i18n('pasteSessionIDRecipient');
|
||||
placeholder = window.i18n('enterSessionID');
|
||||
break;
|
||||
case 'contact':
|
||||
title = window.i18n('addContact');
|
||||
buttonText = window.i18n('next');
|
||||
descriptionLong = window.i18n('usersCanShareTheir...');
|
||||
subtitle = window.i18n('enterSessionID');
|
||||
placeholder = window.i18n('pasteSessionIDRecipient');
|
||||
placeholder = window.i18n('enterSessionID');
|
||||
break;
|
||||
case 'open-group':
|
||||
title = window.i18n('addChannel');
|
||||
buttonText = window.i18n('joinChannel');
|
||||
title = window.i18n('joinOpenGroup');
|
||||
buttonText = window.i18n('joinOpenGroup');
|
||||
descriptionLong = window.i18n('addChannelDescription');
|
||||
subtitle = window.i18n('enterChannelURL');
|
||||
subtitle = window.i18n('enterOpenGroupURL');
|
||||
placeholder = window.i18n('channelUrlPlaceholder');
|
||||
break;
|
||||
case 'closed-group':
|
||||
|
@ -239,13 +239,13 @@ export class SessionClosableOverlay extends React.Component<Props, State> {
|
|||
<UserSearchDropdown
|
||||
searchTerm={searchTerm || ''}
|
||||
updateSearch={updateSearch}
|
||||
placeholder={window.i18n('searchByIDOrDisplayName')}
|
||||
placeholder={window.i18n('searchForAKeyPhrase')}
|
||||
searchResults={searchResults}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isAddContactView && (
|
||||
<PillDivider text={window.i18n('yourPublicKey')} />
|
||||
<PillDivider text={window.i18n('yourSessionID')} />
|
||||
)}
|
||||
|
||||
{isAddContactView && (
|
||||
|
|
|
@ -238,6 +238,7 @@ export class SettingsView extends React.Component<SettingsViewProps, State> {
|
|||
showLinkDeviceButton={!shouldRenderPasswordLock}
|
||||
category={category}
|
||||
isSecondaryDevice={isSecondaryDevice}
|
||||
categoryTitle={window.i18n(`${category}SettingsTitle`)}
|
||||
/>
|
||||
|
||||
<div className="session-settings-view">
|
||||
|
@ -646,7 +647,7 @@ export class SettingsView extends React.Component<SettingsViewProps, State> {
|
|||
id: 'no-linked-device',
|
||||
title: noPairedDeviceText,
|
||||
type: undefined,
|
||||
description: window.i18n('multiDeviceDisabledTemporary'),
|
||||
description: '',
|
||||
category: SessionSettingCategory.Devices,
|
||||
content: {},
|
||||
comparisonValue: undefined,
|
||||
|
|
|
@ -12,6 +12,7 @@ interface Props extends SettingsViewProps {
|
|||
showLinkDeviceButton: boolean | null;
|
||||
// isSecondaryDevice is used to just disable the linkDeviceButton when we are already a secondary device
|
||||
isSecondaryDevice: boolean;
|
||||
categoryTitle: string;
|
||||
}
|
||||
|
||||
export class SettingsHeader extends React.Component<Props, any> {
|
||||
|
@ -65,23 +66,12 @@ export class SettingsHeader extends React.Component<Props, any> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const { category } = this.props;
|
||||
const { category, categoryTitle } = this.props;
|
||||
const { disableLinkDeviceButton } = this.state;
|
||||
const categoryString = String(category);
|
||||
const categoryTitlePrefix =
|
||||
categoryString[0].toUpperCase() + categoryString.substr(1);
|
||||
// Remove 's' on the end to keep words in singular form
|
||||
const categoryTitle =
|
||||
categoryTitlePrefix[categoryTitlePrefix.length - 1] === 's'
|
||||
? `${categoryTitlePrefix.slice(0, -1)} Settings`
|
||||
: `${categoryTitlePrefix} Settings`;
|
||||
const showSearch = false;
|
||||
const showAddDevice = false;
|
||||
/* FIXME enable back to allow linking of device
|
||||
const showAddDevice =
|
||||
const showAddDevice =
|
||||
category === SessionSettingCategory.Devices &&
|
||||
this.props.showLinkDeviceButton;
|
||||
*/
|
||||
|
||||
return (
|
||||
<div className="session-settings-header">
|
||||
|
|
|
@ -87,6 +87,14 @@ export async function handlePairingAuthorisationMessage(
|
|||
pairingAuthorisation: SignalService.IPairingAuthorisationMessage,
|
||||
dataMessage: SignalService.IDataMessage | undefined | null
|
||||
): Promise<void> {
|
||||
if (!window.lokiFeatureFlags.useMultiDevice) {
|
||||
window.log.info(
|
||||
`Received a pairing authorisation message from ${envelope.source} while multi device is disabled.`
|
||||
);
|
||||
await removeFromCache(envelope);
|
||||
return;
|
||||
}
|
||||
|
||||
const { secondaryDevicePubKey, grantSignature } = pairingAuthorisation;
|
||||
const isGrant =
|
||||
grantSignature &&
|
||||
|
|
|
@ -28,6 +28,11 @@ export class MultiDeviceProtocol {
|
|||
public static async fetchPairingAuthorisationsIfNeeded(
|
||||
device: PubKey
|
||||
): Promise<void> {
|
||||
// Disable fetching if we don't want to use multi device
|
||||
if (!window.lokiFeatureFlags.useMultiDevice) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This return here stops an infinite loop when we get all our other devices
|
||||
const ourKey = await UserUtil.getCurrentDevicePubKey();
|
||||
if (!ourKey || device.key === ourKey) {
|
||||
|
|
|
@ -141,9 +141,7 @@ export function getDeleteContactMenuItem(
|
|||
): JSX.Element | null {
|
||||
if (showDeleteContact(isMe, isClosable, isGroup, isPublic, isRss)) {
|
||||
if (isPublic) {
|
||||
return (
|
||||
<MenuItem onClick={action}>{i18n('deletePublicChannel')}</MenuItem>
|
||||
);
|
||||
return <MenuItem onClick={action}>{i18n('leaveOpenGroup')}</MenuItem>;
|
||||
}
|
||||
return <MenuItem onClick={action}>{i18n('deleteContact')}</MenuItem>;
|
||||
}
|
||||
|
@ -159,7 +157,7 @@ export function getLeaveGroupMenuItem(
|
|||
i18n: LocalizerType
|
||||
): JSX.Element | null {
|
||||
if (showLeaveGroup(isKickedFromGroup, isGroup, isPublic, isRss)) {
|
||||
return <MenuItem onClick={action}>{i18n('leaveGroup')}</MenuItem>;
|
||||
return <MenuItem onClick={action}>{i18n('leaveClosedGroup')}</MenuItem>;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,13 @@ function generateFakeAuthorisations(
|
|||
describe('MultiDeviceProtocol', () => {
|
||||
const sandbox = sinon.createSandbox();
|
||||
|
||||
beforeEach(() => {
|
||||
// Enable multidevice for tests
|
||||
TestUtils.stubWindow('lokiFeatureFlags', {
|
||||
useMultiDevice: true,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
TestUtils.restoreStubs();
|
||||
sandbox.restore();
|
||||
|
|
12
ts/window.d.ts
vendored
12
ts/window.d.ts
vendored
|
@ -51,7 +51,17 @@ declare global {
|
|||
libloki: Libloki;
|
||||
libsignal: LibsignalProtocol;
|
||||
log: any;
|
||||
lokiFeatureFlags: any;
|
||||
lokiFeatureFlags: {
|
||||
multiDeviceUnpairing: boolean;
|
||||
privateGroupChats: boolean;
|
||||
useSnodeProxy: boolean;
|
||||
useOnionRequests: boolean;
|
||||
useFileOnionRequests: boolean;
|
||||
enableSenderKeys: boolean;
|
||||
onionRequestHops: number;
|
||||
debugMessageLogs: boolean;
|
||||
useMultiDevice: boolean;
|
||||
};
|
||||
lokiFileServerAPI: LokiFileServerInstance;
|
||||
lokiMessageAPI: LokiMessageInterface;
|
||||
lokiPublicChatAPI: LokiPublicChatFactoryInterface;
|
||||
|
|
Loading…
Reference in a new issue