Files
Signal-Desktop/ts/components/conversation/Quote.md
Scott Nonnenberg c71dcf0139 Show current quoted message above composition field
Note that substantial changes will be required for the updated Android
mockups, putting the quotation into the text box next to the attachment
preview.
2018-04-20 15:23:51 -07:00

23 KiB

With a quotation, text-only replies

Plain text

const outgoing = new Whisper.Message({
  type: 'outgoing',
  body: 'About six',
  sent_at: Date.now() - 18000000,
  quote: {
    text: 'How many ferrets do you have?',
    author: '+12025550011',
    id: Date.now() - 1000,
  },
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));
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>

Replies to you or yourself

const outgoing = new Whisper.Message({
  type: 'outgoing',
  body: 'About six',
  sent_at: Date.now() - 18000000,
  quote: {
    text: 'How many ferrets do you have?',
    author: util.ourNumber,
    id: Date.now() - 1000,
  },
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: util.ourNumber,
  }),
}));
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

const outgoing = new Whisper.Message({
  type: 'outgoing',
  body: 'About six',
  sent_at: Date.now() - 18000000,
  quote: {
    text: 'How many ferrets do you have?',
    author: '+12025550010',
    id: Date.now() - 1000,
  },
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550007',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550002',
  }),
}));
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>

A lot of text in quotation

const outgoing = new Whisper.Message({
  type: 'outgoing',
  body: 'Woo, otters!',
  sent_at: Date.now() - 18000000,
  quote: {
    text:
      'I have lots of things to say. First, I enjoy otters. Second best are cats. ' +
      'After that, probably dogs. And then, you know, reptiles of all types. ' +
      'Then birds. They are dinosaurs, after all. Then cephalapods, because they are ' +
      'really smart.',
    author: '+12025550011',
    id: Date.now() - 1000,
  },
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));
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>

A lot of text in quotation, with icon

const outgoing = new Whisper.Message({
  type: 'outgoing',
  body: 'Woo, otters!',
  sent_at: Date.now() - 18000000,
  quote: {
    text:
      'I have lots of things to say. First, I enjoy otters. Second best are cats. ' +
      'After that, probably dogs. And then, you know, reptiles of all types. ' +
      'Then birds. They are dinosaurs, after all. Then cephalapods, because they are ' +
      'really smart.',
    author: '+12025550011',
    id: Date.now() - 1000,
    attachments: [
      {
        contentType: 'text/plain',
        fileName: 'lorum_ipsum.txt',
      },
    ],
  },
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));
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>

A lot of text in quotation, with image

const quotedMessage = {
  imageUrl: util.gifObjectUrl,
  id: '3234-23423-2342',
};
const outgoing = new Whisper.Message({
  type: 'outgoing',
  body: 'Woo, otters!',
  sent_at: Date.now() - 18000000,
  quote: {
    text:
      'I have lots of things to say. First, I enjoy otters. Second best are cats. ' +
      'After that, probably dogs. And then, you know, reptiles of all types. ' +
      'Then birds. They are dinosaurs, after all. Then cephalapods, because they are ' +
      'really smart.',
    author: '+12025550011',
    id: Date.now() - 1000,
    attachments: [
      {
        contentType: 'image/gif',
        fileName: 'pi.gif',
        thumbnail: {
          contentType: 'image/gif',
        },
      },
    ],
  },
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));

outgoing.quotedMessage = quotedMessage;
incoming.quotedMessage = quotedMessage;

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 caption

const quotedMessage = {
  imageUrl: util.gifObjectUrl,
  id: '3234-23423-2342',
};
const outgoing = new Whisper.Message({
  type: 'outgoing',
  body: "Totally, it's a pretty unintuitive concept.",
  sent_at: Date.now() - 18000000,
  quote: {
    text: 'I am pretty confused about Pi.',
    author: '+12025550011',
    id: Date.now() - 1000,
    attachments: [
      {
        contentType: 'image/gif',
        fileName: 'pi.gif',
        thumbnail: {
          contentType: 'image/gif',
        },
      },
    ],
  },
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));

outgoing.quotedMessage = quotedMessage;
incoming.quotedMessage = quotedMessage;

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

const quotedMessage = {
  imageUrl: util.gifObjectUrl,
};

const outgoing = new Whisper.Message({
  type: 'outgoing',
  body: "Yeah, pi. Tough to wrap your head around.",
  sent_at: Date.now() - 18000000,
  quote: {
    author: '+12025550011',
    id: Date.now() - 1000,
    attachments: [
      {
        contentType: 'image/gif',
        fileName: 'pi.gif',
        thumbnail: {
          contentType: 'image/gif',
        },
      },
    ],
  },
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));

outgoing.quotedMessage = quotedMessage;
incoming.quotedMessage = quotedMessage;

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 no thumbnail

const outgoing = new Whisper.Message({
  type: 'outgoing',
  body: "Yeah, pi. Tough to wrap your head around.",
  sent_at: Date.now() - 18000000,
  quote: {
    author: '+12025550011',
    id: Date.now() - 1000,
    attachments: [
      {
        contentType: 'image/gif',
        fileName: 'pi.gif',
      },
    ],
  },
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));

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

const quotedMessage = {
  imageUrl: util.gifObjectUrl,
};

const outgoing = new Whisper.Message({
  type: 'outgoing',
  body: "Sweet the way the video sneaks up on you!",
  sent_at: Date.now() - 18000000,
  quote: {
    author: '+12025550011',
    text: 'Check out this video I found!',
    id: Date.now() - 1000,
    attachments: [
      {
        contentType: 'video/mp4',
        fileName: 'freezing_bubble.mp4',
        thumbnail: {
          contentType: 'image/gif',
        },
      },
    ],
  },
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));

outgoing.quotedMessage = quotedMessage;
incoming.quotedMessage = quotedMessage;

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

const quotedMessage = {
  imageUrl: util.gifObjectUrl,
};

const outgoing = new Whisper.Message({
  type: 'outgoing',
  body: "Awesome!",
  sent_at: Date.now() - 18000000,
  quote: {
    author: '+12025550011',
    id: Date.now() - 1000,
    attachments: [
      {
        contentType: 'video/mp4',
        fileName: 'freezing_bubble.mp4',
        thumbnail: {
          contentType: 'image/gif',
          data: util.gif,
        }
      },
    ],
  },
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));

outgoing.quotedMessage = quotedMessage;
incoming.quotedMessage = quotedMessage;

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 no thumbnail

const outgoing = new Whisper.Message({
  type: 'outgoing',
  body: "Awesome!",
  sent_at: Date.now() - 18000000,
  quote: {
    author: '+12025550011',
    id: Date.now() - 1000,
    attachments: [
      {
        contentType: 'video/mp4',
        fileName: 'freezing_bubble.mp4',
      },
    ],
  },
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));

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

const outgoing = new Whisper.Message({
  type: 'outgoing',
  body: 'I really like it!',
  sent_at: Date.now() - 18000000,
  quote: {
    author: '+12025550011',
    text: 'Check out this beautiful song!',
    id: Date.now() - 1000,
    attachments: [
      {
        contentType: 'audio/mp3',
        fileName: 'agnus_dei.mp4',
      },
    ],
  },
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));
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

const outgoing = new Whisper.Message({
  type: 'outgoing',
  body: 'I really like it!',
  sent_at: Date.now() - 18000000,
  quote: {
    author: '+12025550011',
    id: Date.now() - 1000,
    attachments: [
      {
        contentType: 'audio/mp3',
        fileName: 'agnus_dei.mp4',
      },
    ],
  },
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));
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

const outgoing = new Whisper.Message({
  type: 'outgoing',
  body: 'I really like it!',
  sent_at: Date.now() - 18000000,
  quote: {
    author: '+12025550011',
    id: Date.now() - 1000,
    attachments: [
      {
        // proposed as of afternoon of 4/6 in Quoted Replies group
        flags: textsecure.protobuf.AttachmentPointer.Flags.VOICE_MESSAGE,
        contentType: 'audio/mp3',
        fileName: 'agnus_dei.mp4',
      },
    ],
  },
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));
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

const outgoing = new Whisper.Message({
  type: 'outgoing',
  body: "I can't read latin.",
  sent_at: Date.now() - 18000000,
  quote: {
    author: '+12025550011',
    text: 'This is my manifesto. Tell me what you think!',
    id: Date.now() - 1000,
    attachments: [
      {
        contentType: 'text/plain',
        fileName: 'lorum_ipsum.txt',
      },
    ],
  },
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));
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

const outgoing = new Whisper.Message({
  type: 'outgoing',
  body: "Sorry, I can't read latin!",
  sent_at: Date.now() - 18000000,
  quote: {
    author: '+12025550011',
    id: Date.now() - 1000,
    attachments: [
      {
        contentType: 'text/plain',
        fileName: 'lorum_ipsum.txt',
      },
    ],
  },
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));
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 a quotation, including attachment

Quote, image attachment, and caption

const outgoing = new Whisper.Message({
  type: 'outgoing',
  body: 'Like pi or so?',
  sent_at: Date.now() - 18000000,
  quote: {
    text: 'How many ferrets do you have?',
    author: '+12025550011',
    id: Date.now() - 1000,
  },
  attachments: [{
    data: util.gif,
    fileName: 'pi.gif',
    contentType: 'image/gif',
  }],
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));
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>

