Browse Source

Initial UI integration.

- Show own fingerprint in settings.
- Show notification in chat while peer identity is requested.
- Show lock icon on encrypted chat messages.
- Show button in chat menu to open dialog with fingerprint of remote peer.

Needs rebuilding of styles after merging.
pull/225/head
Joachim Bauch 11 years ago
parent
commit
72a5d60f9a
  1. 27
      src/styles/components/_chat.scss
  2. 2
      src/styles/global/_variables.scss
  3. 26
      static/js/controllers/chatroomcontroller.js
  4. 9
      static/js/controllers/uicontroller.js
  5. 5
      static/js/directives/buddylist.js
  6. 47
      static/js/directives/chat.js
  7. 10
      static/js/mediastream/api.js
  8. 7
      static/js/services/buddylist.js
  9. 73
      static/js/services/endtoendencryption.js
  10. 4
      static/partials/chatroom.html
  11. 9
      static/partials/settings.html

27
src/styles/components/_chat.scss

@ -220,6 +220,19 @@ @@ -220,6 +220,19 @@
}
}
.identityhint {
color: $chat-typing;
font-size: .8em;
height: 16px;
overflow: hidden;
padding: 0 6px;
white-space: nowrap;
@include breakpt($breakpoint-chat-small, max-height) {
display: none;
}
}
.inputbox {
position: relative;
@ -349,6 +362,15 @@ @@ -349,6 +362,15 @@
width: 12px;
}
&:after {
font-family: FontAwesome;
left: 0;
float: right;
text-align: center;
width: 12px;
margin-left: 1em;
}
&.unread:before {
color: $chat-msg-unread-icon-color;
content: $chat-msg-unread-icon;
@ -378,6 +400,11 @@ @@ -378,6 +400,11 @@
color: $chat-msg-read-icon-color;
content: $chat-msg-read-icon;
}
&.encrypted:after {
color: $chat-msg-encrypted-icon-color;
content: $chat-msg-encrypted-icon;
}
}
.buddyPicture {

2
src/styles/global/_variables.scss

@ -123,6 +123,7 @@ $chat-msg-sent-icon-color: #5882fa !default; @@ -123,6 +123,7 @@ $chat-msg-sent-icon-color: #5882fa !default;
$chat-msg-delivered-icon-color: #5882fa !default;
$chat-msg-received-icon-color: #84b819 !default;
$chat-msg-read-icon-color: $chat-msg-default-icon-color !default;
$chat-msg-encrypted-icon-color: $chat-msg-default-icon-color !default;
$chat-msg-unread-icon: '\f0eb' !default;
$chat-msg-sending-icon: '\f0ec' !default;
@ -130,6 +131,7 @@ $chat-msg-sent-icon: '\f003' !default; @@ -130,6 +131,7 @@ $chat-msg-sent-icon: '\f003' !default;
$chat-msg-delivered-icon: '\f019' !default;
$chat-msg-received-icon: '\f06e' !default;
$chat-msg-read-icon: '\f00c' !default;
$chat-msg-encrypted-icon: '\f023' !default;
$chat-msg-self-background: #fff !default;
$chat-msg-self-color: $font-color !default;

26
static/js/controllers/chatroomcontroller.js

@ -214,7 +214,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p @@ -214,7 +214,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p
}
$scope.display = function(s, nodes, extra_css, title, picture) {
$scope.display = function(s, nodes, extra_css, title, picture, encrypted) {
var container;
var element;
@ -235,6 +235,9 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p @@ -235,6 +235,9 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p
lastMessageContainer = $("<ul>").appendTo(container);
if ($.trim(s)) {
element = $("<li>").html(s);
if (encrypted) {
element.addClass("encrypted");
}
element.prepend('<div class="timestamp-space">');
element.appendTo(lastMessageContainer);
}
@ -267,7 +270,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p @@ -267,7 +270,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p
$scope.display(s, nodes);
});
$scope.append = function(s, nodes) {
$scope.append = function(s, nodes, encrypted) {
if (!lastMessageContainer) {
return;
@ -276,6 +279,9 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p @@ -276,6 +279,9 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p
var scroll = this.canScroll();
var li = $("<li>");
if (encrypted) {
li.addClass("encrypted");
}
li.html(s)
lastMessageContainer.append(li)
@ -325,7 +331,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p @@ -325,7 +331,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p
};
$scope.showmessage = function(from, timestamp, message, nodes) {
$scope.showmessage = function(from, timestamp, message, nodes, encrypted) {
var sessonid = $scope.$parent.$parent.id;
@ -354,7 +360,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p @@ -354,7 +360,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p
var strMessage = s.join(" ");
if (!is_new_message) {
var element = this.append(strMessage, nodes);
var element = this.append(strMessage, nodes, encrypted);
if (element) {
return element;
}
@ -375,7 +381,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p @@ -375,7 +381,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p
nodes = ts;
}
}
return $scope.display(strMessage, nodes, msg.extra_css, msg.title, msg.picture);
return $scope.display(strMessage, nodes, msg.extra_css, msg.title, msg.picture, encrypted);
};
@ -406,7 +412,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p @@ -406,7 +412,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p
$scope.focus();
});
$scope.$on("received", function(event, from, data) {
$scope.$on("received", function(event, from, data, encrypted) {
var sessionid = $scope.$parent.$parent.id;
var mid = data.Mid || null;
@ -482,7 +488,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p @@ -482,7 +488,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p
subscope.from = from;
fileInfo(subscope, function(clonedElement, scope) {
var text = fromself ? translation._("You share file:") : translation._("Incoming file:");
element = $scope.showmessage(from, timestamp, text, clonedElement);
element = $scope.showmessage(from, timestamp, text, clonedElement, encrypted);
});
noop = true;
}
@ -494,7 +500,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p @@ -494,7 +500,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p
subscope.from = from;
geoLocation(subscope, function(clonedElement, scope) {
var text = fromself ? translation._("You shared your location:") : translation._("Location received:");
element = $scope.showmessage(from, timestamp, text, clonedElement);
element = $scope.showmessage(from, timestamp, text, clonedElement, encrypted);
});
noop = true;
}
@ -527,7 +533,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p @@ -527,7 +533,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p
}
}
}
element = $scope.showmessage(from, timestamp, text, clonedElement);
element = $scope.showmessage(from, timestamp, text, clonedElement, encrypted);
});
noop = true;
}
@ -550,7 +556,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p @@ -550,7 +556,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p
message = safeMessage(data.Message);
}
// Show the beast.
element = $scope.showmessage(from, timestamp, message, nodes);
element = $scope.showmessage(from, timestamp, message, nodes, encrypted);
}
if (element && mid && !$scope.isgroupchat) {

9
static/js/controllers/uicontroller.js

@ -116,6 +116,7 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web @@ -116,6 +116,7 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web
$scope.id = $scope.myid = null;
$scope.userid = $scope.myuserid = null;
$scope.suserid = null;
$scope.fingerprint = null;
$scope.peer = null;
$scope.dialing = null;
$scope.conference = null;
@ -536,6 +537,14 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web @@ -536,6 +537,14 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web
}
});
appData.e.on("identity.own", function(event, identity) {
if (identity) {
$scope.fingerprint = identity.getFingerprint();
} else {
$scope.fingerprint = null;
}
});
// Start heartbeat timer.
$window.setInterval(function() {
mediaStream.api.heartbeat(5000, 11500)

5
static/js/directives/buddylist.js

@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
define(['underscore', 'text!partials/buddylist.html'], function(_, template) {
// buddyList
return ["buddyList", "api", "webrtc", "contacts", function(buddyList, api, webrtc, contacts) {
return ["buddyList", "api", "webrtc", "contacts", "appData", function(buddyList, api, webrtc, contacts, appData) {
//console.log("buddyList directive");
@ -125,6 +125,9 @@ define(['underscore', 'text!partials/buddylist.html'], function(_, template) { @@ -125,6 +125,9 @@ define(['underscore', 'text!partials/buddylist.html'], function(_, template) {
onContactUpdated(data);
});
appData.e.on("identity.received", function(event, peer, identity) {
buddylist.onIdentityReceived(peer, identity);
});
}];
var link = function(scope, iElement, iAttrs, controller) {

47
static/js/directives/chat.js

@ -22,7 +22,7 @@ @@ -22,7 +22,7 @@
"use strict";
define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatroom.html'], function($, _, templateChat, templateChatroom) {
return ["$compile", "safeDisplayName", "mediaStream", "safeApply", "desktopNotify", "translation", "playSound", "fileUpload", "randomGen", "buddyData", "appData", "$timeout", "geolocation", function($compile, safeDisplayName, mediaStream, safeApply, desktopNotify, translation, playSound, fileUpload, randomGen, buddyData, appData, $timeout, geolocation) {
return ["$compile", "safeDisplayName", "mediaStream", "safeApply", "alertify", "desktopNotify", "translation", "playSound", "fileUpload", "randomGen", "buddyData", "appData", "$timeout", "geolocation", function($compile, safeDisplayName, mediaStream, safeApply, alertify, desktopNotify, translation, playSound, fileUpload, randomGen, buddyData, appData, $timeout, geolocation) {
var displayName = safeDisplayName;
var groupChatId = "";
@ -57,7 +57,7 @@ define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatro @@ -57,7 +57,7 @@ define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatro
return res;
};
mediaStream.api.e.on("received.chat", function(event, id, from, data, p2p) {
mediaStream.api.e.on("received.chat", function(event, id, from, data, p2p, encrypted) {
//console.log("received", data, id, from);
@ -102,7 +102,7 @@ define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatro @@ -102,7 +102,7 @@ define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatro
}
safeApply(room);
room.$broadcast("received", from, data);
room.$broadcast("received", from, data, encrypted);
});
@ -130,6 +130,28 @@ define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatro @@ -130,6 +130,28 @@ define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatro
}
});
appData.e.on("identity.request", function(event, peer) {
var room = rooms[peer];
if (room) {
room.$apply(function(scope) {
scope.requestPeerIdentity = true;
});
}
});
appData.e.on("identity.received", function(event, peer, identity) {
var room = rooms[peer];
if (room) {
room.$apply(function(scope) {
scope.requestPeerIdentity = false;
if (identity) {
scope.fingerprint = identity.getFingerprint();
} else {
scope.fingerprint = null;
}
});
}
});
$scope.$parent.$on("startchat", function(event, id, options) {
//console.log("startchat requested", event, id);
@ -205,6 +227,10 @@ define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatro @@ -205,6 +227,10 @@ define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatro
subscope = controller.rooms[id] = scope.$new();
translation.inject(subscope);
subscope.id = id;
var bd = buddyData.get(id);
var identity = bd ? bd.identity : null;
subscope.requestPeerIdentity = false;
subscope.fingerprint = identity ? identity.getFingerprint() : null;
subscope.isgroupchat = !!settings.group;
subscope.index = index;
subscope.settings = settings;
@ -269,12 +295,13 @@ define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatro @@ -269,12 +295,13 @@ define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatro
send: function(type, data, origType, origData) {
// We also send to self, to display our own stuff.
if (!noloop) {
var encrypted = (type !== origType);
mediaStream.api.received({
Type: origData.Type,
Data: origData,
From: mediaStream.api.id,
To: peercall.id
});
}, encrypted);
}
return peercall.peerconnection.send(data);
}
@ -289,7 +316,7 @@ define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatro @@ -289,7 +316,7 @@ define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatro
});
}
_.delay(function() {
mediaStream.api.send2("sendChat", function(type, data) {
mediaStream.api.send2("sendChat", function(type, data, encrypted) {
if (!noloop) {
//console.log("looped to self", type, data);
mediaStream.api.received({
@ -297,7 +324,7 @@ define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatro @@ -297,7 +324,7 @@ define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatro
Data: data,
From: mediaStream.api.id,
To: to
});
}, encrypted);
}
})(to, message, status, mid);
}, 100);
@ -329,6 +356,14 @@ define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatro @@ -329,6 +356,14 @@ define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatro
console.error("Failed to receive geolocation", err);
});
};
subscope.showFingerprint = function() {
if (subscope.fingerprint) {
// TODO(fancycode): Show a nicer notification.
var msg = translation._("%1$s has an identity with the fingerprint %2$s.",
displayName(subscope.id), subscope.fingerprint);
alertify.dialog.notify(translation._("Fingerprint"), msg);
}
};
subscope.doClear = function() {
subscope.$broadcast("clear");
};

10
static/js/mediastream/api.js

@ -102,7 +102,9 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) { @@ -102,7 +102,9 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) {
}, this),
sendEncrypted: _.bind(function(type, data) {
if (cb) {
cb(type, data);
var to = data.To;
var encrypted = (to && this.endToEndEncryption);
cb(type, data, encrypted);
}
this.sendEncrypted(type, data);
}, this)
@ -160,7 +162,7 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) { @@ -160,7 +162,7 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) {
return _.bind(f, obj);
};
Api.prototype.received = function(d) {
Api.prototype.received = function(d, encrypted) {
// Store received timestamp.
var now = new Date().getTime();
@ -189,11 +191,13 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) { @@ -189,11 +191,13 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) {
return;
}
this.endToEndEncryption.decrypt(d.From, data, _.bind(function(decrypted) {
d.encrypted = true;
this.processReceived(d, decrypted.Type, decrypted[decrypted.Type]);
}, this));
return;
}
d.encrypted = !!encrypted;
this.processReceived(d, dataType, data);
}
@ -239,7 +243,7 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) { @@ -239,7 +243,7 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) {
break;
case "Chat":
//console.log("chat received", dataType, data);
this.e.triggerHandler("received.chat", [data.To, d.From, data.Chat, d.p2p]);
this.e.triggerHandler("received.chat", [data.To, d.From, data.Chat, d.p2p, d.encrypted]);
break;
case "Conference":
this.e.triggerHandler("received.conference", [data.Id, data.Conference, data.Type, d.To, d.From]);

7
static/js/services/buddylist.js

@ -647,6 +647,13 @@ define(['jquery', 'angular', 'underscore', 'modernizr', 'avltree', 'text!partial @@ -647,6 +647,13 @@ define(['jquery', 'angular', 'underscore', 'modernizr', 'avltree', 'text!partial
};
Buddylist.prototype.onIdentityReceived = function(id, identity) {
var scope = buddyData.get(id);
if (scope) {
scope.identity = identity;
}
};
Buddylist.prototype.click = function(buddyElement, target) {
var be = buddyElement[0];

73
static/js/services/endtoendencryption.js

@ -36,9 +36,11 @@ define([ @@ -36,9 +36,11 @@ define([
return [
"$window",
"$q",
"appData",
function(
$window,
$q
$q,
appData
) {
// Bitflags for the different components that need to be ready for
@ -231,6 +233,24 @@ define([ @@ -231,6 +233,24 @@ define([
return this.save("signed_pre_key_" + this.id + "_" + key.id, data);
};
var PeerIdentity = function(id, identity_key) {
this.id = id;
this.identity_key = identity_key;
};
PeerIdentity.prototype.getFingerprint = function() {
// TODO(jojo): Change this to be the SHA-1 hash of a three-byte key
// id and the public key.
// See https://github.com/WhisperSystems/TextSecure/blob/08ed90c5ece49c92e35c492afb4e60160983015a/src/org/thoughtcrime/securesms/crypto/PublicKey.java#L95
var fingerprint = ByteBuffer.wrap(this.identity_key).toHex();
var pos;
for (pos = fingerprint.length - 4; pos >= 4; pos -= 4) {
fingerprint = fingerprint.substr(0, pos) + "-" +
fingerprint.substr(pos);
}
return fingerprint.substr(2);
};
var EndToEndEncryption = function(api) {
this.e = $({});
this.api = api;
@ -292,36 +312,32 @@ define([ @@ -292,36 +312,32 @@ define([
}
};
EndToEndEncryption.prototype.getIdentityFingerprint = function() {
return this.formatIdentityFingerprint(this.identity_keypair.public);
};
EndToEndEncryption.prototype.formatIdentityFingerprint = function(public_key) {
// TODO(jojo): Change this to be the SHA-1 hash of a three-byte key
// id and the public key.
// See https://github.com/WhisperSystems/TextSecure/blob/08ed90c5ece49c92e35c492afb4e60160983015a/src/org/thoughtcrime/securesms/crypto/PublicKey.java#L95
var fingerprint = ByteBuffer.wrap(public_key).toHex();
var pos;
for (pos = fingerprint.length - 4; pos >= 4; pos -= 4) {
fingerprint = fingerprint.substr(0, pos) + "-" +
fingerprint.substr(pos);
}
return fingerprint.substr(2);
EndToEndEncryption.prototype.setOwnIdentity = function(public_key) {
var identity = new PeerIdentity(null, public_key);
this.own_identity = identity;
appData.e.triggerHandler("identity.own", [identity]);
};
EndToEndEncryption.prototype.storePeerIdentity = function(peer, public_key) {
var fingerprint = this.formatIdentityFingerprint(public_key);
var identity = new PeerIdentity(peer, public_key);
var fingerprint = identity.getFingerprint();
var existing = this.peer_identities[peer];
if (existing === fingerprint) {
return;
} else if (existing && existing !== fingerprint) {
console.warn("Fingerprint changed", {
"peer": peer,
"existing": existing,
"fingerprint": fingerprint
});
if (existing) {
if (existing.getFingerprint() === fingerprint) {
// No change.
return;
} else {
// Uh oh, remote peer has a new identity, this is something
// the user should know about!
appData.e.triggerHandler("identity.changed", [
peer,
existing,
identity
]);
}
}
this.peer_identities[peer] = fingerprint;
this.peer_identities[peer] = identity;
appData.e.triggerHandler("identity.received", [peer, identity]);
};
EndToEndEncryption.prototype.getReadyPromise = function() {
@ -360,6 +376,9 @@ define([ @@ -360,6 +376,9 @@ define([
var deferred = $q.defer();
var doLoadData = _.bind(function() {
this.identity_keypair = this.store.loadKeypair();
if (this.identity_keypair) {
this.setOwnIdentity(this.identity_keypair.public);
}
this.registration_id = this.store.loadRegistrationId();
this.last_resort_pre_key =
this.store.loadSignedPreKey(LAST_RESORT_PRE_KEY_ID);
@ -390,6 +409,7 @@ define([ @@ -390,6 +409,7 @@ define([
.then(_.bind(function(keypair) {
if (this.identity_keypair === null) {
this.identity_keypair = keypair;
this.setOwnIdentity(this.identity_keypair.public);
this.store.saveKeypair(keypair);
}
deferred.resolve(this.identity_keypair);
@ -593,6 +613,7 @@ define([ @@ -593,6 +613,7 @@ define([
"message": message,
"callback": callback
});
appData.e.triggerHandler("identity.request", [peer]);
this.apiSend("EncryptionRequestKeyBundle", {"To": peer});
return;
}

4
static/partials/chatroom.html

@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
<button ng-if="!isgroupchat" class="btn btn-sm btn-default" title="{{_('Start video call')}}" ng-click="doCall()"><i class="fa fa-phone fa-fw"></i></button>
<button class="btn btn-sm btn-default btn-fileupload" title="{{_('Upload files')}}"><i class="fa fa-upload fa-fw"></i></button>
<button class="btn btn-sm btn-default btn-locationshare" title="{{_('Share my location')}}" ng-click="shareGeolocation()"><i class="fa fa-location-arrow fa-fw"></i></button>
<button ng-if="!isgroupchat" ng-show="fingerprint" class="btn btn-sm btn-default btn-fingerprint" title="{{_('Show peer fingerprint')}}" ng-click="showFingerprint()"><i class="fa fa-lock fa-fw"></i></button>
</div>
<div class="btn-group pull-right">
<button class="btn btn-sm btn-default" title="{{_('Clear chat')}}" ng-click="doClear()"><i class="fa fa-eraser fa-fw"></i></button>
@ -19,6 +20,9 @@ @@ -19,6 +20,9 @@
<span ng-switch-when="start"><i class="fa fa-pencil"></i> {{id|displayName}} {{_('is typing...')}}</span>
<span ng-switch-when="stop"><i class="fa fa-pencil"></i> {{id|displayName}} {{_('has stopped typing...')}}</span>
</div>
<div class="identityhint" ng-show="requestPeerIdentity">
<i class="fa fa-lock"></i> {{_('Preparing encrypted session...')}}
</div>
<div class="inputbox">
<div>
<textarea class="input nicescroll form-control" maxlength="{{maxMessageSize}}" ng-disabled="!(enabled)" on-enter="submit()" ng-model="input" placeholder="{{_('Type here to chat...')}}"/>

9
static/partials/settings.html

@ -40,6 +40,13 @@ @@ -40,6 +40,13 @@
<p class="help-block">{{_('Your picture, name and status message identify yourself in calls, chats and rooms.')}}</p>
</div>
</div>
<div class="form-group profile-fingerprint" ng-show="fingerprint">
<label class="col-xs-4 control-label">{{_('Fingerprint')}}</label>
<div class="col-xs-8">
<pre class="help-block">{{fingerprint}}</pre>
<p class="help-block">{{_('You can compare the fingerprint with other peers to confirm your identity.')}}</p>
</div>
</div>
<div ng-if="(withUsers && withUsersRegistration) || userid">
<div class="form-group profile-yourid">
<label class="col-xs-4 control-label">{{_('Your ID')}}</label>
@ -314,4 +321,4 @@ @@ -314,4 +321,4 @@
</fieldset>
</div>
</div>
</div>
</div>

Loading…
Cancel
Save