Porównaj commity

...

10 Commity

5 zmienionych plików z 265 dodań i 87 usunięć

Wyświetl plik

@ -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…' },
@ -41,6 +42,8 @@ class ChatBox extends ImmutablePureComponent {
chat: ImmutablePropTypes.map,
onSetInputRef: PropTypes.func,
me: PropTypes.node,
onAttachment: PropTypes.func,
windowState: PropTypes.string,
}
initialState = () => ({
@ -49,10 +52,27 @@ class ChatBox extends ImmutablePureComponent {
isUploading: false,
uploadProgress: 0,
resetFileKey: fileKeyGen(),
draggingOver: false,
})
state = this.initialState()
componentDidMount() {
this.node.addEventListener('dragenter', this.handleDragEnter, false);
this.node.addEventListener('dragover', this.handleDragOver, false);
this.node.addEventListener('drop', this.handleDrop, false);
this.node.addEventListener('dragleave', this.handleDragLeave, false);
this.node.addEventListener('dragend', this.handleDragEnd, false);
}
componentWillUnmount() {
this.node.removeEventListener('dragenter', this.handleDragEnter);
this.node.removeEventListener('dragover', this.handleDragOver);
this.node.removeEventListener('drop', this.handleDrop);
this.node.removeEventListener('dragleave', this.handleDragLeave);
this.node.removeEventListener('dragend', this.handleDragEnd);
}
clearState = () => {
this.setState(this.initialState());
}
@ -120,14 +140,88 @@ 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);
}
}
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 });
@ -143,11 +237,22 @@ 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 });
});
}
renderUploadArea = () => {
const { windowState } = this.props;
const { draggingOver } = this.state;
if (windowState !== 'open') return null;
return (
<UploadArea active={draggingOver} onClose={this.closeUploadModal} />
);
}
renderAttachment = () => {
const { attachment } = this.state;
if (!attachment) return null;
@ -157,8 +262,8 @@ class ChatBox extends ImmutablePureComponent {
<div className='chat-box__filename'>
{truncateFilename(attachment.preview_url, 20)}
</div>
<div class='chat-box__remove-attachment'>
<IconButton icon='remove' onClick={this.handleRemoveFile} />
<div className='chat-box__remove-attachment'>
<IconButton icon='remove' title='remove' onClick={this.handleRemoveFile} />
</div>
</div>
);
@ -188,10 +293,11 @@ class ChatBox extends ImmutablePureComponent {
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} />
{this.renderUploadArea()}
<div className='chat-box__actions simple_form'>
{this.renderActionButton()}
<textarea

Wyświetl plik

@ -108,6 +108,7 @@ class ChatWindow extends ImmutablePureComponent {
<div className='pane__content'>
<ChatBox
chatId={chat.get('id')}
windowState={pane.get('state')}
onSetInputRef={this.handleInputRef}
/>
</div>

Wyświetl plik

@ -10,6 +10,7 @@ import AutosuggestInput from '../../../components/autosuggest_input';
import PollButtonContainer from '../containers/poll_button_container';
import UploadButtonContainer from '../containers/upload_button_container';
import { defineMessages, injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import SpoilerButtonContainer from '../containers/spoiler_button_container';
import MarkdownButtonContainer from '../containers/markdown_button_container';
import PrivacyDropdownContainer from '../containers/privacy_dropdown_container';
@ -23,6 +24,8 @@ import { length } from 'stringz';
import { countableText } from '../util/counter';
import Icon from 'soapbox/components/icon';
import { get } from 'lodash';
import UploadArea from 'soapbox/features/ui/components/upload_area';
import { uploadCompose } from 'soapbox/actions/compose';
const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
@ -33,7 +36,8 @@ const messages = defineMessages({
publishLoud: { id: 'compose_form.publish_loud', defaultMessage: '{publish}!' },
});
export default @injectIntl
export default @connect()
@injectIntl
class ComposeForm extends ImmutablePureComponent {
state = {
@ -46,9 +50,11 @@ class ComposeForm extends ImmutablePureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
text: PropTypes.string.isRequired,
suggestions: ImmutablePropTypes.list,
spoiler: PropTypes.bool,
sensitive: PropTypes.bool,
privacy: PropTypes.string,
spoilerText: PropTypes.string,
focusDate: PropTypes.instanceOf(Date),
@ -173,10 +179,83 @@ class ComposeForm extends ImmutablePureComponent {
componentDidMount() {
document.addEventListener('click', this.handleClick, true);
this.setCursor(this.props.text.length); // Set cursor at end
const composeForm = document.getElementById('compose-form');
composeForm.addEventListener('dragenter', this.handleDragEnter, false);
composeForm.addEventListener('dragover', this.handleDragOver, false);
composeForm.addEventListener('drop', this.handleDrop, false);
composeForm.addEventListener('dragleave', this.handleDragLeave, false);
}
componentWillUnmount() {
document.removeEventListener('click', this.handleClick, true);
const composeForm = document.getElementById('compose-form');
composeForm.removeEventListener('dragenter', this.handleDragEnter);
composeForm.removeEventListener('dragover', this.handleDragOver);
composeForm.removeEventListener('drop', this.handleDrop);
composeForm.removeEventListener('dragleave', this.handleDragLeave);
}
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) => {
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.form.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 });
}
setAutosuggestTextarea = (c) => {
@ -211,7 +290,9 @@ class ComposeForm extends ImmutablePureComponent {
maybeUpdateFocus = prevProps => {
const spoilerUpdated = this.props.spoiler !== prevProps.spoiler;
if (spoilerUpdated) {
const sensitiveUpdated = this.props.sensitive !== prevProps.sensitive;
const mediaUpdated = this.props.anyMedia !== prevProps.anyMedia;
if (spoilerUpdated || sensitiveUpdated || mediaUpdated) {
switch (this.props.spoiler) {
case true: this.focusSpoilerInput(); break;
case false: this.focusTextarea(); break;
@ -225,6 +306,7 @@ class ComposeForm extends ImmutablePureComponent {
render() {
const { intl, onPaste, showSearch, anyMedia, shouldCondense, autoFocus, isModalOpen, maxTootChars } = this.props;
const { draggingOver } = this.state;
const condensed = shouldCondense && !this.props.text && !this.state.composeFocused;
const disabled = this.props.isSubmitting;
const text = [this.props.spoilerText, countableText(this.props.text)].join('');
@ -245,7 +327,7 @@ class ComposeForm extends ImmutablePureComponent {
});
return (
<div className={composeClassNames} ref={this.setForm} onClick={this.handleClick}>
<div className={composeClassNames} ref={this.setForm} onClick={this.handleClick} id='compose-form'>
<WarningContainer />
{ !shouldCondense && <ReplyIndicatorContainer /> }
@ -271,6 +353,7 @@ class ComposeForm extends ImmutablePureComponent {
<div className='emoji-picker-wrapper'>
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
</div>
<UploadArea active={draggingOver} onClose={this.closeUploadModal} />
<AutosuggestTextarea
ref={(isModalOpen && shouldCondense) ? null : this.setAutosuggestTextarea}

Wyświetl plik

@ -16,6 +16,7 @@ const mapStateToProps = state => ({
suggestions: state.getIn(['compose', 'suggestions']),
spoiler: state.getIn(['compose', 'spoiler']),
spoilerText: state.getIn(['compose', 'spoiler_text']),
sensitive: state.getIn(['compose', 'sensitive']),
privacy: state.getIn(['compose', 'privacy']),
focusDate: state.getIn(['compose', 'focusDate']),
caretPosition: state.getIn(['compose', 'caretPosition']),

Wyświetl plik

@ -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';
@ -316,71 +315,71 @@ 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 });
}
// 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') {
@ -418,12 +417,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);
}
@ -452,11 +445,6 @@ class UI extends React.PureComponent {
componentWillUnmount() {
window.removeEventListener('beforeunload', this.handleBeforeUnload);
window.removeEventListener('resize', this.handleResize);
document.removeEventListener('dragenter', this.handleDragEnter);
document.removeEventListener('dragover', this.handleDragOver);
document.removeEventListener('drop', this.handleDrop);
document.removeEventListener('dragleave', this.handleDragLeave);
document.removeEventListener('dragend', this.handleDragEnd);
this.disconnectStreaming();
}
@ -590,7 +578,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 +636,6 @@ class UI extends React.PureComponent {
<NotificationsContainer />
<LoadingBarContainer className='loading-bar' />
<ModalContainer />
<UploadArea active={draggingOver} onClose={this.closeUploadModal} />
{me && <SidebarMenu />}
{me && !mobile && <ChatPanes />}
<ProfileHoverCard />