mirror of
https://github.com/ecency/ecency-mobile.git
synced 2024-12-20 20:01:56 +03:00
Merge pull request #903 from esteemapp/bugfix/markdown-editor
Fixed markdown editor bugs
This commit is contained in:
commit
682bba49aa
@ -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 });
|
||||
};
|
||||
|
@ -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 });
|
||||
});
|
||||
};
|
||||
|
@ -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 } });
|
||||
});
|
||||
};
|
||||
|
@ -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 } });
|
||||
});
|
||||
};
|
||||
|
@ -11,7 +11,6 @@ export default [
|
||||
iconType: 'FontAwesome',
|
||||
wrapper: '**',
|
||||
onPress: applyWrapFormat,
|
||||
// style: { fontWeight: 'bold' },
|
||||
},
|
||||
{
|
||||
key: 'I',
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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 => ({
|
||||
|
Loading…
Reference in New Issue
Block a user