Merge pull request #903 from esteemapp/bugfix/markdown-editor

Fixed markdown editor bugs
This commit is contained in:
uğur erdal 2019-06-11 22:16:57 +03:00 committed by GitHub
commit 682bba49aa
7 changed files with 69 additions and 67 deletions

View File

@ -1,32 +1,40 @@
import { replaceBetween } from './utils';
export default ({ getState, item, setState }) => {
let { text } = getState();
const { selection } = getState();
text = text || '';
export default async ({ getState, item, setState }) => {
const states = getState();
let { text } = states;
const { selection } = states;
let newText;
let newSelection;
if (selection.start !== selection.end) {
text = text || '';
const isSelected = selection.start === selection.end;
const hasLineBreakOnStart = text.substring(selection.start - 1, selection.start) === '\n';
const hasLineBreakOnEnd = text.substring(selection.end - 1, selection.end) === '\n';
if (!isSelected && hasLineBreakOnStart) {
newText = replaceBetween(
text,
selection,
`${item.prefix} ${text.substring(selection.start, selection.end)}\n`,
);
newSelection = { start: selection.end + 3, end: selection.end + 3 };
} else if (
selection.start === selection.end &&
text.substring(selection.end - 1, selection.end) === '\n'
) {
} else if (!isSelected && !hasLineBreakOnStart) {
newText = replaceBetween(
text,
selection,
`\n${item.prefix} ${text.substring(selection.start, selection.end)}\n`,
);
newSelection = { start: selection.end + 3, end: selection.end + 3 };
} else if (isSelected && hasLineBreakOnEnd) {
newText = replaceBetween(text, selection, `${item.prefix} `);
newSelection = { start: selection.start + 2, end: selection.start + 2 };
} else {
} else if (isSelected && !hasLineBreakOnEnd) {
newText = replaceBetween(text, selection, `\n${item.prefix} `);
newSelection = { start: selection.start + 3, end: selection.start + 3 };
}
setState({ text: newText }, () => {
setTimeout(() => {
setState({ newSelection });
}, 300);
});
await setState({ text: newText, textUpdated: true });
await setState({ newSelection });
};

View File

@ -3,15 +3,15 @@ import { isStringWebLink, replaceBetween } from './utils';
export const writeUrlTextHere = 'https://example.com';
export const writeTextHereString = 'Text here';
export default ({ getState, item, setState, isImage = null }) => {
export default async ({ getState, item, setState, isImage = null }) => {
const { selection, text } = getState();
const imagePrefix = isImage ? '!' : '';
const itemText = item ? item.text : writeTextHereString;
const itemUrl = item ? item.url : writeUrlTextHere;
let newText;
let newSelection;
const selectedText = text.substring(selection.start, selection.end);
if (selection.start !== selection.end) {
if (isStringWebLink(selectedText)) {
newText = replaceBetween(text, selection, `${imagePrefix}[${itemText}](${selectedText})`);
@ -40,7 +40,8 @@ export default ({ getState, item, setState, isImage = null }) => {
};
}
}
setState({ text: newText }, () => {
setState({ newSelection });
await setState({ text: newText, textUpdated: true }, async () => {
await setState({ newSelection });
});
};

View File

@ -8,19 +8,14 @@ export default ({ getState, item, setState }) => {
item.wrapper.concat(text.substring(selection.start, selection.end), item.wrapper),
);
let newPosition;
if (selection.start === selection.end) {
newPosition = selection.end + item.wrapper.length;
} else {
newPosition = selection.end + item.wrapper.length * 2;
}
const extra = {
newSelection: {
start: newPosition,
end: newPosition,
},
};
setState({ text: newText }, () => {
setState({ ...extra });
setState({ text: newText, textUpdated: true }, () => {
setState({ newSelection: { start: newPosition, end: newPosition } });
});
};

View File

@ -1,6 +1,6 @@
import { replaceBetween } from './utils';
export default ({ getState, item, setState }) => {
export default async ({ getState, item, setState }) => {
const { text, selection } = getState();
let newText = replaceBetween(
text,
@ -41,15 +41,8 @@ export default ({ getState, item, setState }) => {
)}`,
);
}
const extra = {
newSelection: {
start: newPosition,
end: newPosition,
},
};
setState({ text: newText }, () => {
setTimeout(() => {
setState({ ...extra });
}, 25);
await setState({ text: newText, textUpdated: true }, async () => {
await setState({ newSelection: { start: newPosition, end: newPosition } });
});
};

View File

@ -11,7 +11,6 @@ export default [
iconType: 'FontAwesome',
wrapper: '**',
onPress: applyWrapFormat,
// style: { fontWeight: 'bold' },
},
{
key: 'I',

View File

@ -22,8 +22,13 @@ export default class MarkdownEditorView extends Component {
this.state = {
text: props.draftBody || '',
selection: { start: 0, end: 0 },
textUpdated: false,
newSelection: null,
};
this.inputRef = React.createRef();
this.galleryRef = React.createRef();
this.clearRef = React.createRef();
}
// Lifecycle functions
@ -47,8 +52,8 @@ export default class MarkdownEditorView extends Component {
) {
applyImageLink({
getState: this._getState,
setState: (state, callback) => {
this.setState(state, callback);
setState: async (state, callback) => {
await this.setState(state, callback);
},
item: { url: nextProps.uploadedImage.url, text: nextProps.uploadedImage.hash },
isImage: !!nextProps.uploadedImage,
@ -76,6 +81,14 @@ export default class MarkdownEditorView extends Component {
// Component functions
_changeText = input => {
const { onChange, handleOnTextChange, handleIsValid, componentID } = this.props;
const { textUpdated } = this.state;
if (textUpdated) {
this.setState({
textUpdated: false,
});
return;
}
this.setState({ text: input });
@ -100,14 +113,16 @@ export default class MarkdownEditorView extends Component {
selection: newSelection,
newSelection: null,
});
} else {
this.setState({
selection: event.nativeEvent.selection,
});
return;
}
this.setState({
selection: event.nativeEvent.selection,
});
};
_getState = () => this.state;
_getState = () => {
return this.state;
};
_renderPreview = () => {
const { text } = this.state;
@ -154,7 +169,7 @@ export default class MarkdownEditorView extends Component {
onPress={() => Formats[9].onPress({ getState, setState })}
/>
<IconButton
onPress={() => this.ActionSheet.show()}
onPress={() => this.galleryRef.current.show()}
style={styles.rightIcons}
size={20}
iconStyle={styles.icon}
@ -163,21 +178,14 @@ export default class MarkdownEditorView extends Component {
/>
<View style={styles.clearButtonWrapper}>
<IconButton
onPress={() => this.ClearActionSheet.show()}
onPress={() => this.clearRef.current.show()}
size={20}
iconStyle={styles.clearIcon}
iconType="FontAwesome"
name="trash"
backgroundColor={styles.clearButtonWrapper.backgroundColor}
/>
</View>
{/* TODO: After alpha */}
{/* <DropdownButton
style={styles.dropdownStyle}
options={['option1', 'option2', 'option3', 'option4']}
iconName="md-more"
iconStyle={styles.dropdownIconStyle}
isHasChildIcon
/> */}
</View>
</StickyBar>
);
@ -203,7 +211,7 @@ export default class MarkdownEditorView extends Component {
{!isPreviewActive ? (
<TextInput
multiline
onChangeText={e => this._changeText(e)}
onChangeText={this._changeText}
onSelectionChange={this._handleOnSelectionChange}
placeholder={intl.formatMessage({
id: isReply ? 'editor.reply_placeholder' : 'editor.default_placeholder',
@ -214,6 +222,7 @@ export default class MarkdownEditorView extends Component {
style={styles.textWrapper}
underlineColorAndroid="transparent"
value={text}
innerRef={this.inputRef}
/>
) : (
this._renderPreview()
@ -222,13 +231,12 @@ export default class MarkdownEditorView extends Component {
this._renderEditorButtons({
getState: this._getState,
setState: (state, callback) => {
this.inputRef.current.focus();
this.setState(state, callback);
},
})}
{/* TODO: This is a problem re-factor */}
<ActionSheet
ref={o => (this.ActionSheet = o)}
ref={this.galleryRef}
options={[
intl.formatMessage({
id: 'editor.open_gallery',
@ -246,7 +254,7 @@ export default class MarkdownEditorView extends Component {
}}
/>
<ActionSheet
ref={o => (this.ClearActionSheet = o)}
ref={this.clearRef}
title={intl.formatMessage({
id: 'alert.clear_alert',
})}
@ -259,9 +267,7 @@ export default class MarkdownEditorView extends Component {
}),
]}
cancelButtonIndex={1}
onPress={index => {
index === 0 && this._handleClear();
}}
onPress={index => index === 0 && this._handleClear()}
/>
</KeyboardAvoidingView>
);

View File

@ -1,9 +1,9 @@
import React from 'react';
import { connect } from 'react-redux';
import { TextInput } from 'react-native';
import { connect } from 'react-redux';
const TextInputView = props => (
<TextInput keyboardAppearance={props.isDarkTheme ? 'dark' : 'light'} {...props} />
const TextInputView = ({ isDarkTheme, innerRef, ...props }) => (
<TextInput ref={innerRef} keyboardAppearance={isDarkTheme ? 'dark' : 'light'} {...props} />
);
const mapStateToProps = state => ({