Allow drag&drop of link to quote in compose form (#36859)

This commit is contained in:
Jeong Arm
2025-11-14 23:14:37 +09:00
committed by GitHub
parent c5eca8ffb2
commit 4ab1d5d724
3 changed files with 34 additions and 14 deletions

View File

@@ -50,6 +50,7 @@ const AutosuggestTextarea = forwardRef(({
onKeyUp, onKeyUp,
onKeyDown, onKeyDown,
onPaste, onPaste,
onDrop,
onFocus, onFocus,
autoFocus = true, autoFocus = true,
lang, lang,
@@ -153,6 +154,12 @@ const AutosuggestTextarea = forwardRef(({
onPaste(e); onPaste(e);
}, [onPaste]); }, [onPaste]);
const handleDrop = useCallback((e) => {
if (onDrop) {
onDrop(e);
}
}, [onDrop]);
// Show the suggestions again whenever they change and the textarea is focused // Show the suggestions again whenever they change and the textarea is focused
useEffect(() => { useEffect(() => {
if (suggestions.size > 0 && textareaRef.current === document.activeElement) { if (suggestions.size > 0 && textareaRef.current === document.activeElement) {
@@ -204,6 +211,7 @@ const AutosuggestTextarea = forwardRef(({
onFocus={handleFocus} onFocus={handleFocus}
onBlur={handleBlur} onBlur={handleBlur}
onPaste={handlePaste} onPaste={handlePaste}
onDrop={handleDrop}
dir='auto' dir='auto'
aria-autocomplete='list' aria-autocomplete='list'
aria-label={placeholder} aria-label={placeholder}
@@ -235,6 +243,7 @@ AutosuggestTextarea.propTypes = {
onKeyUp: PropTypes.func, onKeyUp: PropTypes.func,
onKeyDown: PropTypes.func, onKeyDown: PropTypes.func,
onPaste: PropTypes.func.isRequired, onPaste: PropTypes.func.isRequired,
onDrop: PropTypes.func,
onFocus:PropTypes.func, onFocus:PropTypes.func,
autoFocus: PropTypes.bool, autoFocus: PropTypes.bool,
lang: PropTypes.string, lang: PropTypes.string,

View File

@@ -64,6 +64,7 @@ class ComposeForm extends ImmutablePureComponent {
onSuggestionSelected: PropTypes.func.isRequired, onSuggestionSelected: PropTypes.func.isRequired,
onChangeSpoilerText: PropTypes.func.isRequired, onChangeSpoilerText: PropTypes.func.isRequired,
onPaste: PropTypes.func.isRequired, onPaste: PropTypes.func.isRequired,
onDrop: PropTypes.func.isRequired,
onPickEmoji: PropTypes.func.isRequired, onPickEmoji: PropTypes.func.isRequired,
autoFocus: PropTypes.bool, autoFocus: PropTypes.bool,
withoutNavigation: PropTypes.bool, withoutNavigation: PropTypes.bool,
@@ -249,7 +250,7 @@ class ComposeForm extends ImmutablePureComponent {
}; };
render () { render () {
const { intl, onPaste, autoFocus, withoutNavigation, maxChars, isSubmitting } = this.props; const { intl, onPaste, onDrop, autoFocus, withoutNavigation, maxChars, isSubmitting } = this.props;
const { highlighted } = this.state; const { highlighted } = this.state;
return ( return (
@@ -305,6 +306,7 @@ class ComposeForm extends ImmutablePureComponent {
onSuggestionsClearRequested={this.onSuggestionsClearRequested} onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSuggestionSelected} onSuggestionSelected={this.onSuggestionSelected}
onPaste={onPaste} onPaste={onPaste}
onDrop={onDrop}
autoFocus={autoFocus} autoFocus={autoFocus}
lang={this.props.lang} lang={this.props.lang}
className='compose-form__input' className='compose-form__input'

View File

@@ -18,6 +18,24 @@ import ComposeForm from '../components/compose_form';
const urlLikeRegex = /^https?:\/\/[^\s]+\/[^\s]+$/i; const urlLikeRegex = /^https?:\/\/[^\s]+\/[^\s]+$/i;
const processPasteOrDrop = (transfer, e, dispatch) => {
if (transfer && transfer.files.length === 1) {
dispatch(uploadCompose(transfer.files));
e.preventDefault();
} else if (transfer && transfer.files.length === 0) {
const data = transfer.getData('text/plain');
if (!data.match(urlLikeRegex)) return;
try {
const url = new URL(data);
dispatch(pasteLinkCompose({ url }));
e.preventDefault();
} catch {
return;
}
}
};
const mapStateToProps = state => ({ const mapStateToProps = state => ({
text: state.getIn(['compose', 'text']), text: state.getIn(['compose', 'text']),
suggestions: state.getIn(['compose', 'suggestions']), suggestions: state.getIn(['compose', 'suggestions']),
@@ -85,20 +103,11 @@ const mapDispatchToProps = (dispatch, props) => ({
}, },
onPaste (e) { onPaste (e) {
if (e.clipboardData && e.clipboardData.files.length === 1) { processPasteOrDrop(e.clipboardData, e, dispatch);
dispatch(uploadCompose(e.clipboardData.files)); },
e.preventDefault();
} else if (e.clipboardData && e.clipboardData.files.length === 0) {
const data = e.clipboardData.getData('text/plain');
if (!data.match(urlLikeRegex)) return;
try { onDrop (e) {
const url = new URL(data); processPasteOrDrop(e.dataTransfer, e, dispatch);
dispatch(pasteLinkCompose({ url }));
} catch {
return;
}
}
}, },
onPickEmoji (position, data, needsSpace) { onPickEmoji (position, data, needsSpace) {