New staged attachments UI, multiple image attachments per message
This commit is contained in:
parent
e4babdaef0
commit
985b1d6aa6
22 changed files with 1550 additions and 648 deletions
72
ts/components/CaptionEditor.md
Normal file
72
ts/components/CaptionEditor.md
Normal file
|
@ -0,0 +1,72 @@
|
|||
## Image
|
||||
|
||||
```js
|
||||
let caption = null;
|
||||
|
||||
<div style={{ position: 'relative', width: '100%', height: 500 }}>
|
||||
<CaptionEditor
|
||||
url={util.gifObjectUrl}
|
||||
attachment={{
|
||||
contentType: 'image/jpeg',
|
||||
}}
|
||||
onChangeCaption={caption => console.log('onChangeCaption', caption)}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
</div>;
|
||||
```
|
||||
|
||||
## Image with caption
|
||||
|
||||
```js
|
||||
let caption =
|
||||
"This is the user-provided caption. We show it overlaid on the image. If it's really long, then it wraps, but it doesn't get too close to the edges of the image.";
|
||||
|
||||
<div style={{ position: 'relative', width: '100%', height: 500 }}>
|
||||
<CaptionEditor
|
||||
url="https://placekitten.com/800/600"
|
||||
attachment={{
|
||||
contentType: 'image/jpeg',
|
||||
}}
|
||||
caption={caption}
|
||||
contentType="image/jpeg"
|
||||
onChangeCaption={caption => console.log('onChangeCaption', caption)}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
</div>;
|
||||
```
|
||||
|
||||
## Video
|
||||
|
||||
```js
|
||||
let caption = null;
|
||||
|
||||
<div style={{ position: 'relative', width: '100%', height: 500 }}>
|
||||
<CaptionEditor
|
||||
url="fixtures/pixabay-Soap-Bubble-7141.mp4"
|
||||
attachment={{
|
||||
contentType: 'video/mp4',
|
||||
}}
|
||||
onChangeCaption={caption => console.log('onChangeCaption', caption)}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
</div>;
|
||||
```
|
||||
|
||||
## Video with caption
|
||||
|
||||
```js
|
||||
let caption =
|
||||
"This is the user-provided caption. We show it overlaid on the image. If it's really long, then it wraps, but it doesn't get too close to the edges of the image.";
|
||||
|
||||
<div style={{ position: 'relative', width: '100%', height: 500 }}>
|
||||
<CaptionEditor
|
||||
url="fixtures/pixabay-Soap-Bubble-7141.mp4"
|
||||
attachment={{
|
||||
contentType: 'video/mp4',
|
||||
}}
|
||||
caption={caption}
|
||||
onChangeCaption={caption => console.log('onChangeCaption', caption)}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
</div>;
|
||||
```
|
78
ts/components/CaptionEditor.tsx
Normal file
78
ts/components/CaptionEditor.tsx
Normal file
|
@ -0,0 +1,78 @@
|
|||
// tslint:disable:react-a11y-anchors
|
||||
|
||||
import React from 'react';
|
||||
import * as GoogleChrome from '../util/GoogleChrome';
|
||||
|
||||
import { AttachmentType } from './conversation/types';
|
||||
|
||||
import { Localizer } from '../types/Util';
|
||||
|
||||
interface Props {
|
||||
attachment: AttachmentType;
|
||||
i18n: Localizer;
|
||||
url: string;
|
||||
caption?: string;
|
||||
onChangeCaption?: (caption: string) => void;
|
||||
close?: () => void;
|
||||
}
|
||||
|
||||
export class CaptionEditor extends React.Component<Props> {
|
||||
public renderObject() {
|
||||
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 (
|
||||
<video className="module-caption-editor__video" controls={true}>
|
||||
<source src={url} />
|
||||
</video>
|
||||
);
|
||||
}
|
||||
|
||||
return <div className="module-caption-editor__placeholder" />;
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { caption, i18n, close, onChangeCaption } = this.props;
|
||||
|
||||
return (
|
||||
<div className="module-caption-editor">
|
||||
<div
|
||||
role="button"
|
||||
onClick={close}
|
||||
className="module-caption-editor__close-button"
|
||||
/>
|
||||
<div className="module-caption-editor__media-container">
|
||||
{this.renderObject()}
|
||||
</div>
|
||||
<div className="module-caption-editor__bottom-bar">
|
||||
<div className="module-caption-editor__add-caption-button" />
|
||||
<input
|
||||
type="text"
|
||||
value={caption || ''}
|
||||
maxLength={200}
|
||||
placeholder={i18n('addACaption')}
|
||||
className="module-caption-editor__caption-input"
|
||||
onChange={event => {
|
||||
if (onChangeCaption) {
|
||||
onChangeCaption(event.target.value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
114
ts/components/conversation/AttachmentList.md
Normal file
114
ts/components/conversation/AttachmentList.md
Normal file
|
@ -0,0 +1,114 @@
|
|||
### One image
|
||||
|
||||
```jsx
|
||||
const attachments = [
|
||||
{
|
||||
url: util.gifObjectUrl,
|
||||
contentType: 'image/gif',
|
||||
width: 320,
|
||||
height: 240,
|
||||
},
|
||||
];
|
||||
|
||||
<AttachmentList
|
||||
attachments={attachments}
|
||||
onClose={() => console.log('onClose')}
|
||||
onClickAttachment={attachment => {
|
||||
console.log('onClickAttachment', attachment);
|
||||
}}
|
||||
onCloseAttachment={attachment => {
|
||||
console.log('onCloseAttachment', attachment);
|
||||
}}
|
||||
i18n={util.i18n}
|
||||
/>;
|
||||
```
|
||||
|
||||
### Four images
|
||||
|
||||
```jsx
|
||||
const attachments = [
|
||||
{
|
||||
url: util.gifObjectUrl,
|
||||
contentType: 'image/png',
|
||||
width: 320,
|
||||
height: 240,
|
||||
},
|
||||
{
|
||||
url: util.pngObjectUrl,
|
||||
contentType: 'image/png',
|
||||
width: 800,
|
||||
height: 1200,
|
||||
},
|
||||
{
|
||||
url: util.landscapeObjectUrl,
|
||||
contentType: 'image/png',
|
||||
width: 4496,
|
||||
height: 3000,
|
||||
},
|
||||
{
|
||||
url: util.landscapeGreenObjectUrl,
|
||||
contentType: 'image/png',
|
||||
width: 1000,
|
||||
height: 50,
|
||||
},
|
||||
];
|
||||
|
||||
<div>
|
||||
<AttachmentList
|
||||
attachments={attachments}
|
||||
onClose={() => console.log('onClose')}
|
||||
onClickAttachment={attachment => {
|
||||
console.log('onClickAttachment', attachment);
|
||||
}}
|
||||
onCloseAttachment={attachment => {
|
||||
console.log('onCloseAttachment', attachment);
|
||||
}}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
</div>;
|
||||
```
|
||||
|
||||
### A mix of attachment types
|
||||
|
||||
```jsx
|
||||
const attachments = [
|
||||
{
|
||||
url: util.gifObjectUrl,
|
||||
contentType: 'image/gif',
|
||||
width: 320,
|
||||
height: 240,
|
||||
},
|
||||
{
|
||||
contentType: 'text/plain',
|
||||
fileName: 'manifesto.txt',
|
||||
},
|
||||
{
|
||||
url: util.pngObjectUrl,
|
||||
contentType: 'image/png',
|
||||
width: 800,
|
||||
height: 1200,
|
||||
},
|
||||
];
|
||||
|
||||
<div>
|
||||
<AttachmentList
|
||||
attachments={attachments}
|
||||
onClose={() => console.log('onClose')}
|
||||
onClickAttachment={attachment => {
|
||||
console.log('onClickAttachment', attachment);
|
||||
}}
|
||||
onCloseAttachment={attachment => {
|
||||
console.log('onCloseAttachment', attachment);
|
||||
}}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
</div>;
|
||||
```
|
||||
|
||||
### No attachments provided
|
||||
|
||||
Nothing is shown if attachment list is empty.
|
||||
|
||||
```jsx
|
||||
<AttachmentList attachments={[]} i18n={util.i18n} />
|
||||
```
|
106
ts/components/conversation/AttachmentList.tsx
Normal file
106
ts/components/conversation/AttachmentList.tsx
Normal file
|
@ -0,0 +1,106 @@
|
|||
import React from 'react';
|
||||
// import classNames from 'classnames';
|
||||
|
||||
import {
|
||||
isImageTypeSupported,
|
||||
isVideoTypeSupported,
|
||||
} from '../../util/GoogleChrome';
|
||||
import { AttachmentType } from './types';
|
||||
import { Image } from './Image';
|
||||
import { StagedGenericAttachment } from './StagedGenericAttachment';
|
||||
import { Localizer } from '../../types/Util';
|
||||
|
||||
interface Props {
|
||||
attachments: Array<AttachmentType>;
|
||||
i18n: Localizer;
|
||||
// onError: () => void;
|
||||
onClickAttachment: (attachment: AttachmentType) => void;
|
||||
onCloseAttachment: (attachment: AttachmentType) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const IMAGE_WIDTH = 120;
|
||||
const IMAGE_HEIGHT = 120;
|
||||
|
||||
export class AttachmentList extends React.Component<Props> {
|
||||
// tslint:disable-next-line max-func-body-length */
|
||||
public render() {
|
||||
const {
|
||||
attachments,
|
||||
i18n,
|
||||
// onError,
|
||||
onClickAttachment,
|
||||
onCloseAttachment,
|
||||
onClose,
|
||||
} = this.props;
|
||||
|
||||
if (!attachments.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="module-attachments">
|
||||
{attachments.length > 1 ? (
|
||||
<div className="module-attachments__header">
|
||||
<div
|
||||
role="button"
|
||||
onClick={onClose}
|
||||
className="module-attachments__close-button"
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="module-attachments__rail">
|
||||
{(attachments || []).map((attachment, index) => {
|
||||
const { contentType } = attachment;
|
||||
if (
|
||||
isImageTypeSupported(contentType) ||
|
||||
isVideoTypeSupported(contentType)
|
||||
) {
|
||||
return (
|
||||
<Image
|
||||
key={getUrl(attachment) || attachment.fileName || index}
|
||||
alt={`TODO: attachment number ${index}`}
|
||||
i18n={i18n}
|
||||
attachment={attachment}
|
||||
softCorners={true}
|
||||
playIconOverlay={isVideoAttachment(attachment)}
|
||||
height={IMAGE_HEIGHT}
|
||||
width={IMAGE_WIDTH}
|
||||
url={getUrl(attachment)}
|
||||
closeButton={true}
|
||||
onClick={onClickAttachment}
|
||||
onClickClose={onCloseAttachment}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<StagedGenericAttachment
|
||||
key={getUrl(attachment) || attachment.fileName || index}
|
||||
attachment={attachment}
|
||||
i18n={i18n}
|
||||
onClose={onCloseAttachment}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function isVideoAttachment(attachment?: AttachmentType) {
|
||||
return (
|
||||
attachment &&
|
||||
attachment.contentType &&
|
||||
isVideoTypeSupported(attachment.contentType)
|
||||
);
|
||||
}
|
||||
|
||||
function getUrl(attachment: AttachmentType) {
|
||||
if (attachment.screenshot) {
|
||||
return attachment.screenshot.url;
|
||||
}
|
||||
|
||||
return attachment.url;
|
||||
}
|
|
@ -77,18 +77,21 @@
|
|||
width="199"
|
||||
attachment={{ caption: 'dogs playing' }}
|
||||
url={util.pngObjectUrl}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Image
|
||||
height="149"
|
||||
width="149"
|
||||
attachment={{ caption: 'dogs playing' }}
|
||||
url={util.pngObjectUrl}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Image
|
||||
height="99"
|
||||
width="99"
|
||||
attachment={{ caption: 'dogs playing' }}
|
||||
url={util.pngObjectUrl}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
</div>
|
||||
<hr />
|
||||
|
@ -100,6 +103,7 @@
|
|||
darkOverlay
|
||||
overlayText="+3"
|
||||
url={util.pngObjectUrl}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Image
|
||||
height="149"
|
||||
|
@ -108,6 +112,7 @@
|
|||
darkOverlay
|
||||
overlayText="+3"
|
||||
url={util.pngObjectUrl}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Image
|
||||
height="99"
|
||||
|
@ -116,6 +121,82 @@
|
|||
darkOverlay
|
||||
overlayText="+3"
|
||||
url={util.pngObjectUrl}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### With top-right X and soft corners
|
||||
|
||||
```jsx
|
||||
<div>
|
||||
<div>
|
||||
<Image
|
||||
height="200"
|
||||
width="199"
|
||||
closeButton={true}
|
||||
onClick={() => console.log('onClick')}
|
||||
onClickClose={attachment => console.log('onClickClose', attachment)}
|
||||
softCorners={true}
|
||||
url={util.gifObjectUrl}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Image
|
||||
height="149"
|
||||
width="149"
|
||||
closeButton={true}
|
||||
onClick={() => console.log('onClick')}
|
||||
onClickClose={attachment => console.log('onClickClose', attachment)}
|
||||
softCorners={true}
|
||||
url={util.gifObjectUrl}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Image
|
||||
height="99"
|
||||
width="99"
|
||||
closeButton={true}
|
||||
onClick={() => console.log('onClick')}
|
||||
onClickClose={attachment => console.log('onClickClose', attachment)}
|
||||
softCorners={true}
|
||||
url={util.gifObjectUrl}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<Image
|
||||
height="200"
|
||||
width="199"
|
||||
closeButton={true}
|
||||
attachment={{ caption: 'dogs playing' }}
|
||||
onClick={() => console.log('onClick')}
|
||||
onClickClose={attachment => console.log('onClickClose', attachment)}
|
||||
softCorners={true}
|
||||
url={util.gifObjectUrl}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Image
|
||||
height="149"
|
||||
width="149"
|
||||
closeButton={true}
|
||||
attachment={{ caption: 'dogs playing' }}
|
||||
onClick={() => console.log('onClick')}
|
||||
onClickClose={attachment => console.log('onClickClose', attachment)}
|
||||
softCorners={true}
|
||||
url={util.gifObjectUrl}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
<Image
|
||||
height="99"
|
||||
width="99"
|
||||
closeButton={true}
|
||||
attachment={{ caption: 'dogs playing' }}
|
||||
onClick={() => console.log('onClick')}
|
||||
onClickClose={attachment => console.log('onClickClose', attachment)}
|
||||
softCorners={true}
|
||||
url={util.gifObjectUrl}
|
||||
i18n={util.i18n}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -15,24 +15,29 @@ interface Props {
|
|||
overlayText?: string;
|
||||
|
||||
bottomOverlay?: boolean;
|
||||
closeButton?: boolean;
|
||||
curveBottomLeft?: boolean;
|
||||
curveBottomRight?: boolean;
|
||||
curveTopLeft?: boolean;
|
||||
curveTopRight?: boolean;
|
||||
darkOverlay?: boolean;
|
||||
playIconOverlay?: boolean;
|
||||
softCorners?: boolean;
|
||||
|
||||
i18n: Localizer;
|
||||
onClick?: (attachment: AttachmentType) => void;
|
||||
onClickClose?: (attachment: AttachmentType) => void;
|
||||
onError?: () => void;
|
||||
}
|
||||
|
||||
export class Image extends React.Component<Props> {
|
||||
// tslint:disable-next-line max-func-body-length cyclomatic-complexity
|
||||
public render() {
|
||||
const {
|
||||
alt,
|
||||
attachment,
|
||||
bottomOverlay,
|
||||
closeButton,
|
||||
curveBottomLeft,
|
||||
curveBottomRight,
|
||||
curveTopLeft,
|
||||
|
@ -41,9 +46,11 @@ export class Image extends React.Component<Props> {
|
|||
height,
|
||||
i18n,
|
||||
onClick,
|
||||
onClickClose,
|
||||
onError,
|
||||
overlayText,
|
||||
playIconOverlay,
|
||||
softCorners,
|
||||
url,
|
||||
width,
|
||||
} = this.props;
|
||||
|
@ -52,18 +59,20 @@ export class Image extends React.Component<Props> {
|
|||
|
||||
return (
|
||||
<div
|
||||
role={onClick ? 'button' : undefined}
|
||||
onClick={() => {
|
||||
if (onClick) {
|
||||
onClick(attachment);
|
||||
}
|
||||
}}
|
||||
role="button"
|
||||
className={classNames(
|
||||
'module-image',
|
||||
onClick ? 'module-image__with-click-handler' : null,
|
||||
curveBottomLeft ? 'module-image--curved-bottom-left' : null,
|
||||
curveBottomRight ? 'module-image--curved-bottom-right' : null,
|
||||
curveTopLeft ? 'module-image--curved-top-left' : null,
|
||||
curveTopRight ? 'module-image--curved-top-right' : null
|
||||
curveTopRight ? 'module-image--curved-top-right' : null,
|
||||
softCorners ? 'module-image--soft-corners' : null
|
||||
)}
|
||||
>
|
||||
<img
|
||||
|
@ -88,9 +97,22 @@ export class Image extends React.Component<Props> {
|
|||
curveTopRight ? 'module-image--curved-top-right' : null,
|
||||
curveBottomLeft ? 'module-image--curved-bottom-left' : null,
|
||||
curveBottomRight ? 'module-image--curved-bottom-right' : null,
|
||||
softCorners ? 'module-image--soft-corners' : null,
|
||||
darkOverlay ? 'module-image__border-overlay--dark' : null
|
||||
)}
|
||||
/>
|
||||
{closeButton ? (
|
||||
<div
|
||||
role="button"
|
||||
onClick={(e: React.MouseEvent<{}>) => {
|
||||
e.stopPropagation();
|
||||
if (onClickClose) {
|
||||
onClickClose(attachment);
|
||||
}
|
||||
}}
|
||||
className="module-image__close-button"
|
||||
/>
|
||||
) : null}
|
||||
{bottomOverlay ? (
|
||||
<div
|
||||
className={classNames(
|
||||
|
|
|
@ -24,7 +24,7 @@ interface Props {
|
|||
const MAX_WIDTH = 300;
|
||||
const MAX_HEIGHT = MAX_WIDTH * 1.5;
|
||||
const MIN_WIDTH = 200;
|
||||
const MIN_HEIGHT = 25;
|
||||
const MIN_HEIGHT = 50;
|
||||
|
||||
export class ImageGrid extends React.Component<Props> {
|
||||
// tslint:disable-next-line max-func-body-length */
|
||||
|
|
|
@ -79,52 +79,6 @@ interface State {
|
|||
imageBroken: boolean;
|
||||
}
|
||||
|
||||
function isAudio(attachments?: Array<AttachmentType>) {
|
||||
return (
|
||||
attachments &&
|
||||
attachments[0] &&
|
||||
attachments[0].contentType &&
|
||||
MIME.isAudio(attachments[0].contentType)
|
||||
);
|
||||
}
|
||||
|
||||
function canDisplayImage(attachments?: Array<AttachmentType>) {
|
||||
const { height, width } =
|
||||
attachments && attachments[0] ? attachments[0] : { height: 0, width: 0 };
|
||||
|
||||
return (
|
||||
height &&
|
||||
height > 0 &&
|
||||
height <= 4096 &&
|
||||
width &&
|
||||
width > 0 &&
|
||||
width <= 4096
|
||||
);
|
||||
}
|
||||
|
||||
function getExtension({
|
||||
fileName,
|
||||
contentType,
|
||||
}: {
|
||||
fileName: string;
|
||||
contentType: MIME.MIMEType;
|
||||
}): string | null {
|
||||
if (fileName && fileName.indexOf('.') >= 0) {
|
||||
const lastPeriod = fileName.lastIndexOf('.');
|
||||
const extension = fileName.slice(lastPeriod + 1);
|
||||
if (extension.length) {
|
||||
return extension;
|
||||
}
|
||||
}
|
||||
|
||||
const slash = contentType.indexOf('/');
|
||||
if (slash >= 0) {
|
||||
return contentType.slice(slash + 1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const EXPIRATION_CHECK_MINIMUM = 2000;
|
||||
const EXPIRED_DELAY = 600;
|
||||
|
||||
|
@ -847,3 +801,49 @@ export class Message extends React.Component<Props, State> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function getExtension({
|
||||
fileName,
|
||||
contentType,
|
||||
}: {
|
||||
fileName: string;
|
||||
contentType: MIME.MIMEType;
|
||||
}): string | null {
|
||||
if (fileName && fileName.indexOf('.') >= 0) {
|
||||
const lastPeriod = fileName.lastIndexOf('.');
|
||||
const extension = fileName.slice(lastPeriod + 1);
|
||||
if (extension.length) {
|
||||
return extension;
|
||||
}
|
||||
}
|
||||
|
||||
const slash = contentType.indexOf('/');
|
||||
if (slash >= 0) {
|
||||
return contentType.slice(slash + 1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function isAudio(attachments?: Array<AttachmentType>) {
|
||||
return (
|
||||
attachments &&
|
||||
attachments[0] &&
|
||||
attachments[0].contentType &&
|
||||
MIME.isAudio(attachments[0].contentType)
|
||||
);
|
||||
}
|
||||
|
||||
function canDisplayImage(attachments?: Array<AttachmentType>) {
|
||||
const { height, width } =
|
||||
attachments && attachments[0] ? attachments[0] : { height: 0, width: 0 };
|
||||
|
||||
return (
|
||||
height &&
|
||||
height > 0 &&
|
||||
height <= 4096 &&
|
||||
width &&
|
||||
width > 0 &&
|
||||
width <= 4096
|
||||
);
|
||||
}
|
||||
|
|
44
ts/components/conversation/StagedGenericAttachment.md
Normal file
44
ts/components/conversation/StagedGenericAttachment.md
Normal file
|
@ -0,0 +1,44 @@
|
|||
Text file
|
||||
|
||||
```js
|
||||
const attachment = {
|
||||
contentType: 'text/plain',
|
||||
fileName: 'manifesto.txt',
|
||||
};
|
||||
|
||||
<StagedGenericAttachment
|
||||
attachment={attachment}
|
||||
i18n={util.i18n}
|
||||
onClose={attachment => console.log('onClose', attachment)}
|
||||
/>;
|
||||
```
|
||||
|
||||
File with long name
|
||||
|
||||
```js
|
||||
const attachment = {
|
||||
contentType: 'text/plain',
|
||||
fileName: 'this-is-my-very-important-manifesto-you-must-read-it.txt',
|
||||
};
|
||||
|
||||
<StagedGenericAttachment
|
||||
attachment={attachment}
|
||||
i18n={util.i18n}
|
||||
onClose={attachment => console.log('onClose', attachment)}
|
||||
/>;
|
||||
```
|
||||
|
||||
File with long extension
|
||||
|
||||
```js
|
||||
const attachment = {
|
||||
contentType: 'text/plain',
|
||||
fileName: 'manifesto.reallylongtxt',
|
||||
};
|
||||
|
||||
<StagedGenericAttachment
|
||||
attachment={attachment}
|
||||
i18n={util.i18n}
|
||||
onClose={attachment => console.log('onClose', attachment)}
|
||||
/>;
|
||||
```
|
44
ts/components/conversation/StagedGenericAttachment.tsx
Normal file
44
ts/components/conversation/StagedGenericAttachment.tsx
Normal file
|
@ -0,0 +1,44 @@
|
|||
import React from 'react';
|
||||
|
||||
import { getExtension } from './Message';
|
||||
|
||||
import { Localizer } from '../../types/Util';
|
||||
import { AttachmentType } from './types';
|
||||
|
||||
interface Props {
|
||||
attachment: AttachmentType;
|
||||
onClose: (attachment: AttachmentType) => void;
|
||||
i18n: Localizer;
|
||||
}
|
||||
|
||||
export class StagedGenericAttachment extends React.Component<Props> {
|
||||
public render() {
|
||||
const { attachment, onClose } = this.props;
|
||||
const { fileName, contentType } = attachment;
|
||||
const extension = getExtension({ contentType, fileName });
|
||||
|
||||
return (
|
||||
<div className="module-staged-generic-attachment">
|
||||
<div
|
||||
className="module-staged-generic-attachment__close-button"
|
||||
role="button"
|
||||
onClick={() => {
|
||||
if (onClose) {
|
||||
onClose(attachment);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div className="module-staged-generic-attachment__icon">
|
||||
{extension ? (
|
||||
<div className="module-staged-generic-attachment__icon__extension">
|
||||
{extension}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="module-staged-generic-attachment__filename">
|
||||
{fileName}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -659,468 +659,495 @@
|
|||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " template: $('#conversation').html(),",
|
||||
"lineNumber": 73,
|
||||
"lineNumber": 78,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:26:45.287Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-html(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " template: $('#conversation').html(),",
|
||||
"lineNumber": 73,
|
||||
"lineNumber": 78,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-15T00:38:04.183Z",
|
||||
"reasonDetail": "Getting the value, not setting it"
|
||||
"updated": "2018-12-15T02:26:45.287Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.loadingScreen.$el.prependTo(this.$('.discussion-container'));",
|
||||
"lineNumber": 143,
|
||||
"lineNumber": 148,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-11-14T19:09:08.182Z",
|
||||
"updated": "2018-12-15T02:26:45.287Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-prependTo(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.loadingScreen.$el.prependTo(this.$('.discussion-container'));",
|
||||
"lineNumber": 143,
|
||||
"lineNumber": 148,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-11-14T19:07:46.079Z",
|
||||
"updated": "2018-12-15T02:26:45.287Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " el: this.$('form.send'),",
|
||||
"lineNumber": 147,
|
||||
"line": " el: this.$('.attachment-list'),",
|
||||
"lineNumber": 152,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-11-14T19:07:46.079Z",
|
||||
"updated": "2018-12-15T02:26:45.287Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.conversation-header').append(this.titleView.el);",
|
||||
"lineNumber": 205,
|
||||
"lineNumber": 209,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:26:45.287Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-append(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.conversation-header').append(this.titleView.el);",
|
||||
"lineNumber": 205,
|
||||
"lineNumber": 209,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
"updated": "2018-12-15T02:26:45.287Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.discussion-container').append(this.view.el);",
|
||||
"lineNumber": 211,
|
||||
"lineNumber": 215,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:26:45.287Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-append(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.discussion-container').append(this.view.el);",
|
||||
"lineNumber": 211,
|
||||
"lineNumber": 215,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
"updated": "2018-12-15T02:26:45.287Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$messageField = this.$('.send-message');",
|
||||
"lineNumber": 214,
|
||||
"lineNumber": 218,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:26:45.287Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.send-message').focus(this.focusBottomBar.bind(this));",
|
||||
"lineNumber": 232,
|
||||
"lineNumber": 236,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:26:45.287Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$emojiPanelContainer = this.$('.emoji-panel-container');",
|
||||
"lineNumber": 235,
|
||||
"lineNumber": 239,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:26:45.287Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('input.file-input').click();",
|
||||
"lineNumber": 276,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " const fileField = this.$('input.file-input');",
|
||||
"lineNumber": 279,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " const container = this.$('.discussion-container');",
|
||||
"lineNumber": 421,
|
||||
"lineNumber": 451,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-append(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " container.append(this.banner.el);",
|
||||
"lineNumber": 422,
|
||||
"lineNumber": 452,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.typingBubbleView.$el.appendTo(this.$('.typing-container'));",
|
||||
"lineNumber": 459,
|
||||
"lineNumber": 489,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-11-14T18:51:15.180Z",
|
||||
"reasonDetail": "$() parameter is a hard-coded string"
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-appendTo(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.typingBubbleView.$el.appendTo(this.$('.typing-container'));",
|
||||
"lineNumber": 459,
|
||||
"lineNumber": 489,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-11-14T18:51:15.180Z",
|
||||
"reasonDetail": "Both parameters are known elements from the DOM"
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.send-message').val().length > 0 ||",
|
||||
"lineNumber": 468,
|
||||
"lineNumber": 498,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.capture-audio').hide();",
|
||||
"lineNumber": 471,
|
||||
"lineNumber": 501,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.capture-audio').show();",
|
||||
"lineNumber": 473,
|
||||
"lineNumber": 503,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " if (this.$('.send-message').val().length > 2000) {",
|
||||
"lineNumber": 477,
|
||||
"lineNumber": 507,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.android-length-warning').hide();",
|
||||
"lineNumber": 480,
|
||||
"lineNumber": 510,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-appendTo(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " toast.$el.appendTo(this.$el);",
|
||||
"lineNumber": 518,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " view.$el.appendTo(this.$('.capture-audio'));",
|
||||
"lineNumber": 500,
|
||||
"lineNumber": 537,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-appendTo(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " view.$el.appendTo(this.$('.capture-audio'));",
|
||||
"lineNumber": 500,
|
||||
"lineNumber": 537,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.send-message').attr('disabled', true);",
|
||||
"lineNumber": 502,
|
||||
"lineNumber": 539,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.bottom-bar form').submit();",
|
||||
"lineNumber": 509,
|
||||
"lineNumber": 548,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.send-message').removeAttr('disabled');",
|
||||
"lineNumber": 512,
|
||||
"lineNumber": 551,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.bottom-bar form').removeClass('active');",
|
||||
"lineNumber": 518,
|
||||
"lineNumber": 557,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.bottom-bar form').addClass('active');",
|
||||
"lineNumber": 521,
|
||||
"lineNumber": 560,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " const container = this.$('.discussion-container');",
|
||||
"lineNumber": 609,
|
||||
"lineNumber": 648,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-append(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " container.append(this.scrollDownButton.el);",
|
||||
"lineNumber": 610,
|
||||
"lineNumber": 649,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-appendTo(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " toast.$el.appendTo(this.$el);",
|
||||
"lineNumber": 637,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-appendTo(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " toast.$el.appendTo(this.$el);",
|
||||
"lineNumber": 670,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-appendTo(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " toast.$el.appendTo(this.$el);",
|
||||
"lineNumber": 674,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " const el = this.$(`#${databaseId}`);",
|
||||
"lineNumber": 681,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-appendTo(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " toast.$el.appendTo(this.$el);",
|
||||
"lineNumber": 684,
|
||||
"lineNumber": 676,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-appendTo(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " toast.$el.appendTo(this.$el);",
|
||||
"lineNumber": 709,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-appendTo(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " toast.$el.appendTo(this.$el);",
|
||||
"lineNumber": 713,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " const el = this.$(`#${databaseId}`);",
|
||||
"lineNumber": 720,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-appendTo(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " toast.$el.appendTo(this.$el);",
|
||||
"lineNumber": 723,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " lastSeenEl.insertBefore(this.$(`#${oldestUnread.get('id')}`));",
|
||||
"lineNumber": 861,
|
||||
"lineNumber": 900,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-insertBefore(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " lastSeenEl.insertBefore(this.$(`#${oldestUnread.get('id')}`));",
|
||||
"lineNumber": 861,
|
||||
"lineNumber": 900,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.bar-container').show();",
|
||||
"lineNumber": 916,
|
||||
"lineNumber": 955,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.bar-container').hide();",
|
||||
"lineNumber": 928,
|
||||
"lineNumber": 967,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " const el = this.$(`#${message.id}`);",
|
||||
"lineNumber": 1025,
|
||||
"lineNumber": 1064,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-prepend(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$el.prepend(dialog.el);",
|
||||
"lineNumber": 1098,
|
||||
"lineNumber": 1137,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-appendTo(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " toast.$el.appendTo(this.$el);",
|
||||
"lineNumber": 1121,
|
||||
"lineNumber": 1160,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-10-11T19:22:47.331Z",
|
||||
"reasonDetail": "Operating on already-existing DOM elements"
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-prepend(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$el.prepend(dialog.el);",
|
||||
"lineNumber": 1149,
|
||||
"lineNumber": 1188,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " view.$el.insertBefore(this.$('.panel').first());",
|
||||
"lineNumber": 1283,
|
||||
"lineNumber": 1323,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-insertBefore(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " view.$el.insertBefore(this.$('.panel').first());",
|
||||
"lineNumber": 1283,
|
||||
"lineNumber": 1323,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-prepend(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$el.prepend(dialog.el);",
|
||||
"lineNumber": 1361,
|
||||
"lineNumber": 1401,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.send').prepend(this.quoteView.el);",
|
||||
"lineNumber": 1531,
|
||||
"lineNumber": 1571,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-prepend(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.send').prepend(this.quoteView.el);",
|
||||
"lineNumber": 1531,
|
||||
"lineNumber": 1571,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-appendTo(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " toast.$el.appendTo(this.$el);",
|
||||
"lineNumber": 1555,
|
||||
"lineNumber": 1595,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.bottom-bar form').submit();",
|
||||
"lineNumber": 1610,
|
||||
"lineNumber": 1650,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " const $attachmentPreviews = this.$('.attachment-previews');",
|
||||
"lineNumber": 1619,
|
||||
"lineNumber": 1659,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/conversation_view.js",
|
||||
"line": " this.$('.panel').css('display') === 'none'",
|
||||
"lineNumber": 1650,
|
||||
"lineNumber": 1690,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T02:21:20.921Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
|
@ -1196,103 +1223,58 @@
|
|||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"rule": "jQuery-insertAfter(",
|
||||
"path": "js/views/file_input_view.js",
|
||||
"line": " this.$input = this.$('input[type=file]');",
|
||||
"lineNumber": 45,
|
||||
"line": " toast.$el.insertAfter(this.$el);",
|
||||
"lineNumber": 216,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/file_input_view.js",
|
||||
"line": " this.$('.avatar').hide();",
|
||||
"lineNumber": 88,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/file_input_view.js",
|
||||
"line": " this.$('.attachment-previews').append(this.thumb.render().el);",
|
||||
"lineNumber": 90,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-append(",
|
||||
"path": "js/views/file_input_view.js",
|
||||
"line": " this.$('.attachment-previews').append(this.thumb.render().el);",
|
||||
"lineNumber": 90,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/file_input_view.js",
|
||||
"line": " this.thumb.$('img')[0].onload = () => {",
|
||||
"lineNumber": 98,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/file_input_view.js",
|
||||
"line": " this.thumb.$('img')[0].onerror = () => {",
|
||||
"lineNumber": 101,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T03:04:48.403Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-insertAfter(",
|
||||
"path": "js/views/file_input_view.js",
|
||||
"line": " toast.$el.insertAfter(this.$el);",
|
||||
"lineNumber": 108,
|
||||
"lineNumber": 222,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-insertAfter(",
|
||||
"path": "js/views/file_input_view.js",
|
||||
"line": " toast.$el.insertAfter(this.$el);",
|
||||
"lineNumber": 190,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-10-11T19:22:47.331Z",
|
||||
"reasonDetail": "Operating on already-existing DOM elements"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-insertAfter(",
|
||||
"path": "js/views/file_input_view.js",
|
||||
"line": " toast.$el.insertAfter(this.$el);",
|
||||
"lineNumber": 284,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Interacting with already-existing DOM nodes"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
"path": "js/views/file_input_view.js",
|
||||
"line": " this.$('.avatar').show();",
|
||||
"lineNumber": 388,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"updated": "2018-12-15T03:04:48.403Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-wrap(",
|
||||
"rule": "jQuery-insertAfter(",
|
||||
"path": "js/views/file_input_view.js",
|
||||
"line": " .wrap('<form>')",
|
||||
"lineNumber": 398,
|
||||
"line": " toast.$el.insertAfter(this.$el);",
|
||||
"lineNumber": 230,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "Hard-coded value"
|
||||
"updated": "2018-12-15T03:04:48.403Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-insertAfter(",
|
||||
"path": "js/views/file_input_view.js",
|
||||
"line": " toast.$el.insertAfter(this.$el);",
|
||||
"lineNumber": 236,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-12-15T03:04:48.403Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-insertAfter(",
|
||||
"path": "js/views/file_input_view.js",
|
||||
"line": " toast.$el.insertAfter(this.$el);",
|
||||
"lineNumber": 242,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-12-15T03:04:48.403Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-insertAfter(",
|
||||
"path": "js/views/file_input_view.js",
|
||||
"line": " toast.$el.insertAfter(this.$el);",
|
||||
"lineNumber": 248,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-12-15T03:04:48.403Z",
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-$(",
|
||||
|
@ -3493,7 +3475,7 @@
|
|||
"lineNumber": 4136,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2018-09-19T18:13:29.628Z",
|
||||
"reasonDetail": "<optional>"
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-wrap(",
|
||||
|
@ -4083,7 +4065,7 @@
|
|||
"lineNumber": 483,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2018-09-19T21:59:32.770Z",
|
||||
"reasonDetail": "<optional>"
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "jQuery-appendTo(",
|
||||
|
@ -5849,7 +5831,7 @@
|
|||
"lineNumber": 1699,
|
||||
"reasonCategory": "falseMatch",
|
||||
"updated": "2018-09-18T19:19:27.699Z",
|
||||
"reasonDetail": "<optional>"
|
||||
"reasonDetail": "Protected from arbitrary input"
|
||||
},
|
||||
{
|
||||
"rule": "DOM-innerHTML",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue