2017-06-08_509bba0/509bba0_unpacked_with_node_modules/discord_app/components/UploadArea.js
2022-07-26 10:06:20 -07:00

250 lines
7.5 KiB
JavaScript
Executable file

import React from 'react';
import ReactDOM from 'react-dom';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import Flux from '../lib/flux';
import classNames from 'classnames';
import {Permissions} from '../Constants';
import GuildVerificationStore from '../stores/GuildVerificationStore';
import SelectedChannelStore from '../stores/SelectedChannelStore';
import SelectedGuildStore from '../stores/SelectedGuildStore';
import PermissionMixin from '../mixins/PermissionMixin';
import ChannelStore from '../stores/ChannelStore';
import LayerStore from '../stores/LayerStore';
import ModalActionCreators from '../actions/ModalActionCreators';
import ModalStore from '../stores/ModalStore';
import i18n from '../i18n';
import UploadMixin from '../mixins/web/UploadMixin';
import TimerMixin from 'react-timer-mixin';
import UploadError from './warnings/UploadError';
import '../styles/common/sprites.styl';
import '../styles/upload_modal.styl';
const REMOVE_AFTER_TIME = 1000;
const UploadArea = React.createClass({
mixins: [
Flux.StoreListenerMixin(ChannelStore, LayerStore, SelectedChannelStore, SelectedGuildStore),
PermissionMixin,
PureRenderMixin,
UploadMixin,
TimerMixin,
],
getInitialState() {
return {
dragging: false,
requireConfirm: true,
};
},
getStateFromStores() {
return {
hasLayers: LayerStore.hasLayers(),
channel: SelectedChannelStore.getChannelId() && ChannelStore.getChannel(SelectedChannelStore.getChannelId()),
};
},
componentDidMount() {
document.body.addEventListener('dragover', this.handleDragOver, false);
document.body.addEventListener('drop', this.handleDragLeave, false);
ReactDOM.findDOMNode(this).addEventListener('dragleave', this.handleDragLeave, false);
ReactDOM.findDOMNode(this).addEventListener('drop', this.handleDrop, false);
},
componentWillUnmount() {
document.body.removeEventListener('dragover', this.handleDragOver, false);
document.body.removeEventListener('drop', this.handleDragLeave, false);
ReactDOM.findDOMNode(this).removeEventListener('dragleave', this.handleDragLeave, false);
ReactDOM.findDOMNode(this).removeEventListener('drop', this.handleDrop, false);
},
render() {
const showDragging = this.state.dragging && this.canDropFile();
let title = i18n.Messages.UPLOAD_AREA_TITLE;
if (!this.state.requireConfirm) {
title = i18n.Messages.UPLOAD_AREA_TITLE_NO_CONFIRMATION;
}
let sprites = null;
if (showDragging) {
sprites = (
<div>
<div className="sprite sparkle one" />
<div className="sprite sparkle two" />
<div className="sprite light one" />
<div className="sprite light two" />
<div className="sprite cross one" />
<div className="sprite cross two" />
<div className="sprite pop one" />
</div>
);
}
return (
<div className={classNames('upload-area', {'upload-modal-in': showDragging})}>
<div className="upload-drop-modal">
{sprites}
<div className="bgScale" />
<div className="inner">
<div className="icons">
<div className="wrap-one"><div className="icon one" /></div>
<div className="wrap-three"><div className="icon three" /></div>
<div className="wrap-two"><div className="icon two" /></div>
</div>
<div className="title">
{title}
</div>
<div className="instructions">
<pre>
{i18n.Messages.UPLOAD_AREA_HELP}
</pre>
</div>
</div>
</div>
</div>
);
},
// DND
canDropFile() {
const {channel, hasLayers} = this.state;
return (
channel &&
!hasLayers &&
(channel.isPrivate() ||
(this.can(Permissions.ATTACH_FILES, this.state.channel) &&
GuildVerificationStore.canChatInGuild(SelectedGuildStore.getGuildId())))
);
},
isAllDropFiles(items) {
for (let i = 0; i < items.length; i++) {
try {
const item = items[i].webkitGetAsEntry() || items[i].getAsEntry();
if (item && !item.isFile) {
return false;
}
} catch (e) {
continue;
}
}
return true;
},
// For DnD glitching and not vanishing within an interval
dragOverTimeout: null,
// Explicitly prevent using Discord as a webpage by dropping links into it
preventUnwantedDrop(evt, errorModal = false) {
const dataTransfer = evt.dataTransfer;
const hasLinkOrTextChannels =
dataTransfer.types instanceof Array &&
dataTransfer.types.indexOf('text/uri-list') != -1 &&
dataTransfer.types.indexOf('application/json') == -1;
const hasNonFile = dataTransfer.items != null && !this.isAllDropFiles(dataTransfer.items);
if (hasLinkOrTextChannels || hasNonFile) {
evt.stopPropagation();
evt.preventDefault();
dataTransfer.effectAllowed = 'none';
dataTransfer.dropEffect = 'none';
if (errorModal) {
this.setState({
dragging: false,
});
ModalActionCreators.push(UploadError, {
title: i18n.Messages.UPLOAD_AREA_INVALID_FILE_TYPE_TITLE,
help: i18n.Messages.UPLOAD_AREA_INVALID_FILE_TYPE_HELP,
});
}
return false;
}
return true;
},
handleDragOver(e) {
if (!this.preventUnwantedDrop(e)) return false;
// Just use 'move' for everything in the app.
e.dataTransfer.dropEffect = 'move';
const openModal = ModalStore.getModal();
if (openModal && openModal.modal === UploadError) {
ModalActionCreators.pop();
}
e.stopPropagation();
e.preventDefault();
if (!this.state.dragging) {
// The structure of data transfer types varies on webkit/firefox.
if (
(e.dataTransfer.types instanceof DOMStringList && e.dataTransfer.types.contains('application/x-moz-file')) ||
(e.dataTransfer.types instanceof Array && e.dataTransfer.types.indexOf('Files') != -1)
) {
// Use transactional setState, as it seems like sometimes on windows the events fire in such a way
// that we end up transitioning back to dragging: true when they leave.
this.setState(prevState => {
if (!prevState.dragging) {
return {dragging: true, requireConfirm: !e.shiftKey};
}
return {};
});
}
} else if (this.state.dragging) {
if (e.shiftKey !== !this.state.requireConfirm) {
this.setState({requireConfirm: !e.shiftKey});
}
}
// In case the DnD overlay gets 'frozen', remove it after a second of no dragOver event firing.
this.clearTimeout(this.dragOverTimeout);
this.dragOverTimeout = this.setTimeout(() => {
this.setState({dragging: false, requireConfirm: true});
}, REMOVE_AFTER_TIME);
},
handleDragLeave(e) {
if (this.state.dragging) {
e.stopPropagation();
e.preventDefault();
this.clearDragging();
}
},
clearDragging() {
this.setState({dragging: false, requireConfirm: true});
},
handleDrop(e) {
if (!this.preventUnwantedDrop(e, true)) return false;
if (this.state.dragging) {
e.preventDefault();
e.stopPropagation();
if (this.canDropFile()) {
this.promptToUpload(e.dataTransfer.files, this.state.channel.id, true, this.state.requireConfirm);
}
this.clearDragging();
}
},
});
export default UploadArea;
// WEBPACK FOOTER //
// ./discord_app/components/UploadArea.js