From 98c4b5d77b8659801fa3d0431ab99aab0861237b Mon Sep 17 00:00:00 2001 From: Mikunj Date: Tue, 27 Nov 2018 12:17:12 +1100 Subject: [PATCH] Added editing own nickname. Fix dark theme support. Fix notification titles. --- background.html | 10 +++---- js/background.js | 34 +++++++++++++++++++++++ js/models/conversations.js | 15 +++++++++-- js/modules/signal.js | 2 ++ js/views/app_view.js | 3 ++- js/views/inbox_view.js | 34 +++++++++++++++++++++-- js/views/nickname_dialog_view.js | 36 +++++++++++-------------- stylesheets/_conversation.scss | 8 +++++- stylesheets/_index.scss | 42 ++++++++++++++++++++++++----- stylesheets/_theme_dark.scss | 41 ++++++++++++++++++++++++++++ ts/components/IdentityKeyHeader.tsx | 38 ++++++++++++++++++++++++++ 11 files changed, 226 insertions(+), 37 deletions(-) create mode 100644 ts/components/IdentityKeyHeader.tsx diff --git a/background.html b/background.html index 40dc5bb73..60ce85d54 100644 --- a/background.html +++ b/background.html @@ -63,9 +63,7 @@ -
- Your identity key: {{ identityKey }} -
+
@@ -155,8 +153,10 @@ {{ #title }}

{{ title }}

{{ /title }} -
{{ message }}
- + + {{ #message }} +
{{ message }}
+ {{ /message }}
diff --git a/js/background.js b/js/background.js index 03f49ba4a..7cab7890e 100644 --- a/js/background.js +++ b/js/background.js @@ -568,6 +568,40 @@ } }); + Whisper.events.on('onEditProfile', () => { + const ourNumber = textsecure.storage.user.getNumber(); + const profile = storage.getProfile(ourNumber); + const nickname = profile && profile.name && profile.name.displayName; + if (appView) { + appView.showNicknameDialog({ + title: 'Change your own nickname', + message: 'Note: Your nickname will be visible to your contacts.', + nickname, + onOk: async (newNickname) => { + + // Update our profiles accordingly + if (_.isEmpty(newNickname)) { + await storage.removeProfile(ourNumber); + } else { + await storage.saveProfile(ourNumber, { + name: { + displayName: newNickname, + }, + }); + } + + appView.inboxView.trigger('updateProfile'); + + // Update the conversation if we have it + try { + const conversation = ConversationController.get(ourNumber); + conversation.updateProfile(); + } catch (e) {} + }, + }) + } + }); + Whisper.events.on('showNicknameDialog', options => { if (appView) { appView.showNicknameDialog(options); diff --git a/js/models/conversations.js b/js/models/conversations.js index c83bd0660..0cca4c713 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -1736,7 +1736,15 @@ this.getProfiles(); } }, - // Update profile variables dynamically + /* + Update profile values from the profile in storage. + + Signal has methods of setting data from a profile it fetches. + It fetches this via a server and they aren't saved anywhere. + + We made our own profile storage system so thus to avoid + any future conflict with upstream, we just use this method to update the values. + */ async updateProfile() { const profileName = this.get('profileName'); @@ -2083,7 +2091,10 @@ getTitle() { if (this.isPrivate()) { - return this.get('name') || this.getNumber(); + const profileName = this.getProfileName(); + const number = this.getNumber(); + const name = profileName ? `${profileName} (${number})` : number; + return this.get('name') || name; } return this.get('name') || 'Unknown group'; }, diff --git a/js/modules/signal.js b/js/modules/signal.js index b7755f25b..4ed80eba7 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -43,6 +43,7 @@ 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 { @@ -184,6 +185,7 @@ exports.setup = (options = {}) => { Lightbox, LightboxGallery, MainHeader, + IdentityKeyHeader, MediaGallery, Message, MessageBody, diff --git a/js/views/app_view.js b/js/views/app_view.js index acd6a8329..f21542c24 100644 --- a/js/views/app_view.js +++ b/js/views/app_view.js @@ -178,10 +178,11 @@ }); } }, - showNicknameDialog({ pubKey, title, nickname, onOk, onCancel }) { + showNicknameDialog({ pubKey, title, message, nickname, onOk, onCancel }) { const _title = title || `Change nickname for ${pubKey}`; const dialog = new Whisper.NicknameDialogView({ title: _title, + message, name: nickname, resolve: onOk, reject: onCancel, diff --git a/js/views/inbox_view.js b/js/views/inbox_view.js index 294b33b86..67fb8642b 100644 --- a/js/views/inbox_view.js +++ b/js/views/inbox_view.js @@ -6,6 +6,7 @@ /* global textsecure: false */ /* global Signal: false */ /* global StringView: false */ +/* global storage: false */ // eslint-disable-next-line func-names (function() { @@ -42,7 +43,7 @@ if ($el && $el.length > 0) { $el.remove(); } - } + }, }); Whisper.FontSizeView = Whisper.View.extend({ @@ -113,6 +114,16 @@ 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'), model: { window: options.window }, @@ -184,14 +195,33 @@ this.$el.addClass('expired'); } }, + _getIdentityKeyViewProps() { + const identityKey = textsecure.storage.get('identityKey').pubKey; + const pubKey = StringView.arrayBufferToHex(identityKey); + const profile = storage.getProfile(pubKey); + const name = profile && profile.name && profile.name.displayName; + + return { + identityKey: pubKey, + name, + onEditProfile: async () => { + window.Whisper.events.trigger('onEditProfile'); + }, + } + }, render_attributes() { const identityKey = textsecure.storage.get('identityKey').pubKey; + const pubKey = StringView.arrayBufferToHex(identityKey); + const profile = storage.getProfile(pubKey); + const name = profile && profile.name && profile.name.displayName; + return { welcomeToSignal: i18n('welcomeToSignal'), selectAContact: i18n('selectAContact'), searchForPeopleOrGroups: i18n('searchForPeopleOrGroups'), settings: i18n('settings'), - identityKey: StringView.arrayBufferToHex(identityKey), + identityKey: pubKey, + name, }; }, events: { diff --git a/js/views/nickname_dialog_view.js b/js/views/nickname_dialog_view.js index 1010068db..7af20cb46 100644 --- a/js/views/nickname_dialog_view.js +++ b/js/views/nickname_dialog_view.js @@ -27,6 +27,9 @@ this.render(); this.$input = this.$('input'); + this.$input.val(this.name); + this.$input.focus(); + this.validateNickname(); }, events: { @@ -36,9 +39,6 @@ 'click .clear': 'clear', change: 'validateNickname', }, - isValidNickname(name) { - return (name || '').length < 20; - }, validateNickname() { const nickname = this.$input.val(); @@ -47,19 +47,9 @@ } else { this.$('.clear').show(); } - - if (this.isValidNickname(nickname)) { - this.$('.content').removeClass('invalid'); - this.$('.content').addClass('valid'); - this.$('.ok').show(); - } else { - this.$('.content').removeClass('valid'); - this.$('.ok').hide(); - } }, render_attributes() { return { - name: this.name, message: this.message, showCancel: !this.hideCancel, cancel: this.cancelText, @@ -69,8 +59,7 @@ }; }, ok() { - const nickname = this.$input.val(); - if (!this.isValidNickname(nickname)) return; + const nickname = this.$input.val().trim(); this.remove(); if (this.resolve) { @@ -87,12 +76,19 @@ this.$input.val('').trigger('change'); }, onKeyup(event) { - if (event.key === 'Escape' || event.key === 'Esc') { - this.cancel(); - return; - } - this.validateNickname(); + switch (event.key) { + case 'Enter': + this.ok(); + break; + case 'Escape': + case 'Esc': + this.cancel(); + break; + default: + return; + } + event.preventDefault(); }, focusCancel() { this.$('.cancel').focus(); diff --git a/stylesheets/_conversation.scss b/stylesheets/_conversation.scss index ca468bd9b..ae91dc376 100644 --- a/stylesheets/_conversation.scss +++ b/stylesheets/_conversation.scss @@ -351,7 +351,6 @@ } } - .nickname-dialog { display: flex; align-items: center; @@ -359,6 +358,7 @@ .content { max-width: 75%; + min-width: 60%; padding: 1em; background: white; border-radius: $border-radius; @@ -399,6 +399,12 @@ word-wrap: break-word; /* IE */ word-break: break-all; } + + .message { + font-style: italic; + color: $grey; + font-size: 12px; + } } } diff --git a/stylesheets/_index.scss b/stylesheets/_index.scss index a500041e0..1bb8b18e6 100644 --- a/stylesheets/_index.scss +++ b/stylesheets/_index.scss @@ -71,21 +71,51 @@ } } -.identityKeyWrapper { +.identity-key-wrapper { background-color: $color-black-008-no-tranparency; - text-align: center; - height: 50px; - line-height: 50px; + display: flex; + flex: 1; + height: 60px; + 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; } -.identityKey { +.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: 50px; + top: 60px; bottom: 0; left: 300px; right: 0; diff --git a/stylesheets/_theme_dark.scss b/stylesheets/_theme_dark.scss index 87dc3f85e..5e955ed84 100644 --- a/stylesheets/_theme_dark.scss +++ b/stylesheets/_theme_dark.scss @@ -6,6 +6,16 @@ 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 { @@ -89,6 +99,37 @@ body.dark-theme { } } + .nickname-dialog { + .content { + background: $color-black; + color: $color-dark-05; + + .buttons { + button { + background-color: $color-dark-85; + border-radius: $border-radius; + border: 1px solid $color-dark-60; + color: $color-dark-05; + + &:hover { + background-color: $color-dark-70; + border-color: $color-dark-55; + } + } + } + + input { + color: $color-dark-05; + background-color: $color-dark-70; + border-color: $color-dark-55; + } + + .message { + color: $color-light-35; + } + } + } + .conversation-loading-screen { background-color: $color-gray-95; } diff --git a/ts/components/IdentityKeyHeader.tsx b/ts/components/IdentityKeyHeader.tsx new file mode 100644 index 000000000..b03b60876 --- /dev/null +++ b/ts/components/IdentityKeyHeader.tsx @@ -0,0 +1,38 @@ +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 nickname: {name} +
+ } +
+
+
+ ); + } +}