sforkowany z mirror/soapbox
Roughed out drag and drop to chat boxes and to autosuggest textarea
rodzic
3b8eed17b6
commit
e57b4dbe00
|
@ -7,6 +7,8 @@ import { isRtl } from '../rtl';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import Textarea from 'react-textarea-autosize';
|
||||
import classNames from 'classnames';
|
||||
import UploadArea from 'soapbox/features/ui/components/upload_area';
|
||||
import { uploadCompose } from 'soapbox/actions/compose';
|
||||
|
||||
const textAtCursorMatchesToken = (str, caretPosition) => {
|
||||
let word;
|
||||
|
@ -50,6 +52,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
autoFocus: PropTypes.bool,
|
||||
onFocus: PropTypes.func,
|
||||
onBlur: PropTypes.func,
|
||||
onAttachment: PropTypes.func,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -62,8 +65,25 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
selectedSuggestion: 0,
|
||||
lastToken: null,
|
||||
tokenStart: 0,
|
||||
draggingOver: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener('dragenter', this.handleDragEnter, false);
|
||||
window.addEventListener('dragover', this.handleDragOver, false);
|
||||
window.addEventListener('drop', this.handleDrop, false);
|
||||
window.addEventListener('dragleave', this.handleDragLeave, false);
|
||||
window.addEventListener('dragend', this.handleDragEnd, false);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('dragenter', this.handleDragEnter);
|
||||
window.removeEventListener('dragover', this.handleDragOver);
|
||||
window.removeEventListener('drop', this.handleDrop);
|
||||
window.removeEventListener('dragleave', this.handleDragLeave);
|
||||
window.removeEventListener('dragend', this.handleDragEnd);
|
||||
}
|
||||
|
||||
onChange = (e) => {
|
||||
const [ tokenStart, token ] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart);
|
||||
|
||||
|
@ -170,6 +190,103 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
this.textarea = c;
|
||||
}
|
||||
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
}
|
||||
|
||||
handleAttachment = () => {
|
||||
const { onAttachment } = this.props;
|
||||
onAttachment(true);
|
||||
}
|
||||
dataTransferIsText = (dataTransfer) => {
|
||||
return (dataTransfer && Array.from(dataTransfer.types).includes('text/plain') && dataTransfer.items.length === 1);
|
||||
}
|
||||
|
||||
handleDragEnter = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!this.dragTargets) {
|
||||
this.dragTargets = [];
|
||||
}
|
||||
|
||||
if (this.dragTargets.indexOf(e.target) === -1) {
|
||||
this.dragTargets.push(e.target);
|
||||
}
|
||||
|
||||
if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files')) {
|
||||
this.setState({ draggingOver: true });
|
||||
}
|
||||
}
|
||||
|
||||
handleDragOver = (e) => {
|
||||
if (this.dataTransferIsText(e.dataTransfer)) return false;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
try {
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
} catch (err) {
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
handleAttachment = () => {
|
||||
const { onAttachment } = this.props;
|
||||
onAttachment(true);
|
||||
}
|
||||
|
||||
handleFiles = (files) => {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
this.setState({ isUploading: true });
|
||||
|
||||
const data = new FormData();
|
||||
data.append('file', files[0]);
|
||||
dispatch(uploadCompose(data));
|
||||
dispatch(this.handleAttachment());
|
||||
// dispatch(uploadMedia(data, this.onUploadProgress)).then(response => {
|
||||
// this.setState({ attachment: response.data, isUploading: false });
|
||||
// this.handleAttachment();
|
||||
// }).catch(() => {
|
||||
// this.setState({ isUploading: false });
|
||||
// });
|
||||
}
|
||||
|
||||
handleDrop = (e) => {
|
||||
const { me } = this.props;
|
||||
if (!me) return;
|
||||
|
||||
if (this.dataTransferIsText(e.dataTransfer)) return;
|
||||
e.preventDefault();
|
||||
|
||||
this.setState({ draggingOver: false });
|
||||
this.dragTargets = [];
|
||||
|
||||
if (e.dataTransfer && e.dataTransfer.files.length >= 1) {
|
||||
this.handleFiles(e.dataTransfer.files);
|
||||
// this.props.dispatch(uploadCompose(e.dataTransfer.files));
|
||||
}
|
||||
}
|
||||
|
||||
handleDragLeave = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.dragTargets = this.dragTargets.filter(el => el !== e.target && this.node.contains(el));
|
||||
|
||||
if (this.dragTargets.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ draggingOver: false });
|
||||
}
|
||||
|
||||
closeUploadModal = () => {
|
||||
this.setState({ draggingOver: false });
|
||||
}
|
||||
|
||||
onPaste = (e) => {
|
||||
if (e.clipboardData && e.clipboardData.files.length === 1) {
|
||||
this.props.onPaste(e.clipboardData.files);
|
||||
|
@ -210,7 +327,8 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
|
||||
return [
|
||||
<div className='compose-form__autosuggest-wrapper' key='compose-form__autosuggest-wrapper'>
|
||||
<div className='autosuggest-textarea'>
|
||||
<div className='autosuggest-textarea' ref={this.setRef}>
|
||||
<UploadArea active={draggingOver} onClose={this.closeUploadModal} />
|
||||
<label>
|
||||
<span style={{ display: 'none' }}>{placeholder}</span>
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import { uploadMedia } from 'soapbox/actions/media';
|
|||
import UploadProgress from 'soapbox/features/compose/components/upload_progress';
|
||||
import { truncateFilename } from 'soapbox/utils/media';
|
||||
import IconButton from 'soapbox/components/icon_button';
|
||||
import UploadArea from 'soapbox/features/ui/components/upload_area';
|
||||
|
||||
const messages = defineMessages({
|
||||
placeholder: { id: 'chat_box.input.placeholder', defaultMessage: 'Send a message…' },
|
||||
|
@ -40,6 +41,7 @@ class ChatBox extends ImmutablePureComponent {
|
|||
chat: ImmutablePropTypes.map,
|
||||
onSetInputRef: PropTypes.func,
|
||||
me: PropTypes.node,
|
||||
onAttachment: PropTypes.func,
|
||||
}
|
||||
|
||||
initialState = () => ({
|
||||
|
@ -48,10 +50,27 @@ class ChatBox extends ImmutablePureComponent {
|
|||
isUploading: false,
|
||||
uploadProgress: 0,
|
||||
resetFileKey: fileKeyGen(),
|
||||
draggingOver: false,
|
||||
})
|
||||
|
||||
state = this.initialState()
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener('dragenter', this.handleDragEnter, false);
|
||||
window.addEventListener('dragover', this.handleDragOver, false);
|
||||
window.addEventListener('drop', this.handleDrop, false);
|
||||
window.addEventListener('dragleave', this.handleDragLeave, false);
|
||||
window.addEventListener('dragend', this.handleDragEnd, false);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('dragenter', this.handleDragEnter);
|
||||
window.removeEventListener('dragover', this.handleDragOver);
|
||||
window.removeEventListener('drop', this.handleDrop);
|
||||
window.removeEventListener('dragleave', this.handleDragLeave);
|
||||
window.removeEventListener('dragend', this.handleDragEnd);
|
||||
}
|
||||
|
||||
clearState = () => {
|
||||
this.setState(this.initialState());
|
||||
}
|
||||
|
@ -119,14 +138,89 @@ class ChatBox extends ImmutablePureComponent {
|
|||
|
||||
setInputRef = (el) => {
|
||||
const { onSetInputRef } = this.props;
|
||||
this.inputElem = el;
|
||||
onSetInputRef(el);
|
||||
};
|
||||
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
}
|
||||
|
||||
handleAttachment = () => {
|
||||
const { onAttachment } = this.props;
|
||||
onAttachment(true);
|
||||
}
|
||||
|
||||
handleRemoveFile = (e) => {
|
||||
this.setState({ attachment: undefined, resetFileKey: fileKeyGen() });
|
||||
}
|
||||
|
||||
dataTransferIsText = (dataTransfer) => {
|
||||
return (dataTransfer && Array.from(dataTransfer.types).includes('text/plain') && dataTransfer.items.length === 1);
|
||||
}
|
||||
|
||||
handleDragEnter = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!this.dragTargets) {
|
||||
this.dragTargets = [];
|
||||
}
|
||||
|
||||
if (this.dragTargets.indexOf(e.target) === -1) {
|
||||
this.dragTargets.push(e.target);
|
||||
}
|
||||
|
||||
if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files')) {
|
||||
this.setState({ draggingOver: true });
|
||||
}
|
||||
}
|
||||
|
||||
handleDragOver = (e) => {
|
||||
if (this.dataTransferIsText(e.dataTransfer)) return false;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
try {
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
} catch (err) {
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
handleDrop = (e) => {
|
||||
const { me } = this.props;
|
||||
if (!me) return;
|
||||
|
||||
if (this.dataTransferIsText(e.dataTransfer)) return;
|
||||
e.preventDefault();
|
||||
|
||||
this.setState({ draggingOver: false });
|
||||
this.dragTargets = [];
|
||||
|
||||
if (e.dataTransfer && e.dataTransfer.files.length >= 1) {
|
||||
this.handleFiles(e.dataTransfer.files);
|
||||
// this.props.dispatch(uploadCompose(e.dataTransfer.files));
|
||||
}
|
||||
}
|
||||
|
||||
handleDragLeave = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.dragTargets = this.dragTargets.filter(el => el !== e.target && this.node.contains(el));
|
||||
|
||||
if (this.dragTargets.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ draggingOver: false });
|
||||
}
|
||||
|
||||
closeUploadModal = () => {
|
||||
this.setState({ draggingOver: false });
|
||||
}
|
||||
|
||||
onUploadProgress = (e) => {
|
||||
const { loaded, total } = e;
|
||||
this.setState({ uploadProgress: loaded/total });
|
||||
|
@ -142,6 +236,7 @@ class ChatBox extends ImmutablePureComponent {
|
|||
|
||||
dispatch(uploadMedia(data, this.onUploadProgress)).then(response => {
|
||||
this.setState({ attachment: response.data, isUploading: false });
|
||||
this.handleAttachment();
|
||||
}).catch(() => {
|
||||
this.setState({ isUploading: false });
|
||||
});
|
||||
|
@ -178,13 +273,15 @@ class ChatBox extends ImmutablePureComponent {
|
|||
render() {
|
||||
const { chatMessageIds, chatId, intl } = this.props;
|
||||
const { content, isUploading, uploadProgress } = this.state;
|
||||
const { draggingOver } = this.state;
|
||||
if (!chatMessageIds) return null;
|
||||
|
||||
return (
|
||||
<div className='chat-box' onMouseOver={this.handleHover}>
|
||||
<div className='chat-box' ref={this.setRef} onMouseOver={this.handleHover}>
|
||||
<ChatMessageList chatMessageIds={chatMessageIds} chatId={chatId} />
|
||||
{this.renderAttachment()}
|
||||
<UploadProgress active={isUploading} progress={uploadProgress*100} />
|
||||
<UploadArea active={draggingOver} onClose={this.closeUploadModal} />
|
||||
<div className='chat-box__actions simple_form'>
|
||||
{this.renderActionButton()}
|
||||
<textarea
|
||||
|
|
|
@ -13,7 +13,7 @@ import LoadingBarContainer from './containers/loading_bar_container';
|
|||
import ModalContainer from './containers/modal_container';
|
||||
import { isMobile } from '../../is_mobile';
|
||||
import { debounce } from 'lodash';
|
||||
import { uploadCompose, resetCompose } from '../../actions/compose';
|
||||
import { resetCompose } from '../../actions/compose';
|
||||
import { expandHomeTimeline } from '../../actions/timelines';
|
||||
import { expandNotifications } from '../../actions/notifications';
|
||||
import { fetchReports } from '../../actions/admin';
|
||||
|
@ -22,7 +22,6 @@ import { fetchChats } from 'soapbox/actions/chats';
|
|||
import { clearHeight } from '../../actions/height_cache';
|
||||
import { openModal } from '../../actions/modal';
|
||||
import { WrappedRoute } from './util/react_router_helpers';
|
||||
import UploadArea from './components/upload_area';
|
||||
import TabsBar from './components/tabs_bar';
|
||||
import LinkFooter from './components/link_footer';
|
||||
import FeaturesPanel from './components/features_panel';
|
||||
|
@ -296,7 +295,6 @@ class UI extends React.PureComponent {
|
|||
};
|
||||
|
||||
state = {
|
||||
draggingOver: false,
|
||||
mobile: isMobile(window.innerWidth),
|
||||
};
|
||||
|
||||
|
@ -316,72 +314,6 @@ class UI extends React.PureComponent {
|
|||
this.props.dispatch(clearHeight());
|
||||
}
|
||||
|
||||
handleDragEnter = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!this.dragTargets) {
|
||||
this.dragTargets = [];
|
||||
}
|
||||
|
||||
if (this.dragTargets.indexOf(e.target) === -1) {
|
||||
this.dragTargets.push(e.target);
|
||||
}
|
||||
|
||||
if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files')) {
|
||||
this.setState({ draggingOver: true });
|
||||
}
|
||||
}
|
||||
|
||||
handleDragOver = (e) => {
|
||||
if (this.dataTransferIsText(e.dataTransfer)) return false;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
try {
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
} catch (err) {
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
handleDrop = (e) => {
|
||||
const { me } = this.props;
|
||||
if (!me) return;
|
||||
|
||||
if (this.dataTransferIsText(e.dataTransfer)) return;
|
||||
e.preventDefault();
|
||||
|
||||
this.setState({ draggingOver: false });
|
||||
this.dragTargets = [];
|
||||
|
||||
if (e.dataTransfer && e.dataTransfer.files.length >= 1) {
|
||||
this.props.dispatch(uploadCompose(e.dataTransfer.files));
|
||||
}
|
||||
}
|
||||
|
||||
handleDragLeave = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.dragTargets = this.dragTargets.filter(el => el !== e.target && this.node.contains(el));
|
||||
|
||||
if (this.dragTargets.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ draggingOver: false });
|
||||
}
|
||||
|
||||
dataTransferIsText = (dataTransfer) => {
|
||||
return (dataTransfer && Array.from(dataTransfer.types).includes('text/plain') && dataTransfer.items.length === 1);
|
||||
}
|
||||
|
||||
closeUploadModal = () => {
|
||||
this.setState({ draggingOver: false });
|
||||
}
|
||||
|
||||
handleServiceWorkerPostMessage = ({ data }) => {
|
||||
if (data.type === 'navigate') {
|
||||
this.context.router.history.push(data.path);
|
||||
|
@ -418,12 +350,6 @@ class UI extends React.PureComponent {
|
|||
window.addEventListener('beforeunload', this.handleBeforeUnload, false);
|
||||
window.addEventListener('resize', this.handleResize, { passive: true });
|
||||
|
||||
document.addEventListener('dragenter', this.handleDragEnter, false);
|
||||
document.addEventListener('dragover', this.handleDragOver, false);
|
||||
document.addEventListener('drop', this.handleDrop, false);
|
||||
document.addEventListener('dragleave', this.handleDragLeave, false);
|
||||
document.addEventListener('dragend', this.handleDragEnd, false);
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.addEventListener('message', this.handleServiceWorkerPostMessage);
|
||||
}
|
||||
|
@ -590,7 +516,7 @@ class UI extends React.PureComponent {
|
|||
|
||||
render() {
|
||||
const { streamingUrl } = this.props;
|
||||
const { draggingOver, mobile } = this.state;
|
||||
const { mobile } = this.state;
|
||||
const { intl, children, isComposing, location, dropdownMenuIsOpen, me } = this.props;
|
||||
|
||||
if (me === null || !streamingUrl) return null;
|
||||
|
@ -648,7 +574,6 @@ class UI extends React.PureComponent {
|
|||
<NotificationsContainer />
|
||||
<LoadingBarContainer className='loading-bar' />
|
||||
<ModalContainer />
|
||||
<UploadArea active={draggingOver} onClose={this.closeUploadModal} />
|
||||
{me && <SidebarMenu />}
|
||||
{me && !mobile && <ChatPanes />}
|
||||
<ProfileHoverCard />
|
||||
|
|
Ładowanie…
Reference in New Issue