MessageView: Show menu w/ 'reply to message' on triple-dot click

pull/1/head
Scott Nonnenberg 7 years ago
parent f4d9ab8ba0
commit 12257e1560
No known key found for this signature in database
GPG Key ID: 5F82280C35134661

@ -428,6 +428,10 @@
"selectAContact": { "selectAContact": {
"message": "Select a contact or group to start chatting." "message": "Select a contact or group to start chatting."
}, },
"replyToMessage": {
"message": "Reply to Message",
"description": "Shown in triple-dot menu next to message to allow user to start crafting a message with a quotation"
},
"replyingToYourself": { "replyingToYourself": {
"message": "Replying to Yourself", "message": "Replying to Yourself",
"description": "Shown in iOS theme when you quote yourself" "description": "Shown in iOS theme when you quote yourself"

@ -297,8 +297,13 @@
<span class='timer'></span> <span class='timer'></span>
</div> </div>
{{ #hoverIcon }} {{ #hoverIcon }}
<div class='hover-icon-container'> <div class='menu-container menu'>
<span class='dots-horizontal-icon'></span> <div class='menu-anchor'>
<span class='dots-horizontal-icon'></span>
<ul class='menu-list'>
<li class='reply'>{{ reply }}</li>
</ul>
</div>
</div> </div>
{{ /hoverIcon }} {{ /hoverIcon }}
</div> </div>

@ -4,6 +4,7 @@
/* global _: false */ /* global _: false */
/* global emoji_util: false */ /* global emoji_util: false */
/* global Mustache: false */ /* global Mustache: false */
/* global $: false */
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
(function () { (function () {
@ -216,7 +217,8 @@
'click .status': 'select', 'click .status': 'select',
'click .some-failed': 'select', 'click .some-failed': 'select',
'click .error-message': 'select', 'click .error-message': 'select',
'click .hover-icon-container': 'onReply', 'click .menu-container': 'showMenu',
'click .menu-list .reply': 'onReply',
}, },
retryMessage() { retryMessage() {
const retrys = _.filter( const retrys = _.filter(
@ -227,6 +229,23 @@
this.model.resend(number); this.model.resend(number);
}); });
}, },
showMenu(e) {
if (this.menuVisible) {
return;
}
this.menuVisible = true;
e.stopPropagation();
this.$('.menu-list').show();
$(document).one('click', () => {
this.hideMenu();
});
},
hideMenu() {
this.menuVisible = false;
this.$('.menu-list').hide();
},
onReply() { onReply() {
this.model.trigger('reply', this.model); this.model.trigger('reply', this.model);
}, },
@ -428,6 +447,7 @@
innerBubbleClasses: this.isImageWithoutCaption() ? '' : 'with-tail', innerBubbleClasses: this.isImageWithoutCaption() ? '' : 'with-tail',
hoverIcon: !hasErrors, hoverIcon: !hasErrors,
hasAttachments, hasAttachments,
reply: i18n('replyToMessage'),
}, this.render_partials())); }, this.render_partials()));
this.timeStampView.setElement(this.$('.timestamp')); this.timeStampView.setElement(this.$('.timestamp'));
this.timeStampView.update(); this.timeStampView.update();

@ -353,26 +353,46 @@ li.entry .error-icon-container {
&:hover .error-message { display: inline-block; } &:hover .error-message { display: inline-block; }
} }
li.entry .hover-icon-container { li.entry .menu-container {
position: absolute; position: absolute;
top: 0; top: 0;
left: calc(100% + 5px); left: calc(100% + 5px);
height: 100%; height: 100%;
visibility: hidden; display: flex;
align-items: center;
justify-content: center;
.dots-horizontal-icon { .menu-anchor {
display: block; position: relative;
height: 100%; }
li {
margin: 0px;
} }
cursor: pointer; cursor: pointer;
} }
li.entry:hover .hover-icon-container { .dots-horizontal-icon {
visibility: hidden;
}
li.entry:hover .dots-horizontal-icon {
visibility: visible; visibility: visible;
} }
li.entry.outgoing .menu-container {
left: auto;
right: calc(100% + 5px);
}
.incoming .menu-list {
left: 0;
right: auto;
}
.error-icon { .error-icon {
display: inline-block; display: inline-block;
width: $error-icon-size; width: $error-icon-size;
@ -557,10 +577,6 @@ span.status {
left: auto; left: auto;
right: calc(100% + 5px); right: calc(100% + 5px);
} }
.hover-icon-container {
left: auto;
right: calc(100% + 5px);
}
.avatar, .bubble { .avatar, .bubble {
float: right; float: right;

@ -232,8 +232,13 @@
<span class='timer'></span> <span class='timer'></span>
</div> </div>
{{ #hoverIcon }} {{ #hoverIcon }}
<div class='hover-icon-container'> <div class='menu-container menu'>
<span class='dots-horizontal-icon'></span> <div class='menu-anchor'>
<span class='dots-horizontal-icon'></span>
<ul class='menu-list'>
<li class='reply'>{{ reply }}</li>
</ul>
</div>
</div> </div>
{{ /hoverIcon }} {{ /hoverIcon }}
</div> </div>

@ -52,8 +52,13 @@ window.Whisper.View.Templates = {
<span class='timer'></span> <span class='timer'></span>
</div> </div>
{{ #hoverIcon }} {{ #hoverIcon }}
<div class='hover-icon-container'> <div class='menu-container menu'>
<span class='dots-horizontal-icon'></span> <div class='menu-anchor'>
<span class='dots-horizontal-icon'></span>
<ul class='menu-list'>
<li class='reply'>{{ reply }}</li>
</ul>
</div>
</div> </div>
{{ /hoverIcon }} {{ /hoverIcon }}
</div> </div>

@ -161,10 +161,17 @@ export class Quote extends React.Component<Props, {}> {
return null; return null;
} }
// We don't want the overall click handler for the quote to fire, so we stop
// propagation before handing control to the caller's callback.
const onClick = (e: React.MouseEvent<{}>): void => {
e.stopPropagation();
onClose();
};
// We need the container to give us the flexibility to implement the iOS design. // We need the container to give us the flexibility to implement the iOS design.
return ( return (
<div className="close-container"> <div className="close-container">
<div className="close-button" onClick={onClose}></div> <div className="close-button" onClick={onClick}></div>
</div> </div>
); );
} }

Loading…
Cancel
Save