In iOS theme, join attachment bubble with caption bubble

This commit is contained in:
Scott Nonnenberg 2018-04-06 14:51:52 -07:00
parent 3a76c3c86e
commit ae043bf239
No known key found for this signature in database
GPG Key ID: 5F82280C35134661
9 changed files with 246 additions and 97 deletions

View File

@ -277,10 +277,12 @@
<span class='profileName'>{{ profileName }} </span>
{{ /profileName }}
</div>
<div class='attachments'></div>
<p class='content' dir='auto'>
{{ #message }}<span class='body'>{{ message }}</span>{{ /message }}
</p>
<div class='inner-bubble {{ innerBubbleClasses }}'>
<div class='attachments'></div>
<p class='content' dir='auto'>
{{ #message }}<div class='body'>{{ message }}</div>{{ /message }}
</p>
</div>
<div class='meta'>
<span class='timestamp' data-timestamp={{ timestamp }}></span>
<span class='status hide'></span>

View File

@ -69,7 +69,7 @@
];
Whisper.AttachmentView = Backbone.View.extend({
tagName: 'span',
tagName: 'div',
className() {
if (this.isImage()) {
return 'attachment';

View File

@ -355,6 +355,24 @@
this.timerView.setElement(this.$('.timer'));
this.timerView.update();
},
isImageWithoutCaption: function() {
var attachments = this.model.get('attachments');
var body = this.model.get('body');
if (!attachments || attachments.length === 0) {
return false;
}
if (body && body.trim()) {
return false;
}
var first = attachments[0];
if (first.contentType.startsWith('image/') && first.contentType !== 'image/tiff') {
return true;
}
return false;
},
render: function() {
var contact = this.model.isIncoming() ? this.model.getContact() : null;
this.$el.html(
@ -364,6 +382,7 @@
sender: (contact && contact.getTitle()) || '',
avatar: (contact && contact.getAvatar()),
profileName: (contact && contact.getProfileName()),
innerBubbleClasses: this.isImageWithoutCaption() ? '' : 'with-tail',
}, this.render_partials())
);
this.timeStampView.setElement(this.$('.timestamp'));

View File

@ -451,6 +451,7 @@ span.status {
}
.body {
margin-top: 0.5em;
white-space: pre-wrap;
a {
@ -591,6 +592,7 @@ span.status {
position: relative;
padding: 5px;
padding-right: 10px;
padding-bottom: 0px;
cursor: pointer;

View File

@ -109,11 +109,33 @@ $ios-border-color: rgba(0,0,0,0.1);
.attachments .bubbled {
border-radius: 15px;
margin-bottom: 0.25em;
padding: 10px;
padding-top: 0px;
padding-bottom: 5px;
position: relative;
}
.inner-bubble {
border-radius: 15px;
margin-bottom: 5px;
.body {
margin-top: 0;
display: inline-block;
padding: 10px;
position: relative;
word-break: break-word;
}
.attachments img {
border-radius: 15px;
}
}
.inner-bubble.with-tail {
position: relative;
&:before, &:after {
content: '';
@ -135,51 +157,27 @@ $ios-border-color: rgba(0,0,0,0.1);
bottom: -3px;
background: #eee;
}
}
.bubble {
.content {
margin-bottom: 5px;
.body {
display: inline-block;
padding: 10px;
position: relative;
word-break: break-word;
&:before, &:after {
content: '';
display: block;
border-radius: 20px;
position: absolute;
width: 10px;
}
&:before {
right: -1px;
bottom: -3px;
height: 10px;
border-radius: 20px;
background: $blue;
}
&:after {
height: 11px;
right: -6px;
bottom: -3px;
background: #eee;
}
}
}
.content, .attachments img {
border-radius: 15px;
}
.attachments img {
background-color: white;
}
.meta {
clear: both;
border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;
}
}
.incoming .bubbled {
.meta {
clear: both;
}
.outgoing .inner-bubble.with-tail {
background-color: $blue;
max-width: 100%;
&, .body, a {
@include invert-text-color;
}
float: right;
}
.incoming .inner-bubble.with-tail {
background-color: white;
color: black;
float: left;
@ -194,31 +192,6 @@ $ios-border-color: rgba(0,0,0,0.1);
}
}
.incoming .content {
background-color: white;
color: black;
float: left;
.body {
&:before {
left: -1px;
background-color: white;
}
&:after {
left: -6px;
}
}
}
.outgoing {
.content, .attachments .bubbled {
background-color: $blue;
max-width: 100%;
&, .body, a {
@include invert-text-color;
}
float: right;
}
}
.outgoing .attachments .fileView .icon {
@include color-svg('../images/file.svg', white);
&.audio {
@ -236,7 +209,6 @@ $ios-border-color: rgba(0,0,0,0.1);
a {
border-radius: 15px;
}
margin-bottom: 1px;
}
.hourglass {
@include hourglass(#999);

View File

@ -206,14 +206,22 @@
<script type='text/x-tmpl-mustache' id='message'>
{{> avatar }}
<div class='bubble {{ avatar.color }}'>
<div class='sender' dir='auto'>{{ sender }}</div>
<div class='attachments'></div>
<p class='content' dir='auto'>
{{ #message }}<span class='body'>{{ message }}</span>{{ /message }}
</p>
<div class='sender' dir='auto'>
{{ sender }}
{{ #profileName }}
<span class='profileName'>{{ profileName }} </span>
{{ /profileName }}
</div>
<div class='inner-bubble {{ innerBubbleClasses }}'>
<div class='attachments'></div>
<p class='content' dir='auto'>
{{ #message }}<div class='body'>{{ message }}</div>{{ /message }}
</p>
</div>
<div class='meta'>
<span class='timestamp' data-timestamp={{ timestamp }}></span>
<span class='status hide'></span>
<span class='timer'></span>
</div>
</div>
</script>

View File

@ -32,10 +32,12 @@ window.Whisper.View.Templates = {
<span class='profileName'>{{ profileName }} </span>
{{ /profileName }}
</div>
<div class='attachments'></div>
<p class='content' dir='auto'>
{{ #message }}<span class='body'>{{ message }}</span>{{ /message }}
</p>
<div class='inner-bubble {{ innerBubbleClasses }}'>
<div class='attachments'></div>
<p class='content' dir='auto'>
{{ #message }}<div class='body'>{{ message }}</div>{{ /message }}
</p>
</div>
<div class='meta'>
<span class='timestamp' data-timestamp={{ timestamp }}></span>
<span class='status hide'></span>

View File

@ -1,5 +1,5 @@
Placeholder:
Placeholder component:
```jsx
<util.ConversationContext theme={util.theme}>
@ -7,9 +7,36 @@ Placeholder:
</util.ConversationContext>
```
## With an attachment
## MessageView (Backbone)
### Image
### Plain messages
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
body: 'How are you doing this fine day?',
sent_at: Date.now() - 18000,
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
source: '+12025550100',
type: 'incoming',
}));
const View = Whisper.MessageView;
<util.ConversationContext theme={util.theme}>
<util.BackboneWrapper
View={View}
options={{ model: incoming }}
/>
<util.BackboneWrapper
View={View}
options={{ model: outgoing }}
/>
</util.ConversationContext>
```
### With an attachment
#### Image
```jsx
const outgoing = new Whisper.Message({
@ -39,12 +66,41 @@ const View = Whisper.MessageView;
</util.ConversationContext>
```
### Video
#### Image, no caption
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
body: "Beatiful, isn't it?",
sent_at: Date.now() - 18000000,
attachments: [{
data: util.gif,
fileName: 'pi.gif',
contentType: 'image/gif',
}],
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
source: '+12025550100',
type: 'incoming',
}));
const View = Whisper.MessageView;
<util.ConversationContext theme={util.theme}>
<util.BackboneWrapper
View={View}
options={{ model: incoming }}
/>
<util.BackboneWrapper
View={View}
options={{ model: outgoing }}
/>
</util.ConversationContext>
```
#### Video
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
body: "Beautiful, isn't it?",
sent_at: Date.now() - 10000,
attachments: [{
data: util.mp4,
@ -69,7 +125,36 @@ const View = Whisper.MessageView;
</util.ConversationContext>
```
### Audio
#### Video, no caption
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
sent_at: Date.now() - 10000,
attachments: [{
data: util.mp4,
fileName: 'freezing_bubble.mp4',
contentType: 'video/mp4',
}],
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
source: '+12025550100',
type: 'incoming',
}));
const View = Whisper.MessageView;
<util.ConversationContext theme={util.theme}>
<util.BackboneWrapper
View={View}
options={{ model: incoming }}
/>
<util.BackboneWrapper
View={View}
options={{ model: outgoing }}
/>
</util.ConversationContext>
```
#### Audio
```jsx
const outgoing = new Whisper.Message({
@ -99,12 +184,40 @@ const View = Whisper.MessageView;
</util.ConversationContext>
```
### Voice message
#### Audio, no caption
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
sent_at: Date.now() - 15000,
attachments: [{
data: util.mp3,
fileName: 'agnus_dei.mp3',
contentType: 'audio/mp3',
}],
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
source: '+12025550100',
type: 'incoming',
}));
const View = Whisper.MessageView;
<util.ConversationContext theme={util.theme}>
<util.BackboneWrapper
View={View}
options={{ model: incoming }}
/>
<util.BackboneWrapper
View={View}
options={{ model: outgoing }}
/>
</util.ConversationContext>
```
#### Voice message
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
body: 'This is a nice song',
sent_at: Date.now() - 15000,
attachments: [{
flags: textsecure.protobuf.AttachmentPointer.Flags.VOICE_MESSAGE,
@ -130,7 +243,7 @@ const View = Whisper.MessageView;
</util.ConversationContext>
```
### Other file type
#### Other file type
```jsx
const outgoing = new Whisper.Message({
@ -159,3 +272,32 @@ const View = Whisper.MessageView;
/>
</util.ConversationContext>
```
#### Other file type, no caption
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
sent_at: Date.now() - 15000,
attachments: [{
data: util.txt,
fileName: 'lorum_ipsum.txt',
contentType: 'text/plain',
}],
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
source: '+12025550100',
type: 'incoming',
}));
const View = Whisper.MessageView;
<util.ConversationContext theme={util.theme}>
<util.BackboneWrapper
View={View}
options={{ model: incoming }}
/>
<util.BackboneWrapper
View={View}
options={{ model: outgoing }}
/>
</util.ConversationContext>
```

View File

@ -4,7 +4,7 @@ import React from 'react';
/**
* A placeholder Message component for now, giving the structure of a plain message with
* none of the dynamic functionality. This page will be used to build up our corpus of
* permutations before start moving all message functionality to React.
* permutations before we start moving all message functionality to React.
*/
export class Message extends React.Component<{}, {}> {
public render() {
@ -13,12 +13,14 @@ export class Message extends React.Component<{}, {}> {
<span className="avatar" />
<div className="bubble">
<div className="sender" dir="auto" />
<div className="attachments" />
<p className="content" dir="auto">
<span className="body">
Hi there. How are you doing? Feeling pretty good? Awesome.
</span>
</p>
<div className="inner-bubble">
<div className="attachments" />
<p className="content" dir="auto">
<span className="body">
Hi there. How are you doing? Feeling pretty good? Awesome.
</span>
</p>
</div>
<div className="meta">
<span
className="timestamp"