signal-desktop/ts/components/conversation/Message.md

674 lines
17 KiB
Markdown
Raw Normal View History

Placeholder component:
```jsx
<util.ConversationContext theme={util.theme}>
<Message />
</util.ConversationContext>
```
## MessageView (Backbone)
### 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: '+12025550003',
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>;
```
### In a group conversation
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
body: 'How are you doing this fine day?',
sent_at: Date.now() - 200000,
});
const incoming = new Whisper.Message(
Object.assign({}, outgoing.attributes, {
source: '+12025550003',
type: 'incoming',
})
);
const View = Whisper.MessageView;
<util.ConversationContext theme={util.theme} type="group">
<util.BackboneWrapper View={View} options={{ model: incoming }} />
<util.BackboneWrapper View={View} options={{ model: outgoing }} />
</util.ConversationContext>;
```
### With an error
#### General error
```jsx
const error = new Error('Something went wrong!');
const outgoing = new Whisper.Message({
type: 'outgoing',
body: "This message won't get through...",
sent_at: Date.now() - 200000,
errors: [error],
});
const incoming = new Whisper.Message(
Object.assign({}, outgoing.attributes, {
source: '+12025550003',
type: 'incoming',
body: null,
})
);
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>;
```
#### Network error (outgoing only)
```jsx
const error = new Error('Something went wrong!');
error.name = 'MessageError';
const outgoing = new Whisper.Message({
type: 'outgoing',
sent_at: Date.now() - 200000,
errors: [error],
body: "This message won't get through...",
});
const View = Whisper.MessageView;
<util.ConversationContext theme={util.theme} type="group">
<util.BackboneWrapper View={View} options={{ model: outgoing }} />
</util.ConversationContext>;
```
#### Network error, partial send in group (outgoing only)
```jsx
const error = new Error('Something went wrong!');
error.name = 'MessageError';
const outgoing = new Whisper.Message({
type: 'outgoing',
sent_at: Date.now() - 200000,
errors: [error],
conversationId: util.groupNumber,
body: "This message won't get through...",
});
const View = Whisper.MessageView;
<util.ConversationContext theme={util.theme} type="group">
<util.BackboneWrapper View={View} options={{ model: outgoing }} />
</util.ConversationContext>;
```
#### No message contents
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
sent_at: Date.now() - 200000,
});
const incoming = new Whisper.Message(
Object.assign({}, outgoing.attributes, {
source: '+12025550003',
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>;
```
### Disappearing
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
sent_at: Date.now() - 200000,
expireTimer: 120,
expirationStartTimestamp: Date.now() - 1000,
body: 'This message will self-destruct in two minutes',
});
const incoming = new Whisper.Message(
Object.assign({}, outgoing.attributes, {
source: '+12025550003',
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>;
```
### Notfications
#### Timer change
```jsx
const fromOther = new Whisper.Message({
type: 'incoming',
flags: textsecure.protobuf.DataMessage.Flags.EXPIRATION_TIMER_UPDATE,
source: '+12025550003',
sent_at: Date.now() - 200000,
expireTimer: 120,
expirationStartTimestamp: Date.now() - 1000,
expirationTimerUpdate: {
source: '+12025550003',
},
});
const fromUpdate = new Whisper.Message({
type: 'incoming',
flags: textsecure.protobuf.DataMessage.Flags.EXPIRATION_TIMER_UPDATE,
source: util.ourNumber,
sent_at: Date.now() - 200000,
expireTimer: 120,
expirationStartTimestamp: Date.now() - 1000,
expirationTimerUpdate: {
fromSync: true,
source: util.ourNumber,
},
});
const fromMe = new Whisper.Message({
type: 'incoming',
flags: textsecure.protobuf.DataMessage.Flags.EXPIRATION_TIMER_UPDATE,
source: util.ourNumber,
sent_at: Date.now() - 200000,
expireTimer: 120,
expirationStartTimestamp: Date.now() - 1000,
expirationTimerUpdate: {
source: util.ourNumber,
},
});
const View = Whisper.ExpirationTimerUpdateView;
<util.ConversationContext theme={util.theme}>
<util.BackboneWrapper View={View} options={{ model: fromOther }} />
<util.BackboneWrapper View={View} options={{ model: fromUpdate }} />
<util.BackboneWrapper View={View} options={{ model: fromMe }} />
</util.ConversationContext>;
```
#### Safety number change
```js
const incoming = new Whisper.Message({
type: 'keychange',
sent_at: Date.now() - 200000,
key_changed: '+12025550003',
});
const View = Whisper.KeyChangeView;
<util.ConversationContext theme={util.theme}>
<util.BackboneWrapper View={View} options={{ model: incoming }} />
</util.ConversationContext>;
```
#### Marking as verified
```js
const fromPrimary = new Whisper.Message({
type: 'verified-change',
sent_at: Date.now() - 200000,
verifiedChanged: '+12025550003',
verified: true,
});
const local = new Whisper.Message({
type: 'verified-change',
sent_at: Date.now() - 200000,
verifiedChanged: '+12025550003',
local: true,
verified: true,
});
const View = Whisper.VerifiedChangeView;
<util.ConversationContext theme={util.theme}>
<util.BackboneWrapper View={View} options={{ model: fromPrimary }} />
<util.BackboneWrapper View={View} options={{ model: local }} />
</util.ConversationContext>;
```
#### Marking as not verified
```js
const fromPrimary = new Whisper.Message({
type: 'verified-change',
sent_at: Date.now() - 200000,
verifiedChanged: '+12025550003',
});
const local = new Whisper.Message({
type: 'verified-change',
sent_at: Date.now() - 200000,
verifiedChanged: '+12025550003',
local: true,
});
const View = Whisper.VerifiedChangeView;
<util.ConversationContext theme={util.theme}>
<util.BackboneWrapper View={View} options={{ model: fromPrimary }} />
<util.BackboneWrapper View={View} options={{ model: local }} />
</util.ConversationContext>;
```
#### Group update
```js
const outgoing = new Whisper.Message({
type: 'outgoing',
sent_at: Date.now() - 200000,
group_update: {
joined: ['+12025550007', '+12025550008', '+12025550009'],
},
});
const incoming = new Whisper.Message(
Object.assign({}, outgoing.attributes, {
source: '+12025550003',
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>;
```
#### End session
```js
const outgoing = new Whisper.Message({
type: 'outgoing',
sent_at: Date.now() - 200000,
flags: textsecure.protobuf.DataMessage.Flags.END_SESSION,
});
const incoming = new Whisper.Message(
Object.assign({}, outgoing.attributes, {
source: '+12025550003',
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 with caption
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
body: 'I am pretty confused about Pi.',
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: '+12025550003',
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>;
```
#### Image
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
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: '+12025550003',
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>;
```
#### Image with portrait aspect ratio
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
sent_at: Date.now() - 18000000,
attachments: [
{
data: util.portraitYellow,
fileName: 'portraitYellow.png',
contentType: 'image/png',
},
],
});
const incoming = new Whisper.Message(
Object.assign({}, outgoing.attributes, {
source: '+12025550003',
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>;
```
#### Image with portrait aspect ratio and caption
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
body: 'This is an odd yellow bar. Cool, huh?',
sent_at: Date.now() - 18000000,
attachments: [
{
data: util.portraitYellow,
fileName: 'portraitYellow.png',
contentType: 'image/png',
},
],
});
const incoming = new Whisper.Message(
Object.assign({}, outgoing.attributes, {
source: '+12025550003',
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>;
```
#### Image with landscape aspect ratio
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
sent_at: Date.now() - 18000000,
attachments: [
{
data: util.landscapePurple,
fileName: 'landscapePurple.jpg',
contentType: 'image/jpeg',
},
],
});
const incoming = new Whisper.Message(
Object.assign({}, outgoing.attributes, {
source: '+12025550003',
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>;
```
#### Image with landscape aspect ratio and caption
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
body: "An interesting horizontal bar. It's art.",
sent_at: Date.now() - 18000000,
attachments: [
{
data: util.landscapePurple,
fileName: 'landscapePurple.jpg',
contentType: 'image/jpeg',
},
],
});
const incoming = new Whisper.Message(
Object.assign({}, outgoing.attributes, {
source: '+12025550003',
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 with caption
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
body: "Beautiful, isn't it?",
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: '+12025550003',
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',
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: '+12025550003',
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 with caption
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
body: 'This is a nice song',
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: '+12025550003',
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({
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: '+12025550003',
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',
sent_at: Date.now() - 15000,
attachments: [
{
flags: textsecure.protobuf.AttachmentPointer.Flags.VOICE_MESSAGE,
data: util.mp3,
fileName: 'agnus_dei.mp3',
contentType: 'audio/mp3',
},
],
});
const incoming = new Whisper.Message(
Object.assign({}, outgoing.attributes, {
source: '+12025550003',
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>;
```
#### Other file type with caption
```jsx
const outgoing = new Whisper.Message({
type: 'outgoing',
body: 'My manifesto is now complete!',
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: '+12025550003',
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>;
```
#### Other file type
```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: '+12025550003',
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>;
```