diff --git a/images/close-circle.svg b/images/close-circle.svg new file mode 100644 index 000000000..5a37396e2 --- /dev/null +++ b/images/close-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/js/views/conversation_view.js b/js/views/conversation_view.js index e077e4690..bd2094081 100644 --- a/js/views/conversation_view.js +++ b/js/views/conversation_view.js @@ -130,7 +130,7 @@ 'scroll-to-message', this.scrollToMessage ); - this.listenTo(this.model.messageCollection, 'reply', this.setReplyMessage); + this.listenTo(this.model.messageCollection, 'reply', this.setQuoteMessage); this.lazyUpdateVerified = _.debounce( this.model.updateVerified.bind(this.model), @@ -275,6 +275,9 @@ if (this.scrollDownButton) { this.scrollDownButton.remove(); } + if (this.quoteView) { + this.quoteView.remove(); + } if (this.panels && this.panels.length) { for (let i = 0, max = this.panels.length; i < max; i += 1) { const panel = this.panels[i]; @@ -1062,9 +1065,66 @@ this.focusMessageField(); }, - setMessageReply(message) { + setQuoteMessage(message) { this.quotedMessage = message; console.log('setMessageReply', this.quotedMessage); + + this.renderQuotedMessage(); + }, + + makeQuote(quotedMessage) { + const contact = quotedMessage.getContact(); + const attachments = quotedMessage.get('attachments'); + const first = attachments ? attachments[0] : null; + + return { + author: contact.id, + id: quotedMessage.get('sent_at'), + text: quotedMessage.get('body'), + attachments: !first ? [] : [{ + contentType: first.contentType, + fileName: first.fileName, + }], + }; + }, + + renderQuotedMessage() { + if (this.quoteView) { + this.quoteView.remove(); + this.quoteView = null; + } + if (!this.quotedMessage) { + this.updateMessageFieldSize({}); + return; + } + + const message = new Whisper.Message({ + quote: this.makeQuote(this.quotedMessage), + }); + console.log('quoted message attributes', message.attributes); + message.quotedMessage = this.quotedMessage; + const props = Object.assign({}, message.getPropsForQuote(), { + onClose: () => { + console.log('onClose!'); + this.setQuoteMessage(null); + }, + }); + + this.listenTo(message, 'scroll-to-message', this.scrollToMessage); + + console.log('props', props); + const contact = this.quotedMessage.getContact(); + if (contact) { + this.listenTo(contact, 'change:color', this.renderQuotedMesage); + } + + this.quoteView = new Whisper.ReactWrapperView({ + className: 'quote-wrapper', + Component: window.Signal.Components.Quote, + props, + }); + this.$('.bottom-bar').prepend(this.quoteView.el); + this.updateMessageFieldSize({}); }, async sendMessage(e) { @@ -1168,9 +1228,15 @@ const $attachmentPreviews = this.$('.attachment-previews'); const $bottomBar = this.$('.bottom-bar'); + const includeMargin = true; + const quoteHeight = this.quoteView + ? this.quoteView.$el.outerHeight(includeMargin) + : 0; + const height = this.$messageField.outerHeight() + $attachmentPreviews.outerHeight() + this.$emojiPanelContainer.outerHeight() + + quoteHeight + parseInt($bottomBar.css('min-height'), 10); $bottomBar.outerHeight(height); diff --git a/stylesheets/_conversation.scss b/stylesheets/_conversation.scss index b83997d4a..968201e0b 100644 --- a/stylesheets/_conversation.scss +++ b/stylesheets/_conversation.scss @@ -701,6 +701,7 @@ span.status { cursor: auto; } + position: relative; cursor: pointer; display: flex; flex-direction: row; @@ -718,6 +719,9 @@ span.status { // Accent color border: border-left-width: 3px; border-left-style: solid; + border-top: 1px solid lightgray; + border-bottom: 1px solid lightgray; + border-right: 1px solid lightgray; .primary { flex-grow: 1; @@ -766,6 +770,16 @@ span.status { } } + .close-container { + position: absolute; + top: 0px; + right: 0px; + height: 18px; + width: 18px; + + @include color-svg('../images/x.svg', white); + } + .icon-container { flex: initial; min-width: 48px; @@ -833,6 +847,15 @@ span.status { margin-top: $android-bubble-quote-padding - $android-bubble-padding-vertical; } +.bottom-bar .quoted-message { + margin: 0px; +} + +.bottom-bar .quote-wrapper { + margin-right: 5px; + margin-bottom: 5px; +} + .incoming .quoted-message { background-color: rgba(white, 0.6); border-left-color: white; diff --git a/stylesheets/_ios.scss b/stylesheets/_ios.scss index 26bf2800b..4b7ee8e12 100644 --- a/stylesheets/_ios.scss +++ b/stylesheets/_ios.scss @@ -114,6 +114,9 @@ $ios-border-color: rgba(0,0,0,0.1); } .quoted-message { + border-top-left-radius: 15px; + border-top-right-radius: 15px; + // Not ideal, but necessary to override the specificity of the android theme color // classes used in conversations.scss background-color: white !important; @@ -184,6 +187,28 @@ $ios-border-color: rgba(0,0,0,0.1); } } + .close-container { + flex: initial; + min-width: 32px; + width: 32px; + height: 48px; + position: relative; + + display: flex; + align-items: center; + justify-content: center; + + -webkit-mask: none; + background: none; + + .close-button { + height: 20px; + width: 20px; + + @include color-svg('../images/close-circle.svg', $grey_l4); + } + } + .from-me { .primary { .text, @@ -218,6 +243,56 @@ $ios-border-color: rgba(0,0,0,0.1); background-color: lightgray !important; } + .bottom-bar { + .quote-wrapper { + margin-right: 0px; + margin-bottom: 15px; + } + + .quoted-message { + border-top-left-radius: 0px; + border-top-right-radius: 0px; + + background: none !important; + border: none !important; + + .primary { + padding: 0px; + + .ios-label { + color: $grey_l4; + } + } + + .icon-container { + height: 48px; + width: 48px; + min-width: 48px; + + .circle-background { + left: 6px; + right: 6px; + top: 6px; + bottom: 6px; + + background-color: $blue !important; + } + + .icon { + left: 12px; + right: 12px; + top: 12px; + bottom: 12px; + } + + .inner { + padding: 0px; + height: 48px; + } + } + } + } + .attachments .bubbled { border-radius: 15px; diff --git a/ts/components/conversation/Quote.md b/ts/components/conversation/Quote.md index d5cb30174..838135fce 100644 --- a/ts/components/conversation/Quote.md +++ b/ts/components/conversation/Quote.md @@ -893,3 +893,132 @@ const View = Whisper.MessageView; /> ``` + +### In bottom bar + +#### Plain text + +```jsx +
+
+
+
console.log('Close was clicked!')} + i18n={window.i18n} + /> +
console.log('Close was clicked!')} + i18n={window.i18n} + attachments={[{ + contentType: 'image/jpeg', + fileName: 'llama.jpg', + }]} + /> +
console.log('Close was clicked!')} + i18n={window.i18n} + attachments={[{ + contentType: 'image/gif', + fileName: 'llama.gif', + thumbnail: { + objectUrl: util.gifObjectUrl + }, + }]} + /> +