mirror of
https://github.com/oxen-io/session-desktop.git
synced 2023-12-14 02:12:57 +01:00
commit
ac80ef0d4e
13 changed files with 315 additions and 38 deletions
|
@ -166,6 +166,11 @@
|
|||
"description":
|
||||
"Only available on development modes, menu option to open up the standalone device setup sequence"
|
||||
},
|
||||
"connectingLoad": {
|
||||
"message": "Connecting...",
|
||||
"description":
|
||||
"Message shown on the as a loading screen while we are connecting to something"
|
||||
},
|
||||
"loading": {
|
||||
"message": "Loading...",
|
||||
"description":
|
||||
|
@ -1940,6 +1945,16 @@
|
|||
"message": "Show QR code",
|
||||
"description": "Button action that the user can click to view their QR code"
|
||||
},
|
||||
"showAddServer": {
|
||||
"message": "Add public server",
|
||||
"description":
|
||||
"Button action that the user can click to connect to a new public server"
|
||||
},
|
||||
"addServerDialogTitle": {
|
||||
"message": "Connect to new public server",
|
||||
"description":
|
||||
"Title for the dialog box used to connect to a new public server"
|
||||
},
|
||||
|
||||
"seedViewTitle": {
|
||||
"message":
|
||||
|
@ -1991,6 +2006,15 @@
|
|||
"passwordsDoNotMatch": {
|
||||
"message": "Passwords do not match"
|
||||
},
|
||||
"publicChatExists": {
|
||||
"message": "You are already connected to this public channel"
|
||||
},
|
||||
"connectToServerFail": {
|
||||
"message": "Failed to connect to server. Check URL"
|
||||
},
|
||||
"connectToServerSuccess": {
|
||||
"message": "Successfully connected to new public chat server"
|
||||
},
|
||||
"setPasswordFail": {
|
||||
"message": "Failed to set password"
|
||||
},
|
||||
|
|
|
@ -282,6 +282,29 @@
|
|||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='connecting-to-server-template'>
|
||||
<div class="content">
|
||||
{{ #title }}
|
||||
<h4>{{ title }}</h4>
|
||||
{{ /title }}
|
||||
<div class='buttons'>
|
||||
<button class='cancel' tabindex='2'>{{ cancel }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='add-server-template'>
|
||||
<div class="content">
|
||||
{{ #title }}
|
||||
<h4>{{ title }}</h4>
|
||||
{{ /title }}
|
||||
<input type='text' id='server-url' placeholder='Server Url' autofocus>
|
||||
<div class='error'></div>
|
||||
<div class='buttons'>
|
||||
<button class='cancel' tabindex='2'>{{ cancel }}</button>
|
||||
<button class='ok' tabindex='1'>{{ ok }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<script type='text/x-tmpl-mustache' id='qr-code-template'>
|
||||
<div class="content">
|
||||
<div id="qr">
|
||||
|
@ -692,6 +715,8 @@
|
|||
<script type='text/javascript' src='js/views/password_dialog_view.js'></script>
|
||||
<script type='text/javascript' src='js/views/seed_dialog_view.js'></script>
|
||||
<script type='text/javascript' src='js/views/qr_dialog_view.js'></script>
|
||||
<script type='text/javascript' src='js/views/connecting_to_server_dialog_view.js'></script>
|
||||
<script type='text/javascript' src='js/views/add_server_dialog_view.js'></script>
|
||||
<script type='text/javascript' src='js/views/beta_release_disclaimer_view.js'></script>
|
||||
<script type='text/javascript' src='js/views/identicon_svg_view.js'></script>
|
||||
<script type='text/javascript' src='js/views/install_view.js'></script>
|
||||
|
|
|
@ -233,6 +233,9 @@
|
|||
window.lokiPublicChatAPI = new window.LokiPublicChatAPI(ourKey);
|
||||
// singleton to interface the File server
|
||||
window.lokiFileServerAPI = new window.LokiFileServerAPI(ourKey);
|
||||
await window.lokiFileServerAPI.establishConnection(
|
||||
window.getDefaultFileServer()
|
||||
);
|
||||
// are there limits on tracking, is this unneeded?
|
||||
// window.mixpanel.track("Desktop boot");
|
||||
window.lokiP2pAPI = new window.LokiP2pAPI(ourKey);
|
||||
|
@ -749,6 +752,12 @@
|
|||
}
|
||||
});
|
||||
|
||||
Whisper.events.on('showAddServerDialog', async options => {
|
||||
if (appView) {
|
||||
appView.showAddServerDialog(options);
|
||||
}
|
||||
});
|
||||
|
||||
Whisper.events.on('showQRDialog', async () => {
|
||||
if (appView) {
|
||||
const ourNumber = textsecure.storage.user.getNumber();
|
||||
|
|
|
@ -1421,7 +1421,7 @@
|
|||
options.messageType = message.get('type');
|
||||
options.isPublic = this.isPublic();
|
||||
if (options.isPublic) {
|
||||
options.publicSendData = this.getPublicSendData();
|
||||
options.publicSendData = await this.getPublicSendData();
|
||||
}
|
||||
|
||||
const groupNumbers = this.getRecipients();
|
||||
|
@ -2122,6 +2122,21 @@
|
|||
};
|
||||
},
|
||||
// maybe "Backend" instead of "Source"?
|
||||
async setPublicSource(newServer, newChannelId) {
|
||||
if (!this.isPublic()) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
this.get('server') !== newServer ||
|
||||
this.get('channelId') !== newChannelId
|
||||
) {
|
||||
this.set({ server: newServer });
|
||||
this.set({ channelId: newChannelId });
|
||||
await window.Signal.Data.updateConversation(this.id, this.attributes, {
|
||||
Conversation: Whisper.Conversation,
|
||||
});
|
||||
}
|
||||
},
|
||||
getPublicSource() {
|
||||
if (!this.isPublic()) {
|
||||
return null;
|
||||
|
@ -2132,10 +2147,18 @@
|
|||
conversationId: this.get('id'),
|
||||
};
|
||||
},
|
||||
getPublicSendData() {
|
||||
const serverAPI = lokiPublicChatAPI.findOrCreateServer(
|
||||
async getPublicSendData() {
|
||||
const serverAPI = await lokiPublicChatAPI.findOrCreateServer(
|
||||
this.get('server')
|
||||
);
|
||||
if (!serverAPI) {
|
||||
window.log.warn(
|
||||
`Failed to get serverAPI (${this.get('server')}) for conversation (${
|
||||
this.id
|
||||
})`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
const channelAPI = serverAPI.findOrCreateChannel(
|
||||
this.get('channelId'),
|
||||
this.id
|
||||
|
@ -2397,6 +2420,9 @@
|
|||
|
||||
async deletePublicMessage(message) {
|
||||
const channelAPI = this.getPublicSendData();
|
||||
if (!channelAPI) {
|
||||
return false;
|
||||
}
|
||||
const success = await channelAPI.deleteMessage(message.getServerId());
|
||||
if (success) {
|
||||
this.removeMessage(message.id);
|
||||
|
|
|
@ -28,21 +28,32 @@ class LokiAppDotNetAPI extends EventEmitter {
|
|||
}
|
||||
|
||||
// server getter/factory
|
||||
findOrCreateServer(serverUrl) {
|
||||
async findOrCreateServer(serverUrl) {
|
||||
let thisServer = this.servers.find(
|
||||
server => server.baseServerUrl === serverUrl
|
||||
);
|
||||
if (!thisServer) {
|
||||
log.info(`LokiAppDotNetAPI creating ${serverUrl}`);
|
||||
thisServer = new LokiAppDotNetServerAPI(this, serverUrl);
|
||||
const gotToken = await thisServer.getOrRefreshServerToken();
|
||||
if (!gotToken) {
|
||||
log.warn(`Invalid server ${serverUrl}`);
|
||||
return null;
|
||||
}
|
||||
log.info(`set token ${thisServer.token}`);
|
||||
|
||||
this.servers.push(thisServer);
|
||||
}
|
||||
return thisServer;
|
||||
}
|
||||
|
||||
// channel getter/factory
|
||||
findOrCreateChannel(serverUrl, channelId, conversationId) {
|
||||
const server = this.findOrCreateServer(serverUrl);
|
||||
async findOrCreateChannel(serverUrl, channelId, conversationId) {
|
||||
const server = await this.findOrCreateServer(serverUrl);
|
||||
if (!server) {
|
||||
log.error(`Failed to create server for: ${serverUrl}`);
|
||||
return null;
|
||||
}
|
||||
return server.findOrCreateChannel(channelId, conversationId);
|
||||
}
|
||||
|
||||
|
@ -82,11 +93,6 @@ class LokiAppDotNetServerAPI {
|
|||
this.channels = [];
|
||||
this.tokenPromise = null;
|
||||
this.baseServerUrl = url;
|
||||
const ref = this;
|
||||
(async function justToEnableAsyncToGetToken() {
|
||||
ref.token = await ref.getOrRefreshServerToken();
|
||||
log.info(`set token ${ref.token}`);
|
||||
})();
|
||||
}
|
||||
|
||||
// channel getter/factory
|
||||
|
@ -174,14 +180,14 @@ class LokiAppDotNetServerAPI {
|
|||
|
||||
// request an token from the server
|
||||
async requestToken() {
|
||||
const url = new URL(`${this.baseServerUrl}/loki/v1/get_challenge`);
|
||||
const params = {
|
||||
pubKey: this.chatAPI.ourKey,
|
||||
};
|
||||
url.search = new URLSearchParams(params);
|
||||
|
||||
let res;
|
||||
try {
|
||||
const url = new URL(`${this.baseServerUrl}/loki/v1/get_challenge`);
|
||||
const params = {
|
||||
pubKey: this.chatAPI.ourKey,
|
||||
};
|
||||
url.search = new URLSearchParams(params);
|
||||
|
||||
res = await nodeFetch(url);
|
||||
} catch (e) {
|
||||
return null;
|
||||
|
@ -232,15 +238,12 @@ class LokiAppDotNetServerAPI {
|
|||
url.search = new URLSearchParams(params);
|
||||
}
|
||||
let result;
|
||||
let { token } = this;
|
||||
const token = await this.getOrRefreshServerToken();
|
||||
if (!token) {
|
||||
token = await this.getOrRefreshServerToken();
|
||||
if (!token) {
|
||||
log.error('NO TOKEN');
|
||||
return {
|
||||
err: 'noToken',
|
||||
};
|
||||
}
|
||||
log.error('NO TOKEN');
|
||||
return {
|
||||
err: 'noToken',
|
||||
};
|
||||
}
|
||||
try {
|
||||
const fetchOptions = {};
|
||||
|
|
|
@ -1,19 +1,23 @@
|
|||
/* global log */
|
||||
|
||||
const LokiAppDotNetAPI = require('./loki_app_dot_net_api');
|
||||
|
||||
/* global log */
|
||||
|
||||
const DEVICE_MAPPING_ANNOTATION_KEY = 'network.loki.messenger.devicemapping';
|
||||
|
||||
// returns the LokiFileServerAPI constructor with the serverUrl already consumed
|
||||
function LokiFileServerAPIWrapper(serverUrl) {
|
||||
return LokiFileServerAPI.bind(null, serverUrl);
|
||||
}
|
||||
|
||||
class LokiFileServerAPI {
|
||||
constructor(serverUrl, ourKey) {
|
||||
constructor(ourKey) {
|
||||
this.ourKey = ourKey;
|
||||
this._adnApi = new LokiAppDotNetAPI(ourKey);
|
||||
this._server = this._adnApi.findOrCreateServer(serverUrl);
|
||||
}
|
||||
|
||||
async establishConnection(serverUrl) {
|
||||
this._server = await this._adnApi.findOrCreateServer(serverUrl);
|
||||
// TODO: Handle this failure gracefully
|
||||
if (!this._server) {
|
||||
log.error('Failed to establish connection to file server');
|
||||
}
|
||||
}
|
||||
|
||||
async getUserDeviceMapping(pubKey) {
|
||||
|
@ -59,4 +63,4 @@ class LokiFileServerAPI {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = LokiFileServerAPIWrapper;
|
||||
module.exports = LokiFileServerAPI;
|
||||
|
|
|
@ -88,6 +88,11 @@ class LokiMessageAPI {
|
|||
};
|
||||
|
||||
if (isPublic) {
|
||||
if (!publicSendData) {
|
||||
throw new window.textsecure.PublicChatError(
|
||||
'Missing public send data for public chat message'
|
||||
);
|
||||
}
|
||||
const res = await publicSendData.sendMessage(
|
||||
data.body,
|
||||
data.quote,
|
||||
|
|
88
js/views/add_server_dialog_view.js
Normal file
88
js/views/add_server_dialog_view.js
Normal file
|
@ -0,0 +1,88 @@
|
|||
/* global Whisper, i18n, _ */
|
||||
|
||||
// eslint-disable-next-line func-names
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
window.Whisper = window.Whisper || {};
|
||||
|
||||
Whisper.AddServerDialogView = Whisper.View.extend({
|
||||
templateName: 'add-server-template',
|
||||
className: 'loki-dialog add-server modal',
|
||||
initialize(options = {}) {
|
||||
this.title = i18n('addServerDialogTitle');
|
||||
this.okText = options.okText || i18n('ok');
|
||||
this.cancelText = options.cancelText || i18n('cancel');
|
||||
this.$('input').focus();
|
||||
this.render();
|
||||
},
|
||||
events: {
|
||||
keyup: 'onKeyup',
|
||||
'click .ok': 'confirm',
|
||||
'click .cancel': 'close',
|
||||
},
|
||||
render_attributes() {
|
||||
return {
|
||||
title: this.title,
|
||||
ok: this.okText,
|
||||
cancel: this.cancelText,
|
||||
};
|
||||
},
|
||||
confirm() {
|
||||
// Remove error if there is one
|
||||
this.showError(null);
|
||||
const serverUrl = this.$('#server-url').val().toLowerCase();
|
||||
// TODO: Make this not hard coded
|
||||
const channelId = 1;
|
||||
const dialog = new Whisper.ConnectingToServerDialogView({
|
||||
serverUrl,
|
||||
channelId,
|
||||
});
|
||||
const dialogDelayTimer = setTimeout(() => {
|
||||
this.el.append(dialog.el);
|
||||
}, 200);
|
||||
dialog.once('connectionResult', result => {
|
||||
clearTimeout(dialogDelayTimer);
|
||||
if (result.cancelled) {
|
||||
this.showError(null);
|
||||
return;
|
||||
}
|
||||
if (result.errorCode) {
|
||||
this.showError(result.errorCode);
|
||||
return;
|
||||
}
|
||||
window.Whisper.events.trigger('showToast', {
|
||||
message: i18n('connectToServerSuccess'),
|
||||
});
|
||||
this.close();
|
||||
});
|
||||
dialog.trigger('attemptConnection');
|
||||
},
|
||||
close() {
|
||||
this.remove();
|
||||
},
|
||||
showError(message) {
|
||||
if (_.isEmpty(message)) {
|
||||
this.$('.error').text('');
|
||||
this.$('.error').hide();
|
||||
} else {
|
||||
this.$('.error').text(`Error: ${message}`);
|
||||
this.$('.error').show();
|
||||
}
|
||||
this.$('input').focus();
|
||||
},
|
||||
onKeyup(event) {
|
||||
switch (event.key) {
|
||||
case 'Enter':
|
||||
this.confirm();
|
||||
break;
|
||||
case 'Escape':
|
||||
case 'Esc':
|
||||
this.close();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
});
|
||||
})();
|
|
@ -200,5 +200,9 @@
|
|||
const dialog = new Whisper.QRDialogView({ string });
|
||||
this.el.append(dialog.el);
|
||||
},
|
||||
showAddServerDialog() {
|
||||
const dialog = new Whisper.AddServerDialogView();
|
||||
this.el.append(dialog.el);
|
||||
},
|
||||
});
|
||||
})();
|
||||
|
|
83
js/views/connecting_to_server_dialog_view.js
Normal file
83
js/views/connecting_to_server_dialog_view.js
Normal file
|
@ -0,0 +1,83 @@
|
|||
/* global Whisper, i18n, lokiPublicChatAPI, ConversationController, friends */
|
||||
|
||||
// eslint-disable-next-line func-names
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
window.Whisper = window.Whisper || {};
|
||||
|
||||
Whisper.ConnectingToServerDialogView = Whisper.View.extend({
|
||||
templateName: 'connecting-to-server-template',
|
||||
className: 'loki-dialog connecting-to-server modal',
|
||||
initialize(options = {}) {
|
||||
this.title = i18n('connectingLoad');
|
||||
this.cancelText = options.cancelText || i18n('cancel');
|
||||
this.serverUrl = options.serverUrl;
|
||||
this.channelId = options.channelId;
|
||||
this.once('attemptConnection', () =>
|
||||
this.attemptConnection(options.serverUrl, options.channelId)
|
||||
);
|
||||
this.render();
|
||||
},
|
||||
events: {
|
||||
keyup: 'onKeyup',
|
||||
'click .cancel': 'close',
|
||||
},
|
||||
async attemptConnection(serverUrl, channelId) {
|
||||
const rawServerUrl = serverUrl
|
||||
.replace(/^https?:\/\//i, '')
|
||||
.replace(/[/\\]+$/i, '');
|
||||
const sslServerUrl = `https://${rawServerUrl}`;
|
||||
const conversationId = `publicChat:${channelId}@${rawServerUrl}`;
|
||||
|
||||
const conversationExists = ConversationController.get(conversationId);
|
||||
if (conversationExists) {
|
||||
// We are already a member of this public chat
|
||||
return this.resolveWith({ errorCode: i18n('publicChatExists') });
|
||||
}
|
||||
|
||||
const serverAPI = await lokiPublicChatAPI.findOrCreateServer(
|
||||
sslServerUrl
|
||||
);
|
||||
if (!serverAPI) {
|
||||
// Url incorrect or server not compatible
|
||||
return this.resolveWith({ errorCode: i18n('connectToServerFail') });
|
||||
}
|
||||
|
||||
const conversation = await ConversationController.getOrCreateAndWait(
|
||||
conversationId,
|
||||
'group'
|
||||
);
|
||||
serverAPI.findOrCreateChannel(channelId, conversationId);
|
||||
await conversation.setPublicSource(sslServerUrl, channelId);
|
||||
await conversation.setFriendRequestStatus(
|
||||
friends.friendRequestStatusEnum.friends
|
||||
);
|
||||
return this.resolveWith({ conversation });
|
||||
},
|
||||
resolveWith(result) {
|
||||
this.trigger('connectionResult', result);
|
||||
this.remove();
|
||||
},
|
||||
render_attributes() {
|
||||
return {
|
||||
title: this.title,
|
||||
cancel: this.cancelText,
|
||||
};
|
||||
},
|
||||
close() {
|
||||
this.trigger('connectionResult', { cancelled: true });
|
||||
this.remove();
|
||||
},
|
||||
onKeyup(event) {
|
||||
switch (event.key) {
|
||||
case 'Escape':
|
||||
case 'Esc':
|
||||
this.close();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
});
|
||||
})();
|
|
@ -41,6 +41,7 @@ window.isBehindProxy = () => Boolean(config.proxyUrl);
|
|||
window.JobQueue = JobQueue;
|
||||
window.getStoragePubKey = key =>
|
||||
window.isDev() ? key.substring(0, key.length - 2) : key;
|
||||
window.getDefaultFileServer = () => config.defaultFileServer;
|
||||
|
||||
window.isBeforeVersion = (toCheck, baseVersion) => {
|
||||
try {
|
||||
|
@ -328,10 +329,7 @@ window.LokiMessageAPI = require('./js/modules/loki_message_api');
|
|||
|
||||
window.LokiPublicChatAPI = require('./js/modules/loki_public_chat_api');
|
||||
|
||||
const LokiFileServerAPIWrapper = require('./js/modules/loki_file_server_api');
|
||||
|
||||
// bind first argument as we have it here already
|
||||
window.LokiFileServerAPI = LokiFileServerAPIWrapper(config.defaultFileServer);
|
||||
window.LokiFileServerAPI = require('./js/modules/loki_file_server_api');
|
||||
|
||||
window.LokiRssAPI = require('./js/modules/loki_rss_api');
|
||||
|
||||
|
|
|
@ -567,7 +567,8 @@
|
|||
<script type='text/javascript' src='../js/views/nickname_dialog_view.js' data-cover></script>
|
||||
<script type='text/javascript' src='../js/views/password_dialog_view.js' data-cover></script>
|
||||
<script type='text/javascript' src='../js/views/seed_dialog_view.js' data-cover></script>
|
||||
<script type='text/javascript' src='js/views/qr_dialog_view.js'></script>
|
||||
<script type='text/javascript' src='../js/views/qr_dialog_view.js'></script>
|
||||
<script type='text/javascript' src='../js/views/add_server_dialog_view.js'></script>
|
||||
<script type='text/javascript' src='../js/views/identicon_svg_view.js' data-cover></script>
|
||||
<script type='text/javascript' src='../js/views/last_seen_indicator_view.js' data-cover></script>
|
||||
<script type='text/javascript' src='../js/views/scroll_down_button_view.js' data-cover></script>
|
||||
|
|
|
@ -334,6 +334,13 @@ export class MainHeader extends React.Component<Props, any> {
|
|||
trigger('showQRDialog');
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'showAddServer',
|
||||
name: i18n('showAddServer'),
|
||||
onClick: () => {
|
||||
trigger('showAddServerDialog');
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const passItem = (type: string) => ({
|
||||
|
|
Loading…
Reference in a new issue