Merge remote-tracking branch 'upstream/clearnet' into fix-integration-tests

This commit is contained in:
Audric Ackermann 2020-08-07 10:00:37 +10:00
commit 58dd064e93
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4
29 changed files with 414 additions and 225 deletions

View File

@ -9,6 +9,7 @@ on:
- clearnet
- github-actions
- refactor-ts-react
- clean-en-translation
jobs:
build:

View File

@ -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",

View File

@ -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": "Youre seeing this because you have a secondary device linked to your Session ID. To improve reliability and stability, weve decided to temporarily disable Sessions 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": "Youre seeing this because this is a secondary device in a multi-device setup. To improve reliability and stability, weve decided to temporarily disable Sessions 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": {

View File

@ -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';"

View File

@ -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';

View File

@ -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);
});

View File

@ -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');

View File

@ -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();
}

View File

@ -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

View File

@ -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');

View File

@ -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 });
}

View File

@ -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",

View File

@ -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();
};

View File

@ -209,7 +209,7 @@ textarea {
border-radius: 2px;
height: 33px;
padding: 0px 18px;
line-height: 33px;
// line-height: 33px;
font-size: $session-font-sm;
}

View File

@ -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;
}
}
}

View File

@ -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={() => {

View File

@ -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,

View File

@ -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}

View File

@ -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);
}
);
}

View File

@ -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,
},
];
}

View File

@ -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>
);
}

View File

@ -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 && (

View File

@ -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,

View File

@ -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">

View File

@ -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 &&

View File

@ -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) {

View File

@ -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;
}

View File

@ -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
View File

@ -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;