Added emoji support for composer
• Added: - emoji support for composer - new emoji sheet • Updated: - Textarea to accomodate - Reducers and actions to retrieve caretPosition on text change when typing • Removed: - Unused input type on AutosuggestTextbox - Old emoji sheet.png
This commit is contained in:
parent
85ec3060d9
commit
91a227913a
|
@ -81,7 +81,7 @@ export const ensureComposeIsVisible = (getState, routerHistory) => {
|
|||
}
|
||||
};
|
||||
|
||||
export function changeCompose(text, markdown, replyId, isStandalone) {
|
||||
export function changeCompose(text, markdown, replyId, isStandalone, caretPosition) {
|
||||
return function (dispatch, getState) {
|
||||
const reduxReplyToId = getState().getIn(['compose', 'in_reply_to'])
|
||||
const existingText = getState().getIn(['compose', 'text']).trim()
|
||||
|
@ -105,6 +105,7 @@ export function changeCompose(text, markdown, replyId, isStandalone) {
|
|||
type: COMPOSE_CHANGE,
|
||||
text: text,
|
||||
markdown: markdown,
|
||||
caretPosition: caretPosition,
|
||||
})
|
||||
} else if (existingText.length > 0 && text.trim().length > 0) {
|
||||
dispatch(openModal('CONFIRM', {
|
||||
|
@ -119,6 +120,7 @@ export function changeCompose(text, markdown, replyId, isStandalone) {
|
|||
type: COMPOSE_CHANGE,
|
||||
text: text,
|
||||
markdown: markdown,
|
||||
caretPosition: caretPosition,
|
||||
})
|
||||
}
|
||||
}))
|
||||
|
@ -136,6 +138,7 @@ export function changeCompose(text, markdown, replyId, isStandalone) {
|
|||
type: COMPOSE_CHANGE,
|
||||
text: text,
|
||||
markdown: markdown,
|
||||
caretPosition: caretPosition,
|
||||
})
|
||||
} else if (existingText.length > 0 && text.trim().length > 0) {
|
||||
dispatch(openModal('CONFIRM', {
|
||||
|
@ -149,6 +152,7 @@ export function changeCompose(text, markdown, replyId, isStandalone) {
|
|||
type: COMPOSE_CHANGE,
|
||||
text: text,
|
||||
markdown: markdown,
|
||||
caretPosition: caretPosition,
|
||||
})
|
||||
},
|
||||
}))
|
||||
|
@ -160,6 +164,7 @@ export function changeCompose(text, markdown, replyId, isStandalone) {
|
|||
type: COMPOSE_CHANGE,
|
||||
text: text,
|
||||
markdown: markdown,
|
||||
caretPosition: caretPosition,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -642,10 +647,9 @@ export function changeComposeVisibility(value) {
|
|||
};
|
||||
};
|
||||
|
||||
export function insertEmojiCompose(position, emoji, needsSpace) {
|
||||
export function insertEmojiCompose(emoji, needsSpace) {
|
||||
return {
|
||||
type: COMPOSE_EMOJI_INSERT,
|
||||
position,
|
||||
emoji,
|
||||
needsSpace,
|
||||
};
|
||||
|
|
|
@ -255,61 +255,27 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
|
|||
justifyContentCenter: small,
|
||||
})
|
||||
|
||||
if (textarea) {
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={textareaContainerClasses}>
|
||||
<Textarea
|
||||
inputRef={this.setTextbox}
|
||||
className={textareaClasses}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
autoFocus={false}
|
||||
value={value}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onKeyUp={onKeyUp}
|
||||
onFocus={this.onFocus}
|
||||
onBlur={this.onBlur}
|
||||
onPaste={this.onPaste}
|
||||
aria-autocomplete='list'
|
||||
/>
|
||||
|
||||
{/*<Composer
|
||||
inputRef={this.setTextbox}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
autoFocus={autoFocus}
|
||||
value={value}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onKeyUp={onKeyUp}
|
||||
onFocus={this.onFocus}
|
||||
onBlur={this.onBlur}
|
||||
onPaste={this.onPaste}
|
||||
small={small}
|
||||
/>*/}
|
||||
|
||||
{children}
|
||||
</div>
|
||||
{ /* : todo : put in popover */ }
|
||||
<div className='autosuggest-textarea__suggestions-wrapper'>
|
||||
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
|
||||
{suggestions.map(this.renderSuggestion)}
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.flexGrow1].join(' ')}>
|
||||
<label className={[_s.default].join(' ')}>
|
||||
<span style={{ display: 'none' }}>{placeholder}</span>
|
||||
<Fragment>
|
||||
<div className={textareaContainerClasses}>
|
||||
<Textarea
|
||||
inputRef={this.setTextbox}
|
||||
className={textareaClasses}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
autoFocus={false}
|
||||
value={value}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onKeyUp={onKeyUp}
|
||||
onFocus={this.onFocus}
|
||||
onBlur={this.onBlur}
|
||||
onPaste={this.onPaste}
|
||||
aria-autocomplete='list'
|
||||
/>
|
||||
|
||||
<Input
|
||||
type='text'
|
||||
ref={this.setTextbox}
|
||||
{/*<Composer
|
||||
inputRef={this.setTextbox}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
autoFocus={autoFocus}
|
||||
|
@ -319,20 +285,21 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
|
|||
onKeyUp={onKeyUp}
|
||||
onFocus={this.onFocus}
|
||||
onBlur={this.onBlur}
|
||||
style={style}
|
||||
aria-autocomplete='list'
|
||||
id={id}
|
||||
className={className}
|
||||
maxLength={maxLength}
|
||||
prependIcon={prependIcon}
|
||||
/>
|
||||
</label>
|
||||
onPaste={this.onPaste}
|
||||
small={small}
|
||||
/>*/}
|
||||
|
||||
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
|
||||
{suggestions.map(this.renderSuggestion)}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
{ /* : todo : put in popover */ }
|
||||
<div className='autosuggest-textarea__suggestions-wrapper'>
|
||||
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
|
||||
{suggestions.map(this.renderSuggestion)}
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ const messages = defineMessages({
|
|||
const assetHost = process.env.CDN_HOST || ''
|
||||
let EmojiPicker, Emoji // load asynchronously
|
||||
|
||||
const backgroundImageFn = () => `${assetHost}/emoji/sheet.png`
|
||||
const backgroundImageFn = () => `${assetHost}/emoji/sheet_1.png`
|
||||
const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false
|
||||
|
||||
const perLine = 8
|
||||
|
@ -223,7 +223,7 @@ const mapDispatchToProps = (dispatch) => ({
|
|||
|
||||
onPickEmoji: (emoji) => {
|
||||
dispatch(useEmoji(emoji))
|
||||
dispatch(insertEmojiCompose(0, emoji, false))
|
||||
dispatch(insertEmojiCompose(emoji, false))
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
@ -93,7 +93,13 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
handleChange = (e, markdown) => {
|
||||
this.props.onChange(e.target.value, markdown, this.props.replyToId)
|
||||
let position = null
|
||||
try {
|
||||
position = this.autosuggestTextarea.textbox.selectionStart
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
this.props.onChange(e.target.value, markdown, this.props.replyToId, position)
|
||||
}
|
||||
|
||||
handleComposeFocus = () => {
|
||||
|
@ -131,11 +137,11 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
handleSubmit = () => {
|
||||
// if (this.props.text !== this.autosuggestTextarea.textbox.value) {
|
||||
// // Something changed the text inside the textarea (e.g. browser extensions like Grammarly)
|
||||
// // Update the state to match the current text
|
||||
// this.props.onChange(this.autosuggestTextarea.textbox.value);
|
||||
// }
|
||||
if (this.props.text !== this.autosuggestTextarea.textbox.value) {
|
||||
// Something changed the text inside the textarea (e.g. browser extensions like Grammarly)
|
||||
// Update the state to match the current text
|
||||
this.props.onChange(this.autosuggestTextarea.textbox.value);
|
||||
}
|
||||
|
||||
// Submit disabled:
|
||||
const { isSubmitting, isChangingUpload, isUploading, anyMedia } = this.props;
|
||||
|
@ -198,8 +204,8 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
selectionStart = selectionEnd;
|
||||
}
|
||||
|
||||
// this.autosuggestTextarea.textbox.setSelectionRange(selectionStart, selectionEnd);
|
||||
// this.autosuggestTextarea.textbox.focus();
|
||||
this.autosuggestTextarea.textbox.setSelectionRange(selectionStart, selectionEnd);
|
||||
this.autosuggestTextarea.textbox.focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -318,7 +324,6 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
onPaste={onPaste}
|
||||
autoFocus={shouldAutoFocus}
|
||||
small={shouldCondense}
|
||||
textarea
|
||||
/>
|
||||
|
||||
<div className={actionsContainerClasses}>
|
||||
|
@ -409,7 +414,6 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
onPaste={onPaste}
|
||||
autoFocus={shouldAutoFocus}
|
||||
small={shouldCondense}
|
||||
textarea
|
||||
/>
|
||||
|
||||
{
|
||||
|
@ -456,7 +460,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
<div className={actionsContainerClasses}>
|
||||
<div className={[_s.default, _s.flexRow, _s.mrAuto].join(' ')}>
|
||||
|
||||
{ /* <EmojiPickerButton small={shouldCondense} isMatch={isMatch} /> */ }
|
||||
<EmojiPickerButton small={shouldCondense} isMatch={isMatch} />
|
||||
|
||||
<UploadButton small={shouldCondense} />
|
||||
{ /* <GifSelectorButton small={shouldCondense} /> */}
|
||||
|
|
|
@ -86,8 +86,8 @@ const mapStateToProps = (state, { replyToId, isStandalone }) => {
|
|||
|
||||
const mapDispatchToProps = (dispatch, { reduxReplyToId, replyToId, isStandalone }) => ({
|
||||
|
||||
onChange(text, markdown, newReplyToId) {
|
||||
dispatch(changeCompose(text, markdown, newReplyToId, isStandalone))
|
||||
onChange(text, markdown, newReplyToId, position) {
|
||||
dispatch(changeCompose(text, markdown, newReplyToId, isStandalone, position))
|
||||
},
|
||||
|
||||
onSubmit(group, replyToId, router) {
|
||||
|
|
|
@ -169,14 +169,13 @@ const updateSuggestionTags = (state, token) => {
|
|||
});
|
||||
};
|
||||
|
||||
const insertEmoji = (state, position, emojiData, needsSpace) => {
|
||||
const oldText = state.get('text')
|
||||
const emoji = needsSpace ? ' ' + emojiData.native : emojiData.native
|
||||
const text = `${oldText.slice(0, position)}${emoji} ${oldText.slice(position)}`
|
||||
// console.log("insertEmoji reducer:", emoji, position, emojiData, needsSpace, text)
|
||||
const insertEmoji = (state, emojiData, needsSpace) => {
|
||||
const position = state.get('caretPosition')
|
||||
const oldText = state.get('text');
|
||||
const emoji = needsSpace ? ' ' + emojiData.native : emojiData.native;
|
||||
|
||||
return state.merge({
|
||||
text,
|
||||
text: `${oldText.slice(0, position)}${emoji} ${oldText.slice(position)}`,
|
||||
focusDate: new Date(),
|
||||
caretPosition: position + emoji.length + 1,
|
||||
idempotencyKey: uuid(),
|
||||
|
@ -254,6 +253,7 @@ export default function compose(state = initialState, action) {
|
|||
map.set('text', action.text)
|
||||
map.set('markdown', action.markdown)
|
||||
map.set('idempotencyKey', uuid())
|
||||
map.set('caretPosition', action.caretPosition)
|
||||
if (action.replyId) {
|
||||
map.set('in_reply_to', action.replyId)
|
||||
}
|
||||
|
@ -345,7 +345,7 @@ export default function compose(state = initialState, action) {
|
|||
return state;
|
||||
}
|
||||
case COMPOSE_EMOJI_INSERT:
|
||||
return insertEmoji(state, action.position, action.emoji, action.needsSpace);
|
||||
return insertEmoji(state, action.emoji, action.needsSpace);
|
||||
case COMPOSE_UPLOAD_CHANGE_SUCCESS:
|
||||
return state
|
||||
.set('is_changing_upload', false)
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.4 MiB |
Binary file not shown.
After Width: | Height: | Size: 839 KiB |
Loading…
Reference in New Issue