mirror of
https://github.com/ecency/ecency-mobile.git
synced 2024-12-19 03:11:38 +03:00
Merge branch 'development' of https://github.com/ecency/ecency-mobile into sa/IAP-QR
This commit is contained in:
commit
aaad438287
@ -1,15 +1,12 @@
|
||||
import React, { useState, Fragment, useRef } from 'react';
|
||||
import { View, Text, ActivityIndicator, SafeAreaView } from 'react-native';
|
||||
import { injectIntl } from 'react-intl';
|
||||
import { useSelector } from 'react-redux';
|
||||
import moment from 'moment';
|
||||
|
||||
// Components
|
||||
import { TextButton, Modal, BeneficiaryModal } from '../..';
|
||||
import { TextButton } from '../..';
|
||||
import { IconButton } from '../../iconButton';
|
||||
import { DropdownButton } from '../../dropdownButton';
|
||||
import { TextInput } from '../../textInput';
|
||||
import { DateTimePicker } from '../../dateTimePicker';
|
||||
|
||||
// Constants
|
||||
// Styles
|
||||
@ -28,7 +25,6 @@ const BasicHeaderView = ({
|
||||
intl,
|
||||
isDraftSaved,
|
||||
isDraftSaving,
|
||||
draftId,
|
||||
isFormValid,
|
||||
isHasDropdown,
|
||||
isHasIcons,
|
||||
@ -46,23 +42,14 @@ const BasicHeaderView = ({
|
||||
title,
|
||||
handleOnSubmit,
|
||||
handleOnSearch,
|
||||
handleDatePickerChange,
|
||||
handleRewardChange,
|
||||
handleBeneficiaries,
|
||||
enableViewModeToggle,
|
||||
handleSettingsPress,
|
||||
showThumbSelectionModal,
|
||||
}) => {
|
||||
|
||||
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 scheduleRef = useRef(null);
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
@ -90,27 +77,6 @@ const BasicHeaderView = ({
|
||||
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) => {
|
||||
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>
|
||||
)}
|
||||
</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
|
||||
ref={rewardMenuRef}
|
||||
options={[
|
||||
@ -354,6 +246,7 @@ const BasicHeaderView = ({
|
||||
title="Reward"
|
||||
onPress={_handleRewardMenuSelect}
|
||||
/>
|
||||
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
@ -53,9 +53,13 @@ import { walkthrough } from '../../../redux/constants/walkthroughConstants';
|
||||
|
||||
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 = ({
|
||||
draftBody,
|
||||
handleIsFormValid,
|
||||
handleOpenImagePicker,
|
||||
intl,
|
||||
isPreviewActive,
|
||||
@ -64,9 +68,6 @@ const MarkdownEditorView = ({
|
||||
isUploading,
|
||||
initialFields,
|
||||
onChange,
|
||||
handleOnTextChange,
|
||||
handleIsValid,
|
||||
componentID,
|
||||
uploadedImage,
|
||||
isEdit,
|
||||
post,
|
||||
@ -82,8 +83,6 @@ const MarkdownEditorView = ({
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [text, setText] = useState(draftBody || '');
|
||||
const [selection, setSelection] = useState({ start: 0, end: 0 });
|
||||
const [editable, setEditable] = useState(true);
|
||||
const [bodyInputHeight, setBodyInputHeight] = useState(MIN_BODY_INPUT_HEIGHT);
|
||||
const [isSnippetsOpen, setIsSnippetsOpen] = useState(false);
|
||||
@ -103,9 +102,14 @@ const MarkdownEditorView = ({
|
||||
const draftBtnTooltipRegistered = draftBtnTooltipState.get(walkthrough.EDITOR_DRAFT_BTN);
|
||||
const headerText = post && (post.summary || postBodySummary(post, 150, Platform.OS));
|
||||
|
||||
useEffect(() => {
|
||||
bodyText = '';
|
||||
bodySelection = {start:0, end:0};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isPreviewActive) {
|
||||
_setTextAndSelection({ selection: { start: 0, end: 0 }, text });
|
||||
_setTextAndSelection({ selection: { start: 0, end: 0 }, text: bodyText });
|
||||
}
|
||||
}, [isPreviewActive]);
|
||||
|
||||
@ -121,7 +125,7 @@ const MarkdownEditorView = ({
|
||||
}, [onLoadDraftPress]);
|
||||
|
||||
useEffect(() => {
|
||||
if (text === '' && draftBody !== '') {
|
||||
if (bodyText === '' && draftBody !== '') {
|
||||
let draftBodyLength = draftBody.length;
|
||||
_setTextAndSelection({
|
||||
selection: { start: draftBodyLength, end: draftBodyLength },
|
||||
@ -165,8 +169,8 @@ const MarkdownEditorView = ({
|
||||
useEffect(() => {
|
||||
if (uploadedImage && uploadedImage.shouldInsert && !isUploading) {
|
||||
applyMediaLink({
|
||||
text,
|
||||
selection,
|
||||
text: bodyText,
|
||||
selection: bodySelection,
|
||||
setTextAndSelection: _setTextAndSelection,
|
||||
items: [{ url: uploadedImage.url, text: uploadedImage.hash }],
|
||||
});
|
||||
@ -178,7 +182,7 @@ const MarkdownEditorView = ({
|
||||
}, [uploadedImage, isUploading]);
|
||||
|
||||
useEffect(() => {
|
||||
setText(draftBody);
|
||||
bodyText = draftBody;
|
||||
}, [draftBody]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -190,17 +194,7 @@ const MarkdownEditorView = ({
|
||||
}
|
||||
}, [autoFocusText]);
|
||||
|
||||
useEffect(() => {
|
||||
const nextText = text.replace(text, '');
|
||||
|
||||
if (nextText && nextText.length > 0) {
|
||||
_changeText(text);
|
||||
|
||||
if (handleIsFormValid) {
|
||||
handleIsFormValid(text);
|
||||
}
|
||||
}
|
||||
}, [text]);
|
||||
|
||||
const changeUser = async () => {
|
||||
dispatch(toggleAccountsBottomSheet(!isVisibleAccountsBottomSheet));
|
||||
@ -208,8 +202,8 @@ const MarkdownEditorView = ({
|
||||
|
||||
const _onApplyUsername = (username) => {
|
||||
applyUsername({
|
||||
text,
|
||||
selection,
|
||||
text: bodyText,
|
||||
selection: bodySelection,
|
||||
setTextAndSelection: _setTextAndSelection,
|
||||
username,
|
||||
});
|
||||
@ -217,23 +211,17 @@ const MarkdownEditorView = ({
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
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) {
|
||||
onChange(input);
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (handleIsValid) {
|
||||
handleIsValid(componentID, !!(input && input.length));
|
||||
}
|
||||
|
||||
if (handleOnTextChange) {
|
||||
handleOnTextChange(input);
|
||||
}
|
||||
});
|
||||
|
||||
const _handleOnSelectionChange = async (event) => {
|
||||
setSelection(event.nativeEvent.selection);
|
||||
bodySelection = event.nativeEvent.selection;
|
||||
};
|
||||
|
||||
const _handleOnContentSizeChange = async (event) => {
|
||||
@ -243,35 +231,38 @@ const MarkdownEditorView = ({
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const _setTextAndSelection = useCallback(({ selection: _selection, text: _text }) => {
|
||||
console.log('_text : ', _text);
|
||||
// console.log('_text : ', _text);
|
||||
inputRef.current.setNativeProps({
|
||||
text: _text,
|
||||
});
|
||||
|
||||
// Workaround for iOS selection update issue
|
||||
const isIos = Platform.OS === 'ios';
|
||||
if (isIos) {
|
||||
setTimeout(() => {
|
||||
inputRef.current.setNativeProps({
|
||||
selection: _selection,
|
||||
});
|
||||
setSelection(_selection);
|
||||
}, 100);
|
||||
} else {
|
||||
const _updateSelection = () => {
|
||||
bodySelection = _selection
|
||||
inputRef.current.setNativeProps({
|
||||
selection: _selection,
|
||||
});
|
||||
setSelection(_selection);
|
||||
}
|
||||
setIsSnippetsOpen(false);
|
||||
_changeText(_text);
|
||||
});
|
||||
|
||||
console.log('text : ', text);
|
||||
// Workaround for iOS selection update issue
|
||||
if (Platform.OS === 'ios') {
|
||||
setTimeout(() => {
|
||||
_updateSelection();
|
||||
}, 100);
|
||||
} else {
|
||||
_updateSelection()
|
||||
}
|
||||
|
||||
if (isSnippetsOpen) {
|
||||
setIsSnippetsOpen(false);
|
||||
}
|
||||
|
||||
_changeText(_text);
|
||||
}, []);
|
||||
|
||||
const _renderPreview = () => (
|
||||
<ScrollView style={styles.previewContainer}>
|
||||
{text ? (
|
||||
<PostBody body={renderPostBody(text, true, Platform.OS === 'ios' ? false : true)} />
|
||||
{bodyText ? (
|
||||
<PostBody body={renderPostBody(bodyText, true, Platform.OS === 'ios' ? false : true)} />
|
||||
) : (
|
||||
<Text>...</Text>
|
||||
)}
|
||||
@ -280,8 +271,8 @@ const MarkdownEditorView = ({
|
||||
|
||||
const _handleOnSnippetReceived = (snippetText) => {
|
||||
applySnippet({
|
||||
text,
|
||||
selection,
|
||||
text: bodyText,
|
||||
selection: bodySelection,
|
||||
setTextAndSelection: _setTextAndSelection,
|
||||
snippetText: `\n${snippetText}\n`,
|
||||
});
|
||||
@ -295,8 +286,8 @@ const MarkdownEditorView = ({
|
||||
|
||||
if (items.length) {
|
||||
applyMediaLink({
|
||||
text,
|
||||
selection,
|
||||
text: bodyText,
|
||||
selection: bodySelection,
|
||||
setTextAndSelection: _setTextAndSelection,
|
||||
items,
|
||||
});
|
||||
@ -305,8 +296,8 @@ const MarkdownEditorView = ({
|
||||
|
||||
const _handleOnAddLinkPress = () => {
|
||||
insertLinkModalRef.current?.showModal({
|
||||
selectedText: text.slice(selection.start, selection.end),
|
||||
selection: selection,
|
||||
selectedText: bodyText.slice(bodySelection.start, bodySelection.end),
|
||||
selection: bodySelection,
|
||||
});
|
||||
inputRef.current?.blur();
|
||||
};
|
||||
@ -315,7 +306,7 @@ const MarkdownEditorView = ({
|
||||
};
|
||||
const _handleInsertLink = ({ snippetText, selection }) => {
|
||||
applySnippet({
|
||||
text,
|
||||
text: bodyText,
|
||||
selection,
|
||||
setTextAndSelection: _setTextAndSelection,
|
||||
snippetText,
|
||||
@ -332,7 +323,12 @@ const MarkdownEditorView = ({
|
||||
iconType={item.iconType}
|
||||
name={item.icon}
|
||||
onPress={() =>
|
||||
item.onPress({ text, selection, setTextAndSelection: _setTextAndSelection, item })
|
||||
item.onPress({
|
||||
text: bodyText,
|
||||
selection: bodySelection,
|
||||
setTextAndSelection: _setTextAndSelection,
|
||||
item
|
||||
})
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
@ -427,7 +423,7 @@ const MarkdownEditorView = ({
|
||||
const _handleClear = (index) => {
|
||||
if (index === 0) {
|
||||
initialFields();
|
||||
setText('');
|
||||
|
||||
_setTextAndSelection({ text: '', selection: { start: 0, end: 0 } });
|
||||
}
|
||||
};
|
||||
@ -513,7 +509,7 @@ const MarkdownEditorView = ({
|
||||
const _innerContent = (
|
||||
<>
|
||||
{isAndroidOreo() ? _renderEditorWithoutScroll() : _renderEditorWithScroll()}
|
||||
<UsernameAutofillBar text={text} selection={selection} onApplyUsername={_onApplyUsername} />
|
||||
<UsernameAutofillBar text={bodyText} selection={bodySelection} onApplyUsername={_onApplyUsername} />
|
||||
{_renderFloatingDraftButton()}
|
||||
{!isPreviewActive && _renderEditorButtons()}
|
||||
</>
|
@ -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 { UserAvatar } from '../..';
|
||||
import { lookupAccounts } from '../../../providers/hive/dhive';
|
||||
@ -22,21 +22,25 @@ export const UsernameAutofillBar = ({text, selection, onApplyUsername}:Props) =>
|
||||
|
||||
useEffect(() => {
|
||||
if (selection.start === selection.end && text) {
|
||||
const word = extractWordAtIndex(text, selection.start);
|
||||
console.log('selection word is: ', word);
|
||||
if (word.startsWith('@') && word.length > 3) {
|
||||
_handleUserSearch(word.substring(1));
|
||||
} else {
|
||||
setSearchedUsers([]);
|
||||
setQuery('')
|
||||
_handleUserSearch.cancel();
|
||||
}
|
||||
_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);
|
||||
if (word.startsWith('@') && word.length > 1) {
|
||||
_handleUserSearch(word.substring(1));
|
||||
} else {
|
||||
setSearchedUsers([]);
|
||||
setQuery('')
|
||||
_handleUserSearch.cancel();
|
||||
}
|
||||
}, 300, {leading:true}),[]);
|
||||
|
||||
const _handleUserSearch = debounce(async (username) => {
|
||||
|
||||
const _handleUserSearch = useCallback(debounce(async (username) => {
|
||||
if(query !== username){
|
||||
let users = [];
|
||||
if (username) {
|
||||
@ -47,7 +51,7 @@ export const UsernameAutofillBar = ({text, selection, onApplyUsername}:Props) =>
|
||||
setSearchedUsers(users);
|
||||
}
|
||||
|
||||
}, 200, {leading:true});
|
||||
}, 200, {leading:true}), []);
|
||||
|
||||
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { PureComponent, Fragment } from 'react';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
class PostFormView extends PureComponent {
|
||||
constructor(props) {
|
||||
@ -18,9 +19,12 @@ class PostFormView extends PureComponent {
|
||||
};
|
||||
|
||||
_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);
|
||||
if (componentID === 'body') {
|
||||
handleBodyChange(value);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
@ -33,7 +37,10 @@ class PostFormView extends PureComponent {
|
||||
return React.cloneElement(child, {
|
||||
onSubmitEditing: (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',
|
||||
isPreviewActive,
|
||||
});
|
||||
|
@ -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 styles from './quickReplyModalStyles';
|
||||
import { View, Text, Alert, TouchableOpacity, Keyboard, Platform } from 'react-native';
|
||||
@ -14,11 +14,12 @@ import {
|
||||
updateDraftCache,
|
||||
} from '../../redux/actions/cacheActions';
|
||||
import { default as ROUTES } from '../../constants/routeNames';
|
||||
import get from 'lodash/get';
|
||||
import {get, debounce} from 'lodash';
|
||||
import { navigate } from '../../navigation/service';
|
||||
import { postBodySummary } from '@ecency/render-helper';
|
||||
import { Draft } from '../../redux/reducers/cacheReducer';
|
||||
import { RootState } from '../../redux/store/store';
|
||||
import comment from '../../constants/options/comment';
|
||||
|
||||
export interface QuickReplyModalContentProps {
|
||||
fetchPost?: any;
|
||||
@ -43,7 +44,7 @@ export const QuickReplyModalContent = ({
|
||||
|
||||
const [commentValue, setCommentValue] = useState('');
|
||||
const [isSending, setIsSending] = useState(false);
|
||||
const [quickCommentDraft, setQuickCommentDraft] = useState<Draft>(null);
|
||||
|
||||
|
||||
const headerText =
|
||||
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) {
|
||||
const quickComment: Draft = drafts.get(draftId);
|
||||
setCommentValue(quickComment.body);
|
||||
setQuickCommentDraft(quickComment);
|
||||
} else {
|
||||
setCommentValue('');
|
||||
}
|
||||
@ -73,25 +73,24 @@ export const QuickReplyModalContent = ({
|
||||
};
|
||||
|
||||
// add quick comment value into cache
|
||||
const _addQuickCommentIntoCache = () => {
|
||||
const date = new Date();
|
||||
const updatedStamp = date.toISOString().substring(0, 19);
|
||||
const _addQuickCommentIntoCache = (value = commentValue) => {
|
||||
|
||||
const quickCommentDraftData: Draft = {
|
||||
author: currentAccount.name,
|
||||
body: commentValue,
|
||||
created: quickCommentDraft ? quickCommentDraft.created : updatedStamp,
|
||||
updated: updatedStamp,
|
||||
expiresAt: date.getTime() + 604800000, // 7 days expiry time
|
||||
body: value
|
||||
};
|
||||
|
||||
//add quick comment cache entry
|
||||
dispatch(updateDraftCache(draftId, quickCommentDraftData));
|
||||
};
|
||||
|
||||
|
||||
// handle close press
|
||||
const _handleClosePress = () => {
|
||||
sheetModalRef.current?.setModalVisible(false);
|
||||
};
|
||||
|
||||
|
||||
// navigate to post on summary press
|
||||
const _handleOnSummaryPress = () => {
|
||||
Keyboard.dismiss();
|
||||
@ -105,6 +104,7 @@ export const QuickReplyModalContent = ({
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// handle submit reply
|
||||
const _submitReply = async () => {
|
||||
let stateTimer;
|
||||
@ -207,12 +207,22 @@ export const QuickReplyModalContent = ({
|
||||
params: {
|
||||
isReply: true,
|
||||
post: selectedPost,
|
||||
quickReplyText: commentValue,
|
||||
fetchPost,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const _deboucedCacheUpdate = useCallback(debounce(_addQuickCommentIntoCache, 500),[])
|
||||
|
||||
const _onChangeText = (value) => {
|
||||
setCommentValue(value);
|
||||
_deboucedCacheUpdate(value)
|
||||
}
|
||||
|
||||
|
||||
|
||||
//VIEW_RENDERERS
|
||||
|
||||
const _renderSheetHeader = () => (
|
||||
@ -284,9 +294,7 @@ export const QuickReplyModalContent = ({
|
||||
<View style={styles.inputContainer}>
|
||||
<TextInput
|
||||
innerRef={inputRef}
|
||||
onChangeText={(value) => {
|
||||
setCommentValue(value);
|
||||
}}
|
||||
onChangeText={_onChangeText}
|
||||
value={commentValue}
|
||||
// autoFocus
|
||||
placeholder={intl.formatMessage({
|
||||
|
@ -69,6 +69,7 @@ export const UploadsGalleryModal = forwardRef(({
|
||||
}, [uploadedImage])
|
||||
|
||||
|
||||
|
||||
//save image to user gallery
|
||||
const _addUploadedImageToGallery = async () => {
|
||||
try {
|
||||
@ -138,6 +139,8 @@ export const UploadsGalleryModal = forwardRef(({
|
||||
setShowModal(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//renders footer with add snipept button and shows new snippet modal
|
||||
const _renderFloatingPanel = () => {
|
||||
|
||||
@ -242,7 +245,7 @@ export const UploadsGalleryModal = forwardRef(({
|
||||
};
|
||||
|
||||
|
||||
const _renderHeaderContent = (
|
||||
const _renderHeaderContent = () => (
|
||||
<>
|
||||
{isUploading && <ProgressBar progress={uploadProgress} />}
|
||||
</>
|
||||
@ -251,30 +254,33 @@ export const UploadsGalleryModal = forwardRef(({
|
||||
|
||||
|
||||
|
||||
const _renderContent = (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.bodyWrapper}>
|
||||
|
||||
{_renderHeaderContent}
|
||||
<FlatList
|
||||
data={mediaUploads}
|
||||
keyExtractor={(item) => `item_${item.url}`}
|
||||
renderItem={_renderItem}
|
||||
ListEmptyComponent={_renderEmptyContent}
|
||||
ListFooterComponent={<View style={styles.listEmptyFooter} />}
|
||||
extraData={indices}
|
||||
numColumns={2}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={isLoading}
|
||||
onRefresh={_getMediaUploads}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
const _renderContent = () => {
|
||||
console.log("Rendering uploaded images")
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.bodyWrapper}>
|
||||
|
||||
{_renderHeaderContent()}
|
||||
<FlatList
|
||||
data={mediaUploads}
|
||||
keyExtractor={(item) => `item_${item.url}`}
|
||||
renderItem={_renderItem}
|
||||
ListEmptyComponent={_renderEmptyContent}
|
||||
ListFooterComponent={<View style={styles.listEmptyFooter} />}
|
||||
extraData={indices}
|
||||
numColumns={2}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={isLoading}
|
||||
onRefresh={_getMediaUploads}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
{_renderFloatingPanel()}
|
||||
</View>
|
||||
{_renderFloatingPanel()}
|
||||
</View>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
@ -290,7 +296,7 @@ export const UploadsGalleryModal = forwardRef(({
|
||||
animationType="slide"
|
||||
style={styles.modalStyle}
|
||||
>
|
||||
{_renderContent}
|
||||
{showModal && _renderContent()}
|
||||
</Modal>
|
||||
|
||||
);
|
||||
|
@ -2,93 +2,92 @@ import { renderPostBody } from '@ecency/render-helper';
|
||||
import { Platform } from 'react-native';
|
||||
import { makeJsonMetadataReply } from '../../utils/editor';
|
||||
import {
|
||||
UPDATE_VOTE_CACHE,
|
||||
PURGE_EXPIRED_CACHE,
|
||||
UPDATE_COMMENT_CACHE,
|
||||
DELETE_COMMENT_CACHE_ENTRY,
|
||||
UPDATE_DRAFT_CACHE,
|
||||
DELETE_DRAFT_CACHE_ENTRY,
|
||||
} from '../constants/constants';
|
||||
UPDATE_VOTE_CACHE,
|
||||
PURGE_EXPIRED_CACHE,
|
||||
UPDATE_COMMENT_CACHE,
|
||||
DELETE_COMMENT_CACHE_ENTRY,
|
||||
UPDATE_DRAFT_CACHE,
|
||||
DELETE_DRAFT_CACHE_ENTRY,
|
||||
} from '../constants/constants';
|
||||
import { Comment, Draft, Vote } from '../reducers/cacheReducer';
|
||||
|
||||
|
||||
|
||||
|
||||
export const updateVoteCache = (postPath:string, vote:Vote) => ({
|
||||
payload:{
|
||||
postPath,
|
||||
vote
|
||||
},
|
||||
type: UPDATE_VOTE_CACHE
|
||||
})
|
||||
|
||||
|
||||
interface CommentCacheOptions {
|
||||
isUpdate?:boolean;
|
||||
parentTags?:Array<string>;
|
||||
}
|
||||
export const updateVoteCache = (postPath: string, vote: Vote) => ({
|
||||
payload: {
|
||||
postPath,
|
||||
vote
|
||||
},
|
||||
type: UPDATE_VOTE_CACHE
|
||||
})
|
||||
|
||||
export const updateCommentCache = (commentPath:string, comment:Comment, options:CommentCacheOptions = {isUpdate:false}) => {
|
||||
|
||||
console.log("body received:", comment.markdownBody);
|
||||
const updated = new Date();
|
||||
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
|
||||
|
||||
if(options.isUpdate && !comment.created){
|
||||
throw new Error("For comment update, created prop must be provided from original comment data to update local cache");
|
||||
}
|
||||
interface CommentCacheOptions {
|
||||
isUpdate?: boolean;
|
||||
parentTags?: Array<string>;
|
||||
}
|
||||
|
||||
if(!options.parentTags && !comment.json_metadata){
|
||||
throw new Error("either of json_metadata in comment data or parentTags in options must be provided");
|
||||
}
|
||||
export const updateCommentCache = (commentPath: string, comment: Comment, options: CommentCacheOptions = { isUpdate: false }) => {
|
||||
|
||||
comment.created = comment.created || updatedStamp; //created will be set only once for new comment;
|
||||
comment.updated = comment.updated || updatedStamp;
|
||||
comment.expiresAt = comment.expiresAt || updated.getTime() + 6000000;//600000;
|
||||
comment.active_votes = comment.active_votes || [];
|
||||
comment.net_rshares = comment.net_rshares || 0;
|
||||
comment.author_reputation = comment.author_reputation || 25;
|
||||
comment.total_payout = comment.total_payout || 0;
|
||||
comment.json_metadata = comment.json_metadata || makeJsonMetadataReply(options.parentTags)
|
||||
comment.isDeletable = comment.isDeletable || true;
|
||||
console.log("body received:", comment.markdownBody);
|
||||
const updated = new Date();
|
||||
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
|
||||
|
||||
comment.body = renderPostBody({
|
||||
author:comment.author,
|
||||
permlink:comment.permlink,
|
||||
last_update:comment.updated,
|
||||
body:comment.markdownBody,
|
||||
}, true, Platform.OS === 'android');
|
||||
if (options.isUpdate && !comment.created) {
|
||||
throw new Error("For comment update, created prop must be provided from original comment data to update local cache");
|
||||
}
|
||||
|
||||
return ({
|
||||
payload:{
|
||||
commentPath,
|
||||
comment
|
||||
},
|
||||
type: UPDATE_COMMENT_CACHE
|
||||
})
|
||||
}
|
||||
if (!options.parentTags && !comment.json_metadata) {
|
||||
throw new Error("either of json_metadata in comment data or parentTags in options must be provided");
|
||||
}
|
||||
|
||||
export const deleteCommentCacheEntry = (commentPath:string) => ({
|
||||
payload:commentPath,
|
||||
type: DELETE_COMMENT_CACHE_ENTRY
|
||||
})
|
||||
|
||||
export const updateDraftCache = (id:string, draft:Draft) => ({
|
||||
payload:{
|
||||
id,
|
||||
draft
|
||||
comment.created = comment.created || updatedStamp; //created will be set only once for new comment;
|
||||
comment.updated = comment.updated || updatedStamp;
|
||||
comment.expiresAt = comment.expiresAt || updated.getTime() + 6000000;//600000;
|
||||
comment.active_votes = comment.active_votes || [];
|
||||
comment.net_rshares = comment.net_rshares || 0;
|
||||
comment.author_reputation = comment.author_reputation || 25;
|
||||
comment.total_payout = comment.total_payout || 0;
|
||||
comment.json_metadata = comment.json_metadata || makeJsonMetadataReply(options.parentTags)
|
||||
comment.isDeletable = comment.isDeletable || true;
|
||||
|
||||
comment.body = renderPostBody({
|
||||
author: comment.author,
|
||||
permlink: comment.permlink,
|
||||
last_update: comment.updated,
|
||||
body: comment.markdownBody,
|
||||
}, true, Platform.OS === 'android');
|
||||
|
||||
return ({
|
||||
payload: {
|
||||
commentPath,
|
||||
comment
|
||||
},
|
||||
type: UPDATE_DRAFT_CACHE
|
||||
type: UPDATE_COMMENT_CACHE
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteDraftCacheEntry = (id:string) => ({
|
||||
payload:id,
|
||||
type: DELETE_DRAFT_CACHE_ENTRY
|
||||
})
|
||||
export const deleteCommentCacheEntry = (commentPath: string) => ({
|
||||
payload: commentPath,
|
||||
type: DELETE_COMMENT_CACHE_ENTRY
|
||||
})
|
||||
|
||||
export const updateDraftCache = (id: string, draft: Draft) => ({
|
||||
payload: {
|
||||
id,
|
||||
draft
|
||||
},
|
||||
type: UPDATE_DRAFT_CACHE
|
||||
})
|
||||
|
||||
export const deleteDraftCacheEntry = (id: string) => ({
|
||||
payload: id,
|
||||
type: DELETE_DRAFT_CACHE_ENTRY
|
||||
})
|
||||
|
||||
export const purgeExpiredCache = () => ({
|
||||
type: PURGE_EXPIRED_CACHE
|
||||
})
|
||||
|
||||
export const purgeExpiredCache = () => ({
|
||||
type: PURGE_EXPIRED_CACHE
|
||||
})
|
||||
|
||||
|
@ -113,6 +113,7 @@ export const UPDATE_COMMENT_CACHE = 'UPDATE_COMMENT_CACHE';
|
||||
export const DELETE_COMMENT_CACHE_ENTRY = 'DELETE_COMMENT_CACHE_ENTRY';
|
||||
export const UPDATE_DRAFT_CACHE = 'UPDATE_DRAFT_CACHE';
|
||||
export const DELETE_DRAFT_CACHE_ENTRY = 'DELETE_DRAFT_CACHE_ENTRY';
|
||||
export const DEFAULT_USER_DRAFT_ID = 'DEFAULT_USER_DRAFT_ID_';
|
||||
|
||||
// TOOLTIPS
|
||||
export const REGISTER_TOOLTIP = 'REGISTER_TOOLTIP';
|
||||
|
@ -28,10 +28,12 @@ export interface Comment {
|
||||
|
||||
export interface Draft {
|
||||
author: string,
|
||||
body?:string,
|
||||
created?:string,
|
||||
updated?:string,
|
||||
expiresAt:number;
|
||||
body:string,
|
||||
title?:string,
|
||||
tags?:string,
|
||||
created?:number,
|
||||
updated?:number,
|
||||
expiresAt?:number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
@ -93,7 +95,16 @@ const initialState:State = {
|
||||
if(!state.drafts){
|
||||
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 {
|
||||
...state, //spread operator in requried here, otherwise persist do not register change
|
||||
lastUpdate: {
|
||||
|
@ -114,7 +114,7 @@ const PostOptionsModal = forwardRef(({
|
||||
handleThumbSelection(index)
|
||||
}
|
||||
|
||||
const _renderContent = (
|
||||
const _renderContent = () => (
|
||||
<View style={styles.fillSpace}>
|
||||
<KeyboardAwareScrollView style={styles.fillSpace} >
|
||||
<View style={styles.container}>
|
||||
@ -214,7 +214,7 @@ const PostOptionsModal = forwardRef(({
|
||||
animationType="slide"
|
||||
style={styles.modalStyle}
|
||||
>
|
||||
{_renderContent}
|
||||
{_renderContent()}
|
||||
</Modal>
|
||||
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
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 get from 'lodash/get';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
@ -25,7 +25,6 @@ import {
|
||||
reblog,
|
||||
postComment,
|
||||
} from '../../../providers/hive/dhive';
|
||||
import { setDraftPost, getDraftPost } from '../../../realm/realm';
|
||||
|
||||
// Constants
|
||||
import { default as ROUTES } from '../../../constants/routeNames';
|
||||
@ -45,8 +44,8 @@ import {
|
||||
import EditorScreen from '../screen/editorScreen';
|
||||
import bugsnapInstance from '../../../config/bugsnag';
|
||||
import { removeBeneficiaries, setBeneficiaries } from '../../../redux/actions/editorActions';
|
||||
import { TEMP_BENEFICIARIES_ID } from '../../../redux/constants/constants';
|
||||
import { updateCommentCache } from '../../../redux/actions/cacheActions';
|
||||
import { DEFAULT_USER_DRAFT_ID, TEMP_BENEFICIARIES_ID } from '../../../redux/constants/constants';
|
||||
import { deleteDraftCacheEntry, updateCommentCache, updateDraftCache } from '../../../redux/actions/cacheActions';
|
||||
|
||||
/*
|
||||
* Props Name Description Value
|
||||
@ -90,7 +89,7 @@ class EditorContainer extends Component<any, any> {
|
||||
const { currentAccount, navigation } = this.props;
|
||||
const username = currentAccount && currentAccount.name ? currentAccount.name : '';
|
||||
let isReply;
|
||||
let quickReplyText;
|
||||
let draftId;
|
||||
let isEdit;
|
||||
let post;
|
||||
let _draft;
|
||||
@ -133,12 +132,19 @@ class EditorContainer extends Component<any, any> {
|
||||
}
|
||||
|
||||
if (navigationParams.isReply) {
|
||||
({ isReply, quickReplyText } = navigationParams);
|
||||
({ isReply } = navigationParams);
|
||||
if(post){
|
||||
draftId = `${currentAccount.name}/${post.author}/${post.permlink}`
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isReply,
|
||||
quickReplyText,
|
||||
draftId,
|
||||
autoFocusText: true,
|
||||
});
|
||||
if (draftId) {
|
||||
this._getStorageDraft(username, isReply, { _id: draftId });
|
||||
}
|
||||
}
|
||||
|
||||
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._requestKeyboardFocus();
|
||||
@ -194,49 +200,54 @@ class EditorContainer extends Component<any, any> {
|
||||
}
|
||||
|
||||
_getStorageDraft = async (username, isReply, paramDraft) => {
|
||||
if (isReply) {
|
||||
const draftReply = await AsyncStorage.getItem('temp-reply');
|
||||
const { drafts } = this.props;
|
||||
|
||||
if (draftReply) {
|
||||
if (isReply) {
|
||||
const _draft = drafts.get(paramDraft._id);
|
||||
if (_draft && _draft.body) {
|
||||
this.setState({
|
||||
draftPost: {
|
||||
body: draftReply,
|
||||
body: _draft.body,
|
||||
},
|
||||
});
|
||||
}
|
||||
} else {
|
||||
getDraftPost(username, paramDraft && paramDraft._id).then((result) => {
|
||||
//if result is return and param draft available, compare timestamp, use latest
|
||||
//if no draft, use result anayways
|
||||
if (result && (!paramDraft || paramDraft.timestamp < result.timestamp)) {
|
||||
this.setState({
|
||||
draftPost: {
|
||||
body: get(result, 'body', ''),
|
||||
title: get(result, 'title', ''),
|
||||
tags: get(result, 'tags', '').split(','),
|
||||
isDraft: paramDraft ? true : false,
|
||||
draftId: paramDraft ? paramDraft._id : null,
|
||||
},
|
||||
});
|
||||
}
|
||||
//TOOD: get draft from redux after reply side is complete
|
||||
const _draftId = paramDraft ? paramDraft._id : DEFAULT_USER_DRAFT_ID + username;
|
||||
const _localDraft = drafts.get(_draftId);
|
||||
|
||||
//if above fails with either no result returned or timestamp is old,
|
||||
// and use draft form nav param if available.
|
||||
else if (paramDraft) {
|
||||
const _tags = paramDraft.tags.includes(' ')
|
||||
? paramDraft.tags.split(' ')
|
||||
: paramDraft.tags.split(',');
|
||||
this.setState({
|
||||
draftPost: {
|
||||
title: paramDraft.title,
|
||||
body: paramDraft.body,
|
||||
tags: _tags,
|
||||
},
|
||||
isDraft: true,
|
||||
draftId: paramDraft._id,
|
||||
});
|
||||
}
|
||||
});
|
||||
//if _draft is returned and param draft is available, compare timestamp, use latest
|
||||
//if no draft, use result anayways
|
||||
|
||||
if (_localDraft && (!paramDraft || paramDraft.timestamp < _localDraft.updated)) {
|
||||
this.setState({
|
||||
draftPost: {
|
||||
body: get(_localDraft, 'body', ''),
|
||||
title: get(_localDraft, 'title', ''),
|
||||
tags: get(_localDraft, 'tags', '').split(','),
|
||||
isDraft: paramDraft ? true : false,
|
||||
draftId: paramDraft ? paramDraft._id : null,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
//if above fails with either no result returned or timestamp is old,
|
||||
// and use draft form nav param if available.
|
||||
else if (paramDraft) {
|
||||
const _tags = paramDraft.tags.includes(' ')
|
||||
? paramDraft.tags.split(' ')
|
||||
: paramDraft.tags.split(',');
|
||||
this.setState({
|
||||
draftPost: {
|
||||
title: paramDraft.title,
|
||||
body: paramDraft.body,
|
||||
tags: _tags,
|
||||
},
|
||||
isDraft: true,
|
||||
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
|
||||
* empty editor, load non-remote draft or most recent remote draft based on timestamps
|
||||
* prompts user as well
|
||||
* @param isReply
|
||||
**/
|
||||
_fetchDraftsForComparison = async (isReply) => {
|
||||
const { currentAccount, isLoggedIn, intl, dispatch } = this.props;
|
||||
const { currentAccount, isLoggedIn, intl, dispatch, drafts } = this.props;
|
||||
const username = get(currentAccount, 'name', '');
|
||||
|
||||
//initilizes editor with reply or non remote id less draft
|
||||
@ -282,28 +293,29 @@ class EditorContainer extends Component<any, any> {
|
||||
return;
|
||||
}
|
||||
|
||||
const drafts = await getDrafts(username);
|
||||
const idLessDraft = await getDraftPost(username);
|
||||
const remoteDrafts = await getDrafts(username);
|
||||
|
||||
const idLessDraft = drafts.get(DEFAULT_USER_DRAFT_ID + username)
|
||||
|
||||
const loadRecentDraft = () => {
|
||||
//if no draft available means local draft is recent
|
||||
if (drafts.length == 0) {
|
||||
if (remoteDrafts.length == 0) {
|
||||
_getStorageDraftGeneral(false);
|
||||
return;
|
||||
}
|
||||
|
||||
//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,
|
||||
);
|
||||
const _draft = drafts[0];
|
||||
const _draft = remoteDrafts[0];
|
||||
|
||||
//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 (
|
||||
idLessDraft &&
|
||||
(idLessDraft.title !== '' || idLessDraft.tags !== '' || idLessDraft.body !== '') &&
|
||||
new Date(_draft.modified).getTime() < idLessDraft.timestamp
|
||||
new Date(_draft.modified).getTime() < idLessDraft.updated
|
||||
) {
|
||||
_getStorageDraftGeneral(false);
|
||||
return;
|
||||
@ -317,7 +329,7 @@ class EditorContainer extends Component<any, any> {
|
||||
this._getStorageDraft(username, isReply, _draft);
|
||||
};
|
||||
|
||||
if (drafts.length > 0 || (idLessDraft && idLessDraft.timestamp > 0)) {
|
||||
if (remoteDrafts.length > 0 || (idLessDraft && idLessDraft.updated > 0)) {
|
||||
this.setState({
|
||||
onLoadDraftPress: loadRecentDraft,
|
||||
});
|
||||
@ -523,9 +535,14 @@ class EditorContainer extends Component<any, any> {
|
||||
};
|
||||
|
||||
_saveDraftToDB = async (fields, saveAsNew = false) => {
|
||||
const { isDraftSaved, draftId, thumbIndex } = this.state;
|
||||
const { isDraftSaved, draftId, thumbIndex, isReply } = this.state;
|
||||
const { currentAccount, dispatch, intl } = this.props;
|
||||
|
||||
if (isReply) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const beneficiaries = this._extractBeneficiaries();
|
||||
|
||||
try {
|
||||
@ -574,16 +591,8 @@ class EditorContainer extends Component<any, any> {
|
||||
|
||||
//clear local copy if darft save is successful
|
||||
const username = get(currentAccount, 'name', '');
|
||||
setDraftPost(
|
||||
{
|
||||
title: '',
|
||||
body: '',
|
||||
tags: '',
|
||||
timestamp: 0,
|
||||
},
|
||||
username,
|
||||
saveAsNew ? draftId : undefined
|
||||
);
|
||||
|
||||
dispatch(deleteDraftCacheEntry(draftId || (DEFAULT_USER_DRAFT_ID + username)))
|
||||
}
|
||||
|
||||
|
||||
@ -626,26 +635,28 @@ class EditorContainer extends Component<any, any> {
|
||||
return;
|
||||
}
|
||||
|
||||
const { currentAccount } = this.props;
|
||||
const { currentAccount, dispatch } = this.props;
|
||||
const username = currentAccount && currentAccount.name ? currentAccount.name : '';
|
||||
|
||||
const draftField = {
|
||||
...fields,
|
||||
title: fields.title,
|
||||
body: fields.body,
|
||||
tags: fields.tags && fields.tags.length > 0 ? fields.tags.toString() : '',
|
||||
};
|
||||
author: username,
|
||||
}
|
||||
|
||||
//save reply data
|
||||
if (isReply && draftField.body !== null) {
|
||||
await AsyncStorage.setItem('temp-reply', draftField.body);
|
||||
dispatch(updateDraftCache(draftId, draftField))
|
||||
|
||||
//save existing draft data locally
|
||||
} else if (draftId) {
|
||||
setDraftPost(draftField, username, draftId);
|
||||
dispatch(updateDraftCache(draftId, draftField))
|
||||
}
|
||||
|
||||
//update editor data locally
|
||||
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
|
||||
setDraftPost(
|
||||
{
|
||||
title: '',
|
||||
body: '',
|
||||
tags: '',
|
||||
timestamp: 0,
|
||||
},
|
||||
currentAccount.name,
|
||||
);
|
||||
dispatch(deleteDraftCacheEntry(DEFAULT_USER_DRAFT_ID + currentAccount.name))
|
||||
|
||||
dispatch(removeBeneficiaries(TEMP_BENEFICIARIES_ID))
|
||||
if (draftId) {
|
||||
@ -1139,15 +1142,8 @@ class EditorContainer extends Component<any, any> {
|
||||
}),
|
||||
),
|
||||
);
|
||||
setDraftPost(
|
||||
{
|
||||
title: '',
|
||||
body: '',
|
||||
tags: '',
|
||||
timestamp: 0,
|
||||
},
|
||||
currentAccount.name,
|
||||
);
|
||||
|
||||
dispatch(deleteDraftCacheEntry(DEFAULT_USER_DRAFT_ID + currentAccount.name))
|
||||
|
||||
setTimeout(() => {
|
||||
navigation.replace(ROUTES.SCREENS.DRAFTS,
|
||||
@ -1167,17 +1163,10 @@ class EditorContainer extends Component<any, any> {
|
||||
_initialEditor = () => {
|
||||
const {
|
||||
currentAccount: { name },
|
||||
dispatch
|
||||
} = this.props;
|
||||
|
||||
setDraftPost(
|
||||
{
|
||||
title: '',
|
||||
body: '',
|
||||
tags: '',
|
||||
timestamp: 0,
|
||||
},
|
||||
name,
|
||||
);
|
||||
dispatch(deleteDraftCacheEntry(DEFAULT_USER_DRAFT_ID + name))
|
||||
|
||||
this.setState({
|
||||
uploadedImage: null,
|
||||
@ -1271,7 +1260,8 @@ const mapStateToProps = (state) => ({
|
||||
isDefaultFooter: state.account.isDefaultFooter,
|
||||
isLoggedIn: state.application.isLoggedIn,
|
||||
pinCode: state.application.pin,
|
||||
beneficiariesMap: state.editor.beneficiariesMap
|
||||
beneficiariesMap: state.editor.beneficiariesMap,
|
||||
drafts: state.cache.drafts,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(EditorContainer));
|
||||
|
@ -39,7 +39,6 @@ class EditorScreen extends Component {
|
||||
* ------------------------------------------------
|
||||
* @prop { type } name - Description....
|
||||
*/
|
||||
thumbSelectionModalRef = null;
|
||||
postOptionsModalRef = null;
|
||||
|
||||
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) => {
|
||||
this.setState({
|
||||
@ -367,7 +360,6 @@ class EditorScreen extends Component {
|
||||
isLoggedIn,
|
||||
isPostSending,
|
||||
isReply,
|
||||
quickReplyText,
|
||||
isUploading,
|
||||
post,
|
||||
uploadedImage,
|
||||
@ -406,8 +398,8 @@ class EditorScreen extends Component {
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
console.log('fields :', fields);
|
||||
console.log('quickReplyText : ', quickReplyText);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<View style={globalStyles.defaultContainer}>
|
||||
@ -430,11 +422,11 @@ console.log('quickReplyText : ', quickReplyText);
|
||||
isReply={isReply}
|
||||
quickTitle={wordsCount > 0 && `${wordsCount} words`}
|
||||
rightButtonText={rightButtonText}
|
||||
showThumbSelectionModal={this._showThumbSelectionModal}
|
||||
handleSettingsPress={this._handleSettingsPress}
|
||||
/>
|
||||
<PostForm
|
||||
handleFormUpdate={this._handleFormUpdate}
|
||||
handleBodyChange={this._setWordsCount}
|
||||
handleOnSubmit={this._handleOnSubmit}
|
||||
isFormValid={isFormValid}
|
||||
isPreviewActive={isPreviewActive}
|
||||
@ -451,10 +443,7 @@ console.log('quickReplyText : ', quickReplyText);
|
||||
)}
|
||||
<MarkdownEditor
|
||||
componentID="body"
|
||||
draftBody={isReply ? quickReplyText : fields && fields.body}
|
||||
handleOnTextChange={this._setWordsCount}
|
||||
handleFormUpdate={this._handleFormUpdate}
|
||||
handleIsFormValid={this._handleIsFormValid}
|
||||
draftBody={fields && fields.body}
|
||||
isFormValid={isFormValid}
|
||||
handleOpenImagePicker={handleOnImagePicker}
|
||||
intl={intl}
|
||||
@ -476,12 +465,9 @@ console.log('quickReplyText : ', quickReplyText);
|
||||
uploadProgress={uploadProgress}
|
||||
/>
|
||||
</PostForm>
|
||||
|
||||
{_renderCommunityModal()}
|
||||
<ThumbSelectionModal
|
||||
ref={(componentRef) => (this.thumbSelectionModalRef = componentRef)}
|
||||
thumbIndex={thumbIndex}
|
||||
onThumbSelection={this._handleOnThumbSelection}
|
||||
/>
|
||||
|
||||
<PostOptionsModal
|
||||
ref={(componentRef) => (this.postOptionsModalRef = componentRef)}
|
||||
body={fields.body}
|
||||
|
@ -45,25 +45,40 @@ export const generatePermlink = (title, random = false) => {
|
||||
};
|
||||
|
||||
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,]/
|
||||
let word = '';
|
||||
for(let i = index; i >= 0 && (!END_REGEX.test(text[i]) || i === index); i--){
|
||||
if(text[i]){
|
||||
word += text[i];
|
||||
for(let i = indexChunk; i >= 0 && (!END_REGEX.test(textChunk[i]) || i === indexChunk); i--){
|
||||
if(textChunk[i]){
|
||||
word += textChunk[i];
|
||||
}
|
||||
}
|
||||
word = word.split('').reverse().join('');
|
||||
|
||||
if(!END_REGEX.test(text[index])){
|
||||
for(let i = index + 1; i < text.length && !END_REGEX.test(text[i]); i++){
|
||||
if(text[i]){
|
||||
word += text[i];
|
||||
if(!END_REGEX.test(textChunk[indexChunk])){
|
||||
for(let i = indexChunk + 1; i < textChunk.length && !END_REGEX.test(textChunk[i]); i++){
|
||||
if(textChunk[i]){
|
||||
word += textChunk[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return word;
|
||||
|
||||
}
|
||||
|
||||
export const generateReplyPermlink = (toAuthor) => {
|
||||
|
Loading…
Reference in New Issue
Block a user