+
+
diff --git a/js/background.js b/js/background.js
index fc0c6d215..ebc613361 100644
--- a/js/background.js
+++ b/js/background.js
@@ -580,7 +580,6 @@
nickname: displayName,
onOk: async (newName) => {
await storage.setProfileName(newName);
- appView.inboxView.trigger('updateProfile');
// Update the conversation if we have it
const conversation = ConversationController.get(ourNumber);
diff --git a/js/models/conversations.js b/js/models/conversations.js
index 946dd04c5..6fe724ead 100644
--- a/js/models/conversations.js
+++ b/js/models/conversations.js
@@ -869,6 +869,11 @@
this.set({ id: this.id });
return 'Invalid ID Length';
}
+
+ // Check if the id is prefixed by 05
+ if (!/^05/.test(this.id)) {
+ return 'Invalid Pubkey Format';
+ }
}
return null;
@@ -1616,12 +1621,12 @@
await this.updateProfile();
},
async setProfile(profile) {
- if (_.isEqual(this.get('profile'), profile)) return;
-
- this.set({ profile });
- await window.Signal.Data.updateConversation(this.id, this.attributes, {
- Conversation: Whisper.Conversation,
- });
+ if (!_.isEqual(this.get('profile'), profile)) {
+ this.set({ profile });
+ await window.Signal.Data.updateConversation(this.id, this.attributes, {
+ Conversation: Whisper.Conversation,
+ });
+ }
await this.updateProfile();
},
diff --git a/js/modules/signal.js b/js/modules/signal.js
index 4ed80eba7..b7755f25b 100644
--- a/js/modules/signal.js
+++ b/js/modules/signal.js
@@ -43,7 +43,6 @@ const {
MediaGallery,
} = require('../../ts/components/conversation/media-gallery/MediaGallery');
const { MainHeader } = require('../../ts/components/MainHeader');
-const { IdentityKeyHeader } = require('../../ts/components/IdentityKeyHeader');
const { Message } = require('../../ts/components/conversation/Message');
const { MessageBody } = require('../../ts/components/conversation/MessageBody');
const {
@@ -185,7 +184,6 @@ exports.setup = (options = {}) => {
Lightbox,
LightboxGallery,
MainHeader,
- IdentityKeyHeader,
MediaGallery,
Message,
MessageBody,
diff --git a/js/views/conversation_search_view.js b/js/views/conversation_search_view.js
index 9f9b33505..eb1d3a398 100644
--- a/js/views/conversation_search_view.js
+++ b/js/views/conversation_search_view.js
@@ -73,7 +73,7 @@
// Update the contact model
this.new_contact_view.model.set('id', query);
- this.new_contact_view.render().$el.show();
+ this.new_contact_view.render().$el.hide();
this.new_contact_view.validate();
this.hideHints();
@@ -89,10 +89,9 @@
// This will allow us to show the last message when searching
this.typeahead_view.collection.forEach(c => c.updateLastMessage());
- // Check if the query is in the model list
- // If it is then hide the new contact view
- const modelExists = this.typeahead_view.collection.find(item => item.get('id') === query);
- if (modelExists) this.new_contact_view.$el.hide();
+ // Show the new contact view if we already have results
+ if (this.typeahead_view.collection.length === 0)
+ this.new_contact_view.$el.show();
})
);
/* eslint-enable more/no-then */
diff --git a/js/views/inbox_view.js b/js/views/inbox_view.js
index 74980bae6..643cd135e 100644
--- a/js/views/inbox_view.js
+++ b/js/views/inbox_view.js
@@ -5,9 +5,7 @@
/* global i18n: false */
/* global Whisper: false */
/* global textsecure: false */
-/* global Signal: false */
-/* global StringView: false */
-/* global storage: false */
+/* global clipboard: false */
// eslint-disable-next-line func-names
(function() {
@@ -104,26 +102,10 @@
// eslint-disable-next-line no-new
new Whisper.FontSizeView({ el: this.$el });
- const ourNumber = textsecure.storage.user.getNumber();
- const me = ConversationController.getOrCreate(ourNumber, 'private');
- this.mainHeaderView = new Whisper.ReactWrapperView({
- className: 'main-header-wrapper',
- Component: Signal.Components.MainHeader,
- props: me.format(),
+ this.mainHeaderView = new Whisper.MainHeaderView({
+ el: this.$('.main-header-placeholder'),
+ items: this.getMainHeaderItems(),
});
- const update = () => this.mainHeaderView.update(me.format());
- this.listenTo(me, 'change', update);
- this.$('.main-header-placeholder').append(this.mainHeaderView.el);
-
- this.identityKeyView = new Whisper.ReactWrapperView({
- className: 'identity-key-wrapper',
- Component: Signal.Components.IdentityKeyHeader,
- props: this._getIdentityKeyViewProps(),
- });
- this.on('updateProfile', () => {
- this.identityKeyView.update(this._getIdentityKeyViewProps());
- })
- this.$('.identity-key-placeholder').append(this.identityKeyView.el);
this.conversation_stack = new Whisper.ConversationStack({
el: this.$('.conversation-stack'),
@@ -221,20 +203,6 @@
this.$el.addClass('expired');
}
},
- _getIdentityKeyViewProps() {
- const identityKey = textsecure.storage.get('identityKey').pubKey;
- const pubKey = StringView.arrayBufferToHex(identityKey);
- const profile = storage.getLocalProfile();
- const name = profile && profile.name && profile.name.displayName;
-
- return {
- identityKey: pubKey,
- name,
- onEditProfile: async () => {
- window.Whisper.events.trigger('onEditProfile');
- },
- }
- },
render_attributes() {
return {
welcomeToSignal: i18n('welcomeToSignal'),
@@ -357,6 +325,30 @@
onClick(e) {
this.closeRecording(e);
},
+ getMainHeaderItems() {
+ return [
+ this._mainHeaderItem('copyPublicKey', () => {
+ const ourNumber = textsecure.storage.user.getNumber();
+ clipboard.writeText(ourNumber);
+
+ const toast = new Whisper.MessageToastView({
+ message: i18n('copiedPublicKey'),
+ });
+ toast.$el.appendTo(this.$('.gutter'));
+ toast.render();
+ }),
+ this._mainHeaderItem('editDisplayName', () => {
+ window.Whisper.events.trigger('onEditProfile');
+ }),
+ ];
+ },
+ _mainHeaderItem(textKey, onClick) {
+ return {
+ id: textKey,
+ text: i18n(textKey),
+ onClick,
+ };
+ },
});
Whisper.ExpiredAlertBanner = Whisper.View.extend({
diff --git a/js/views/main_header_view.js b/js/views/main_header_view.js
new file mode 100644
index 000000000..6366f914d
--- /dev/null
+++ b/js/views/main_header_view.js
@@ -0,0 +1,57 @@
+/* global Whisper, textsecure, ConversationController, Signal, i18n */
+
+// eslint-disable-next-line func-names
+(function() {
+ 'use strict';
+
+ window.Whisper = window.Whisper || {};
+
+ Whisper.MainHeaderView = Whisper.View.extend({
+ templateName: 'main-header-placeholder',
+ events: {
+ 'click .main-header-title-wrapper': 'onClick',
+ 'click .edit-name': 'onEditProfile',
+ 'click .copy-key': 'onCopyKey',
+ },
+ initialize(options) {
+ this.items = options.items || [];
+
+ this.ourNumber = textsecure.storage.user.getNumber();
+ const me = ConversationController.getOrCreate(this.ourNumber, 'private');
+
+ this.mainHeaderView = new Whisper.ReactWrapperView({
+ className: 'main-header-wrapper',
+ Component: Signal.Components.MainHeader,
+ props: me.format(),
+ });
+ const update = () => this.mainHeaderView.update(me.format());
+ this.listenTo(me, 'change', update);
+
+ this.render();
+ this.$('.main-header-title-wrapper').prepend(this.mainHeaderView.el);
+
+ this.$toggle = this.$('.main-header-content-toggle');
+ this.$content = this.$('.main-header-content-wrapper');
+ this.$content.hide();
+
+ this.registerCallbacks();
+ },
+ registerCallbacks() {
+ this.items.forEach(item => {
+ if (item.onClick) {
+ this.$(`#${item.id}`).click(item.onClick);
+ }
+ })
+ },
+ render_attributes() {
+ return {
+ items: this.items,
+ };
+ },
+ onClick() {
+ // Toggle section visibility
+ this.$content.slideToggle('fast');
+ this.$toggle.toggleClass('main-header-content-toggle-visible');
+ },
+ });
+ })();
diff --git a/js/views/toast_view.js b/js/views/toast_view.js
index ae90feb25..599e65370 100644
--- a/js/views/toast_view.js
+++ b/js/views/toast_view.js
@@ -28,4 +28,13 @@
setTimeout(this.close.bind(this), 2000);
},
});
+
+ Whisper.MessageToastView = Whisper.ToastView.extend({
+ initialize(options) {
+ this.message = options.message || '-';
+ },
+ render_attributes() {
+ return { toastMessage: this.message };
+ },
+ })
})();
diff --git a/preload.js b/preload.js
index f868614a2..9c5ea103e 100644
--- a/preload.js
+++ b/preload.js
@@ -7,6 +7,7 @@ const semver = require('semver');
const { deferredToPromise } = require('./js/modules/deferred_to_promise');
const { app } = electron.remote;
+const { clipboard } = electron;
window.PROTO_ROOT = 'protos';
const config = require('url').parse(window.location.toString(), true).query;
@@ -277,6 +278,8 @@ window.React = require('react');
window.ReactDOM = require('react-dom');
window.moment = require('moment');
+window.clipboard = clipboard;
+
const Signal = require('./js/modules/signal');
const i18n = require('./js/modules/i18n');
const Attachments = require('./app/attachments');
diff --git a/stylesheets/_index.scss b/stylesheets/_index.scss
index 711ee8152..344eab95b 100644
--- a/stylesheets/_index.scss
+++ b/stylesheets/_index.scss
@@ -33,6 +33,7 @@
flex-direction: column;
float: left;
width: 300px;
+ position: relative;
.tool-bar {
margin-top: 8px;
@@ -53,6 +54,10 @@
}
}
+ .toast {
+ bottom: 78px;
+ }
+
.content {
overflow-y: auto;
max-height: calc(100% - 88px);
@@ -142,51 +147,9 @@
}
}
-.identity-key-wrapper {
- background-color: $color-black-008-no-tranparency;
- display: flex;
- flex: 1;
- height: $header-height;
- padding-left: 16px;
- padding-right: 16px;
-}
-
-.identity-key-container {
- display: flex;
- flex: 1;
- flex-direction: row;
- align-items: center;
- justify-content: space-around;
- white-space: nowrap;
- overflow-x: hidden;
-}
-
-.identity-key-text-container {
- flex: 1;
- text-align: center;
- flex-direction: column;
-}
-
-.identity-key-container div {
- overflow-x: hidden;
- text-overflow: ellipsis;
-}
-
-.identity-key_bold {
- font-weight: bold;
-}
-
-.identity-key-wrapper__pencil-icon {
- @include color-svg('../images/lead-pencil.svg', $color-gray-60);
- height: 20px;
- width: 20px;
- margin-left: 4px;
- cursor: pointer;
-}
-
.underneathIdentityWrapper {
position: absolute;
- top: $header-height;
+ top: 0;
bottom: 0;
left: 300px;
right: 0;
diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss
index a2ecc0b96..2e534ab57 100644
--- a/stylesheets/_modules.scss
+++ b/stylesheets/_modules.scss
@@ -2180,15 +2180,73 @@
// Module: Main Header
+.main-header-title-wrapper {
+ flex: 1;
+ flex-direction: row;
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+
+ &:hover {
+ background-color: $color-dark-75;
+ }
+}
+
+.main-header-content-wrapper {
+ color: $color-dark-05;
+
+ div {
+ padding: 12px;
+ background-color: $color-dark-90;
+
+ cursor: pointer;
+
+ &:hover {
+ background-color: $color-dark-75;
+ }
+ }
+}
+
+.main-header-wrapper {
+ overflow-x: hidden;
+ flex: 1;
+}
+
.module-main-header {
height: $header-height;
- margin-left: 16px;
+ padding-left: 16px;
display: flex;
flex-direction: row;
align-items: center;
}
+.main-header-content-toggle {
+ width: 3em;
+ line-height: 3em;
+ font-weight: bold;
+ overflow: hidden;
+ user-select: none;
+ color: white;
+ text-align: center;
+
+ &:after {
+ -webkit-transition: all .35s;
+ -o-transition: all .35s;
+ transition: all .35s;
+ width: 3em;
+ line-height: 3em;
+ height: 3em;
+ content: '\25BE';
+ display: inline-block;
+ }
+}
+
+.main-header-content-toggle-visible::after {
+ transform: rotate(180deg);
+}
+
+
.module-main-header__app-name {
font-size: 16px;
line-height: 24px;
@@ -2197,6 +2255,14 @@
color: $color-dark-05;
}
+.module-main-header__contact-name {
+ font-weight: 300;
+ margin-left: 12px;
+ color: $color-dark-05;
+ overflow-x: auto;
+ flex: 1;
+}
+
// Third-party module: react-contextmenu
.react-contextmenu {
diff --git a/stylesheets/_theme_dark.scss b/stylesheets/_theme_dark.scss
index 8cfea9fe2..7d9137355 100644
--- a/stylesheets/_theme_dark.scss
+++ b/stylesheets/_theme_dark.scss
@@ -6,16 +6,6 @@ body.dark-theme {
}
.dark-theme {
- // identity key
- .identity-key-wrapper {
- background-color:$color-gray-85;
- }
-
- .identity-key-wrapper__pencil-icon {
- @include color-svg('../images/lead-pencil.svg', $color-gray-25);
- }
-
-
// _conversation
.conversation {
diff --git a/stylesheets/_variables.scss b/stylesheets/_variables.scss
index f7bb40910..98236c7d8 100644
--- a/stylesheets/_variables.scss
+++ b/stylesheets/_variables.scss
@@ -115,6 +115,7 @@ $color-dark-60: #797a7c;
$color-dark-70: #414347;
$color-dark-75: #292c33;
$color-dark-85: #1a1c20;
+$color-dark-90: #121417;
$color-black-008: rgba($color-black, 0.08);
$color-black-008-no-tranparency: #ededed;
$color-black-016-no-tranparency: #d9d9d9;
diff --git a/test/index.html b/test/index.html
index 9dbcf2fa2..fdf60a6cc 100644
--- a/test/index.html
+++ b/test/index.html
@@ -374,6 +374,7 @@
+
diff --git a/ts/components/IdentityKeyHeader.tsx b/ts/components/IdentityKeyHeader.tsx
deleted file mode 100644
index a345cef0e..000000000
--- a/ts/components/IdentityKeyHeader.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import React from 'react';
-
-interface Props {
- identityKey: string;
- name?: string;
- onEditProfile: () => void;
-}
-
-export class IdentityKeyHeader extends React.Component
{
- public render() {
- const {
- name,
- identityKey,
- onEditProfile,
- } = this.props;
-
- return (
-
-
-
- Your identity key: {identityKey}
-
- {!!name &&
-
- Your display name: {name}
-
- }
-
-
-
- );
- }
-}
diff --git a/ts/components/MainHeader.tsx b/ts/components/MainHeader.tsx
index d49911daa..0f81533bb 100644
--- a/ts/components/MainHeader.tsx
+++ b/ts/components/MainHeader.tsx
@@ -1,6 +1,7 @@
import React from 'react';
import { Avatar } from './Avatar';
+import { ContactName } from './conversation/ContactName';
import { Localizer } from '../types/Util';
@@ -25,10 +26,11 @@ export class MainHeader extends React.Component {
name,
phoneNumber,
profileName,
+ onClick
} = this.props;
return (
-
+
{
profileName={profileName}
size={28}
/>
- Loki Messenger
+
+
+
);
}
diff --git a/ts/components/conversation/ContactName.tsx b/ts/components/conversation/ContactName.tsx
index dbe17c24a..ef2755207 100644
--- a/ts/components/conversation/ContactName.tsx
+++ b/ts/components/conversation/ContactName.tsx
@@ -28,7 +28,7 @@ export class ContactName extends React.Component
{
return (
{profileElement}
-
+