Do not download media if in call
This commit is contained in:
parent
d22add261b
commit
a096220990
16 changed files with 274 additions and 47 deletions
|
@ -4964,6 +4964,86 @@ button.module-conversation-details__action-button {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.module-image--not-downloaded {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
i {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-color: $color-gray-75;
|
||||
border-radius: 48px;
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
height: 17px;
|
||||
width: 17px;
|
||||
@include color-svg('../images/icons/v2/arrow-down-24.svg', $color-white);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
i {
|
||||
background-color: $color-black;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
i {
|
||||
background-color: $color-gray-75;
|
||||
border: 4px solid $ultramarine-ui-light;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.module-image__download-pending {
|
||||
position: relative;
|
||||
|
||||
&--spinner-container {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&--spinner {
|
||||
background-color: $color-gray-75;
|
||||
border-radius: 48px;
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
|
||||
.module-image-spinner {
|
||||
&__container {
|
||||
margin: 12px auto;
|
||||
}
|
||||
|
||||
&__arc {
|
||||
background-color: $color-gray-75;
|
||||
}
|
||||
|
||||
&__circle {
|
||||
background-color: $color-white;
|
||||
}
|
||||
|
||||
@include dark-theme {
|
||||
&__arc {
|
||||
background-color: $color-gray-75;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.module-image--with-background {
|
||||
@include light-theme {
|
||||
background-color: $color-white;
|
||||
|
|
|
@ -17,18 +17,25 @@ export const SpinnerDirections = [
|
|||
export type SpinnerDirection = typeof SpinnerDirections[number];
|
||||
|
||||
export type Props = {
|
||||
moduleClassName?: string;
|
||||
direction?: SpinnerDirection;
|
||||
size?: string;
|
||||
svgSize: SpinnerSvgSize;
|
||||
direction?: SpinnerDirection;
|
||||
};
|
||||
|
||||
export const Spinner = ({ size, svgSize, direction }: Props): JSX.Element => (
|
||||
export const Spinner = ({
|
||||
moduleClassName,
|
||||
size,
|
||||
svgSize,
|
||||
direction,
|
||||
}: Props): JSX.Element => (
|
||||
<div
|
||||
className={classNames(
|
||||
'module-spinner__container',
|
||||
`module-spinner__container--${svgSize}`,
|
||||
direction ? `module-spinner__container--${direction}` : null,
|
||||
direction ? `module-spinner__container--${svgSize}-${direction}` : null
|
||||
direction ? `module-spinner__container--${svgSize}-${direction}` : null,
|
||||
moduleClassName ? `${moduleClassName}__container` : null
|
||||
)}
|
||||
style={{
|
||||
height: size,
|
||||
|
@ -40,7 +47,8 @@ export const Spinner = ({ size, svgSize, direction }: Props): JSX.Element => (
|
|||
'module-spinner__circle',
|
||||
`module-spinner__circle--${svgSize}`,
|
||||
direction ? `module-spinner__circle--${direction}` : null,
|
||||
direction ? `module-spinner__circle--${svgSize}-${direction}` : null
|
||||
direction ? `module-spinner__circle--${svgSize}-${direction}` : null,
|
||||
moduleClassName ? `${moduleClassName}__circle` : null
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
|
@ -48,7 +56,8 @@ export const Spinner = ({ size, svgSize, direction }: Props): JSX.Element => (
|
|||
'module-spinner__arc',
|
||||
`module-spinner__arc--${svgSize}`,
|
||||
direction ? `module-spinner__arc--${direction}` : null,
|
||||
direction ? `module-spinner__arc--${svgSize}-${direction}` : null
|
||||
direction ? `module-spinner__arc--${svgSize}-${direction}` : null,
|
||||
moduleClassName ? `${moduleClassName}__arc` : null
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -121,6 +121,20 @@ story.add('Pending', () => {
|
|||
return <Image {...props} />;
|
||||
});
|
||||
|
||||
story.add('Pending w/blurhash', () => {
|
||||
const props = createProps();
|
||||
props.attachment.pending = true;
|
||||
|
||||
return (
|
||||
<Image
|
||||
{...props}
|
||||
blurHash="LDA,FDBnm+I=p{tkIUI;~UkpELV]"
|
||||
width={300}
|
||||
height={400}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
story.add('Curved Corners', () => {
|
||||
const props = createProps({
|
||||
curveBottomLeft: true,
|
||||
|
@ -176,6 +190,7 @@ story.add('Blurhash', () => {
|
|||
|
||||
return <Image {...props} />;
|
||||
});
|
||||
|
||||
story.add('Missing Image', () => {
|
||||
const defaultProps = createProps();
|
||||
const props = {
|
||||
|
|
|
@ -7,7 +7,7 @@ import { Blurhash } from 'react-blurhash';
|
|||
|
||||
import { Spinner } from '../Spinner';
|
||||
import { LocalizerType } from '../../types/Util';
|
||||
import { AttachmentType } from '../../types/Attachment';
|
||||
import { AttachmentType, hasNotDownloaded } from '../../types/Attachment';
|
||||
|
||||
export type Props = {
|
||||
alt: string;
|
||||
|
@ -44,10 +44,10 @@ export type Props = {
|
|||
|
||||
export class Image extends React.Component<Props> {
|
||||
private canClick() {
|
||||
const { onClick, attachment, url } = this.props;
|
||||
const { onClick, attachment, blurHash, url } = this.props;
|
||||
const { pending } = attachment || { pending: true };
|
||||
|
||||
return Boolean(onClick && !pending && url);
|
||||
return Boolean(onClick && !pending && (url || blurHash));
|
||||
}
|
||||
|
||||
public handleClick = (event: React.MouseEvent): void => {
|
||||
|
@ -87,6 +87,46 @@ export class Image extends React.Component<Props> {
|
|||
}
|
||||
};
|
||||
|
||||
public renderPending = (): JSX.Element => {
|
||||
const { blurHash, height, i18n, width } = this.props;
|
||||
|
||||
if (blurHash) {
|
||||
return (
|
||||
<div className="module-image__download-pending">
|
||||
<Blurhash
|
||||
hash={blurHash}
|
||||
width={width}
|
||||
height={height}
|
||||
style={{ display: 'block' }}
|
||||
/>
|
||||
<div className="module-image__download-pending--spinner-container">
|
||||
<div
|
||||
className="module-image__download-pending--spinner"
|
||||
title={i18n('loading')}
|
||||
>
|
||||
<Spinner moduleClassName="module-image-spinner" svgSize="small" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="module-image__loading-placeholder"
|
||||
style={{
|
||||
height: `${height}px`,
|
||||
width: `${width}px`,
|
||||
lineHeight: `${height}px`,
|
||||
textAlign: 'center',
|
||||
}}
|
||||
title={i18n('loading')}
|
||||
>
|
||||
<Spinner svgSize="normal" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
const {
|
||||
alt,
|
||||
|
@ -116,19 +156,20 @@ export class Image extends React.Component<Props> {
|
|||
|
||||
const { caption, pending } = attachment || { caption: null, pending: true };
|
||||
const canClick = this.canClick();
|
||||
const imgNotDownloaded = hasNotDownloaded(attachment);
|
||||
|
||||
const overlayClassName = classNames(
|
||||
'module-image__border-overlay',
|
||||
noBorder ? null : 'module-image__border-overlay--with-border',
|
||||
canClick ? 'module-image__border-overlay--with-click-handler' : null,
|
||||
curveTopLeft ? 'module-image--curved-top-left' : null,
|
||||
curveTopRight ? 'module-image--curved-top-right' : null,
|
||||
curveBottomLeft ? 'module-image--curved-bottom-left' : null,
|
||||
curveBottomRight ? 'module-image--curved-bottom-right' : null,
|
||||
smallCurveTopLeft ? 'module-image--small-curved-top-left' : null,
|
||||
softCorners ? 'module-image--soft-corners' : null,
|
||||
darkOverlay ? 'module-image__border-overlay--dark' : null
|
||||
);
|
||||
const overlayClassName = classNames('module-image__border-overlay', {
|
||||
'module-image__border-overlay--with-border': !noBorder,
|
||||
'module-image__border-overlay--with-click-handler': canClick,
|
||||
'module-image--curved-top-left': curveTopLeft,
|
||||
'module-image--curved-top-right': curveTopRight,
|
||||
'module-image--curved-bottom-left': curveBottomLeft,
|
||||
'module-image--curved-bottom-right': curveBottomRight,
|
||||
'module-image--small-curved-top-left': smallCurveTopLeft,
|
||||
'module-image--soft-corners': softCorners,
|
||||
'module-image__border-overlay--dark': darkOverlay,
|
||||
'module-image--not-downloaded': imgNotDownloaded,
|
||||
});
|
||||
|
||||
const overlay = canClick ? (
|
||||
// Not sure what this button does.
|
||||
|
@ -139,7 +180,9 @@ export class Image extends React.Component<Props> {
|
|||
onClick={this.handleClick}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
tabIndex={tabIndex}
|
||||
/>
|
||||
>
|
||||
{imgNotDownloaded ? <i /> : null}
|
||||
</button>
|
||||
) : null;
|
||||
|
||||
/* eslint-disable no-nested-ternary */
|
||||
|
@ -157,18 +200,7 @@ export class Image extends React.Component<Props> {
|
|||
)}
|
||||
>
|
||||
{pending ? (
|
||||
<div
|
||||
className="module-image__loading-placeholder"
|
||||
style={{
|
||||
height: `${height}px`,
|
||||
width: `${width}px`,
|
||||
lineHeight: `${height}px`,
|
||||
textAlign: 'center',
|
||||
}}
|
||||
title={i18n('loading')}
|
||||
>
|
||||
<Spinner svgSize="normal" />
|
||||
</div>
|
||||
this.renderPending()
|
||||
) : url ? (
|
||||
<img
|
||||
onError={onError}
|
||||
|
|
|
@ -75,6 +75,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
isTapToView: overrideProps.isTapToView,
|
||||
isTapToViewError: overrideProps.isTapToViewError,
|
||||
isTapToViewExpired: overrideProps.isTapToViewExpired,
|
||||
kickOffAttachmentDownload: action('kickOffAttachmentDownload'),
|
||||
openConversation: action('openConversation'),
|
||||
openLink: action('openLink'),
|
||||
previews: overrideProps.previews || [],
|
||||
|
|
|
@ -35,6 +35,8 @@ import {
|
|||
getGridDimensions,
|
||||
getImageDimensions,
|
||||
hasImage,
|
||||
hasNotDownloaded,
|
||||
hasVideoBlurHash,
|
||||
hasVideoScreenshot,
|
||||
isAudio,
|
||||
isImage,
|
||||
|
@ -162,6 +164,10 @@ export type PropsActions = {
|
|||
}) => void;
|
||||
showContactModal: (contactId: string) => void;
|
||||
|
||||
kickOffAttachmentDownload: (options: {
|
||||
attachment: AttachmentType;
|
||||
messageId: string;
|
||||
}) => void;
|
||||
showVisualAttachment: (options: {
|
||||
attachment: AttachmentType;
|
||||
messageId: string;
|
||||
|
@ -657,6 +663,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
direction,
|
||||
i18n,
|
||||
id,
|
||||
kickOffAttachmentDownload,
|
||||
quote,
|
||||
showVisualAttachment,
|
||||
isSticker,
|
||||
|
@ -680,7 +687,8 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
displayImage &&
|
||||
!imageBroken &&
|
||||
((isImage(attachments) && hasImage(attachments)) ||
|
||||
(isVideo(attachments) && hasVideoScreenshot(attachments)))
|
||||
(isVideo(attachments) &&
|
||||
(hasVideoBlurHash(attachments) || hasVideoScreenshot(attachments))))
|
||||
) {
|
||||
const prefix = isSticker ? 'sticker' : 'attachment';
|
||||
const bottomOverlay = !isSticker && !collapseMetadata;
|
||||
|
@ -713,7 +721,11 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
onError={this.handleImageError}
|
||||
tabIndex={tabIndex}
|
||||
onClick={attachment => {
|
||||
showVisualAttachment({ attachment, messageId: id });
|
||||
if (hasNotDownloaded(attachment)) {
|
||||
kickOffAttachmentDownload({ attachment, messageId: id });
|
||||
} else {
|
||||
showVisualAttachment({ attachment, messageId: id });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -1517,7 +1529,8 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
return (
|
||||
displayImage &&
|
||||
((isImage(attachments) && hasImage(attachments)) ||
|
||||
(isVideo(attachments) && hasVideoScreenshot(attachments)))
|
||||
(isVideo(attachments) &&
|
||||
(hasVideoBlurHash(attachments) || hasVideoScreenshot(attachments))))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1922,6 +1935,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
id,
|
||||
isTapToView,
|
||||
isTapToViewExpired,
|
||||
kickOffAttachmentDownload,
|
||||
openConversation,
|
||||
showContactDetail,
|
||||
showVisualAttachment,
|
||||
|
@ -1953,6 +1967,24 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!imageBroken &&
|
||||
attachments &&
|
||||
attachments.length > 0 &&
|
||||
!isAttachmentPending &&
|
||||
(isImage(attachments) || isVideo(attachments)) &&
|
||||
hasNotDownloaded(attachments[0])
|
||||
) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const attachment = attachments[0];
|
||||
|
||||
kickOffAttachmentDownload({ attachment, messageId: id });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!imageBroken &&
|
||||
attachments &&
|
||||
|
@ -1960,7 +1992,8 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
!isAttachmentPending &&
|
||||
canDisplayImage(attachments) &&
|
||||
((isImage(attachments) && hasImage(attachments)) ||
|
||||
(isVideo(attachments) && hasVideoScreenshot(attachments)))
|
||||
(isVideo(attachments) &&
|
||||
(hasVideoBlurHash(attachments) || hasVideoScreenshot(attachments))))
|
||||
) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
|
|
@ -33,6 +33,7 @@ const defaultMessage: MessageProps = {
|
|||
i18n,
|
||||
id: 'my-message',
|
||||
interactionMode: 'keyboard',
|
||||
kickOffAttachmentDownload: action('kickOffAttachmentDownload'),
|
||||
openConversation: () => null,
|
||||
openLink: () => null,
|
||||
previews: [],
|
||||
|
|
|
@ -36,6 +36,7 @@ const defaultMessageProps: MessagesProps = {
|
|||
i18n,
|
||||
id: 'messageId',
|
||||
interactionMode: 'keyboard',
|
||||
kickOffAttachmentDownload: () => null,
|
||||
openConversation: () => null,
|
||||
openLink: () => null,
|
||||
previews: [],
|
||||
|
|
|
@ -231,6 +231,7 @@ const actions = () => ({
|
|||
openConversation: action('openConversation'),
|
||||
showContactDetail: action('showContactDetail'),
|
||||
showContactModal: action('showContactModal'),
|
||||
kickOffAttachmentDownload: action('kickOffAttachmentDownload'),
|
||||
showVisualAttachment: action('showVisualAttachment'),
|
||||
downloadAttachment: action('downloadAttachment'),
|
||||
displayTapToViewMessage: action('displayTapToViewMessage'),
|
||||
|
|
|
@ -45,6 +45,7 @@ const getDefaultProps = () => ({
|
|||
retrySend: action('retrySend'),
|
||||
deleteMessage: action('deleteMessage'),
|
||||
deleteMessageForEveryone: action('deleteMessageForEveryone'),
|
||||
kickOffAttachmentDownload: action('kickOffAttachmentDownload'),
|
||||
showMessageDetail: action('showMessageDetail'),
|
||||
openConversation: action('openConversation'),
|
||||
showContactDetail: action('showContactDetail'),
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
ConversationType,
|
||||
} from '../state/ducks/conversations';
|
||||
import { getActiveCall } from '../state/ducks/calling';
|
||||
import { getCallSelector } from '../state/selectors/calling';
|
||||
import { getCallSelector, isInCall } from '../state/selectors/calling';
|
||||
import { PropsData } from '../components/conversation/Message';
|
||||
import { CallbackResultType } from '../textsecure/SendMessage';
|
||||
import { ExpirationTimerOptions } from '../util/ExpirationTimerOptions';
|
||||
|
@ -38,6 +38,7 @@ import {
|
|||
getCallingNotificationText,
|
||||
} from '../util/callingNotification';
|
||||
import { PropsType as ProfileChangeNotificationPropsType } from '../components/conversation/ProfileChangeNotification';
|
||||
import { isImage, isVideo } from '../types/Attachment';
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
/* eslint-disable more/no-then */
|
||||
|
@ -2136,14 +2137,23 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
}
|
||||
|
||||
canDownload(): boolean {
|
||||
const conversation = this.getConversation();
|
||||
const isAccepted = Boolean(conversation && conversation.getAccepted());
|
||||
|
||||
if (this.isOutgoing()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isAccepted;
|
||||
const conversation = this.getConversation();
|
||||
const isAccepted = Boolean(conversation && conversation.getAccepted());
|
||||
if (!isAccepted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure that all attachments are downloadable
|
||||
const attachments = this.get('attachments');
|
||||
if (attachments && attachments.length) {
|
||||
return attachments.every(attachment => Boolean(attachment.path));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
canReply(): boolean {
|
||||
|
@ -3527,8 +3537,16 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
|
||||
// Only queue attachments for downloads if this is an outgoing message
|
||||
// or we've accepted the conversation
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
if (this.getConversation()!.getAccepted() || message.isOutgoing()) {
|
||||
const reduxState = window.reduxStore.getState();
|
||||
const attachments = this.get('attachments') || [];
|
||||
const shouldHoldOffDownload =
|
||||
(isImage(attachments) || isVideo(attachments)) &&
|
||||
isInCall(reduxState);
|
||||
if (
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
(this.getConversation()!.getAccepted() || message.isOutgoing()) &&
|
||||
!shouldHoldOffDownload
|
||||
) {
|
||||
await message.queueAttachmentDownloads();
|
||||
}
|
||||
|
||||
|
|
|
@ -8,12 +8,18 @@ import {
|
|||
CallingStateType,
|
||||
CallsByConversationType,
|
||||
DirectCallStateType,
|
||||
getActiveCall,
|
||||
} from '../ducks/calling';
|
||||
import { CallMode, CallState } from '../../types/Calling';
|
||||
import { getOwn } from '../../util/getOwn';
|
||||
|
||||
const getCalling = (state: StateType): CallingStateType => state.calling;
|
||||
|
||||
export const isInCall = createSelector(
|
||||
getCalling,
|
||||
(state: CallingStateType): boolean => Boolean(getActiveCall(state))
|
||||
);
|
||||
|
||||
export const getCallsByConversation = createSelector(
|
||||
getCalling,
|
||||
(state: CallingStateType): CallsByConversationType =>
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
getCallsByConversation,
|
||||
getCallSelector,
|
||||
getIncomingCall,
|
||||
isInCall,
|
||||
} from '../../../state/selectors/calling';
|
||||
import { getEmptyState, CallingStateType } from '../../../state/ducks/calling';
|
||||
|
||||
|
@ -132,4 +133,14 @@ describe('state/selectors/calling', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isInCall', () => {
|
||||
it('returns should be false if we are not in a call', () => {
|
||||
assert.isFalse(isInCall(getEmptyRootState()));
|
||||
});
|
||||
|
||||
it('should be true if we are in a call', () => {
|
||||
assert.isTrue(isInCall(getCallingState(stateWithActiveDirectCall)));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -169,6 +169,16 @@ export function isVideoAttachment(
|
|||
);
|
||||
}
|
||||
|
||||
export function hasNotDownloaded(attachment?: AttachmentType): boolean {
|
||||
return Boolean(attachment && !attachment.url && attachment.blurHash);
|
||||
}
|
||||
|
||||
export function hasVideoBlurHash(attachments?: Array<AttachmentType>): boolean {
|
||||
const firstAttachment = attachments ? attachments[0] : null;
|
||||
|
||||
return Boolean(firstAttachment && firstAttachment.blurHash);
|
||||
}
|
||||
|
||||
export function hasVideoScreenshot(
|
||||
attachments?: Array<AttachmentType>
|
||||
): string | null | undefined {
|
||||
|
|
|
@ -14841,7 +14841,7 @@
|
|||
"rule": "React-createRef",
|
||||
"path": "ts/components/conversation/Message.tsx",
|
||||
"line": " public audioRef: React.RefObject<HTMLAudioElement> = React.createRef();",
|
||||
"lineNumber": 214,
|
||||
"lineNumber": 220,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-09-08T20:19:01.913Z"
|
||||
},
|
||||
|
@ -14849,7 +14849,7 @@
|
|||
"rule": "React-createRef",
|
||||
"path": "ts/components/conversation/Message.tsx",
|
||||
"line": " public focusRef: React.RefObject<HTMLDivElement> = React.createRef();",
|
||||
"lineNumber": 216,
|
||||
"lineNumber": 222,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-09-08T20:19:01.913Z"
|
||||
},
|
||||
|
@ -14857,7 +14857,7 @@
|
|||
"rule": "React-createRef",
|
||||
"path": "ts/components/conversation/Message.tsx",
|
||||
"line": " > = React.createRef();",
|
||||
"lineNumber": 220,
|
||||
"lineNumber": 226,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-08-28T19:36:40.817Z"
|
||||
},
|
||||
|
|
|
@ -742,6 +742,13 @@ Whisper.ConversationView = Whisper.View.extend({
|
|||
const showContactDetail = (options: any) => {
|
||||
this.showContactDetail(options);
|
||||
};
|
||||
const kickOffAttachmentDownload = async (options: any) => {
|
||||
if (!this.model.messageCollection) {
|
||||
throw new Error('Message collection does not exist');
|
||||
}
|
||||
const message = this.model.messageCollection.get(options.messageId);
|
||||
await message.queueAttachmentDownloads();
|
||||
};
|
||||
const showVisualAttachment = (options: any) => {
|
||||
this.showLightbox(options);
|
||||
};
|
||||
|
@ -924,6 +931,7 @@ Whisper.ConversationView = Whisper.View.extend({
|
|||
displayTapToViewMessage,
|
||||
downloadAttachment,
|
||||
downloadNewVersion,
|
||||
kickOffAttachmentDownload,
|
||||
loadNewerMessages,
|
||||
loadNewestMessages: this.loadNewestMessages.bind(this),
|
||||
loadAndScroll: this.loadAndScroll.bind(this),
|
||||
|
|
Loading…
Add table
Reference in a new issue