Display proper text when quoting view once message
This commit is contained in:
parent
81227066ce
commit
b009967a83
9 changed files with 102 additions and 12 deletions
|
@ -2226,15 +2226,15 @@
|
||||||
},
|
},
|
||||||
"message--getDescription--disappearing-media": {
|
"message--getDescription--disappearing-media": {
|
||||||
"message": "View-once Media",
|
"message": "View-once Media",
|
||||||
"description": "Shown in notifications and in the left pane after view-once message is deleted."
|
"description": "Shown in notifications and in the left pane after view-once message is deleted. Also shown when quoting a view once media."
|
||||||
},
|
},
|
||||||
"message--getDescription--disappearing-photo": {
|
"message--getDescription--disappearing-photo": {
|
||||||
"message": "View-once Photo",
|
"message": "View-once Photo",
|
||||||
"description": "Shown in notifications and in the left pane when a message is a view once photo."
|
"description": "Shown in notifications and in the left pane when a message is a view once photo. Also shown when quoting a view once photo."
|
||||||
},
|
},
|
||||||
"message--getDescription--disappearing-video": {
|
"message--getDescription--disappearing-video": {
|
||||||
"message": "View-once Video",
|
"message": "View-once Video",
|
||||||
"description": "Shown in notifications and in the left pane when a message is a view once video."
|
"description": "Shown in notifications and in the left pane when a message is a view once video. Also shown when quoting a view once video."
|
||||||
},
|
},
|
||||||
"message--deletedForEveryone": {
|
"message--deletedForEveryone": {
|
||||||
"message": "This message was deleted.",
|
"message": "This message was deleted.",
|
||||||
|
|
|
@ -1789,6 +1789,9 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05',
|
||||||
.module-quote__icon-container__icon--movie {
|
.module-quote__icon-container__icon--movie {
|
||||||
@include color-svg('../images/movie.svg', $color-ultramarine);
|
@include color-svg('../images/movie.svg', $color-ultramarine);
|
||||||
}
|
}
|
||||||
|
.module-quote__icon-container__icon--view-once {
|
||||||
|
@include color-svg('../images/icons/v2/view-once-24.svg', $color-ultramarine);
|
||||||
|
}
|
||||||
|
|
||||||
.module-quote__generic-file {
|
.module-quote__generic-file {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -149,6 +149,7 @@ export type PropsData = {
|
||||||
authorName?: string;
|
authorName?: string;
|
||||||
bodyRanges?: BodyRangesType;
|
bodyRanges?: BodyRangesType;
|
||||||
referencedMessageNotFound: boolean;
|
referencedMessageNotFound: boolean;
|
||||||
|
isViewOnce: boolean;
|
||||||
};
|
};
|
||||||
previews: Array<LinkPreviewType>;
|
previews: Array<LinkPreviewType>;
|
||||||
isExpired?: boolean;
|
isExpired?: boolean;
|
||||||
|
@ -1062,7 +1063,7 @@ export class Message extends React.Component<Props, State> {
|
||||||
|
|
||||||
const withContentAbove =
|
const withContentAbove =
|
||||||
conversationType === 'group' && direction === 'incoming';
|
conversationType === 'group' && direction === 'incoming';
|
||||||
const { referencedMessageNotFound } = quote;
|
const { isViewOnce, referencedMessageNotFound } = quote;
|
||||||
|
|
||||||
const clickHandler = disableScroll
|
const clickHandler = disableScroll
|
||||||
? undefined
|
? undefined
|
||||||
|
@ -1087,6 +1088,7 @@ export class Message extends React.Component<Props, State> {
|
||||||
bodyRanges={quote.bodyRanges}
|
bodyRanges={quote.bodyRanges}
|
||||||
conversationColor={conversationColor}
|
conversationColor={conversationColor}
|
||||||
customColor={customColor}
|
customColor={customColor}
|
||||||
|
isViewOnce={isViewOnce}
|
||||||
referencedMessageNotFound={referencedMessageNotFound}
|
referencedMessageNotFound={referencedMessageNotFound}
|
||||||
isFromMe={quote.isFromMe}
|
isFromMe={quote.isFromMe}
|
||||||
withContentAbove={withContentAbove}
|
withContentAbove={withContentAbove}
|
||||||
|
|
|
@ -81,6 +81,7 @@ const renderInMessage = ({
|
||||||
conversationColor,
|
conversationColor,
|
||||||
isFromMe,
|
isFromMe,
|
||||||
rawAttachment,
|
rawAttachment,
|
||||||
|
isViewOnce,
|
||||||
referencedMessageNotFound,
|
referencedMessageNotFound,
|
||||||
text: quoteText,
|
text: quoteText,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
|
@ -96,6 +97,7 @@ const renderInMessage = ({
|
||||||
conversationColor,
|
conversationColor,
|
||||||
isFromMe,
|
isFromMe,
|
||||||
rawAttachment,
|
rawAttachment,
|
||||||
|
isViewOnce,
|
||||||
referencedMessageNotFound,
|
referencedMessageNotFound,
|
||||||
sentAt: Date.now() - 30 * 1000,
|
sentAt: Date.now() - 30 * 1000,
|
||||||
text: quoteText,
|
text: quoteText,
|
||||||
|
@ -133,6 +135,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||||
'referencedMessageNotFound',
|
'referencedMessageNotFound',
|
||||||
overrideProps.referencedMessageNotFound || false
|
overrideProps.referencedMessageNotFound || false
|
||||||
),
|
),
|
||||||
|
isViewOnce: boolean('isViewOnce', overrideProps.isViewOnce || false),
|
||||||
text: text(
|
text: text(
|
||||||
'text',
|
'text',
|
||||||
isString(overrideProps.text)
|
isString(overrideProps.text)
|
||||||
|
@ -247,6 +250,20 @@ story.add('Image Attachment w/o Thumbnail', () => {
|
||||||
return <Quote {...props} />;
|
return <Quote {...props} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
story.add('Image Tap-to-View', () => {
|
||||||
|
const props = createProps({
|
||||||
|
text: '',
|
||||||
|
isViewOnce: true,
|
||||||
|
rawAttachment: {
|
||||||
|
contentType: IMAGE_PNG,
|
||||||
|
fileName: 'sax.png',
|
||||||
|
isVoiceMessage: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return <Quote {...props} />;
|
||||||
|
});
|
||||||
|
|
||||||
story.add('Video Only', () => {
|
story.add('Video Only', () => {
|
||||||
const props = createProps({
|
const props = createProps({
|
||||||
rawAttachment: {
|
rawAttachment: {
|
||||||
|
@ -293,6 +310,20 @@ story.add('Video Attachment w/o Thumbnail', () => {
|
||||||
return <Quote {...props} />;
|
return <Quote {...props} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
story.add('Video Tap-to-View', () => {
|
||||||
|
const props = createProps({
|
||||||
|
text: '',
|
||||||
|
isViewOnce: true,
|
||||||
|
rawAttachment: {
|
||||||
|
contentType: VIDEO_MP4,
|
||||||
|
fileName: 'great-video.mp4',
|
||||||
|
isVoiceMessage: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return <Quote {...props} />;
|
||||||
|
});
|
||||||
|
|
||||||
story.add('Audio Only', () => {
|
story.add('Audio Only', () => {
|
||||||
const props = createProps({
|
const props = createProps({
|
||||||
rawAttachment: {
|
rawAttachment: {
|
||||||
|
@ -359,6 +390,20 @@ story.add('Other File Only', () => {
|
||||||
return <Quote {...props} />;
|
return <Quote {...props} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
story.add('Media Tap-to-View', () => {
|
||||||
|
const props = createProps({
|
||||||
|
text: '',
|
||||||
|
isViewOnce: true,
|
||||||
|
rawAttachment: {
|
||||||
|
contentType: AUDIO_MP3,
|
||||||
|
fileName: 'great-video.mp3',
|
||||||
|
isVoiceMessage: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return <Quote {...props} />;
|
||||||
|
});
|
||||||
|
|
||||||
story.add('Other File Attachment', () => {
|
story.add('Other File Attachment', () => {
|
||||||
const props = createProps({
|
const props = createProps({
|
||||||
rawAttachment: {
|
rawAttachment: {
|
||||||
|
|
|
@ -31,6 +31,7 @@ export type Props = {
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
text: string;
|
text: string;
|
||||||
rawAttachment?: QuotedAttachmentType;
|
rawAttachment?: QuotedAttachmentType;
|
||||||
|
isViewOnce: boolean;
|
||||||
referencedMessageNotFound: boolean;
|
referencedMessageNotFound: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -83,19 +84,32 @@ function getObjectUrl(thumbnail: Attachment | undefined): string | undefined {
|
||||||
|
|
||||||
function getTypeLabel({
|
function getTypeLabel({
|
||||||
i18n,
|
i18n,
|
||||||
|
isViewOnce = false,
|
||||||
contentType,
|
contentType,
|
||||||
isVoiceMessage,
|
isVoiceMessage,
|
||||||
}: {
|
}: {
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
|
isViewOnce?: boolean;
|
||||||
contentType: MIME.MIMEType;
|
contentType: MIME.MIMEType;
|
||||||
isVoiceMessage: boolean;
|
isVoiceMessage: boolean;
|
||||||
}): string | undefined {
|
}): string | undefined {
|
||||||
if (GoogleChrome.isVideoTypeSupported(contentType)) {
|
if (GoogleChrome.isVideoTypeSupported(contentType)) {
|
||||||
|
if (isViewOnce) {
|
||||||
|
return i18n('message--getDescription--disappearing-video');
|
||||||
|
}
|
||||||
return i18n('video');
|
return i18n('video');
|
||||||
}
|
}
|
||||||
if (GoogleChrome.isImageTypeSupported(contentType)) {
|
if (GoogleChrome.isImageTypeSupported(contentType)) {
|
||||||
|
if (isViewOnce) {
|
||||||
|
return i18n('message--getDescription--disappearing-photo');
|
||||||
|
}
|
||||||
return i18n('photo');
|
return i18n('photo');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isViewOnce) {
|
||||||
|
return i18n('message--getDescription--disappearing-media');
|
||||||
|
}
|
||||||
|
|
||||||
if (MIME.isAudio(contentType) && isVoiceMessage) {
|
if (MIME.isAudio(contentType) && isVoiceMessage) {
|
||||||
return i18n('voiceMessage');
|
return i18n('voiceMessage');
|
||||||
}
|
}
|
||||||
|
@ -217,7 +231,7 @@ export class Quote extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public renderIconContainer(): JSX.Element | null {
|
public renderIconContainer(): JSX.Element | null {
|
||||||
const { rawAttachment } = this.props;
|
const { rawAttachment, isViewOnce } = this.props;
|
||||||
const { imageBroken } = this.state;
|
const { imageBroken } = this.state;
|
||||||
const attachment = getAttachment(rawAttachment);
|
const attachment = getAttachment(rawAttachment);
|
||||||
|
|
||||||
|
@ -228,6 +242,10 @@ export class Quote extends React.Component<Props, State> {
|
||||||
const { contentType, thumbnail } = attachment;
|
const { contentType, thumbnail } = attachment;
|
||||||
const objectUrl = getObjectUrl(thumbnail);
|
const objectUrl = getObjectUrl(thumbnail);
|
||||||
|
|
||||||
|
if (isViewOnce) {
|
||||||
|
return this.renderIcon('view-once');
|
||||||
|
}
|
||||||
|
|
||||||
if (GoogleChrome.isVideoTypeSupported(contentType)) {
|
if (GoogleChrome.isVideoTypeSupported(contentType)) {
|
||||||
return objectUrl && !imageBroken
|
return objectUrl && !imageBroken
|
||||||
? this.renderImage(objectUrl, 'play')
|
? this.renderImage(objectUrl, 'play')
|
||||||
|
@ -246,7 +264,14 @@ export class Quote extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public renderText(): JSX.Element | null {
|
public renderText(): JSX.Element | null {
|
||||||
const { bodyRanges, i18n, text, rawAttachment, isIncoming } = this.props;
|
const {
|
||||||
|
bodyRanges,
|
||||||
|
i18n,
|
||||||
|
text,
|
||||||
|
rawAttachment,
|
||||||
|
isIncoming,
|
||||||
|
isViewOnce,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
if (text) {
|
if (text) {
|
||||||
const quoteText = bodyRanges
|
const quoteText = bodyRanges
|
||||||
|
@ -274,7 +299,12 @@ export class Quote extends React.Component<Props, State> {
|
||||||
|
|
||||||
const { contentType, isVoiceMessage } = attachment;
|
const { contentType, isVoiceMessage } = attachment;
|
||||||
|
|
||||||
const typeLabel = getTypeLabel({ i18n, contentType, isVoiceMessage });
|
const typeLabel = getTypeLabel({
|
||||||
|
i18n,
|
||||||
|
isViewOnce,
|
||||||
|
contentType,
|
||||||
|
isVoiceMessage,
|
||||||
|
});
|
||||||
if (typeLabel) {
|
if (typeLabel) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
1
ts/model-types.d.ts
vendored
1
ts/model-types.d.ts
vendored
|
@ -64,6 +64,7 @@ export type QuotedMessageType = {
|
||||||
bodyRanges: BodyRangesType;
|
bodyRanges: BodyRangesType;
|
||||||
id: string;
|
id: string;
|
||||||
referencedMessageNotFound: boolean;
|
referencedMessageNotFound: boolean;
|
||||||
|
isViewOnce: boolean;
|
||||||
text: string;
|
text: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ import {
|
||||||
trimForDisplay,
|
trimForDisplay,
|
||||||
verifyAccessKey,
|
verifyAccessKey,
|
||||||
} from '../Crypto';
|
} from '../Crypto';
|
||||||
import { GroupChangeClass } from '../textsecure.d';
|
import { GroupChangeClass, DataMessageClass } from '../textsecure.d';
|
||||||
import { BodyRangesType } from '../types/Util';
|
import { BodyRangesType } from '../types/Util';
|
||||||
import { getTextWithMentions } from '../util';
|
import { getTextWithMentions } from '../util';
|
||||||
import { migrateColor } from '../util/migrateColor';
|
import { migrateColor } from '../util/migrateColor';
|
||||||
|
@ -3130,7 +3130,7 @@ export class ConversationModel extends window.Backbone
|
||||||
|
|
||||||
async makeQuote(
|
async makeQuote(
|
||||||
quotedMessage: typeof window.Whisper.MessageType
|
quotedMessage: typeof window.Whisper.MessageType
|
||||||
): Promise<WhatIsThis> {
|
): Promise<DataMessageClass.Quote> {
|
||||||
const { getName } = Contact;
|
const { getName } = Contact;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
const contact = quotedMessage.getContact()!;
|
const contact = quotedMessage.getContact()!;
|
||||||
|
@ -3150,6 +3150,7 @@ export class ConversationModel extends window.Backbone
|
||||||
bodyRanges: quotedMessage.get('bodyRanges'),
|
bodyRanges: quotedMessage.get('bodyRanges'),
|
||||||
id: quotedMessage.get('sent_at'),
|
id: quotedMessage.get('sent_at'),
|
||||||
text: body || embeddedContactName,
|
text: body || embeddedContactName,
|
||||||
|
isViewOnce: quotedMessage.isTapToView(),
|
||||||
attachments: quotedMessage.isTapToView()
|
attachments: quotedMessage.isTapToView()
|
||||||
? [{ contentType: 'image/jpeg', fileName: null }]
|
? [{ contentType: 'image/jpeg', fileName: null }]
|
||||||
: await this.getQuoteAttachment(attachments, preview, sticker),
|
: await this.getQuoteAttachment(attachments, preview, sticker),
|
||||||
|
|
|
@ -1243,6 +1243,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
authorUuid,
|
authorUuid,
|
||||||
bodyRanges,
|
bodyRanges,
|
||||||
id: sentAt,
|
id: sentAt,
|
||||||
|
isViewOnce,
|
||||||
referencedMessageNotFound,
|
referencedMessageNotFound,
|
||||||
text,
|
text,
|
||||||
} = quote;
|
} = quote;
|
||||||
|
@ -1342,6 +1343,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
rawAttachment: firstAttachment
|
rawAttachment: firstAttachment
|
||||||
? this.processQuoteAttachment(firstAttachment)
|
? this.processQuoteAttachment(firstAttachment)
|
||||||
: undefined,
|
: undefined,
|
||||||
|
isViewOnce,
|
||||||
referencedMessageNotFound: !foundReference,
|
referencedMessageNotFound: !foundReference,
|
||||||
sentAt: Number(sentAt),
|
sentAt: Number(sentAt),
|
||||||
text: this.createNonBreakingLastSeparator(text),
|
text: this.createNonBreakingLastSeparator(text),
|
||||||
|
@ -3363,10 +3365,15 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
contentType: 'image/jpeg',
|
contentType: 'image/jpeg',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
quote.isViewOnce = true;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
quote.isViewOnce = false;
|
||||||
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
quote.text = originalMessage.get('body');
|
quote.text = originalMessage.get('body');
|
||||||
if (firstAttachment) {
|
if (firstAttachment) {
|
||||||
|
|
7
ts/textsecure.d.ts
vendored
7
ts/textsecure.d.ts
vendored
|
@ -653,14 +653,15 @@ export declare namespace DataMessageClass {
|
||||||
|
|
||||||
// Note: deep nesting
|
// Note: deep nesting
|
||||||
class Quote {
|
class Quote {
|
||||||
id: ProtoBigNumberType | null;
|
id?: ProtoBigNumberType | null;
|
||||||
authorUuid: string | null;
|
authorUuid?: string | null;
|
||||||
text: string | null;
|
text?: string | null;
|
||||||
attachments?: Array<DataMessageClass.Quote.QuotedAttachment>;
|
attachments?: Array<DataMessageClass.Quote.QuotedAttachment>;
|
||||||
bodyRanges?: Array<DataMessageClass.BodyRange>;
|
bodyRanges?: Array<DataMessageClass.BodyRange>;
|
||||||
|
|
||||||
// Added later during processing
|
// Added later during processing
|
||||||
referencedMessageNotFound?: boolean;
|
referencedMessageNotFound?: boolean;
|
||||||
|
isViewOnce?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class BodyRange {
|
class BodyRange {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue