2020-10-30 20:34:04 +00:00
|
|
|
// Copyright 2018-2020 Signal Messenger, LLC
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
2018-12-02 01:48:53 +00:00
|
|
|
import React from 'react';
|
|
|
|
import * as GoogleChrome from '../util/GoogleChrome';
|
|
|
|
|
2019-01-14 21:49:58 +00:00
|
|
|
import { AttachmentType } from '../types/Attachment';
|
2018-12-02 01:48:53 +00:00
|
|
|
|
2019-01-14 21:49:58 +00:00
|
|
|
import { LocalizerType } from '../types/Util';
|
2018-12-02 01:48:53 +00:00
|
|
|
|
2020-08-17 18:55:07 +00:00
|
|
|
export interface Props {
|
2018-12-02 01:48:53 +00:00
|
|
|
attachment: AttachmentType;
|
2019-01-14 21:49:58 +00:00
|
|
|
i18n: LocalizerType;
|
2018-12-02 01:48:53 +00:00
|
|
|
url: string;
|
|
|
|
caption?: string;
|
2019-01-15 17:33:23 +00:00
|
|
|
onSave?: (caption: string) => void;
|
2018-12-02 01:48:53 +00:00
|
|
|
close?: () => void;
|
|
|
|
}
|
|
|
|
|
2019-01-15 17:33:23 +00:00
|
|
|
interface State {
|
|
|
|
caption: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
export class CaptionEditor extends React.Component<Props, State> {
|
2019-11-07 21:36:16 +00:00
|
|
|
private readonly handleKeyDownBound: (
|
2019-01-15 17:33:23 +00:00
|
|
|
event: React.KeyboardEvent<HTMLInputElement>
|
|
|
|
) => void;
|
2020-09-12 00:46:52 +00:00
|
|
|
|
2019-01-14 21:49:58 +00:00
|
|
|
private readonly setFocusBound: () => void;
|
2020-09-12 00:46:52 +00:00
|
|
|
|
2019-01-14 21:49:58 +00:00
|
|
|
private readonly onChangeBound: (
|
|
|
|
event: React.FormEvent<HTMLInputElement>
|
|
|
|
) => void;
|
2020-09-12 00:46:52 +00:00
|
|
|
|
2019-01-14 21:49:58 +00:00
|
|
|
private readonly onSaveBound: () => void;
|
2020-09-12 00:46:52 +00:00
|
|
|
|
2019-01-14 21:49:58 +00:00
|
|
|
private readonly inputRef: React.RefObject<HTMLInputElement>;
|
2019-01-12 00:01:11 +00:00
|
|
|
|
|
|
|
constructor(props: Props) {
|
|
|
|
super(props);
|
|
|
|
|
2019-01-15 17:33:23 +00:00
|
|
|
const { caption } = props;
|
|
|
|
this.state = {
|
|
|
|
caption: caption || '',
|
|
|
|
};
|
|
|
|
|
2019-11-07 21:36:16 +00:00
|
|
|
this.handleKeyDownBound = this.handleKeyDown.bind(this);
|
2019-01-12 00:01:11 +00:00
|
|
|
this.setFocusBound = this.setFocus.bind(this);
|
2019-01-15 17:33:23 +00:00
|
|
|
this.onChangeBound = this.onChange.bind(this);
|
|
|
|
this.onSaveBound = this.onSave.bind(this);
|
2019-01-14 21:49:58 +00:00
|
|
|
this.inputRef = React.createRef();
|
|
|
|
}
|
|
|
|
|
2020-09-12 00:46:52 +00:00
|
|
|
public componentDidMount(): void {
|
2019-01-14 21:49:58 +00:00
|
|
|
// Forcing focus after a delay due to some focus contention with ConversationView
|
|
|
|
setTimeout(() => {
|
|
|
|
this.setFocus();
|
|
|
|
}, 200);
|
2019-01-12 00:01:11 +00:00
|
|
|
}
|
|
|
|
|
2020-09-12 00:46:52 +00:00
|
|
|
public handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
2019-01-15 17:33:23 +00:00
|
|
|
const { close, onSave } = this.props;
|
2019-01-12 00:01:11 +00:00
|
|
|
|
2019-01-15 17:33:23 +00:00
|
|
|
if (close && event.key === 'Escape') {
|
2019-01-12 00:01:11 +00:00
|
|
|
close();
|
2019-11-07 21:36:16 +00:00
|
|
|
|
|
|
|
event.stopPropagation();
|
|
|
|
event.preventDefault();
|
2019-01-12 00:01:11 +00:00
|
|
|
}
|
2019-01-15 17:33:23 +00:00
|
|
|
|
|
|
|
if (onSave && event.key === 'Enter') {
|
|
|
|
const { caption } = this.state;
|
|
|
|
onSave(caption);
|
2019-11-07 21:36:16 +00:00
|
|
|
|
|
|
|
event.stopPropagation();
|
|
|
|
event.preventDefault();
|
2019-01-15 17:33:23 +00:00
|
|
|
}
|
2019-01-12 00:01:11 +00:00
|
|
|
}
|
|
|
|
|
2020-09-12 00:46:52 +00:00
|
|
|
public setFocus(): void {
|
2019-01-14 21:49:58 +00:00
|
|
|
if (this.inputRef.current) {
|
|
|
|
this.inputRef.current.focus();
|
2019-01-12 00:01:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-12 00:46:52 +00:00
|
|
|
public onSave(): void {
|
2019-01-15 17:33:23 +00:00
|
|
|
const { onSave } = this.props;
|
|
|
|
const { caption } = this.state;
|
|
|
|
|
|
|
|
if (onSave) {
|
|
|
|
onSave(caption);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-12 00:46:52 +00:00
|
|
|
public onChange(event: React.FormEvent<HTMLInputElement>): void {
|
|
|
|
const { value } = event.target as HTMLInputElement;
|
2019-01-15 17:33:23 +00:00
|
|
|
|
|
|
|
this.setState({
|
|
|
|
caption: value,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-09-12 00:46:52 +00:00
|
|
|
public renderObject(): JSX.Element {
|
2018-12-02 01:48:53 +00:00
|
|
|
const { url, i18n, attachment } = this.props;
|
|
|
|
const { contentType } = attachment || { contentType: null };
|
|
|
|
|
|
|
|
const isImageTypeSupported = GoogleChrome.isImageTypeSupported(contentType);
|
|
|
|
if (isImageTypeSupported) {
|
|
|
|
return (
|
|
|
|
<img
|
|
|
|
className="module-caption-editor__image"
|
|
|
|
alt={i18n('imageAttachmentAlt')}
|
|
|
|
src={url}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const isVideoTypeSupported = GoogleChrome.isVideoTypeSupported(contentType);
|
|
|
|
if (isVideoTypeSupported) {
|
|
|
|
return (
|
2020-09-12 00:46:52 +00:00
|
|
|
<video className="module-caption-editor__video" controls>
|
2018-12-02 01:48:53 +00:00
|
|
|
<source src={url} />
|
|
|
|
</video>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return <div className="module-caption-editor__placeholder" />;
|
|
|
|
}
|
|
|
|
|
2020-09-12 00:46:52 +00:00
|
|
|
// Events handled by props
|
|
|
|
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
|
|
|
public render(): JSX.Element {
|
2019-01-15 17:33:23 +00:00
|
|
|
const { i18n, close } = this.props;
|
|
|
|
const { caption } = this.state;
|
2019-11-07 21:36:16 +00:00
|
|
|
const onKeyDown = close ? this.handleKeyDownBound : undefined;
|
2018-12-02 01:48:53 +00:00
|
|
|
|
|
|
|
return (
|
2019-01-12 00:01:11 +00:00
|
|
|
<div
|
2020-09-12 00:46:52 +00:00
|
|
|
role="presentation"
|
2019-01-12 00:01:11 +00:00
|
|
|
onClick={this.setFocusBound}
|
|
|
|
className="module-caption-editor"
|
|
|
|
>
|
2018-12-02 01:48:53 +00:00
|
|
|
<div
|
2019-11-07 21:36:16 +00:00
|
|
|
// Okay that this isn't a button; the escape key can be used to close this view
|
2018-12-02 01:48:53 +00:00
|
|
|
role="button"
|
|
|
|
onClick={close}
|
|
|
|
className="module-caption-editor__close-button"
|
2020-09-12 00:46:52 +00:00
|
|
|
tabIndex={0}
|
|
|
|
aria-label={i18n('close')}
|
2018-12-02 01:48:53 +00:00
|
|
|
/>
|
|
|
|
<div className="module-caption-editor__media-container">
|
|
|
|
{this.renderObject()}
|
|
|
|
</div>
|
|
|
|
<div className="module-caption-editor__bottom-bar">
|
2019-01-15 17:33:23 +00:00
|
|
|
<div className="module-caption-editor__input-container">
|
|
|
|
<input
|
|
|
|
type="text"
|
2019-01-14 21:49:58 +00:00
|
|
|
ref={this.inputRef}
|
2019-01-15 17:33:23 +00:00
|
|
|
value={caption}
|
|
|
|
maxLength={200}
|
|
|
|
placeholder={i18n('addACaption')}
|
|
|
|
className="module-caption-editor__caption-input"
|
2019-11-07 21:36:16 +00:00
|
|
|
onKeyDown={onKeyDown}
|
2019-01-15 17:33:23 +00:00
|
|
|
onChange={this.onChangeBound}
|
|
|
|
/>
|
|
|
|
{caption ? (
|
2019-11-07 21:36:16 +00:00
|
|
|
<button
|
2020-09-12 00:46:52 +00:00
|
|
|
type="button"
|
2019-01-15 17:33:23 +00:00
|
|
|
onClick={this.onSaveBound}
|
|
|
|
className="module-caption-editor__save-button"
|
|
|
|
>
|
|
|
|
{i18n('save')}
|
2019-11-07 21:36:16 +00:00
|
|
|
</button>
|
2019-01-15 17:33:23 +00:00
|
|
|
) : null}
|
|
|
|
</div>
|
2018-12-02 01:48:53 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
2020-09-12 00:46:52 +00:00
|
|
|
/* eslint-enable jsx-a11y/click-events-have-key-events */
|
2018-12-02 01:48:53 +00:00
|
|
|
}
|