summaryrefslogtreecommitdiffstats
path: root/webapp/components/file_attachment.jsx
diff options
context:
space:
mode:
authorHarrison Healey <harrisonmhealey@gmail.com>2016-09-30 11:06:30 -0400
committerGitHub <noreply@github.com>2016-09-30 11:06:30 -0400
commit8a0e649f989a824bb3bbfd1900a5b8e5383b47e1 (patch)
tree4b424929fe13ebec438d2f41a2729e37e5160720 /webapp/components/file_attachment.jsx
parenta2deeed597dea15d9b7ca237be71988469f58cdd (diff)
downloadchat-8a0e649f989a824bb3bbfd1900a5b8e5383b47e1.tar.gz
chat-8a0e649f989a824bb3bbfd1900a5b8e5383b47e1.tar.bz2
chat-8a0e649f989a824bb3bbfd1900a5b8e5383b47e1.zip
PLT-3105 Files table migration (#4068)
* Implemented initial changes for files table * Removed *_benchmark_test.go files * Re-implemented GetPublicFile and added support for old path * Localization for files table * Moved file system code into utils package * Finished server-side changes and added initial upgrade script * Added getPostFiles api * Re-add Extension and HasPreviewImage fields to FileInfo * Removed unused translation * Fixed merge conflicts left over after permissions changes * Forced FileInfo.extension to be lower case * Changed FileUploadResponse to contain the FileInfos instead of FileIds * Fixed permissions on getFile* calls * Fixed notifications for file uploads * Added initial version of client code for files changes * Permanently added FileIds field to Post object and removed Post.HasFiles * Updated PostStore.Update to be usable in more circumstances * Re-added Filenames field and switched file migration to be entirely lazy-loaded * Increased max listener count for FileStore * Removed unused fileInfoCache * Moved file system code back into api * Removed duplicate test case * Fixed unit test running on ports other than 8065 * Renamed HasPermissionToPostContext to HasPermissionToChannelByPostContext * Refactored handleImages to make it more easily understandable * Renamed getPostFiles to getFileInfosForPost * Re-added pre-FileIds posts to analytics * Changed files to be saved as their ids as opposed to id/filename.ext * Renamed FileInfo.UserId to FileInfo.CreatorId * Fixed detection of language in CodePreview * Fixed switching between threads in the RHS not loading new files * Add serverside protection against a rare bug where the client sends the same file twice for a single post * Refactored the important parts of uploadFile api call into a function that can be called without a web context
Diffstat (limited to 'webapp/components/file_attachment.jsx')
-rw-r--r--webapp/components/file_attachment.jsx251
1 files changed, 83 insertions, 168 deletions
diff --git a/webapp/components/file_attachment.jsx b/webapp/components/file_attachment.jsx
index cba9d8288..23d8d2446 100644
--- a/webapp/components/file_attachment.jsx
+++ b/webapp/components/file_attachment.jsx
@@ -1,204 +1,111 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import $ from 'jquery';
-import ReactDOM from 'react-dom';
-import * as utils from 'utils/utils.jsx';
-import Client from 'client/web_client.jsx';
import Constants from 'utils/constants.jsx';
+import FileStore from 'stores/file_store.jsx';
+import * as Utils from 'utils/utils.jsx';
-import {intlShape, injectIntl, defineMessages} from 'react-intl';
import {Tooltip, OverlayTrigger} from 'react-bootstrap';
-const holders = defineMessages({
- download: {
- id: 'file_attachment.download',
- defaultMessage: 'Download'
- }
-});
-
import React from 'react';
-class FileAttachment extends React.Component {
+export default class FileAttachment extends React.Component {
constructor(props) {
super(props);
this.loadFiles = this.loadFiles.bind(this);
- this.addBackgroundImage = this.addBackgroundImage.bind(this);
this.onAttachmentClick = this.onAttachmentClick.bind(this);
- this.canSetState = false;
- this.state = {fileSize: -1};
+ this.state = {
+ loaded: Utils.getFileType(props.fileInfo.extension) !== 'image'
+ };
}
+
componentDidMount() {
this.loadFiles();
}
- componentDidUpdate(prevProps) {
- if (this.props.filename !== prevProps.filename) {
- this.loadFiles();
- }
- }
- loadFiles() {
- this.canSetState = true;
-
- var filename = this.props.filename;
-
- if (filename) {
- var fileInfo = this.getFileInfoFromName(filename);
- var type = utils.getFileType(fileInfo.ext);
-
- if (type === 'image') {
- var self = this; // Need this reference since we use the given "this"
- $('<img/>').attr('src', fileInfo.path + '_thumb.jpg').on('load', (function loadWrapper(path, name) {
- return function loader() {
- $(this).remove();
- if (name in self.refs) {
- var imgDiv = ReactDOM.findDOMNode(self.refs[name]);
-
- $(imgDiv).removeClass('post-image__load');
- $(imgDiv).addClass('post-image');
-
- var width = this.width || $(this).width();
- var height = this.height || $(this).height();
-
- if (width < Constants.THUMBNAIL_WIDTH &&
- height < Constants.THUMBNAIL_HEIGHT) {
- $(imgDiv).addClass('small');
- } else {
- $(imgDiv).addClass('normal');
- }
- self.addBackgroundImage(name, path);
- }
- };
- }(fileInfo.path, filename)));
- }
+ componentWillReceiveProps(nextProps) {
+ if (nextProps.fileInfo.id !== this.props.fileInfo.id) {
+ this.setState({
+ loaded: Utils.getFileType(nextProps.fileInfo.extension) !== 'image'
+ });
}
}
- componentWillUnmount() {
- // keep track of when this component is mounted so that we can asynchronously change state without worrying about whether or not we're mounted
- this.canSetState = false;
- }
- shouldComponentUpdate(nextProps, nextState) {
- if (!utils.areObjectsEqual(nextProps, this.props)) {
- return true;
- }
-
- // the only time this object should update is when it receives an updated file size which we can usually handle without re-rendering
- if (nextState.fileSize !== this.state.fileSize) {
- if (this.refs.fileSize) {
- // update the UI element to display the file size without re-rendering the whole component
- ReactDOM.findDOMNode(this.refs.fileSize).innerHTML = utils.fileSizeToString(nextState.fileSize);
- return false;
- }
-
- // we can't find the element that should hold the file size so we must not have rendered yet
- return true;
+ componentDidUpdate(prevProps) {
+ if (!this.state.loaded && this.props.fileInfo.id !== prevProps.fileInfo.id) {
+ this.loadFiles();
}
-
- return true;
- }
- getFileInfoFromName(name) {
- var fileInfo = utils.splitFileLocation(name);
-
- fileInfo.path = Client.getFilesRoute() + '/get' + fileInfo.path;
-
- return fileInfo;
}
- addBackgroundImage(name, path) {
- var fileUrl = path;
- if (name in this.refs) {
- if (!path) {
- fileUrl = this.getFileInfoFromName(name).path;
- }
+ loadFiles() {
+ const fileInfo = this.props.fileInfo;
+ const fileType = Utils.getFileType(fileInfo.extension);
- var imgDiv = ReactDOM.findDOMNode(this.refs[name]);
- var re1 = new RegExp(' ', 'g');
- var re2 = new RegExp('\\(', 'g');
- var re3 = new RegExp('\\)', 'g');
- var url = fileUrl.replace(re1, '%20').replace(re2, '%28').replace(re3, '%29');
+ if (fileType === 'image') {
+ const thumbnailUrl = FileStore.getFileThumbnailUrl(fileInfo.id);
- $(imgDiv).css('background-image', 'url(' + url + '_thumb.jpg)');
- }
- }
- removeBackgroundImage(name) {
- if (name in this.refs) {
- $(ReactDOM.findDOMNode(this.refs[name])).css('background-image', 'initial');
+ const img = new Image();
+ img.onload = () => {
+ this.setState({loaded: true});
+ };
+ img.load(thumbnailUrl);
}
}
+
onAttachmentClick(e) {
e.preventDefault();
this.props.handleImageClick(this.props.index);
}
+
render() {
- var filename = this.props.filename;
+ const fileInfo = this.props.fileInfo;
+ const fileName = fileInfo.name;
+ const fileUrl = FileStore.getFileUrl(fileInfo.id);
- var fileInfo = utils.splitFileLocation(filename);
- var fileUrl = utils.getFileUrl(filename);
- var type = utils.getFileType(fileInfo.ext);
+ let thumbnail;
+ if (this.state.loaded) {
+ const type = Utils.getFileType(fileInfo.extension);
- var thumbnail;
- if (type === 'image') {
- thumbnail = (
- <div
- ref={filename}
- className='post-image__load'
- />
- );
- } else {
- thumbnail = <div className={'file-icon ' + utils.getIconClassName(type)}/>;
- }
+ if (type === 'image') {
+ let className = 'post-image';
- var fileSizeString = '';
- if (this.state.fileSize < 0) {
- Client.getFileInfo(
- filename,
- (data) => {
- if (this.canSetState) {
- this.setState({fileSize: parseInt(data.size, 10)});
- }
- },
- () => {
- // Do nothing
+ if (fileInfo.width < Constants.THUMBNAIL_WIDTH && fileInfo.height < Constants.THUMBNAIL_HEIGHT) {
+ className += ' small';
+ } else {
+ className += ' normal';
}
- );
+
+ thumbnail = (
+ <div
+ className={className}
+ style={{
+ backgroundImage: `url(${FileStore.getFileThumbnailUrl(fileInfo.id)})`
+ }}
+ />
+ );
+ } else {
+ thumbnail = <div className={'file-icon ' + Utils.getIconClassName(type)}/>;
+ }
} else {
- fileSizeString = utils.fileSizeToString(this.state.fileSize);
+ thumbnail = <div className='post-image__load'/>;
}
- var filenameString = decodeURIComponent(utils.getFileName(filename));
- var trimmedFilename;
- if (filenameString.length > 35) {
- trimmedFilename = filenameString.substring(0, Math.min(35, filenameString.length)) + '...';
+ let trimmedFilename;
+ if (fileName.length > 35) {
+ trimmedFilename = fileName.substring(0, Math.min(35, fileName.length)) + '...';
} else {
- trimmedFilename = filenameString;
+ trimmedFilename = fileName;
}
- var filenameOverlay = (
- <OverlayTrigger
- delayShow={1000}
- placement='top'
- overlay={<Tooltip id='file-name__tooltip'>{this.props.intl.formatMessage(holders.download) + ' "' + filenameString + '"'}</Tooltip>}
- >
- <a
- href={fileUrl}
- download={filenameString}
- className='post-image__name'
- target='_blank'
- rel='noopener noreferrer'
- >
- {trimmedFilename}
- </a>
- </OverlayTrigger>
- );
+ let filenameOverlay;
if (this.props.compactDisplay) {
filenameOverlay = (
<OverlayTrigger
delayShow={1000}
placement='top'
- overlay={<Tooltip id='file-name__tooltip'>{filenameString}</Tooltip>}
+ overlay={<Tooltip id='file-name__tooltip'>{fileName}</Tooltip>}
>
<a
href='#'
@@ -214,13 +121,28 @@ class FileAttachment extends React.Component {
</a>
</OverlayTrigger>
);
+ } else {
+ filenameOverlay = (
+ <OverlayTrigger
+ delayShow={1000}
+ placement='top'
+ overlay={<Tooltip id='file-name__tooltip'>{Utils.localizeMessage('file_attachment.download', 'Download') + ' "' + fileName + '"'}</Tooltip>}
+ >
+ <a
+ href={fileUrl}
+ download={fileName}
+ className='post-image__name'
+ target='_blank'
+ rel='noopener noreferrer'
+ >
+ {trimmedFilename}
+ </a>
+ </OverlayTrigger>
+ );
}
return (
- <div
- className='post-image__column'
- key={filename}
- >
+ <div className='post-image__column'>
<a
className='post-image__thumbnail'
href='#'
@@ -233,17 +155,15 @@ class FileAttachment extends React.Component {
<div>
<a
href={fileUrl}
- download={filenameString}
+ download={fileName}
className='post-image__download'
target='_blank'
rel='noopener noreferrer'
>
- <span
- className='fa fa-download'
- />
+ <span className='fa fa-download'/>
</a>
- <span className='post-image__type'>{fileInfo.ext.toUpperCase()}</span>
- <span className='post-image__size'>{fileSizeString}</span>
+ <span className='post-image__type'>{fileInfo.extension.toUpperCase()}</span>
+ <span className='post-image__size'>{Utils.fileSizeToString(fileInfo.size)}</span>
</div>
</div>
</div>
@@ -252,10 +172,7 @@ class FileAttachment extends React.Component {
}
FileAttachment.propTypes = {
- intl: intlShape.isRequired,
-
- // a list of file pathes displayed by the parent FileAttachmentList
- filename: React.PropTypes.string.isRequired,
+ fileInfo: React.PropTypes.object.isRequired,
// the index of this attachment preview in the parent FileAttachmentList
index: React.PropTypes.number.isRequired,
@@ -264,6 +181,4 @@ FileAttachment.propTypes = {
handleImageClick: React.PropTypes.func,
compactDisplay: React.PropTypes.bool
-};
-
-export default injectIntl(FileAttachment);
+}; \ No newline at end of file