Quote, image attachment

const outgoing = new Whisper.Message({
  type: 'outgoing',
  sent_at: Date.now() - 18000000,
  quote: {
    text: 'How many ferrets do you have?',
    author: '+12025550011',
    id: Date.now() - 1000,
  },
  attachments: [{
    data: util.gif,
    fileName: 'pi.gif',
    contentType: 'image/gif',
  }],
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));
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>

Quote, video attachment

const outgoing = new Whisper.Message({
  type: 'outgoing',
  sent_at: Date.now() - 18000000,
  quote: {
    text: 'How many ferrets do you have?',
    author: '+12025550011',
    id: Date.now() - 1000,
  },
  attachments: [{
    data: util.mp4,
    fileName: 'freezing_bubble.mp4',
    contentType: 'video/mp4',
  }],
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));
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>

Quote, audio attachment

const outgoing = new Whisper.Message({
  type: 'outgoing',
  sent_at: Date.now() - 18000000,
  quote: {
    text: 'How many ferrets do you have?',
    author: '+12025550011',
    id: Date.now() - 1000,
  },
  attachments: [{
    data: util.mp3,
    fileName: 'agnus_dei.mp3',
    contentType: 'audio/mp3',
  }],
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),
}));
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>

Quote, file attachment

const outgoing = new Whisper.Message({
  type: 'outgoing',
  sent_at: Date.now() - 18000000,
  quote: {
    text: 'How many ferrets do you have?',
    author: '+12025550011',
    id: Date.now() - 1000,
  },
  attachments: [{
    data: util.txt,
    fileName: 'lorum_ipsum.txt',
    contentType: 'text/plain',
  }],
});
const incoming = new Whisper.Message(Object.assign({}, outgoing.attributes, {
  source: '+12025550011',
  type: 'incoming',
  quote: Object.assign({}, outgoing.attributes.quote, {
    author: '+12025550005',
  }),

}));
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 bottom bar

Plain text

<div className={util.theme}>
  <div className="bottom-bar">
    <Quote
      text="How many ferrets do you have?"
      authorColor="blue"
      authorTitle={util.ourNumber}
      authorProfileName="Mr. Blue"
      id={Date.now() - 1000}
      i18n={window.i18n}
    />
  </div>
</div>

With an icon

<div className={util.theme}>
  <div className="bottom-bar">
    <Quote
      text="How many ferrets do you have?"
      authorColor="blue"
      authorTitle={util.ourNumber}
      authorProfileName="Mr. Blue"
      id={Date.now() - 1000}
      i18n={window.i18n}
      attachments={[{
        contentType: 'image/jpeg',
        fileName: 'llama.jpg',
      }]}
    />
  </div>
</div>

With an image

<div className={util.theme}>
  <div className="bottom-bar">
    <Quote
      text="How many ferrets do you have?"
      authorColor="blue"
      authorTitle={util.ourNumber}
      authorProfileName="Mr. Blue"
      id={Date.now() - 1000}
      i18n={window.i18n}
      attachments={[{
        contentType: 'image/gif',
        fileName: 'llama.gif',
        thumbnail: {
          objectUrl: util.gifObjectUrl
        },
      }]}
    />
  </div>
</div>

With a close button

<div className={util.theme}>
  <div className="bottom-bar">
    <Quote
      text="How many ferrets do you have?"
      authorColor="blue"
      authorTitle={util.ourNumber}
      authorProfileName="Mr. Blue"
      id={Date.now() - 1000}
      onClose={() => console.log('Close was clicked!')}
      i18n={window.i18n}
    />
  </div>
</div>

With a close button and icon

<div className={util.theme}>
  <div className="bottom-bar">
    <Quote
      text="How many ferrets do you have?"
      authorColor="blue"
      authorTitle={util.ourNumber}
      authorProfileName="Mr. Blue"
      id={Date.now() - 1000}
      onClose={() => console.log('Close was clicked!')}
      i18n={window.i18n}
      attachments={[{
        contentType: 'image/jpeg',
        fileName: 'llama.jpg',
      }]}
    />
  </div>
</div>

With a close button and image

<div className={util.theme}>
  <div className="bottom-bar">
    <Quote
      text="How many ferrets do you have?"
      authorColor="blue"
      authorTitle={util.ourNumber}
      authorProfileName="Mr. Blue"
      id={Date.now() - 1000}
      onClose={() => console.log('Close was clicked!')}
      i18n={window.i18n}
      attachments={[{
        contentType: 'image/gif',
        fileName: 'llama.gif',
        thumbnail: {
          objectUrl: util.gifObjectUrl
        },
      }]}
    />
  </div>
</div>