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;
|
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 {
|
.module-image--with-background {
|
||||||
@include light-theme {
|
@include light-theme {
|
||||||
background-color: $color-white;
|
background-color: $color-white;
|
||||||
|
|
|
@ -17,18 +17,25 @@ export const SpinnerDirections = [
|
||||||
export type SpinnerDirection = typeof SpinnerDirections[number];
|
export type SpinnerDirection = typeof SpinnerDirections[number];
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
|
moduleClassName?: string;
|
||||||
|
direction?: SpinnerDirection;
|
||||||
size?: string;
|
size?: string;
|
||||||
svgSize: SpinnerSvgSize;
|
svgSize: SpinnerSvgSize;
|
||||||
direction?: SpinnerDirection;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Spinner = ({ size, svgSize, direction }: Props): JSX.Element => (
|
export const Spinner = ({
|
||||||
|
moduleClassName,
|
||||||
|
size,
|
||||||
|
svgSize,
|
||||||
|
direction,
|
||||||
|
}: Props): JSX.Element => (
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'module-spinner__container',
|
'module-spinner__container',
|
||||||
`module-spinner__container--${svgSize}`,
|
`module-spinner__container--${svgSize}`,
|
||||||
direction ? `module-spinner__container--${direction}` : null,
|
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={{
|
style={{
|
||||||
height: size,
|
height: size,
|
||||||
|
@ -40,7 +47,8 @@ export const Spinner = ({ size, svgSize, direction }: Props): JSX.Element => (
|
||||||
'module-spinner__circle',
|
'module-spinner__circle',
|
||||||
`module-spinner__circle--${svgSize}`,
|
`module-spinner__circle--${svgSize}`,
|
||||||
direction ? `module-spinner__circle--${direction}` : null,
|
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
|
<div
|
||||||
|
@ -48,7 +56,8 @@ export const Spinner = ({ size, svgSize, direction }: Props): JSX.Element => (
|
||||||
'module-spinner__arc',
|
'module-spinner__arc',
|
||||||
`module-spinner__arc--${svgSize}`,
|
`module-spinner__arc--${svgSize}`,
|
||||||
direction ? `module-spinner__arc--${direction}` : null,
|
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>
|
</div>
|
||||||
|
|
|
@ -121,6 +121,20 @@ story.add('Pending', () => {
|
||||||
return <Image {...props} />;
|
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', () => {
|
story.add('Curved Corners', () => {
|
||||||
const props = createProps({
|
const props = createProps({
|
||||||
curveBottomLeft: true,
|
curveBottomLeft: true,
|
||||||
|
@ -176,6 +190,7 @@ story.add('Blurhash', () => {
|
||||||
|
|
||||||
return <Image {...props} />;
|
return <Image {...props} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
story.add('Missing Image', () => {
|
story.add('Missing Image', () => {
|
||||||
const defaultProps = createProps();
|
const defaultProps = createProps();
|
||||||
const props = {
|
const props = {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { Blurhash } from 'react-blurhash';
|
||||||
|
|
||||||
import { Spinner } from '../Spinner';
|
import { Spinner } from '../Spinner';
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType } from '../../types/Util';
|
||||||
import { AttachmentType } from '../../types/Attachment';
|
import { AttachmentType, hasNotDownloaded } from '../../types/Attachment';
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
alt: string;
|
alt: string;
|
||||||
|
@ -44,10 +44,10 @@ export type Props = {
|
||||||
|
|
||||||
export class Image extends React.Component<Props> {
|
export class Image extends React.Component<Props> {
|
||||||
private canClick() {
|
private canClick() {
|
||||||
const { onClick, attachment, url } = this.props;
|
const { onClick, attachment, blurHash, url } = this.props;
|
||||||
const { pending } = attachment || { pending: true };
|
const { pending } = attachment || { pending: true };
|
||||||
|
|
||||||
return Boolean(onClick && !pending && url);
|
return Boolean(onClick && !pending && (url || blurHash));
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleClick = (event: React.MouseEvent): void => {
|
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 {
|
public render(): JSX.Element {
|
||||||
const {
|
const {
|
||||||
alt,
|
alt,
|
||||||
|
@ -116,19 +156,20 @@ export class Image extends React.Component<Props> {
|
||||||
|
|
||||||
const { caption, pending } = attachment || { caption: null, pending: true };
|
const { caption, pending } = attachment || { caption: null, pending: true };
|
||||||
const canClick = this.canClick();
|
const canClick = this.canClick();
|
||||||
|
const imgNotDownloaded = hasNotDownloaded(attachment);
|
||||||
|
|
||||||
const overlayClassName = classNames(
|
const overlayClassName = classNames('module-image__border-overlay', {
|
||||||
'module-image__border-overlay',
|
'module-image__border-overlay--with-border': !noBorder,
|
||||||
noBorder ? null : 'module-image__border-overlay--with-border',
|
'module-image__border-overlay--with-click-handler': canClick,
|
||||||
canClick ? 'module-image__border-overlay--with-click-handler' : null,
|
'module-image--curved-top-left': curveTopLeft,
|
||||||
curveTopLeft ? 'module-image--curved-top-left' : null,
|
'module-image--curved-top-right': curveTopRight,
|
||||||
curveTopRight ? 'module-image--curved-top-right' : null,
|
'module-image--curved-bottom-left': curveBottomLeft,
|
||||||
curveBottomLeft ? 'module-image--curved-bottom-left' : null,
|
'module-image--curved-bottom-right': curveBottomRight,
|
||||||
curveBottomRight ? 'module-image--curved-bottom-right' : null,
|
'module-image--small-curved-top-left': smallCurveTopLeft,
|
||||||
smallCurveTopLeft ? 'module-image--small-curved-top-left' : null,
|
'module-image--soft-corners': softCorners,
|
||||||
softCorners ? 'module-image--soft-corners' : null,
|
'module-image__border-overlay--dark': darkOverlay,
|
||||||
darkOverlay ? 'module-image__border-overlay--dark' : null
|
'module-image--not-downloaded': imgNotDownloaded,
|
||||||
);
|
});
|
||||||
|
|
||||||
const overlay = canClick ? (
|
const overlay = canClick ? (
|
||||||
// Not sure what this button does.
|
// Not sure what this button does.
|
||||||
|
@ -139,7 +180,9 @@ export class Image extends React.Component<Props> {
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
onKeyDown={this.handleKeyDown}
|
onKeyDown={this.handleKeyDown}
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
/>
|
>
|
||||||
|
{imgNotDownloaded ? <i /> : null}
|
||||||
|
</button>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
/* eslint-disable no-nested-ternary */
|
/* eslint-disable no-nested-ternary */
|
||||||
|
@ -157,18 +200,7 @@ export class Image extends React.Component<Props> {
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{pending ? (
|
{pending ? (
|
||||||
<div
|
this.renderPending()
|
||||||
className="module-image__loading-placeholder"
|
|
||||||
style={{
|
|
||||||
height: `${height}px`,
|
|
||||||
width: `${width}px`,
|
|
||||||
lineHeight: `${height}px`,
|
|
||||||
textAlign: 'center',
|
|
||||||
}}
|
|
||||||
title={i18n('loading')}
|
|
||||||
>
|
|
||||||
<Spinner svgSize="normal" />
|
|
||||||
</div>
|
|
||||||
) : url ? (
|
) : url ? (
|
||||||
<img
|
<img
|
||||||
onError={onError}
|
onError={onError}
|
||||||
|
|
|
@ -75,6 +75,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||||
isTapToView: overrideProps.isTapToView,
|
isTapToView: overrideProps.isTapToView,
|
||||||
isTapToViewError: overrideProps.isTapToViewError,
|
isTapToViewError: overrideProps.isTapToViewError,
|
||||||
isTapToViewExpired: overrideProps.isTapToViewExpired,
|
isTapToViewExpired: overrideProps.isTapToViewExpired,
|
||||||
|
kickOffAttachmentDownload: action('kickOffAttachmentDownload'),
|
||||||
openConversation: action('openConversation'),
|
openConversation: action('openConversation'),
|
||||||
openLink: action('openLink'),
|
openLink: action('openLink'),
|
||||||
previews: overrideProps.previews || [],
|
previews: overrideProps.previews || [],
|
||||||
|
|
|
@ -35,6 +35,8 @@ import {
|
||||||
getGridDimensions,
|
getGridDimensions,
|
||||||
getImageDimensions,
|
getImageDimensions,
|
||||||
hasImage,
|
hasImage,
|
||||||
|
hasNotDownloaded,
|
||||||
|
hasVideoBlurHash,
|
||||||
hasVideoScreenshot,
|
hasVideoScreenshot,
|
||||||
isAudio,
|
isAudio,
|
||||||
isImage,
|
isImage,
|
||||||
|
@ -162,6 +164,10 @@ export type PropsActions = {
|
||||||
}) => void;
|
}) => void;
|
||||||
showContactModal: (contactId: string) => void;
|
showContactModal: (contactId: string) => void;
|
||||||
|
|
||||||
|
kickOffAttachmentDownload: (options: {
|
||||||
|
attachment: AttachmentType;
|
||||||
|
messageId: string;
|
||||||
|
}) => void;
|
||||||
showVisualAttachment: (options: {
|
showVisualAttachment: (options: {
|
||||||
attachment: AttachmentType;
|
attachment: AttachmentType;
|
||||||
messageId: string;
|
messageId: string;
|
||||||
|
@ -657,6 +663,7 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
direction,
|
direction,
|
||||||
i18n,
|
i18n,
|
||||||
id,
|
id,
|
||||||
|
kickOffAttachmentDownload,
|
||||||
quote,
|
quote,
|
||||||
showVisualAttachment,
|
showVisualAttachment,
|
||||||
isSticker,
|
isSticker,
|
||||||
|
@ -680,7 +687,8 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
displayImage &&
|
displayImage &&
|
||||||
!imageBroken &&
|
!imageBroken &&
|
||||||
((isImage(attachments) && hasImage(attachments)) ||
|
((isImage(attachments) && hasImage(attachments)) ||
|
||||||
(isVideo(attachments) && hasVideoScreenshot(attachments)))
|
(isVideo(attachments) &&
|
||||||
|
(hasVideoBlurHash(attachments) || hasVideoScreenshot(attachments))))
|
||||||
) {
|
) {
|
||||||
const prefix = isSticker ? 'sticker' : 'attachment';
|
const prefix = isSticker ? 'sticker' : 'attachment';
|
||||||
const bottomOverlay = !isSticker && !collapseMetadata;
|
const bottomOverlay = !isSticker && !collapseMetadata;
|
||||||
|
@ -713,7 +721,11 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
onError={this.handleImageError}
|
onError={this.handleImageError}
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
onClick={attachment => {
|
onClick={attachment => {
|
||||||
showVisualAttachment({ attachment, messageId: id });
|
if (hasNotDownloaded(attachment)) {
|
||||||
|
kickOffAttachmentDownload({ attachment, messageId: id });
|
||||||
|
} else {
|
||||||
|
showVisualAttachment({ attachment, messageId: id });
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1517,7 +1529,8 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
return (
|
return (
|
||||||
displayImage &&
|
displayImage &&
|
||||||
((isImage(attachments) && hasImage(attachments)) ||
|
((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,
|
id,
|
||||||
isTapToView,
|
isTapToView,
|
||||||
isTapToViewExpired,
|
isTapToViewExpired,
|
||||||
|
kickOffAttachmentDownload,
|
||||||
openConversation,
|
openConversation,
|
||||||
showContactDetail,
|
showContactDetail,
|
||||||
showVisualAttachment,
|
showVisualAttachment,
|
||||||
|
@ -1953,6 +1967,24 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
return;
|
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 (
|
if (
|
||||||
!imageBroken &&
|
!imageBroken &&
|
||||||
attachments &&
|
attachments &&
|
||||||
|
@ -1960,7 +1992,8 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
!isAttachmentPending &&
|
!isAttachmentPending &&
|
||||||
canDisplayImage(attachments) &&
|
canDisplayImage(attachments) &&
|
||||||
((isImage(attachments) && hasImage(attachments)) ||
|
((isImage(attachments) && hasImage(attachments)) ||
|
||||||
(isVideo(attachments) && hasVideoScreenshot(attachments)))
|
(isVideo(attachments) &&
|
||||||
|
(hasVideoBlurHash(attachments) || hasVideoScreenshot(attachments))))
|
||||||
) {
|
) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
|
@ -33,6 +33,7 @@ const defaultMessage: MessageProps = {
|
||||||
i18n,
|
i18n,
|
||||||
id: 'my-message',
|
id: 'my-message',
|
||||||
interactionMode: 'keyboard',
|
interactionMode: 'keyboard',
|
||||||
|
kickOffAttachmentDownload: action('kickOffAttachmentDownload'),
|
||||||
openConversation: () => null,
|
openConversation: () => null,
|
||||||
openLink: () => null,
|
openLink: () => null,
|
||||||
previews: [],
|
previews: [],
|
||||||
|
|
|
@ -36,6 +36,7 @@ const defaultMessageProps: MessagesProps = {
|
||||||
i18n,
|
i18n,
|
||||||
id: 'messageId',
|
id: 'messageId',
|
||||||
interactionMode: 'keyboard',
|
interactionMode: 'keyboard',
|
||||||
|
kickOffAttachmentDownload: () => null,
|
||||||
openConversation: () => null,
|
openConversation: () => null,
|
||||||
openLink: () => null,
|
openLink: () => null,
|
||||||
previews: [],
|
previews: [],
|
||||||
|
|
|
@ -231,6 +231,7 @@ const actions = () => ({
|
||||||
openConversation: action('openConversation'),
|
openConversation: action('openConversation'),
|
||||||
showContactDetail: action('showContactDetail'),
|
showContactDetail: action('showContactDetail'),
|
||||||
showContactModal: action('showContactModal'),
|
showContactModal: action('showContactModal'),
|
||||||
|
kickOffAttachmentDownload: action('kickOffAttachmentDownload'),
|
||||||
showVisualAttachment: action('showVisualAttachment'),
|
showVisualAttachment: action('showVisualAttachment'),
|
||||||
downloadAttachment: action('downloadAttachment'),
|
downloadAttachment: action('downloadAttachment'),
|
||||||
displayTapToViewMessage: action('displayTapToViewMessage'),
|
displayTapToViewMessage: action('displayTapToViewMessage'),
|
||||||
|
|
|
@ -45,6 +45,7 @@ const getDefaultProps = () => ({
|
||||||
retrySend: action('retrySend'),
|
retrySend: action('retrySend'),
|
||||||
deleteMessage: action('deleteMessage'),
|
deleteMessage: action('deleteMessage'),
|
||||||
deleteMessageForEveryone: action('deleteMessageForEveryone'),
|
deleteMessageForEveryone: action('deleteMessageForEveryone'),
|
||||||
|
kickOffAttachmentDownload: action('kickOffAttachmentDownload'),
|
||||||
showMessageDetail: action('showMessageDetail'),
|
showMessageDetail: action('showMessageDetail'),
|
||||||
openConversation: action('openConversation'),
|
openConversation: action('openConversation'),
|
||||||
showContactDetail: action('showContactDetail'),
|
showContactDetail: action('showContactDetail'),
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {
|
||||||
ConversationType,
|
ConversationType,
|
||||||
} from '../state/ducks/conversations';
|
} from '../state/ducks/conversations';
|
||||||
import { getActiveCall } from '../state/ducks/calling';
|
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 { PropsData } from '../components/conversation/Message';
|
||||||
import { CallbackResultType } from '../textsecure/SendMessage';
|
import { CallbackResultType } from '../textsecure/SendMessage';
|
||||||
import { ExpirationTimerOptions } from '../util/ExpirationTimerOptions';
|
import { ExpirationTimerOptions } from '../util/ExpirationTimerOptions';
|
||||||
|
@ -38,6 +38,7 @@ import {
|
||||||
getCallingNotificationText,
|
getCallingNotificationText,
|
||||||
} from '../util/callingNotification';
|
} from '../util/callingNotification';
|
||||||
import { PropsType as ProfileChangeNotificationPropsType } from '../components/conversation/ProfileChangeNotification';
|
import { PropsType as ProfileChangeNotificationPropsType } from '../components/conversation/ProfileChangeNotification';
|
||||||
|
import { isImage, isVideo } from '../types/Attachment';
|
||||||
|
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
/* eslint-disable more/no-then */
|
/* eslint-disable more/no-then */
|
||||||
|
@ -2136,14 +2137,23 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
canDownload(): boolean {
|
canDownload(): boolean {
|
||||||
const conversation = this.getConversation();
|
|
||||||
const isAccepted = Boolean(conversation && conversation.getAccepted());
|
|
||||||
|
|
||||||
if (this.isOutgoing()) {
|
if (this.isOutgoing()) {
|
||||||
return true;
|
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 {
|
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
|
// Only queue attachments for downloads if this is an outgoing message
|
||||||
// or we've accepted the conversation
|
// or we've accepted the conversation
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
const reduxState = window.reduxStore.getState();
|
||||||
if (this.getConversation()!.getAccepted() || message.isOutgoing()) {
|
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();
|
await message.queueAttachmentDownloads();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,18 @@ import {
|
||||||
CallingStateType,
|
CallingStateType,
|
||||||
CallsByConversationType,
|
CallsByConversationType,
|
||||||
DirectCallStateType,
|
DirectCallStateType,
|
||||||
|
getActiveCall,
|
||||||
} from '../ducks/calling';
|
} from '../ducks/calling';
|
||||||
import { CallMode, CallState } from '../../types/Calling';
|
import { CallMode, CallState } from '../../types/Calling';
|
||||||
import { getOwn } from '../../util/getOwn';
|
import { getOwn } from '../../util/getOwn';
|
||||||
|
|
||||||
const getCalling = (state: StateType): CallingStateType => state.calling;
|
const getCalling = (state: StateType): CallingStateType => state.calling;
|
||||||
|
|
||||||
|
export const isInCall = createSelector(
|
||||||
|
getCalling,
|
||||||
|
(state: CallingStateType): boolean => Boolean(getActiveCall(state))
|
||||||
|
);
|
||||||
|
|
||||||
export const getCallsByConversation = createSelector(
|
export const getCallsByConversation = createSelector(
|
||||||
getCalling,
|
getCalling,
|
||||||
(state: CallingStateType): CallsByConversationType =>
|
(state: CallingStateType): CallsByConversationType =>
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
getCallsByConversation,
|
getCallsByConversation,
|
||||||
getCallSelector,
|
getCallSelector,
|
||||||
getIncomingCall,
|
getIncomingCall,
|
||||||
|
isInCall,
|
||||||
} from '../../../state/selectors/calling';
|
} from '../../../state/selectors/calling';
|
||||||
import { getEmptyState, CallingStateType } from '../../../state/ducks/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(
|
export function hasVideoScreenshot(
|
||||||
attachments?: Array<AttachmentType>
|
attachments?: Array<AttachmentType>
|
||||||
): string | null | undefined {
|
): string | null | undefined {
|
||||||
|
|
|
@ -14841,7 +14841,7 @@
|
||||||
"rule": "React-createRef",
|
"rule": "React-createRef",
|
||||||
"path": "ts/components/conversation/Message.tsx",
|
"path": "ts/components/conversation/Message.tsx",
|
||||||
"line": " public audioRef: React.RefObject<HTMLAudioElement> = React.createRef();",
|
"line": " public audioRef: React.RefObject<HTMLAudioElement> = React.createRef();",
|
||||||
"lineNumber": 214,
|
"lineNumber": 220,
|
||||||
"reasonCategory": "usageTrusted",
|
"reasonCategory": "usageTrusted",
|
||||||
"updated": "2020-09-08T20:19:01.913Z"
|
"updated": "2020-09-08T20:19:01.913Z"
|
||||||
},
|
},
|
||||||
|
@ -14849,7 +14849,7 @@
|
||||||
"rule": "React-createRef",
|
"rule": "React-createRef",
|
||||||
"path": "ts/components/conversation/Message.tsx",
|
"path": "ts/components/conversation/Message.tsx",
|
||||||
"line": " public focusRef: React.RefObject<HTMLDivElement> = React.createRef();",
|
"line": " public focusRef: React.RefObject<HTMLDivElement> = React.createRef();",
|
||||||
"lineNumber": 216,
|
"lineNumber": 222,
|
||||||
"reasonCategory": "usageTrusted",
|
"reasonCategory": "usageTrusted",
|
||||||
"updated": "2020-09-08T20:19:01.913Z"
|
"updated": "2020-09-08T20:19:01.913Z"
|
||||||
},
|
},
|
||||||
|
@ -14857,7 +14857,7 @@
|
||||||
"rule": "React-createRef",
|
"rule": "React-createRef",
|
||||||
"path": "ts/components/conversation/Message.tsx",
|
"path": "ts/components/conversation/Message.tsx",
|
||||||
"line": " > = React.createRef();",
|
"line": " > = React.createRef();",
|
||||||
"lineNumber": 220,
|
"lineNumber": 226,
|
||||||
"reasonCategory": "usageTrusted",
|
"reasonCategory": "usageTrusted",
|
||||||
"updated": "2020-08-28T19:36:40.817Z"
|
"updated": "2020-08-28T19:36:40.817Z"
|
||||||
},
|
},
|
||||||
|
|
|
@ -742,6 +742,13 @@ Whisper.ConversationView = Whisper.View.extend({
|
||||||
const showContactDetail = (options: any) => {
|
const showContactDetail = (options: any) => {
|
||||||
this.showContactDetail(options);
|
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) => {
|
const showVisualAttachment = (options: any) => {
|
||||||
this.showLightbox(options);
|
this.showLightbox(options);
|
||||||
};
|
};
|
||||||
|
@ -924,6 +931,7 @@ Whisper.ConversationView = Whisper.View.extend({
|
||||||
displayTapToViewMessage,
|
displayTapToViewMessage,
|
||||||
downloadAttachment,
|
downloadAttachment,
|
||||||
downloadNewVersion,
|
downloadNewVersion,
|
||||||
|
kickOffAttachmentDownload,
|
||||||
loadNewerMessages,
|
loadNewerMessages,
|
||||||
loadNewestMessages: this.loadNewestMessages.bind(this),
|
loadNewestMessages: this.loadNewestMessages.bind(this),
|
||||||
loadAndScroll: this.loadAndScroll.bind(this),
|
loadAndScroll: this.loadAndScroll.bind(this),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue