Merge branch 'development' of https://github.com/ecency/ecency-mobile into sa/IAP-QR

This commit is contained in:
Sadaqat Ali 2022-06-10 16:49:46 +05:00
commit aaad438287
13 changed files with 351 additions and 435 deletions

View File

@ -1,15 +1,12 @@
import React, { useState, Fragment, useRef } from 'react'; import React, { useState, Fragment, useRef } from 'react';
import { View, Text, ActivityIndicator, SafeAreaView } from 'react-native'; import { View, Text, ActivityIndicator, SafeAreaView } from 'react-native';
import { injectIntl } from 'react-intl'; import { injectIntl } from 'react-intl';
import { useSelector } from 'react-redux';
import moment from 'moment';
// Components // Components
import { TextButton, Modal, BeneficiaryModal } from '../..'; import { TextButton } from '../..';
import { IconButton } from '../../iconButton'; import { IconButton } from '../../iconButton';
import { DropdownButton } from '../../dropdownButton'; import { DropdownButton } from '../../dropdownButton';
import { TextInput } from '../../textInput'; import { TextInput } from '../../textInput';
import { DateTimePicker } from '../../dateTimePicker';
// Constants // Constants
// Styles // Styles
@ -28,7 +25,6 @@ const BasicHeaderView = ({
intl, intl,
isDraftSaved, isDraftSaved,
isDraftSaving, isDraftSaving,
draftId,
isFormValid, isFormValid,
isHasDropdown, isHasDropdown,
isHasIcons, isHasIcons,
@ -46,23 +42,14 @@ const BasicHeaderView = ({
title, title,
handleOnSubmit, handleOnSubmit,
handleOnSearch, handleOnSearch,
handleDatePickerChange,
handleRewardChange, handleRewardChange,
handleBeneficiaries,
enableViewModeToggle, enableViewModeToggle,
handleSettingsPress, handleSettingsPress,
showThumbSelectionModal,
}) => { }) => {
const [isInputVisible, setIsInputVisible] = useState(false); const [isInputVisible, setIsInputVisible] = useState(false);
const [beneficiaryModal, setBeneficiaryModal] = useState(false);
const [showScheduleModal, setShowScheduleModal] = useState(false);
const [scheduledDate, setScheduledDate] = useState('');
const username = useSelector((state) => state.account.currentAccount.name);
const settingMenuRef = useRef(null);
const rewardMenuRef = useRef(null); const rewardMenuRef = useRef(null);
const scheduleRef = useRef(null);
/** /**
* *
@ -90,27 +77,6 @@ const BasicHeaderView = ({
handleOnSearch(value); handleOnSearch(value);
}; };
const _handleSettingMenuSelect = (index) => {
switch (index) {
case 0:
setShowScheduleModal(true);
break;
case 1:
if (showThumbSelectionModal) {
showThumbSelectionModal();
}
break;
case 2:
rewardMenuRef.current.show();
break;
case 3:
setBeneficiaryModal(true);
break;
default:
break;
}
};
const _handleRewardMenuSelect = (index) => { const _handleRewardMenuSelect = (index) => {
let rewardType = 'default'; let rewardType = 'default';
@ -131,32 +97,7 @@ const BasicHeaderView = ({
} }
}; };
const _handleOnSaveBeneficiaries = (beneficiaries) => {
const _beneficiaries = beneficiaries.map((item) => ({
account: item.account,
weight: item.weight,
}));
setBeneficiaryModal(false);
if (handleBeneficiaries) {
handleBeneficiaries(_beneficiaries);
}
};
const _handleDatePickerChange = (datePickerValue) => {
setScheduledDate(datePickerValue);
};
const _onPressDone = () => {
let dateString = scheduledDate;
if (dateString === '') {
dateString = moment().format();
}
setScheduledDate('');
handleDatePickerChange(dateString);
setShowScheduleModal(false);
};
/** /**
* *
@ -291,57 +232,8 @@ const BasicHeaderView = ({
</Fragment> </Fragment>
)} )}
</View> </View>
<Modal
isOpen={beneficiaryModal}
isFullScreen
isCloseButton
presentationStyle="formSheet"
handleOnModalClose={() => setBeneficiaryModal(false)}
title={intl.formatMessage({ id: 'editor.beneficiaries' })}
animationType="slide"
style={styles.beneficiaryModal}
>
<BeneficiaryModal
username={username}
handleOnSaveBeneficiaries={_handleOnSaveBeneficiaries}
draftId={draftId}
/>
</Modal>
<Modal
isFullScreen={false}
isOpen={showScheduleModal}
isBottomModal
isTransparent
isRadius
coverScreen={false}
title={intl.formatMessage({ id: 'editor.schedule_modal_title' })}
hasRightText
rightText="Done"
onPressRightText={_onPressDone}
onBackdropPress={() => setShowScheduleModal(false)}
>
<SafeAreaView style={styles.dateTimeModal}>
<DateTimePicker
type="datetime"
onChanged={_handleDatePickerChange}
disabled={!isFormValid}
ref={scheduleRef}
/>
</SafeAreaView>
</Modal>
<OptionsModal
ref={settingMenuRef}
options={[
intl.formatMessage({ id: 'editor.setting_schedule' }),
intl.formatMessage({ id: 'editor.setting_thumb' }),
intl.formatMessage({ id: 'editor.setting_reward' }),
intl.formatMessage({ id: 'editor.setting_beneficiary' }),
intl.formatMessage({ id: 'alert.cancel' }),
]}
cancelButtonIndex={4}
title={intl.formatMessage({ id: 'editor.options' })}
onPress={_handleSettingMenuSelect}
/>
<OptionsModal <OptionsModal
ref={rewardMenuRef} ref={rewardMenuRef}
options={[ options={[
@ -354,6 +246,7 @@ const BasicHeaderView = ({
title="Reward" title="Reward"
onPress={_handleRewardMenuSelect} onPress={_handleRewardMenuSelect}
/> />
</SafeAreaView> </SafeAreaView>
); );
}; };

View File

@ -53,9 +53,13 @@ import { walkthrough } from '../../../redux/constants/walkthroughConstants';
const MIN_BODY_INPUT_HEIGHT = 300; const MIN_BODY_INPUT_HEIGHT = 300;
//These variable keep track of body text input state,
//this helps keep load on minimal compared to both useState and useRef;
var bodyText = '';
var bodySelection = {start: 0, end: 0};
const MarkdownEditorView = ({ const MarkdownEditorView = ({
draftBody, draftBody,
handleIsFormValid,
handleOpenImagePicker, handleOpenImagePicker,
intl, intl,
isPreviewActive, isPreviewActive,
@ -64,9 +68,6 @@ const MarkdownEditorView = ({
isUploading, isUploading,
initialFields, initialFields,
onChange, onChange,
handleOnTextChange,
handleIsValid,
componentID,
uploadedImage, uploadedImage,
isEdit, isEdit,
post, post,
@ -82,8 +83,6 @@ const MarkdownEditorView = ({
}) => { }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const [text, setText] = useState(draftBody || '');
const [selection, setSelection] = useState({ start: 0, end: 0 });
const [editable, setEditable] = useState(true); const [editable, setEditable] = useState(true);
const [bodyInputHeight, setBodyInputHeight] = useState(MIN_BODY_INPUT_HEIGHT); const [bodyInputHeight, setBodyInputHeight] = useState(MIN_BODY_INPUT_HEIGHT);
const [isSnippetsOpen, setIsSnippetsOpen] = useState(false); const [isSnippetsOpen, setIsSnippetsOpen] = useState(false);
@ -103,9 +102,14 @@ const MarkdownEditorView = ({
const draftBtnTooltipRegistered = draftBtnTooltipState.get(walkthrough.EDITOR_DRAFT_BTN); const draftBtnTooltipRegistered = draftBtnTooltipState.get(walkthrough.EDITOR_DRAFT_BTN);
const headerText = post && (post.summary || postBodySummary(post, 150, Platform.OS)); const headerText = post && (post.summary || postBodySummary(post, 150, Platform.OS));
useEffect(() => {
bodyText = '';
bodySelection = {start:0, end:0};
}, []);
useEffect(() => { useEffect(() => {
if (!isPreviewActive) { if (!isPreviewActive) {
_setTextAndSelection({ selection: { start: 0, end: 0 }, text }); _setTextAndSelection({ selection: { start: 0, end: 0 }, text: bodyText });
} }
}, [isPreviewActive]); }, [isPreviewActive]);
@ -121,7 +125,7 @@ const MarkdownEditorView = ({
}, [onLoadDraftPress]); }, [onLoadDraftPress]);
useEffect(() => { useEffect(() => {
if (text === '' && draftBody !== '') { if (bodyText === '' && draftBody !== '') {
let draftBodyLength = draftBody.length; let draftBodyLength = draftBody.length;
_setTextAndSelection({ _setTextAndSelection({
selection: { start: draftBodyLength, end: draftBodyLength }, selection: { start: draftBodyLength, end: draftBodyLength },
@ -165,8 +169,8 @@ const MarkdownEditorView = ({
useEffect(() => { useEffect(() => {
if (uploadedImage && uploadedImage.shouldInsert && !isUploading) { if (uploadedImage && uploadedImage.shouldInsert && !isUploading) {
applyMediaLink({ applyMediaLink({
text, text: bodyText,
selection, selection: bodySelection,
setTextAndSelection: _setTextAndSelection, setTextAndSelection: _setTextAndSelection,
items: [{ url: uploadedImage.url, text: uploadedImage.hash }], items: [{ url: uploadedImage.url, text: uploadedImage.hash }],
}); });
@ -178,7 +182,7 @@ const MarkdownEditorView = ({
}, [uploadedImage, isUploading]); }, [uploadedImage, isUploading]);
useEffect(() => { useEffect(() => {
setText(draftBody); bodyText = draftBody;
}, [draftBody]); }, [draftBody]);
useEffect(() => { useEffect(() => {
@ -190,17 +194,7 @@ const MarkdownEditorView = ({
} }
}, [autoFocusText]); }, [autoFocusText]);
useEffect(() => {
const nextText = text.replace(text, '');
if (nextText && nextText.length > 0) {
_changeText(text);
if (handleIsFormValid) {
handleIsFormValid(text);
}
}
}, [text]);
const changeUser = async () => { const changeUser = async () => {
dispatch(toggleAccountsBottomSheet(!isVisibleAccountsBottomSheet)); dispatch(toggleAccountsBottomSheet(!isVisibleAccountsBottomSheet));
@ -208,8 +202,8 @@ const MarkdownEditorView = ({
const _onApplyUsername = (username) => { const _onApplyUsername = (username) => {
applyUsername({ applyUsername({
text, text: bodyText,
selection, selection: bodySelection,
setTextAndSelection: _setTextAndSelection, setTextAndSelection: _setTextAndSelection,
username, username,
}); });
@ -217,23 +211,17 @@ const MarkdownEditorView = ({
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
const _changeText = useCallback((input) => { const _changeText = useCallback((input) => {
setText(input); bodyText = input;
//NOTE: onChange method is called by direct parent of MarkdownEditor that is PostForm, do not remove
if (onChange) { if (onChange) {
onChange(input); onChange(input);
} }
}, []);
if (handleIsValid) {
handleIsValid(componentID, !!(input && input.length));
}
if (handleOnTextChange) {
handleOnTextChange(input);
}
});
const _handleOnSelectionChange = async (event) => { const _handleOnSelectionChange = async (event) => {
setSelection(event.nativeEvent.selection); bodySelection = event.nativeEvent.selection;
}; };
const _handleOnContentSizeChange = async (event) => { const _handleOnContentSizeChange = async (event) => {
@ -243,35 +231,38 @@ const MarkdownEditorView = ({
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
const _setTextAndSelection = useCallback(({ selection: _selection, text: _text }) => { const _setTextAndSelection = useCallback(({ selection: _selection, text: _text }) => {
console.log('_text : ', _text); // console.log('_text : ', _text);
inputRef.current.setNativeProps({ inputRef.current.setNativeProps({
text: _text, text: _text,
}); });
// Workaround for iOS selection update issue const _updateSelection = () => {
const isIos = Platform.OS === 'ios'; bodySelection = _selection
if (isIos) {
setTimeout(() => {
inputRef.current.setNativeProps({ inputRef.current.setNativeProps({
selection: _selection, selection: _selection,
}); });
setSelection(_selection); }
// Workaround for iOS selection update issue
if (Platform.OS === 'ios') {
setTimeout(() => {
_updateSelection();
}, 100); }, 100);
} else { } else {
inputRef.current.setNativeProps({ _updateSelection()
selection: _selection,
});
setSelection(_selection);
} }
setIsSnippetsOpen(false);
_changeText(_text);
});
console.log('text : ', text); if (isSnippetsOpen) {
setIsSnippetsOpen(false);
}
_changeText(_text);
}, []);
const _renderPreview = () => ( const _renderPreview = () => (
<ScrollView style={styles.previewContainer}> <ScrollView style={styles.previewContainer}>
{text ? ( {bodyText ? (
<PostBody body={renderPostBody(text, true, Platform.OS === 'ios' ? false : true)} /> <PostBody body={renderPostBody(bodyText, true, Platform.OS === 'ios' ? false : true)} />
) : ( ) : (
<Text>...</Text> <Text>...</Text>
)} )}
@ -280,8 +271,8 @@ const MarkdownEditorView = ({
const _handleOnSnippetReceived = (snippetText) => { const _handleOnSnippetReceived = (snippetText) => {
applySnippet({ applySnippet({
text, text: bodyText,
selection, selection: bodySelection,
setTextAndSelection: _setTextAndSelection, setTextAndSelection: _setTextAndSelection,
snippetText: `\n${snippetText}\n`, snippetText: `\n${snippetText}\n`,
}); });
@ -295,8 +286,8 @@ const MarkdownEditorView = ({
if (items.length) { if (items.length) {
applyMediaLink({ applyMediaLink({
text, text: bodyText,
selection, selection: bodySelection,
setTextAndSelection: _setTextAndSelection, setTextAndSelection: _setTextAndSelection,
items, items,
}); });
@ -305,8 +296,8 @@ const MarkdownEditorView = ({
const _handleOnAddLinkPress = () => { const _handleOnAddLinkPress = () => {
insertLinkModalRef.current?.showModal({ insertLinkModalRef.current?.showModal({
selectedText: text.slice(selection.start, selection.end), selectedText: bodyText.slice(bodySelection.start, bodySelection.end),
selection: selection, selection: bodySelection,
}); });
inputRef.current?.blur(); inputRef.current?.blur();
}; };
@ -315,7 +306,7 @@ const MarkdownEditorView = ({
}; };
const _handleInsertLink = ({ snippetText, selection }) => { const _handleInsertLink = ({ snippetText, selection }) => {
applySnippet({ applySnippet({
text, text: bodyText,
selection, selection,
setTextAndSelection: _setTextAndSelection, setTextAndSelection: _setTextAndSelection,
snippetText, snippetText,
@ -332,7 +323,12 @@ const MarkdownEditorView = ({
iconType={item.iconType} iconType={item.iconType}
name={item.icon} name={item.icon}
onPress={() => onPress={() =>
item.onPress({ text, selection, setTextAndSelection: _setTextAndSelection, item }) item.onPress({
text: bodyText,
selection: bodySelection,
setTextAndSelection: _setTextAndSelection,
item
})
} }
/> />
</View> </View>
@ -427,7 +423,7 @@ const MarkdownEditorView = ({
const _handleClear = (index) => { const _handleClear = (index) => {
if (index === 0) { if (index === 0) {
initialFields(); initialFields();
setText('');
_setTextAndSelection({ text: '', selection: { start: 0, end: 0 } }); _setTextAndSelection({ text: '', selection: { start: 0, end: 0 } });
} }
}; };
@ -513,7 +509,7 @@ const MarkdownEditorView = ({
const _innerContent = ( const _innerContent = (
<> <>
{isAndroidOreo() ? _renderEditorWithoutScroll() : _renderEditorWithScroll()} {isAndroidOreo() ? _renderEditorWithoutScroll() : _renderEditorWithScroll()}
<UsernameAutofillBar text={text} selection={selection} onApplyUsername={_onApplyUsername} /> <UsernameAutofillBar text={bodyText} selection={bodySelection} onApplyUsername={_onApplyUsername} />
{_renderFloatingDraftButton()} {_renderFloatingDraftButton()}
{!isPreviewActive && _renderEditorButtons()} {!isPreviewActive && _renderEditorButtons()}
</> </>

View File

@ -1,4 +1,4 @@
import React, {useState, useEffect} from 'react'; import React, {useState, useEffect, useCallback} from 'react';
import { View, FlatList, Text, TouchableOpacity } from "react-native" import { View, FlatList, Text, TouchableOpacity } from "react-native"
import { UserAvatar } from '../..'; import { UserAvatar } from '../..';
import { lookupAccounts } from '../../../providers/hive/dhive'; import { lookupAccounts } from '../../../providers/hive/dhive';
@ -22,21 +22,25 @@ export const UsernameAutofillBar = ({text, selection, onApplyUsername}:Props) =>
useEffect(() => { useEffect(() => {
if (selection.start === selection.end && text) { if (selection.start === selection.end && text) {
const word = extractWordAtIndex(text, selection.start); _processTextForSearch(text, selection.start);
}
}, [text, selection])
const _processTextForSearch = useCallback(debounce((text:string, index:number) => {
const word = extractWordAtIndex(text, index);
console.log('selection word is: ', word); console.log('selection word is: ', word);
if (word.startsWith('@') && word.length > 3) { if (word.startsWith('@') && word.length > 1) {
_handleUserSearch(word.substring(1)); _handleUserSearch(word.substring(1));
} else { } else {
setSearchedUsers([]); setSearchedUsers([]);
setQuery('') setQuery('')
_handleUserSearch.cancel(); _handleUserSearch.cancel();
} }
} }, 300, {leading:true}),[]);
}, [text, selection])
const _handleUserSearch = useCallback(debounce(async (username) => {
const _handleUserSearch = debounce(async (username) => {
if(query !== username){ if(query !== username){
let users = []; let users = [];
if (username) { if (username) {
@ -47,7 +51,7 @@ export const UsernameAutofillBar = ({text, selection, onApplyUsername}:Props) =>
setSearchedUsers(users); setSearchedUsers(users);
} }
}, 200, {leading:true}); }, 200, {leading:true}), []);

View File

@ -1,4 +1,5 @@
import React, { PureComponent, Fragment } from 'react'; import React, { PureComponent, Fragment } from 'react';
import { debounce } from 'lodash';
class PostFormView extends PureComponent { class PostFormView extends PureComponent {
constructor(props) { constructor(props) {
@ -18,9 +19,12 @@ class PostFormView extends PureComponent {
}; };
_handleOnChange = (componentID, value, isValid = null) => { _handleOnChange = (componentID, value, isValid = null) => {
const { handleFormUpdate } = this.props; const { handleFormUpdate, handleBodyChange } = this.props;
console.log('update fields state :', componentID, value);
handleFormUpdate(componentID, value, !!isValid || !!value); handleFormUpdate(componentID, value, !!isValid || !!value);
if (componentID === 'body') {
handleBodyChange(value);
}
}; };
render() { render() {
@ -33,7 +37,10 @@ class PostFormView extends PureComponent {
return React.cloneElement(child, { return React.cloneElement(child, {
onSubmitEditing: (item) => onSubmitEditing: (item) =>
this._handleOnSubmitEditing(child.props.returnKeyType, item), this._handleOnSubmitEditing(child.props.returnKeyType, item),
onChange: (value) => this._handleOnChange(child.props.componentID, value), onChange: debounce(
(value) => this._handleOnChange(child.props.componentID, value),
500,
),
returnKeyType: isFormValid ? 'done' : 'next', returnKeyType: isFormValid ? 'done' : 'next',
isPreviewActive, isPreviewActive,
}); });

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState, useCallback } from 'react';
import EStyleSheet from 'react-native-extended-stylesheet'; import EStyleSheet from 'react-native-extended-stylesheet';
import styles from './quickReplyModalStyles'; import styles from './quickReplyModalStyles';
import { View, Text, Alert, TouchableOpacity, Keyboard, Platform } from 'react-native'; import { View, Text, Alert, TouchableOpacity, Keyboard, Platform } from 'react-native';
@ -14,11 +14,12 @@ import {
updateDraftCache, updateDraftCache,
} from '../../redux/actions/cacheActions'; } from '../../redux/actions/cacheActions';
import { default as ROUTES } from '../../constants/routeNames'; import { default as ROUTES } from '../../constants/routeNames';
import get from 'lodash/get'; import {get, debounce} from 'lodash';
import { navigate } from '../../navigation/service'; import { navigate } from '../../navigation/service';
import { postBodySummary } from '@ecency/render-helper'; import { postBodySummary } from '@ecency/render-helper';
import { Draft } from '../../redux/reducers/cacheReducer'; import { Draft } from '../../redux/reducers/cacheReducer';
import { RootState } from '../../redux/store/store'; import { RootState } from '../../redux/store/store';
import comment from '../../constants/options/comment';
export interface QuickReplyModalContentProps { export interface QuickReplyModalContentProps {
fetchPost?: any; fetchPost?: any;
@ -43,7 +44,7 @@ export const QuickReplyModalContent = ({
const [commentValue, setCommentValue] = useState(''); const [commentValue, setCommentValue] = useState('');
const [isSending, setIsSending] = useState(false); const [isSending, setIsSending] = useState(false);
const [quickCommentDraft, setQuickCommentDraft] = useState<Draft>(null);
const headerText = const headerText =
selectedPost && (selectedPost.summary || postBodySummary(selectedPost, 150, Platform.OS as any)); selectedPost && (selectedPost.summary || postBodySummary(selectedPost, 150, Platform.OS as any));
@ -60,7 +61,6 @@ export const QuickReplyModalContent = ({
if (drafts.has(draftId) && currentAccount.name === drafts.get(draftId).author) { if (drafts.has(draftId) && currentAccount.name === drafts.get(draftId).author) {
const quickComment: Draft = drafts.get(draftId); const quickComment: Draft = drafts.get(draftId);
setCommentValue(quickComment.body); setCommentValue(quickComment.body);
setQuickCommentDraft(quickComment);
} else { } else {
setCommentValue(''); setCommentValue('');
} }
@ -73,25 +73,24 @@ export const QuickReplyModalContent = ({
}; };
// add quick comment value into cache // add quick comment value into cache
const _addQuickCommentIntoCache = () => { const _addQuickCommentIntoCache = (value = commentValue) => {
const date = new Date();
const updatedStamp = date.toISOString().substring(0, 19);
const quickCommentDraftData: Draft = { const quickCommentDraftData: Draft = {
author: currentAccount.name, author: currentAccount.name,
body: commentValue, body: value
created: quickCommentDraft ? quickCommentDraft.created : updatedStamp,
updated: updatedStamp,
expiresAt: date.getTime() + 604800000, // 7 days expiry time
}; };
//add quick comment cache entry //add quick comment cache entry
dispatch(updateDraftCache(draftId, quickCommentDraftData)); dispatch(updateDraftCache(draftId, quickCommentDraftData));
}; };
// handle close press // handle close press
const _handleClosePress = () => { const _handleClosePress = () => {
sheetModalRef.current?.setModalVisible(false); sheetModalRef.current?.setModalVisible(false);
}; };
// navigate to post on summary press // navigate to post on summary press
const _handleOnSummaryPress = () => { const _handleOnSummaryPress = () => {
Keyboard.dismiss(); Keyboard.dismiss();
@ -105,6 +104,7 @@ export const QuickReplyModalContent = ({
}); });
}; };
// handle submit reply // handle submit reply
const _submitReply = async () => { const _submitReply = async () => {
let stateTimer; let stateTimer;
@ -207,12 +207,22 @@ export const QuickReplyModalContent = ({
params: { params: {
isReply: true, isReply: true,
post: selectedPost, post: selectedPost,
quickReplyText: commentValue,
fetchPost, fetchPost,
}, },
}); });
} }
}; };
const _deboucedCacheUpdate = useCallback(debounce(_addQuickCommentIntoCache, 500),[])
const _onChangeText = (value) => {
setCommentValue(value);
_deboucedCacheUpdate(value)
}
//VIEW_RENDERERS //VIEW_RENDERERS
const _renderSheetHeader = () => ( const _renderSheetHeader = () => (
@ -284,9 +294,7 @@ export const QuickReplyModalContent = ({
<View style={styles.inputContainer}> <View style={styles.inputContainer}>
<TextInput <TextInput
innerRef={inputRef} innerRef={inputRef}
onChangeText={(value) => { onChangeText={_onChangeText}
setCommentValue(value);
}}
value={commentValue} value={commentValue}
// autoFocus // autoFocus
placeholder={intl.formatMessage({ placeholder={intl.formatMessage({

View File

@ -69,6 +69,7 @@ export const UploadsGalleryModal = forwardRef(({
}, [uploadedImage]) }, [uploadedImage])
//save image to user gallery //save image to user gallery
const _addUploadedImageToGallery = async () => { const _addUploadedImageToGallery = async () => {
try { try {
@ -138,6 +139,8 @@ export const UploadsGalleryModal = forwardRef(({
setShowModal(false); setShowModal(false);
} }
//renders footer with add snipept button and shows new snippet modal //renders footer with add snipept button and shows new snippet modal
const _renderFloatingPanel = () => { const _renderFloatingPanel = () => {
@ -242,7 +245,7 @@ export const UploadsGalleryModal = forwardRef(({
}; };
const _renderHeaderContent = ( const _renderHeaderContent = () => (
<> <>
{isUploading && <ProgressBar progress={uploadProgress} />} {isUploading && <ProgressBar progress={uploadProgress} />}
</> </>
@ -251,11 +254,13 @@ export const UploadsGalleryModal = forwardRef(({
const _renderContent = ( const _renderContent = () => {
console.log("Rendering uploaded images")
return (
<View style={styles.container}> <View style={styles.container}>
<View style={styles.bodyWrapper}> <View style={styles.bodyWrapper}>
{_renderHeaderContent} {_renderHeaderContent()}
<FlatList <FlatList
data={mediaUploads} data={mediaUploads}
keyExtractor={(item) => `item_${item.url}`} keyExtractor={(item) => `item_${item.url}`}
@ -275,6 +280,7 @@ export const UploadsGalleryModal = forwardRef(({
{_renderFloatingPanel()} {_renderFloatingPanel()}
</View> </View>
) )
}
return ( return (
@ -290,7 +296,7 @@ export const UploadsGalleryModal = forwardRef(({
animationType="slide" animationType="slide"
style={styles.modalStyle} style={styles.modalStyle}
> >
{_renderContent} {showModal && _renderContent()}
</Modal> </Modal>
); );

View File

@ -8,38 +8,38 @@ import {
DELETE_COMMENT_CACHE_ENTRY, DELETE_COMMENT_CACHE_ENTRY,
UPDATE_DRAFT_CACHE, UPDATE_DRAFT_CACHE,
DELETE_DRAFT_CACHE_ENTRY, DELETE_DRAFT_CACHE_ENTRY,
} from '../constants/constants'; } from '../constants/constants';
import { Comment, Draft, Vote } from '../reducers/cacheReducer'; import { Comment, Draft, Vote } from '../reducers/cacheReducer';
export const updateVoteCache = (postPath:string, vote:Vote) => ({ export const updateVoteCache = (postPath: string, vote: Vote) => ({
payload:{ payload: {
postPath, postPath,
vote vote
}, },
type: UPDATE_VOTE_CACHE type: UPDATE_VOTE_CACHE
}) })
interface CommentCacheOptions { interface CommentCacheOptions {
isUpdate?:boolean; isUpdate?: boolean;
parentTags?:Array<string>; parentTags?: Array<string>;
} }
export const updateCommentCache = (commentPath:string, comment:Comment, options:CommentCacheOptions = {isUpdate:false}) => { export const updateCommentCache = (commentPath: string, comment: Comment, options: CommentCacheOptions = { isUpdate: false }) => {
console.log("body received:", comment.markdownBody); console.log("body received:", comment.markdownBody);
const updated = new Date(); const updated = new Date();
updated.setSeconds(updated.getSeconds() - 5); //make cache delayed by 5 seconds to avoid same updated stamp in post data updated.setSeconds(updated.getSeconds() - 5); //make cache delayed by 5 seconds to avoid same updated stamp in post data
const updatedStamp = updated.toISOString().substring(0, 19); //server only return 19 character time string without timezone part const updatedStamp = updated.toISOString().substring(0, 19); //server only return 19 character time string without timezone part
if(options.isUpdate && !comment.created){ if (options.isUpdate && !comment.created) {
throw new Error("For comment update, created prop must be provided from original comment data to update local cache"); throw new Error("For comment update, created prop must be provided from original comment data to update local cache");
} }
if(!options.parentTags && !comment.json_metadata){ if (!options.parentTags && !comment.json_metadata) {
throw new Error("either of json_metadata in comment data or parentTags in options must be provided"); throw new Error("either of json_metadata in comment data or parentTags in options must be provided");
} }
@ -54,41 +54,40 @@ import { Comment, Draft, Vote } from '../reducers/cacheReducer';
comment.isDeletable = comment.isDeletable || true; comment.isDeletable = comment.isDeletable || true;
comment.body = renderPostBody({ comment.body = renderPostBody({
author:comment.author, author: comment.author,
permlink:comment.permlink, permlink: comment.permlink,
last_update:comment.updated, last_update: comment.updated,
body:comment.markdownBody, body: comment.markdownBody,
}, true, Platform.OS === 'android'); }, true, Platform.OS === 'android');
return ({ return ({
payload:{ payload: {
commentPath, commentPath,
comment comment
}, },
type: UPDATE_COMMENT_CACHE type: UPDATE_COMMENT_CACHE
}) })
} }
export const deleteCommentCacheEntry = (commentPath:string) => ({ export const deleteCommentCacheEntry = (commentPath: string) => ({
payload:commentPath, payload: commentPath,
type: DELETE_COMMENT_CACHE_ENTRY type: DELETE_COMMENT_CACHE_ENTRY
}) })
export const updateDraftCache = (id:string, draft:Draft) => ({ export const updateDraftCache = (id: string, draft: Draft) => ({
payload:{ payload: {
id, id,
draft draft
}, },
type: UPDATE_DRAFT_CACHE type: UPDATE_DRAFT_CACHE
}) })
export const deleteDraftCacheEntry = (id:string) => ({ export const deleteDraftCacheEntry = (id: string) => ({
payload:id, payload: id,
type: DELETE_DRAFT_CACHE_ENTRY type: DELETE_DRAFT_CACHE_ENTRY
}) })
export const purgeExpiredCache = () => ({ export const purgeExpiredCache = () => ({
type: PURGE_EXPIRED_CACHE type: PURGE_EXPIRED_CACHE
}) })

View File

@ -113,6 +113,7 @@ export const UPDATE_COMMENT_CACHE = 'UPDATE_COMMENT_CACHE';
export const DELETE_COMMENT_CACHE_ENTRY = 'DELETE_COMMENT_CACHE_ENTRY'; export const DELETE_COMMENT_CACHE_ENTRY = 'DELETE_COMMENT_CACHE_ENTRY';
export const UPDATE_DRAFT_CACHE = 'UPDATE_DRAFT_CACHE'; export const UPDATE_DRAFT_CACHE = 'UPDATE_DRAFT_CACHE';
export const DELETE_DRAFT_CACHE_ENTRY = 'DELETE_DRAFT_CACHE_ENTRY'; export const DELETE_DRAFT_CACHE_ENTRY = 'DELETE_DRAFT_CACHE_ENTRY';
export const DEFAULT_USER_DRAFT_ID = 'DEFAULT_USER_DRAFT_ID_';
// TOOLTIPS // TOOLTIPS
export const REGISTER_TOOLTIP = 'REGISTER_TOOLTIP'; export const REGISTER_TOOLTIP = 'REGISTER_TOOLTIP';

View File

@ -28,10 +28,12 @@ export interface Comment {
export interface Draft { export interface Draft {
author: string, author: string,
body?:string, body:string,
created?:string, title?:string,
updated?:string, tags?:string,
expiresAt:number; created?:number,
updated?:number,
expiresAt?:number;
} }
interface State { interface State {
@ -93,7 +95,16 @@ const initialState:State = {
if(!state.drafts){ if(!state.drafts){
state.drafts = new Map<string, Draft>(); state.drafts = new Map<string, Draft>();
} }
state.drafts.set(payload.id, payload.draft);
const curTime = new Date().getTime();
const curDraft = state.drafts.get(payload.id);
const payloadDraft = payload.draft;
payloadDraft.created = curDraft ? curDraft.created : curTime;
payloadDraft.updated = curTime;
payloadDraft.expiresAt = curTime + 604800000 // 7 days ms
state.drafts.set(payload.id, payloadDraft);
return { return {
...state, //spread operator in requried here, otherwise persist do not register change ...state, //spread operator in requried here, otherwise persist do not register change
lastUpdate: { lastUpdate: {

View File

@ -114,7 +114,7 @@ const PostOptionsModal = forwardRef(({
handleThumbSelection(index) handleThumbSelection(index)
} }
const _renderContent = ( const _renderContent = () => (
<View style={styles.fillSpace}> <View style={styles.fillSpace}>
<KeyboardAwareScrollView style={styles.fillSpace} > <KeyboardAwareScrollView style={styles.fillSpace} >
<View style={styles.container}> <View style={styles.container}>
@ -214,7 +214,7 @@ const PostOptionsModal = forwardRef(({
animationType="slide" animationType="slide"
style={styles.modalStyle} style={styles.modalStyle}
> >
{_renderContent} {_renderContent()}
</Modal> </Modal>
); );

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { injectIntl } from 'react-intl'; import { injectIntl } from 'react-intl';
import { Alert, Platform } from 'react-native'; import { Alert } from 'react-native';
import ImagePicker from 'react-native-image-crop-picker'; import ImagePicker from 'react-native-image-crop-picker';
import get from 'lodash/get'; import get from 'lodash/get';
import AsyncStorage from '@react-native-community/async-storage'; import AsyncStorage from '@react-native-community/async-storage';
@ -25,7 +25,6 @@ import {
reblog, reblog,
postComment, postComment,
} from '../../../providers/hive/dhive'; } from '../../../providers/hive/dhive';
import { setDraftPost, getDraftPost } from '../../../realm/realm';
// Constants // Constants
import { default as ROUTES } from '../../../constants/routeNames'; import { default as ROUTES } from '../../../constants/routeNames';
@ -45,8 +44,8 @@ import {
import EditorScreen from '../screen/editorScreen'; import EditorScreen from '../screen/editorScreen';
import bugsnapInstance from '../../../config/bugsnag'; import bugsnapInstance from '../../../config/bugsnag';
import { removeBeneficiaries, setBeneficiaries } from '../../../redux/actions/editorActions'; import { removeBeneficiaries, setBeneficiaries } from '../../../redux/actions/editorActions';
import { TEMP_BENEFICIARIES_ID } from '../../../redux/constants/constants'; import { DEFAULT_USER_DRAFT_ID, TEMP_BENEFICIARIES_ID } from '../../../redux/constants/constants';
import { updateCommentCache } from '../../../redux/actions/cacheActions'; import { deleteDraftCacheEntry, updateCommentCache, updateDraftCache } from '../../../redux/actions/cacheActions';
/* /*
* Props Name Description Value * Props Name Description Value
@ -90,7 +89,7 @@ class EditorContainer extends Component<any, any> {
const { currentAccount, navigation } = this.props; const { currentAccount, navigation } = this.props;
const username = currentAccount && currentAccount.name ? currentAccount.name : ''; const username = currentAccount && currentAccount.name ? currentAccount.name : '';
let isReply; let isReply;
let quickReplyText; let draftId;
let isEdit; let isEdit;
let post; let post;
let _draft; let _draft;
@ -133,12 +132,19 @@ class EditorContainer extends Component<any, any> {
} }
if (navigationParams.isReply) { if (navigationParams.isReply) {
({ isReply, quickReplyText } = navigationParams); ({ isReply } = navigationParams);
if(post){
draftId = `${currentAccount.name}/${post.author}/${post.permlink}`
}
this.setState({ this.setState({
isReply, isReply,
quickReplyText, draftId,
autoFocusText: true, autoFocusText: true,
}); });
if (draftId) {
this._getStorageDraft(username, isReply, { _id: draftId });
}
} }
if (navigationParams.isEdit) { if (navigationParams.isEdit) {
@ -158,7 +164,7 @@ class EditorContainer extends Component<any, any> {
} }
} }
if (!isEdit && !_draft && !hasSharedIntent) { if (!isEdit && !_draft && !draftId && !hasSharedIntent) {
this._fetchDraftsForComparison(isReply); this._fetchDraftsForComparison(isReply);
} }
this._requestKeyboardFocus(); this._requestKeyboardFocus();
@ -194,26 +200,31 @@ class EditorContainer extends Component<any, any> {
} }
_getStorageDraft = async (username, isReply, paramDraft) => { _getStorageDraft = async (username, isReply, paramDraft) => {
if (isReply) { const { drafts } = this.props;
const draftReply = await AsyncStorage.getItem('temp-reply');
if (draftReply) { if (isReply) {
const _draft = drafts.get(paramDraft._id);
if (_draft && _draft.body) {
this.setState({ this.setState({
draftPost: { draftPost: {
body: draftReply, body: _draft.body,
}, },
}); });
} }
} else { } else {
getDraftPost(username, paramDraft && paramDraft._id).then((result) => { //TOOD: get draft from redux after reply side is complete
//if result is return and param draft available, compare timestamp, use latest const _draftId = paramDraft ? paramDraft._id : DEFAULT_USER_DRAFT_ID + username;
const _localDraft = drafts.get(_draftId);
//if _draft is returned and param draft is available, compare timestamp, use latest
//if no draft, use result anayways //if no draft, use result anayways
if (result && (!paramDraft || paramDraft.timestamp < result.timestamp)) {
if (_localDraft && (!paramDraft || paramDraft.timestamp < _localDraft.updated)) {
this.setState({ this.setState({
draftPost: { draftPost: {
body: get(result, 'body', ''), body: get(_localDraft, 'body', ''),
title: get(result, 'title', ''), title: get(_localDraft, 'title', ''),
tags: get(result, 'tags', '').split(','), tags: get(_localDraft, 'tags', '').split(','),
isDraft: paramDraft ? true : false, isDraft: paramDraft ? true : false,
draftId: paramDraft ? paramDraft._id : null, draftId: paramDraft ? paramDraft._id : null,
}, },
@ -236,7 +247,7 @@ class EditorContainer extends Component<any, any> {
draftId: paramDraft._id, draftId: paramDraft._id,
}); });
} }
}); ;
} }
}; };
@ -251,14 +262,14 @@ class EditorContainer extends Component<any, any> {
}; };
/** /**
* this fucntion is run if editor is access used mid tab or reply section * this fucntion is run if editor is access fused mid tab or reply section
* it fetches fresh drafts and run some comparions to load one of following * it fetches fresh drafts and run some comparions to load one of following
* empty editor, load non-remote draft or most recent remote draft based on timestamps * empty editor, load non-remote draft or most recent remote draft based on timestamps
* prompts user as well * prompts user as well
* @param isReply * @param isReply
**/ **/
_fetchDraftsForComparison = async (isReply) => { _fetchDraftsForComparison = async (isReply) => {
const { currentAccount, isLoggedIn, intl, dispatch } = this.props; const { currentAccount, isLoggedIn, intl, dispatch, drafts } = this.props;
const username = get(currentAccount, 'name', ''); const username = get(currentAccount, 'name', '');
//initilizes editor with reply or non remote id less draft //initilizes editor with reply or non remote id less draft
@ -282,28 +293,29 @@ class EditorContainer extends Component<any, any> {
return; return;
} }
const drafts = await getDrafts(username); const remoteDrafts = await getDrafts(username);
const idLessDraft = await getDraftPost(username);
const idLessDraft = drafts.get(DEFAULT_USER_DRAFT_ID + username)
const loadRecentDraft = () => { const loadRecentDraft = () => {
//if no draft available means local draft is recent //if no draft available means local draft is recent
if (drafts.length == 0) { if (remoteDrafts.length == 0) {
_getStorageDraftGeneral(false); _getStorageDraftGeneral(false);
return; return;
} }
//sort darts based on timestamps //sort darts based on timestamps
drafts.sort((d1, d2) => remoteDrafts.sort((d1, d2) =>
new Date(d1.modified).getTime() < new Date(d2.modified).getTime() ? 1 : -1, new Date(d1.modified).getTime() < new Date(d2.modified).getTime() ? 1 : -1,
); );
const _draft = drafts[0]; const _draft = remoteDrafts[0];
//if unsaved local draft is more latest then remote draft, use that instead //if unsaved local draft is more latest then remote draft, use that instead
//if editor was opened from draft screens, this code will be skipped anyways. //if editor was opened from draft screens, this code will be skipped anyways.
if ( if (
idLessDraft && idLessDraft &&
(idLessDraft.title !== '' || idLessDraft.tags !== '' || idLessDraft.body !== '') && (idLessDraft.title !== '' || idLessDraft.tags !== '' || idLessDraft.body !== '') &&
new Date(_draft.modified).getTime() < idLessDraft.timestamp new Date(_draft.modified).getTime() < idLessDraft.updated
) { ) {
_getStorageDraftGeneral(false); _getStorageDraftGeneral(false);
return; return;
@ -317,7 +329,7 @@ class EditorContainer extends Component<any, any> {
this._getStorageDraft(username, isReply, _draft); this._getStorageDraft(username, isReply, _draft);
}; };
if (drafts.length > 0 || (idLessDraft && idLessDraft.timestamp > 0)) { if (remoteDrafts.length > 0 || (idLessDraft && idLessDraft.updated > 0)) {
this.setState({ this.setState({
onLoadDraftPress: loadRecentDraft, onLoadDraftPress: loadRecentDraft,
}); });
@ -523,9 +535,14 @@ class EditorContainer extends Component<any, any> {
}; };
_saveDraftToDB = async (fields, saveAsNew = false) => { _saveDraftToDB = async (fields, saveAsNew = false) => {
const { isDraftSaved, draftId, thumbIndex } = this.state; const { isDraftSaved, draftId, thumbIndex, isReply } = this.state;
const { currentAccount, dispatch, intl } = this.props; const { currentAccount, dispatch, intl } = this.props;
if (isReply) {
return;
}
const beneficiaries = this._extractBeneficiaries(); const beneficiaries = this._extractBeneficiaries();
try { try {
@ -574,16 +591,8 @@ class EditorContainer extends Component<any, any> {
//clear local copy if darft save is successful //clear local copy if darft save is successful
const username = get(currentAccount, 'name', ''); const username = get(currentAccount, 'name', '');
setDraftPost(
{ dispatch(deleteDraftCacheEntry(draftId || (DEFAULT_USER_DRAFT_ID + username)))
title: '',
body: '',
tags: '',
timestamp: 0,
},
username,
saveAsNew ? draftId : undefined
);
} }
@ -626,26 +635,28 @@ class EditorContainer extends Component<any, any> {
return; return;
} }
const { currentAccount } = this.props; const { currentAccount, dispatch } = this.props;
const username = currentAccount && currentAccount.name ? currentAccount.name : ''; const username = currentAccount && currentAccount.name ? currentAccount.name : '';
const draftField = { const draftField = {
...fields, title: fields.title,
body: fields.body,
tags: fields.tags && fields.tags.length > 0 ? fields.tags.toString() : '', tags: fields.tags && fields.tags.length > 0 ? fields.tags.toString() : '',
}; author: username,
}
//save reply data //save reply data
if (isReply && draftField.body !== null) { if (isReply && draftField.body !== null) {
await AsyncStorage.setItem('temp-reply', draftField.body); dispatch(updateDraftCache(draftId, draftField))
//save existing draft data locally //save existing draft data locally
} else if (draftId) { } else if (draftId) {
setDraftPost(draftField, username, draftId); dispatch(updateDraftCache(draftId, draftField))
} }
//update editor data locally //update editor data locally
else if (!isReply) { else if (!isReply) {
setDraftPost(draftField, username); dispatch(updateDraftCache(DEFAULT_USER_DRAFT_ID + username, draftField));
} }
}; };
@ -744,15 +755,7 @@ class EditorContainer extends Component<any, any> {
} }
//post publish updates //post publish updates
setDraftPost( dispatch(deleteDraftCacheEntry(DEFAULT_USER_DRAFT_ID + currentAccount.name))
{
title: '',
body: '',
tags: '',
timestamp: 0,
},
currentAccount.name,
);
dispatch(removeBeneficiaries(TEMP_BENEFICIARIES_ID)) dispatch(removeBeneficiaries(TEMP_BENEFICIARIES_ID))
if (draftId) { if (draftId) {
@ -1139,15 +1142,8 @@ class EditorContainer extends Component<any, any> {
}), }),
), ),
); );
setDraftPost(
{ dispatch(deleteDraftCacheEntry(DEFAULT_USER_DRAFT_ID + currentAccount.name))
title: '',
body: '',
tags: '',
timestamp: 0,
},
currentAccount.name,
);
setTimeout(() => { setTimeout(() => {
navigation.replace(ROUTES.SCREENS.DRAFTS, navigation.replace(ROUTES.SCREENS.DRAFTS,
@ -1167,17 +1163,10 @@ class EditorContainer extends Component<any, any> {
_initialEditor = () => { _initialEditor = () => {
const { const {
currentAccount: { name }, currentAccount: { name },
dispatch
} = this.props; } = this.props;
setDraftPost( dispatch(deleteDraftCacheEntry(DEFAULT_USER_DRAFT_ID + name))
{
title: '',
body: '',
tags: '',
timestamp: 0,
},
name,
);
this.setState({ this.setState({
uploadedImage: null, uploadedImage: null,
@ -1271,7 +1260,8 @@ const mapStateToProps = (state) => ({
isDefaultFooter: state.account.isDefaultFooter, isDefaultFooter: state.account.isDefaultFooter,
isLoggedIn: state.application.isLoggedIn, isLoggedIn: state.application.isLoggedIn,
pinCode: state.application.pin, pinCode: state.application.pin,
beneficiariesMap: state.editor.beneficiariesMap beneficiariesMap: state.editor.beneficiariesMap,
drafts: state.cache.drafts,
}); });
export default connect(mapStateToProps)(injectIntl(EditorContainer)); export default connect(mapStateToProps)(injectIntl(EditorContainer));

View File

@ -39,7 +39,6 @@ class EditorScreen extends Component {
* ------------------------------------------------ * ------------------------------------------------
* @prop { type } name - Description.... * @prop { type } name - Description....
*/ */
thumbSelectionModalRef = null;
postOptionsModalRef = null; postOptionsModalRef = null;
constructor(props) { constructor(props) {
@ -206,12 +205,6 @@ class EditorScreen extends Component {
} }
}; };
_showThumbSelectionModal = () => {
const { fields } = this.state;
if (this.thumbSelectionModalRef) {
this.thumbSelectionModalRef.show(fields.body);
}
};
_handleScheduleChange = (datetime:string|null) => { _handleScheduleChange = (datetime:string|null) => {
this.setState({ this.setState({
@ -367,7 +360,6 @@ class EditorScreen extends Component {
isLoggedIn, isLoggedIn,
isPostSending, isPostSending,
isReply, isReply,
quickReplyText,
isUploading, isUploading,
post, post,
uploadedImage, uploadedImage,
@ -406,8 +398,8 @@ class EditorScreen extends Component {
</Modal> </Modal>
); );
}; };
console.log('fields :', fields);
console.log('quickReplyText : ', quickReplyText);
return ( return (
<View style={globalStyles.defaultContainer}> <View style={globalStyles.defaultContainer}>
@ -430,11 +422,11 @@ console.log('quickReplyText : ', quickReplyText);
isReply={isReply} isReply={isReply}
quickTitle={wordsCount > 0 && `${wordsCount} words`} quickTitle={wordsCount > 0 && `${wordsCount} words`}
rightButtonText={rightButtonText} rightButtonText={rightButtonText}
showThumbSelectionModal={this._showThumbSelectionModal}
handleSettingsPress={this._handleSettingsPress} handleSettingsPress={this._handleSettingsPress}
/> />
<PostForm <PostForm
handleFormUpdate={this._handleFormUpdate} handleFormUpdate={this._handleFormUpdate}
handleBodyChange={this._setWordsCount}
handleOnSubmit={this._handleOnSubmit} handleOnSubmit={this._handleOnSubmit}
isFormValid={isFormValid} isFormValid={isFormValid}
isPreviewActive={isPreviewActive} isPreviewActive={isPreviewActive}
@ -451,10 +443,7 @@ console.log('quickReplyText : ', quickReplyText);
)} )}
<MarkdownEditor <MarkdownEditor
componentID="body" componentID="body"
draftBody={isReply ? quickReplyText : fields && fields.body} draftBody={fields && fields.body}
handleOnTextChange={this._setWordsCount}
handleFormUpdate={this._handleFormUpdate}
handleIsFormValid={this._handleIsFormValid}
isFormValid={isFormValid} isFormValid={isFormValid}
handleOpenImagePicker={handleOnImagePicker} handleOpenImagePicker={handleOnImagePicker}
intl={intl} intl={intl}
@ -476,12 +465,9 @@ console.log('quickReplyText : ', quickReplyText);
uploadProgress={uploadProgress} uploadProgress={uploadProgress}
/> />
</PostForm> </PostForm>
{_renderCommunityModal()} {_renderCommunityModal()}
<ThumbSelectionModal
ref={(componentRef) => (this.thumbSelectionModalRef = componentRef)}
thumbIndex={thumbIndex}
onThumbSelection={this._handleOnThumbSelection}
/>
<PostOptionsModal <PostOptionsModal
ref={(componentRef) => (this.postOptionsModalRef = componentRef)} ref={(componentRef) => (this.postOptionsModalRef = componentRef)}
body={fields.body} body={fields.body}

View File

@ -45,25 +45,40 @@ export const generatePermlink = (title, random = false) => {
}; };
export const extractWordAtIndex = (text:string, index:number) => { export const extractWordAtIndex = (text:string, index:number) => {
const RANGE = 50;
const _start = index - RANGE;
const _end = index + RANGE;
const _length = text.length;
const textChunk = text.substring(_start > 0 ? _start : 0, _end < _length ? _end : _length);
const indexChunk = index < 50 ? index : (
_length - index < 50 ? textChunk.length - (_length - index) :
RANGE
);
console.log('char at index: ', textChunk[indexChunk]);
const END_REGEX = /[\s,]/ const END_REGEX = /[\s,]/
let word = ''; let word = '';
for(let i = index; i >= 0 && (!END_REGEX.test(text[i]) || i === index); i--){ for(let i = indexChunk; i >= 0 && (!END_REGEX.test(textChunk[i]) || i === indexChunk); i--){
if(text[i]){ if(textChunk[i]){
word += text[i]; word += textChunk[i];
} }
} }
word = word.split('').reverse().join(''); word = word.split('').reverse().join('');
if(!END_REGEX.test(text[index])){ if(!END_REGEX.test(textChunk[indexChunk])){
for(let i = index + 1; i < text.length && !END_REGEX.test(text[i]); i++){ for(let i = indexChunk + 1; i < textChunk.length && !END_REGEX.test(textChunk[i]); i++){
if(text[i]){ if(textChunk[i]){
word += text[i]; word += textChunk[i];
} }
} }
} }
return word; return word;
} }
export const generateReplyPermlink = (toAuthor) => { export const generateReplyPermlink = (toAuthor) => {