Manual download of attachments with no blurHash
This commit is contained in:
parent
ed786898fb
commit
34285054f6
12 changed files with 117 additions and 32 deletions
|
@ -5021,11 +5021,17 @@ button.module-conversation-details__action-button {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: $color-gray-75;
|
|
||||||
border-radius: 48px;
|
border-radius: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
width: 48px;
|
width: 48px;
|
||||||
|
|
||||||
|
@include light-theme {
|
||||||
|
background-color: $color-gray-65;
|
||||||
|
}
|
||||||
|
@include dark-theme {
|
||||||
|
background-color: $color-gray-75;
|
||||||
|
}
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
content: '';
|
content: '';
|
||||||
height: 17px;
|
height: 17px;
|
||||||
|
|
|
@ -326,7 +326,7 @@ type WhatIsThis = import('./window.d').WhatIsThis;
|
||||||
window.Events = {
|
window.Events = {
|
||||||
getDeviceName: () => window.textsecure.storage.user.getDeviceName(),
|
getDeviceName: () => window.textsecure.storage.user.getDeviceName(),
|
||||||
|
|
||||||
getThemeSetting: () =>
|
getThemeSetting: (): 'light' | 'dark' | 'system' =>
|
||||||
window.storage.get(
|
window.storage.get(
|
||||||
'theme-setting',
|
'theme-setting',
|
||||||
window.platform === 'darwin' ? 'system' : 'light'
|
window.platform === 'darwin' ? 'system' : 'light'
|
||||||
|
@ -751,6 +751,7 @@ type WhatIsThis = import('./window.d').WhatIsThis;
|
||||||
platform: window.platform,
|
platform: window.platform,
|
||||||
i18n: window.i18n,
|
i18n: window.i18n,
|
||||||
interactionMode: window.getInteractionMode(),
|
interactionMode: window.getInteractionMode(),
|
||||||
|
theme: window.Events.getThemeSetting(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2162,6 +2163,11 @@ type WhatIsThis = import('./window.d').WhatIsThis;
|
||||||
if (view) {
|
if (view) {
|
||||||
view.applyTheme();
|
view.applyTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const theme = window.Events.getThemeSetting();
|
||||||
|
window.reduxActions.user.userChanged({
|
||||||
|
theme: theme === 'system' ? window.systemTheme : theme,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const FIVE_MINUTES = 5 * 60 * 1000;
|
const FIVE_MINUTES = 5 * 60 * 1000;
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { storiesOf } from '@storybook/react';
|
||||||
import { pngUrl } from '../../storybook/Fixtures';
|
import { pngUrl } from '../../storybook/Fixtures';
|
||||||
import { Image, Props } from './Image';
|
import { Image, Props } from './Image';
|
||||||
import { IMAGE_PNG } from '../../types/MIME';
|
import { IMAGE_PNG } from '../../types/MIME';
|
||||||
|
import { ThemeType } from '../../types/Util';
|
||||||
import { setup as setupI18n } from '../../../js/modules/i18n';
|
import { setup as setupI18n } from '../../../js/modules/i18n';
|
||||||
import enMessages from '../../../_locales/en/messages.json';
|
import enMessages from '../../../_locales/en/messages.json';
|
||||||
|
|
||||||
|
@ -56,6 +57,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||||
),
|
),
|
||||||
softCorners: boolean('softCorners', overrideProps.softCorners || false),
|
softCorners: boolean('softCorners', overrideProps.softCorners || false),
|
||||||
tabIndex: number('tabIndex', overrideProps.tabIndex || 0),
|
tabIndex: number('tabIndex', overrideProps.tabIndex || 0),
|
||||||
|
theme: text('theme', overrideProps.theme || 'light') as ThemeType,
|
||||||
url: text('url', overrideProps.url || pngUrl),
|
url: text('url', overrideProps.url || pngUrl),
|
||||||
width: number('width', overrideProps.width || 100),
|
width: number('width', overrideProps.width || 100),
|
||||||
});
|
});
|
||||||
|
@ -191,6 +193,32 @@ story.add('Blurhash', () => {
|
||||||
return <Image {...props} />;
|
return <Image {...props} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
story.add('undefined blurHash (light)', () => {
|
||||||
|
const defaultProps = createProps();
|
||||||
|
const props = {
|
||||||
|
...defaultProps,
|
||||||
|
blurHash: undefined,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
url: undefined as any,
|
||||||
|
theme: ThemeType.light,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Image {...props} />;
|
||||||
|
});
|
||||||
|
|
||||||
|
story.add('undefined blurHash (dark)', () => {
|
||||||
|
const defaultProps = createProps();
|
||||||
|
const props = {
|
||||||
|
...defaultProps,
|
||||||
|
blurHash: undefined,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
url: undefined as any,
|
||||||
|
theme: ThemeType.dark,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Image {...props} />;
|
||||||
|
});
|
||||||
|
|
||||||
story.add('Missing Image', () => {
|
story.add('Missing Image', () => {
|
||||||
const defaultProps = createProps();
|
const defaultProps = createProps();
|
||||||
const props = {
|
const props = {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import classNames from 'classnames';
|
||||||
import { Blurhash } from 'react-blurhash';
|
import { Blurhash } from 'react-blurhash';
|
||||||
|
|
||||||
import { Spinner } from '../Spinner';
|
import { Spinner } from '../Spinner';
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType, ThemeType } from '../../types/Util';
|
||||||
import { AttachmentType, hasNotDownloaded } from '../../types/Attachment';
|
import { AttachmentType, hasNotDownloaded } from '../../types/Attachment';
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
|
@ -37,6 +37,7 @@ export type Props = {
|
||||||
blurHash?: string;
|
blurHash?: string;
|
||||||
|
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
|
theme?: ThemeType;
|
||||||
onClick?: (attachment: AttachmentType) => void;
|
onClick?: (attachment: AttachmentType) => void;
|
||||||
onClickClose?: (attachment: AttachmentType) => void;
|
onClickClose?: (attachment: AttachmentType) => void;
|
||||||
onError?: () => void;
|
onError?: () => void;
|
||||||
|
@ -44,10 +45,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, blurHash, url } = this.props;
|
const { onClick, attachment } = this.props;
|
||||||
const { pending } = attachment || { pending: true };
|
const { pending } = attachment || { pending: true };
|
||||||
|
|
||||||
return Boolean(onClick && !pending && (url || blurHash));
|
return Boolean(onClick && !pending);
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleClick = (event: React.MouseEvent): void => {
|
public handleClick = (event: React.MouseEvent): void => {
|
||||||
|
@ -150,6 +151,7 @@ export class Image extends React.Component<Props> {
|
||||||
smallCurveTopLeft,
|
smallCurveTopLeft,
|
||||||
softCorners,
|
softCorners,
|
||||||
tabIndex,
|
tabIndex,
|
||||||
|
theme,
|
||||||
url,
|
url,
|
||||||
width = 0,
|
width = 0,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
@ -158,6 +160,12 @@ export class Image extends React.Component<Props> {
|
||||||
const canClick = this.canClick();
|
const canClick = this.canClick();
|
||||||
const imgNotDownloaded = hasNotDownloaded(attachment);
|
const imgNotDownloaded = hasNotDownloaded(attachment);
|
||||||
|
|
||||||
|
const defaulBlurHash =
|
||||||
|
theme === ThemeType.dark
|
||||||
|
? 'L05OQnoffQofoffQfQfQfQfQfQfQ'
|
||||||
|
: 'L1Q]+w-;fQ-;~qfQfQfQfQfQfQfQ';
|
||||||
|
const resolvedBlurHash = blurHash || defaulBlurHash;
|
||||||
|
|
||||||
const overlayClassName = classNames('module-image__border-overlay', {
|
const overlayClassName = classNames('module-image__border-overlay', {
|
||||||
'module-image__border-overlay--with-border': !noBorder,
|
'module-image__border-overlay--with-border': !noBorder,
|
||||||
'module-image__border-overlay--with-click-handler': canClick,
|
'module-image__border-overlay--with-click-handler': canClick,
|
||||||
|
@ -210,9 +218,9 @@ export class Image extends React.Component<Props> {
|
||||||
width={width}
|
width={width}
|
||||||
src={url}
|
src={url}
|
||||||
/>
|
/>
|
||||||
) : blurHash ? (
|
) : resolvedBlurHash ? (
|
||||||
<Blurhash
|
<Blurhash
|
||||||
hash={blurHash}
|
hash={resolvedBlurHash}
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
style={{ display: 'block' }}
|
style={{ display: 'block' }}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
|
|
||||||
import { Image } from './Image';
|
import { Image } from './Image';
|
||||||
|
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType, ThemeType } from '../../types/Util';
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
attachments: Array<AttachmentType>;
|
attachments: Array<AttachmentType>;
|
||||||
|
@ -28,6 +28,7 @@ export type Props = {
|
||||||
tabIndex?: number;
|
tabIndex?: number;
|
||||||
|
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
|
theme?: ThemeType;
|
||||||
|
|
||||||
onError: () => void;
|
onError: () => void;
|
||||||
onClick?: (attachment: AttachmentType) => void;
|
onClick?: (attachment: AttachmentType) => void;
|
||||||
|
@ -42,6 +43,7 @@ export const ImageGrid = ({
|
||||||
onError,
|
onError,
|
||||||
onClick,
|
onClick,
|
||||||
tabIndex,
|
tabIndex,
|
||||||
|
theme,
|
||||||
withContentAbove,
|
withContentAbove,
|
||||||
withContentBelow,
|
withContentBelow,
|
||||||
}: Props): JSX.Element | null => {
|
}: Props): JSX.Element | null => {
|
||||||
|
@ -75,6 +77,7 @@ export const ImageGrid = ({
|
||||||
<Image
|
<Image
|
||||||
alt={getAlt(attachments[0], i18n)}
|
alt={getAlt(attachments[0], i18n)}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
blurHash={attachments[0].blurHash}
|
blurHash={attachments[0].blurHash}
|
||||||
bottomOverlay={withBottomOverlay}
|
bottomOverlay={withBottomOverlay}
|
||||||
noBorder={isSticker}
|
noBorder={isSticker}
|
||||||
|
@ -102,6 +105,7 @@ export const ImageGrid = ({
|
||||||
<Image
|
<Image
|
||||||
alt={getAlt(attachments[0], i18n)}
|
alt={getAlt(attachments[0], i18n)}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
attachment={attachments[0]}
|
attachment={attachments[0]}
|
||||||
blurHash={attachments[0].blurHash}
|
blurHash={attachments[0].blurHash}
|
||||||
bottomOverlay={withBottomOverlay}
|
bottomOverlay={withBottomOverlay}
|
||||||
|
@ -118,6 +122,7 @@ export const ImageGrid = ({
|
||||||
<Image
|
<Image
|
||||||
alt={getAlt(attachments[1], i18n)}
|
alt={getAlt(attachments[1], i18n)}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
blurHash={attachments[1].blurHash}
|
blurHash={attachments[1].blurHash}
|
||||||
bottomOverlay={withBottomOverlay}
|
bottomOverlay={withBottomOverlay}
|
||||||
noBorder={false}
|
noBorder={false}
|
||||||
|
@ -141,6 +146,7 @@ export const ImageGrid = ({
|
||||||
<Image
|
<Image
|
||||||
alt={getAlt(attachments[0], i18n)}
|
alt={getAlt(attachments[0], i18n)}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
blurHash={attachments[0].blurHash}
|
blurHash={attachments[0].blurHash}
|
||||||
bottomOverlay={withBottomOverlay}
|
bottomOverlay={withBottomOverlay}
|
||||||
noBorder={false}
|
noBorder={false}
|
||||||
|
@ -158,6 +164,7 @@ export const ImageGrid = ({
|
||||||
<Image
|
<Image
|
||||||
alt={getAlt(attachments[1], i18n)}
|
alt={getAlt(attachments[1], i18n)}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
blurHash={attachments[1].blurHash}
|
blurHash={attachments[1].blurHash}
|
||||||
curveTopRight={curveTopRight}
|
curveTopRight={curveTopRight}
|
||||||
height={99}
|
height={99}
|
||||||
|
@ -171,6 +178,7 @@ export const ImageGrid = ({
|
||||||
<Image
|
<Image
|
||||||
alt={getAlt(attachments[2], i18n)}
|
alt={getAlt(attachments[2], i18n)}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
blurHash={attachments[2].blurHash}
|
blurHash={attachments[2].blurHash}
|
||||||
bottomOverlay={withBottomOverlay}
|
bottomOverlay={withBottomOverlay}
|
||||||
noBorder={false}
|
noBorder={false}
|
||||||
|
@ -196,6 +204,7 @@ export const ImageGrid = ({
|
||||||
<Image
|
<Image
|
||||||
alt={getAlt(attachments[0], i18n)}
|
alt={getAlt(attachments[0], i18n)}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
blurHash={attachments[0].blurHash}
|
blurHash={attachments[0].blurHash}
|
||||||
curveTopLeft={curveTopLeft}
|
curveTopLeft={curveTopLeft}
|
||||||
noBorder={false}
|
noBorder={false}
|
||||||
|
@ -210,6 +219,7 @@ export const ImageGrid = ({
|
||||||
<Image
|
<Image
|
||||||
alt={getAlt(attachments[1], i18n)}
|
alt={getAlt(attachments[1], i18n)}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
blurHash={attachments[1].blurHash}
|
blurHash={attachments[1].blurHash}
|
||||||
curveTopRight={curveTopRight}
|
curveTopRight={curveTopRight}
|
||||||
playIconOverlay={isVideoAttachment(attachments[1])}
|
playIconOverlay={isVideoAttachment(attachments[1])}
|
||||||
|
@ -226,6 +236,7 @@ export const ImageGrid = ({
|
||||||
<Image
|
<Image
|
||||||
alt={getAlt(attachments[2], i18n)}
|
alt={getAlt(attachments[2], i18n)}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
blurHash={attachments[2].blurHash}
|
blurHash={attachments[2].blurHash}
|
||||||
bottomOverlay={withBottomOverlay}
|
bottomOverlay={withBottomOverlay}
|
||||||
noBorder={false}
|
noBorder={false}
|
||||||
|
@ -241,6 +252,7 @@ export const ImageGrid = ({
|
||||||
<Image
|
<Image
|
||||||
alt={getAlt(attachments[3], i18n)}
|
alt={getAlt(attachments[3], i18n)}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
blurHash={attachments[3].blurHash}
|
blurHash={attachments[3].blurHash}
|
||||||
bottomOverlay={withBottomOverlay}
|
bottomOverlay={withBottomOverlay}
|
||||||
noBorder={false}
|
noBorder={false}
|
||||||
|
@ -271,6 +283,7 @@ export const ImageGrid = ({
|
||||||
<Image
|
<Image
|
||||||
alt={getAlt(attachments[0], i18n)}
|
alt={getAlt(attachments[0], i18n)}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
blurHash={attachments[0].blurHash}
|
blurHash={attachments[0].blurHash}
|
||||||
curveTopLeft={curveTopLeft}
|
curveTopLeft={curveTopLeft}
|
||||||
attachment={attachments[0]}
|
attachment={attachments[0]}
|
||||||
|
@ -284,6 +297,7 @@ export const ImageGrid = ({
|
||||||
<Image
|
<Image
|
||||||
alt={getAlt(attachments[1], i18n)}
|
alt={getAlt(attachments[1], i18n)}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
blurHash={attachments[1].blurHash}
|
blurHash={attachments[1].blurHash}
|
||||||
curveTopRight={curveTopRight}
|
curveTopRight={curveTopRight}
|
||||||
playIconOverlay={isVideoAttachment(attachments[1])}
|
playIconOverlay={isVideoAttachment(attachments[1])}
|
||||||
|
@ -299,6 +313,7 @@ export const ImageGrid = ({
|
||||||
<Image
|
<Image
|
||||||
alt={getAlt(attachments[2], i18n)}
|
alt={getAlt(attachments[2], i18n)}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
blurHash={attachments[2].blurHash}
|
blurHash={attachments[2].blurHash}
|
||||||
bottomOverlay={withBottomOverlay}
|
bottomOverlay={withBottomOverlay}
|
||||||
noBorder={isSticker}
|
noBorder={isSticker}
|
||||||
|
@ -314,6 +329,7 @@ export const ImageGrid = ({
|
||||||
<Image
|
<Image
|
||||||
alt={getAlt(attachments[3], i18n)}
|
alt={getAlt(attachments[3], i18n)}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
blurHash={attachments[3].blurHash}
|
blurHash={attachments[3].blurHash}
|
||||||
bottomOverlay={withBottomOverlay}
|
bottomOverlay={withBottomOverlay}
|
||||||
noBorder={isSticker}
|
noBorder={isSticker}
|
||||||
|
@ -328,6 +344,7 @@ export const ImageGrid = ({
|
||||||
<Image
|
<Image
|
||||||
alt={getAlt(attachments[4], i18n)}
|
alt={getAlt(attachments[4], i18n)}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
blurHash={attachments[4].blurHash}
|
blurHash={attachments[4].blurHash}
|
||||||
bottomOverlay={withBottomOverlay}
|
bottomOverlay={withBottomOverlay}
|
||||||
noBorder={isSticker}
|
noBorder={isSticker}
|
||||||
|
|
|
@ -36,7 +36,6 @@ import {
|
||||||
getImageDimensions,
|
getImageDimensions,
|
||||||
hasImage,
|
hasImage,
|
||||||
hasNotDownloaded,
|
hasNotDownloaded,
|
||||||
hasVideoBlurHash,
|
|
||||||
hasVideoScreenshot,
|
hasVideoScreenshot,
|
||||||
isAudio,
|
isAudio,
|
||||||
isImage,
|
isImage,
|
||||||
|
@ -47,7 +46,7 @@ import { ContactType } from '../../types/Contact';
|
||||||
|
|
||||||
import { getIncrement } from '../../util/timer';
|
import { getIncrement } from '../../util/timer';
|
||||||
import { isFileDangerous } from '../../util/isFileDangerous';
|
import { isFileDangerous } from '../../util/isFileDangerous';
|
||||||
import { BodyRangesType, LocalizerType } from '../../types/Util';
|
import { BodyRangesType, LocalizerType, ThemeType } from '../../types/Util';
|
||||||
import { ColorType } from '../../types/Colors';
|
import { ColorType } from '../../types/Colors';
|
||||||
import { createRefMerger } from '../_util';
|
import { createRefMerger } from '../_util';
|
||||||
import { emojiToData } from '../emoji/lib';
|
import { emojiToData } from '../emoji/lib';
|
||||||
|
@ -141,6 +140,7 @@ export type PropsData = {
|
||||||
|
|
||||||
export type PropsHousekeeping = {
|
export type PropsHousekeeping = {
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
|
theme?: ThemeType;
|
||||||
disableMenu?: boolean;
|
disableMenu?: boolean;
|
||||||
disableScroll?: boolean;
|
disableScroll?: boolean;
|
||||||
collapseMetadata?: boolean;
|
collapseMetadata?: boolean;
|
||||||
|
@ -675,7 +675,9 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
showVisualAttachment,
|
showVisualAttachment,
|
||||||
isSticker,
|
isSticker,
|
||||||
text,
|
text,
|
||||||
|
theme,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { imageBroken } = this.state;
|
const { imageBroken } = this.state;
|
||||||
|
|
||||||
if (!attachments || !attachments[0]) {
|
if (!attachments || !attachments[0]) {
|
||||||
|
@ -693,9 +695,7 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
if (
|
if (
|
||||||
displayImage &&
|
displayImage &&
|
||||||
!imageBroken &&
|
!imageBroken &&
|
||||||
((isImage(attachments) && hasImage(attachments)) ||
|
(isImage(attachments) || isVideo(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;
|
||||||
|
@ -725,6 +725,7 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
stickerSize={STICKER_SIZE}
|
stickerSize={STICKER_SIZE}
|
||||||
bottomOverlay={bottomOverlay}
|
bottomOverlay={bottomOverlay}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
onError={this.handleImageError}
|
onError={this.handleImageError}
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
onClick={attachment => {
|
onClick={attachment => {
|
||||||
|
@ -783,7 +784,11 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
if (!firstAttachment.url) {
|
if (hasNotDownloaded(firstAttachment)) {
|
||||||
|
kickOffAttachmentDownload({
|
||||||
|
attachment: firstAttachment,
|
||||||
|
messageId: id,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -841,6 +846,7 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
openLink,
|
openLink,
|
||||||
previews,
|
previews,
|
||||||
quote,
|
quote,
|
||||||
|
theme,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
// Attachments take precedence over Link Previews
|
// Attachments take precedence over Link Previews
|
||||||
|
@ -885,6 +891,7 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
withContentBelow
|
withContentBelow
|
||||||
onError={this.handleImageError}
|
onError={this.handleImageError}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="module-message__link-preview__content">
|
<div className="module-message__link-preview__content">
|
||||||
|
@ -1546,12 +1553,7 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
if (attachments && attachments.length) {
|
if (attachments && attachments.length) {
|
||||||
const displayImage = canDisplayImage(attachments);
|
const displayImage = canDisplayImage(attachments);
|
||||||
|
|
||||||
return (
|
return displayImage && (isImage(attachments) || isVideo(attachments));
|
||||||
displayImage &&
|
|
||||||
((isImage(attachments) && hasImage(attachments)) ||
|
|
||||||
(isVideo(attachments) &&
|
|
||||||
(hasVideoBlurHash(attachments) || hasVideoScreenshot(attachments))))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previews && previews.length) {
|
if (previews && previews.length) {
|
||||||
|
@ -2012,8 +2014,7 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
!isAttachmentPending &&
|
!isAttachmentPending &&
|
||||||
canDisplayImage(attachments) &&
|
canDisplayImage(attachments) &&
|
||||||
((isImage(attachments) && hasImage(attachments)) ||
|
((isImage(attachments) && hasImage(attachments)) ||
|
||||||
(isVideo(attachments) &&
|
(isVideo(attachments) && hasVideoScreenshot(attachments)))
|
||||||
(hasVideoBlurHash(attachments) || hasVideoScreenshot(attachments))))
|
|
||||||
) {
|
) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType, ThemeType } from '../../types/Util';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Message,
|
Message,
|
||||||
|
@ -126,6 +126,7 @@ type PropsLocalType = {
|
||||||
selectMessage: (messageId: string, conversationId: string) => unknown;
|
selectMessage: (messageId: string, conversationId: string) => unknown;
|
||||||
renderContact: SmartContactRendererType;
|
renderContact: SmartContactRendererType;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
|
theme?: ThemeType;
|
||||||
};
|
};
|
||||||
|
|
||||||
type PropsActionsType = MessageActionsType &
|
type PropsActionsType = MessageActionsType &
|
||||||
|
@ -145,6 +146,7 @@ export class TimelineItem extends React.PureComponent<PropsType> {
|
||||||
isSelected,
|
isSelected,
|
||||||
item,
|
item,
|
||||||
i18n,
|
i18n,
|
||||||
|
theme,
|
||||||
messageSizeChanged,
|
messageSizeChanged,
|
||||||
renderContact,
|
renderContact,
|
||||||
returnToActiveCall,
|
returnToActiveCall,
|
||||||
|
@ -159,7 +161,9 @@ export class TimelineItem extends React.PureComponent<PropsType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.type === 'message') {
|
if (item.type === 'message') {
|
||||||
return <Message {...this.props} {...item.data} i18n={i18n} />;
|
return (
|
||||||
|
<Message {...this.props} {...item.data} i18n={i18n} theme={theme} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let notification;
|
let notification;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
import { trigger } from '../../shims/events';
|
import { trigger } from '../../shims/events';
|
||||||
|
|
||||||
import { NoopActionType } from './noop';
|
import { NoopActionType } from './noop';
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType, ThemeType } from '../../types/Util';
|
||||||
|
|
||||||
// State
|
// State
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ export type UserStateType = {
|
||||||
regionCode: string;
|
regionCode: string;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
interactionMode: 'mouse' | 'keyboard';
|
interactionMode: 'mouse' | 'keyboard';
|
||||||
|
theme: ThemeType;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
@ -31,6 +32,7 @@ type UserChangedActionType = {
|
||||||
ourNumber?: string;
|
ourNumber?: string;
|
||||||
regionCode?: string;
|
regionCode?: string;
|
||||||
interactionMode?: 'mouse' | 'keyboard';
|
interactionMode?: 'mouse' | 'keyboard';
|
||||||
|
theme?: ThemeType;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,10 +47,11 @@ export const actions = {
|
||||||
|
|
||||||
function userChanged(attributes: {
|
function userChanged(attributes: {
|
||||||
interactionMode?: 'mouse' | 'keyboard';
|
interactionMode?: 'mouse' | 'keyboard';
|
||||||
ourConversationId: string;
|
ourConversationId?: string;
|
||||||
ourNumber: string;
|
ourNumber?: string;
|
||||||
ourUuid: string;
|
ourUuid?: string;
|
||||||
regionCode: string;
|
regionCode?: string;
|
||||||
|
theme?: ThemeType;
|
||||||
}): UserChangedActionType {
|
}): UserChangedActionType {
|
||||||
return {
|
return {
|
||||||
type: 'USER_CHANGED',
|
type: 'USER_CHANGED',
|
||||||
|
@ -78,6 +81,7 @@ export function getEmptyState(): UserStateType {
|
||||||
regionCode: 'missing',
|
regionCode: 'missing',
|
||||||
platform: 'missing',
|
platform: 'missing',
|
||||||
interactionMode: 'mouse',
|
interactionMode: 'mouse',
|
||||||
|
theme: ThemeType.light,
|
||||||
i18n: () => 'missing',
|
i18n: () => 'missing',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
import { LocalizerType } from '../../types/Util';
|
import { LocalizerType, ThemeType } from '../../types/Util';
|
||||||
|
|
||||||
import { StateType } from '../reducer';
|
import { StateType } from '../reducer';
|
||||||
import { UserStateType } from '../ducks/user';
|
import { UserStateType } from '../ducks/user';
|
||||||
|
@ -59,3 +59,8 @@ export const getTempPath = createSelector(
|
||||||
getUser,
|
getUser,
|
||||||
(state: UserStateType): string => state.tempPath
|
(state: UserStateType): string => state.tempPath
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const getTheme = createSelector(
|
||||||
|
getUser,
|
||||||
|
(state: UserStateType): ThemeType => state.theme
|
||||||
|
);
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { mapDispatchToProps } from '../actions';
|
||||||
import { StateType } from '../reducer';
|
import { StateType } from '../reducer';
|
||||||
|
|
||||||
import { TimelineItem } from '../../components/conversation/TimelineItem';
|
import { TimelineItem } from '../../components/conversation/TimelineItem';
|
||||||
import { getIntl } from '../selectors/user';
|
import { getIntl, getTheme } from '../selectors/user';
|
||||||
import {
|
import {
|
||||||
getMessageSelector,
|
getMessageSelector,
|
||||||
getSelectedMessage,
|
getSelectedMessage,
|
||||||
|
@ -47,6 +47,7 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
|
||||||
isSelected,
|
isSelected,
|
||||||
renderContact,
|
renderContact,
|
||||||
i18n: getIntl(state),
|
i18n: getIntl(state),
|
||||||
|
theme: getTheme(state),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -170,7 +170,7 @@ export function isVideoAttachment(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hasNotDownloaded(attachment?: AttachmentType): boolean {
|
export function hasNotDownloaded(attachment?: AttachmentType): boolean {
|
||||||
return Boolean(attachment && !attachment.url && attachment.blurHash);
|
return Boolean(attachment && !attachment.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hasVideoBlurHash(attachments?: Array<AttachmentType>): boolean {
|
export function hasVideoBlurHash(attachments?: Array<AttachmentType>): boolean {
|
||||||
|
|
|
@ -24,3 +24,8 @@ export type LocalizerType = (
|
||||||
key: string,
|
key: string,
|
||||||
values?: Array<string | null> | ReplacementValuesType
|
values?: Array<string | null> | ReplacementValuesType
|
||||||
) => string;
|
) => string;
|
||||||
|
|
||||||
|
export enum ThemeType {
|
||||||
|
'light' = 'light',
|
||||||
|
'dark' = 'dark',
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue