summaryrefslogtreecommitdiffstats
path: root/web/react/components/file_attachment.jsx
blob: 45e6c5e2817ea2c0f0f54d6b87b1519b27d05360 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.

var utils = require('../utils/utils.jsx');
var Client = require('../utils/client.jsx');
var Constants = require('../utils/constants.jsx');

module.exports = React.createClass({
    displayName: "FileAttachment",
    canSetState: false,
    propTypes: {
        // a list of file pathes displayed by the parent FileAttachmentList
        filename: React.PropTypes.string.isRequired,
        // the index of this attachment preview in the parent FileAttachmentList
        index: React.PropTypes.number.isRequired,
        // the identifier of the modal dialog used to preview files
        modalId: React.PropTypes.string.isRequired,
        // handler for when the thumbnail is clicked
        handleImageClick: React.PropTypes.func
    },
    getInitialState: function() {
        return {fileSize: -1};
    },
    componentDidMount: function() {
        this.loadFiles();
    },
    componentDidUpdate: function(prevProps) {
        if (this.props.filename !== prevProps.filename) {
            this.loadFiles();
        }
    },
    loadFiles: function() {
        this.canSetState = true;

        var filename = this.props.filename;

        if (filename) {
            var fileInfo = utils.splitFileLocation(filename);
            var type = utils.getFileType(fileInfo.ext);

            // This is a temporary patch to fix issue with old files using absolute paths
            if (fileInfo.path.indexOf("/api/v1/files/get") != -1) {
                fileInfo.path = fileInfo.path.split("/api/v1/files/get")[1];
            }
            fileInfo.path = utils.getWindowLocationOrigin() + "/api/v1/files/get" + fileInfo.path;

            if (type === "image") {
                var self = this;
                $('<img/>').attr('src', fileInfo.path+'_thumb.jpg').load(function(path, name){ return function() {
                    $(this).remove();
                    if (name in self.refs) {
                        var imgDiv = self.refs[name].getDOMNode();

                        $(imgDiv).removeClass('post__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');
                        }

                        var re1 = new RegExp(' ', 'g');
                        var re2 = new RegExp('\\(', 'g');
                        var re3 = new RegExp('\\)', 'g');
                        var url = path.replace(re1, '%20').replace(re2, '%28').replace(re3, '%29');
                        $(imgDiv).css('background-image', 'url('+url+'_thumb.jpg)');
                    }
                }}(fileInfo.path, filename));
            }
        }
    },
    componentWillUnmount: function() {
        // 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: function(nextProps, nextState) {
        if (!utils.areStatesEqual(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
                this.refs.fileSize.getDOMNode().innerHTML = utils.fileSizeToString(nextState.fileSize);

                return false;
            } else {
                // we can't find the element that should hold the file size so we must not have rendered yet
                return true;
            }
        } else {
            return true;
        }
    },
    render: function() {
        var filename = this.props.filename;

        var fileInfo = utils.splitFileLocation(filename);
        var type = utils.getFileType(fileInfo.ext);

        var thumbnail;
        if (type === "image") {
            thumbnail = <div ref={filename} className="post__load" style={{backgroundImage: 'url(/static/images/load.gif)'}}/>;
        } else {
            thumbnail = <div className={"file-icon "+utils.getIconClassName(type)}/>;
        }

        var fileSizeString = "";
        if (this.state.fileSize < 0) {
            var self = this;

            Client.getFileInfo(
                filename,
                function(data) {
                    if (self.canSetState) {
                        self.setState({fileSize: parseInt(data["size"], 10)});
                    }
                },
                function(err) {
                }
            );
        } else {
            fileSizeString = utils.fileSizeToString(this.state.fileSize);
        }

        var filenameString = decodeURIComponent(utils.getFileName(filename));
        var trimmedFilename;
        if (filenameString.length > 35) {
            trimmedFilename = filenameString.substring(0, Math.min(35, filenameString.length)) + "...";
        } else {
            trimmedFilename = filenameString;
        }

        return (
            <div className="post-image__column" key={filename}>
                <a className="post-image__thumbnail" href="#" onClick={this.props.handleImageClick}
                    data-img-id={this.props.index} data-toggle="modal" data-target={"#" + this.props.modalId }>
                    {thumbnail}
                </a>
                <div className="post-image__details">
                    <div data-toggle="tooltip" title={filenameString} className="post-image__name">{trimmedFilename}</div>
                    <div>
                        <span className="post-image__type">{fileInfo.ext.toUpperCase()}</span>
                        <span className="post-image__size">{fileSizeString}</span>
                    </div>
                </div>
            </div>
        );
    }
});