function component editor screen

This commit is contained in:
Nouman Tahir 2023-01-20 20:26:44 +05:00
parent f30d8bb3ac
commit cbea074ba8
2 changed files with 984 additions and 454 deletions

View File

@ -1,19 +1,19 @@
import React, { Component } from 'react';
import React, { useRef, useState, useEffect } from 'react';
import { Alert, View } from 'react-native';
import { injectIntl } from 'react-intl';
import { useIntl } from 'react-intl';
import { get, isNull, isEqual } from 'lodash';
// Utils
import { extractMetadata, getWordsCount, makeJsonMetadata } from '../../../utils/editor';
import { extractMetadata, getWordsCount, makeJsonMetadata, delay } from '../../../utils/editor';
// Components
import {
BasicHeader,
PostForm,
MarkdownEditor,
SelectCommunityAreaView,
SelectCommunityModalContainer,
Modal,
BasicHeader,
PostForm,
MarkdownEditor,
SelectCommunityAreaView,
SelectCommunityModalContainer,
Modal,
} from '../../../components';
// dhive
@ -26,488 +26,505 @@ import { isCommunity } from '../../../utils/communityValidation';
import styles from './editorScreenStyles';
import PostOptionsModal from '../children/postOptionsModal';
import { useAppSelector } from '../../../hooks';
class EditorScreen extends Component {
/* Props
* ------------------------------------------------
* @prop { type } name - Description....
*/
postOptionsModalRef = null;
const EditorScreen = ({
paramFiles,
handleOnImagePicker,
isDraftSaved,
isDraftSaving,
draftId,
isEdit,
isLoggedIn,
isPostSending,
isReply,
isUploading,
post,
uploadedImage,
handleOnBackPress,
handleSchedulePress,
handleRewardChange,
handleShouldReblogChange,
autoFocusText,
sharedSnippetText,
onLoadDraftPress,
thumbUrl,
uploadProgress,
rewardType,
setIsUploading,
draftPost,
tags,
community,
initialEditor,
saveDraftToDB,
saveCurrentDraft,
updateDraftFields,
handleOnSubmit,
setThumbUrl,
handleFormChanged,
getBeneficiaries
constructor(props) {
super(props);
this.state = {
isFormValid: false,
isPreviewActive: false,
wordsCount: null,
isRemoveTag: false,
fields: {
title: (props.draftPost && props.draftPost.title) || '',
body: (props.draftPost && props.draftPost.body) || '',
tags: (props.draftPost && props.draftPost.tags) || props.tags || [],
community: props.community || [],
isValid: false,
},
isCommunitiesListModalOpen: false,
selectedCommunity: null,
selectedAccount: null,
scheduledFor: null,
};
}
}) => {
/* Props
* ------------------------------------------------
* @prop { type } name - Description....
*/
// Component Life Cycles
componentDidMount() {
const { draftPost, currentAccount } = this.props;
const intl = useIntl();
if (draftPost) {
if (draftPost.tags?.length > 0 && isCommunity(draftPost.tags[0])) {
this._getCommunity(draftPost.tags[0]);
} else {
this.setState({
selectedAccount: currentAccount,
});
}
}
}
const postOptionsModalRef = useRef<any>(null);
const prevDraftRef = useRef(null);
componentWillUnmount() {
const { isEdit } = this.props;
if (!isEdit) {
this._saveDraftToDB();
}
}
const currentAccount = useAppSelector(state => state.account.currentAccount);
UNSAFE_componentWillReceiveProps = async (nextProps) => {
const { draftPost, isUploading, community, currentAccount } = this.props;
if (nextProps.draftPost && draftPost !== nextProps.draftPost) {
if (nextProps.draftPost.tags?.length > 0 && isCommunity(nextProps.draftPost.tags[0])) {
this._getCommunity(nextProps.draftPost.tags[0]);
} else {
this.setState({
selectedAccount: currentAccount,
});
}
const [state, setState] = useState({
isFormValid: false,
isPreviewActive: false,
wordsCount: 0,
isRemoveTag: false,
fields: {
title: (draftPost && draftPost.title) || '',
body: (draftPost && draftPost.body) || '',
tags: (draftPost && draftPost.tags) || tags || [],
community: community || [],
isValid: false,
},
isCommunitiesListModalOpen: false,
selectedCommunity: null,
selectedAccount: null,
scheduledFor: null,
});
useEffect(() => {
if (draftPost && prevDraftRef.current !== draftPost) {
if (draftPost.tags?.length > 0 && isCommunity(draftPost.tags[0])) {
_getCommunity(draftPost.tags[0]);
}
setState({
...state,
selectedAccount: currentAccount,
fields: {
...state.fields,
...draftPost
}
});
await this.setState((prevState) => {
if (community && community.length > 0) {
nextProps.draftPost.tags = [...community, ...nextProps.draftPost.tags];
}
return {
fields: {
...prevState.fields,
...nextProps.draftPost,
},
};
});
}
prevDraftRef.current = draftPost;
if (isUploading !== nextProps) {
this._handleFormUpdate();
}
//unmount
return () => {
if (!isEdit) {
_saveDraftToDB();
}
}
}, [draftPost])
if (community && community.length > 0) {
this._getCommunity(community[0]);
this._handleOnTagAdded(community);
}
};
// Component Functions
_initialFields = () => {
const { initialEditor } = this.props;
useEffect(() => {
setState({
...state,
fields: {
...state.fields,
community: community[0] || []
}
})
_handleOnTagAdded(community);
}, [community])
this.setState({
fields: {
title: '',
body: '',
tags: [],
isValid: false,
},
isRemoveTag: true,
});
if (initialEditor) {
initialEditor();
}
};
useEffect(() => {
if (!isUploading) {
_handleFormUpdate();
}
}, [isUploading])
_handleOnPressPreviewButton = () => {
const { isPreviewActive } = this.state;
this.setState({ isPreviewActive: !isPreviewActive }, () => {
this._handleIsFormValid();
});
};
useEffect(() => {
_handleIsFormValid();
}, [state.fields])
_setWordsCount = (content) => {
const _wordsCount = getWordsCount(content);
const { wordsCount } = this.state;
if (_wordsCount !== wordsCount) {
this.setState({ wordsCount: _wordsCount });
}
};
_handleOnSaveButtonPress = () => {
const { draftId, intl } = this.props;
if (draftId) {
Alert.alert(intl.formatMessage({ id: 'editor.draft_save_title' }), '', [
{
text: intl.formatMessage({ id: 'editor.draft_update' }),
onPress: () => this._saveDraftToDB(),
},
{
text: intl.formatMessage({ id: 'editor.draft_save_new' }),
onPress: () => this._saveDraftToDB(true),
},
{
text: intl.formatMessage({ id: 'alert.cancel' }),
onPress: () => {},
style: 'cancel',
},
]);
return;
}
this._saveDraftToDB();
};
// Component Functions
const _initialFields = () => {
_saveCurrentDraft = (fields) => {
const { saveCurrentDraft, updateDraftFields } = this.props;
if (this.changeTimer) {
clearTimeout(this.changeTimer);
}
this.changeTimer = setTimeout(() => {
saveCurrentDraft(fields);
updateDraftFields(fields);
}, 300);
};
_handleOnSubmit = () => {
const { handleOnSubmit, handleSchedulePress } = this.props;
const { fields, scheduledFor } = this.state;
if (scheduledFor && handleSchedulePress) {
handleSchedulePress(scheduledFor, fields);
return;
}
if (handleOnSubmit) {
handleOnSubmit({ fields });
}
};
_handleOnThumbSelection = (url: string) => {
const { setThumbUrl } = this.props;
if (setThumbUrl) {
setThumbUrl(url);
}
};
_handleScheduleChange = (datetime: string | null) => {
this.setState({
scheduledFor: datetime,
});
};
_handleRewardChange = (value) => {
const { handleRewardChange } = this.props;
handleRewardChange(value);
};
_handleSettingsPress = () => {
if (this.postOptionsModalRef) {
this.postOptionsModalRef.show();
}
};
_handleIsFormValid = (bodyText) => {
const { fields } = this.state;
const { isReply, isLoggedIn } = this.props;
let isFormValid;
if (isReply) {
isFormValid = get(fields, 'body').length > 0;
} else {
isFormValid =
get(fields, 'title', '') &&
get(fields, 'title', '').length < 255 &&
(get(fields, 'body', '') || (bodyText && bodyText > 0)) &&
get(fields, 'tags', null) &&
get(fields, 'tags', null).length <= 10 &&
isLoggedIn;
}
this.setState({ isFormValid });
};
_handleFormUpdate = (componentID, content) => {
const { handleFormChanged, thumbUrl, rewardType, getBeneficiaries } = this.props;
const { fields: _fields } = this.state;
const fields = { ..._fields };
if (componentID === 'body') {
fields.body = content;
} else if (componentID === 'title') {
fields.title = content;
} else if (componentID === 'tag-area') {
fields.tags = content;
}
const meta = Object.assign({}, extractMetadata(fields.body, thumbUrl), {
tags: fields.tags,
beneficiaries: getBeneficiaries(),
rewardType,
});
const jsonMeta = makeJsonMetadata(meta, fields.tags);
fields.meta = jsonMeta;
if (
get(fields, 'body', '').trim() !== get(_fields, 'body', '').trim() ||
get(fields, 'title', '').trim() !== get(_fields, 'title', '').trim() ||
get(fields, 'tags') !== get(_fields, 'tags') ||
!isEqual(get(fields, 'meta'), get(_fields, 'meta'))
) {
console.log('jsonMeta : ', jsonMeta);
handleFormChanged();
this._saveCurrentDraft(fields);
}
this.setState({ fields }, () => {
this._handleIsFormValid();
});
};
_handleOnTagAdded = async (tags) => {
const { currentAccount } = this.props;
if (tags.length > 0) {
if (!isCommunity(tags[0])) {
this.setState({
selectedCommunity: null,
selectedAccount: currentAccount,
setState({
...state,
fields: {
title: '',
body: '',
tags: [],
isValid: false,
community: []
},
isRemoveTag: true,
});
}
if (initialEditor) {
initialEditor();
}
};
const _handleOnPressPreviewButton = async () => {
const { isPreviewActive } = state;
setState({ ...state, isPreviewActive: !isPreviewActive })
await delay(200)
_handleIsFormValid();
};
const _setWordsCount = (content) => {
const _wordsCount = getWordsCount(content);
const { wordsCount } = state;
if (_wordsCount !== wordsCount) {
setState({ ...state, wordsCount: _wordsCount });
}
};
const _handleOnSaveButtonPress = () => {
if (draftId) {
Alert.alert(intl.formatMessage({ id: 'editor.draft_save_title' }), '', [
{
text: intl.formatMessage({ id: 'editor.draft_update' }),
onPress: () => _saveDraftToDB(),
},
{
text: intl.formatMessage({ id: 'editor.draft_save_new' }),
onPress: () => _saveDraftToDB(true),
},
{
text: intl.formatMessage({ id: 'alert.cancel' }),
onPress: () => { },
style: 'cancel',
},
]);
return;
}
_saveDraftToDB();
};
const _saveCurrentDraft = (fields) => {
saveCurrentDraft(fields);
updateDraftFields(fields);
};
const _handleOnSubmit = () => {
const { fields, scheduledFor } = state;
if (scheduledFor && handleSchedulePress) {
handleSchedulePress(scheduledFor, fields);
return;
}
if (handleOnSubmit) {
handleOnSubmit({ fields });
}
};
const _handleOnThumbSelection = (url: string) => {
if (setThumbUrl) {
setThumbUrl(url);
}
};
const _handleScheduleChange = (datetime: string | null) => {
setState({
...state,
scheduledFor: datetime,
});
};
const _handleRewardChange = (value) => {
handleRewardChange(value);
};
const _handleSettingsPress = () => {
if (postOptionsModalRef.current) {
postOptionsModalRef.current.show();
}
};
const _handleIsFormValid = (bodyText?) => {
const { fields } = state;
let isFormValid;
if (isReply) {
isFormValid = get(fields, 'body').length > 0;
} else {
isFormValid =
get(fields, 'title', '') &&
get(fields, 'title', '').length < 255 &&
(get(fields, 'body', '') || (bodyText && bodyText > 0)) &&
get(fields, 'tags', null) &&
get(fields, 'tags', null).length <= 10 &&
isLoggedIn;
}
setState({ ...state, isFormValid });
};
const _handleFormUpdate = async (componentID?, content?) => {
const { fields: _fields } = state;
const fields = { ..._fields };
if (componentID === 'body') {
fields.body = content;
} else if (componentID === 'title') {
fields.title = content;
} else if (componentID === 'tag-area') {
fields.tags = content;
}
const meta = Object.assign({}, extractMetadata(fields.body, thumbUrl), {
tags: fields.tags,
beneficiaries: getBeneficiaries(),
rewardType,
});
const jsonMeta = makeJsonMetadata(meta, fields.tags);
fields.meta = jsonMeta;
if (
get(fields, 'body', '').trim() !== get(_fields, 'body', '').trim() ||
get(fields, 'title', '').trim() !== get(_fields, 'title', '').trim() ||
get(fields, 'tags') !== get(_fields, 'tags') ||
!isEqual(get(fields, 'meta'), get(_fields, 'meta'))
) {
console.log('jsonMeta : ', jsonMeta);
handleFormChanged();
_saveCurrentDraft(fields);
}
setState({ ...state, fields })
};
const _handleOnTagAdded = async (tags) => {
if (tags.length > 0) {
if (!isCommunity(tags[0])) {
setState({
...state,
selectedCommunity: null,
selectedAccount: currentAccount,
});
}
}
const { fields: _fields } = state;
const __tags = tags; // .map((t) => t.replace(/([^a-z0-9-]+)/gi, '').toLowerCase());
const __fields = { ..._fields, tags: __tags };
setState({ ...state, fields: __fields, isRemoveTag: false });
_handleFormUpdate('tag-data', tags);
};
const _handleChangeTitle = async (text) => {
const { fields: _fields } = state;
_fields.title = text;
setState({ ...state, fields: _fields });
_handleFormUpdate('title', text);
};
const _handlePressCommunity = (community) => {
const { fields, selectedCommunity } = state;
const tags = [...fields.tags];
if (community == null) {
if (!isNull(selectedCommunity)) {
tags.shift();
}
} else {
if (!isNull(selectedCommunity)) {
tags.shift();
}
tags.unshift(community.name);
}
setState({
...state,
fields: { ...fields, tags },
isCommunitiesListModalOpen: false,
selectedCommunity: community,
selectedAccount: community ? null : currentAccount,
});
};
const _getCommunity = (hive) => {
getCommunity(hive)
.then((community) => {
setState({
...state,
selectedCommunity: community
});
})
.catch((error) => {
console.log(error);
});
};
const _saveDraftToDB = (saveAsNew?: boolean) => {
const { fields } = state;
// save draft only if any of field is valid
if (fields.body || fields.title) {
saveDraftToDB(fields, saveAsNew);
}
}
const { fields: _fields } = this.state;
const __tags = tags; // .map((t) => t.replace(/([^a-z0-9-]+)/gi, '').toLowerCase());
const __fields = { ..._fields, tags: __tags };
this.setState({ fields: __fields, isRemoveTag: false }, () => {
this._handleFormUpdate('tag-area', __fields.tags);
});
};
_handleChangeTitle = (text) => {
const { fields: _fields } = this.state;
_fields.title = text;
this.setState({ fields: _fields }, () => {
this._handleFormUpdate('title', _fields.title);
});
};
_handlePressCommunity = (community) => {
const { fields, selectedCommunity } = this.state;
const { currentAccount } = this.props;
const tags = [...fields.tags];
if (community == null) {
if (!isNull(selectedCommunity)) {
tags.shift();
}
} else {
if (!isNull(selectedCommunity)) {
tags.shift();
}
tags.unshift(community.name);
}
this.setState({
fields: { ...fields, tags },
isCommunitiesListModalOpen: false,
selectedCommunity: community,
selectedAccount: community ? null : currentAccount,
});
};
_getCommunity = (hive) => {
getCommunity(hive)
.then((community) => {
this.setState({ selectedCommunity: community });
})
.catch((error) => {
console.log(error);
});
};
_saveDraftToDB(saveAsNew?: boolean) {
const { saveDraftToDB } = this.props;
const { fields } = this.state;
// save draft only if any of field is valid
if (fields.body || fields.title) {
saveDraftToDB(fields, saveAsNew);
}
}
render() {
const {
fields,
isPreviewActive,
wordsCount,
isFormValid,
isCommunitiesListModalOpen,
selectedCommunity,
selectedAccount,
scheduledFor,
} = this.state;
const {
paramFiles,
handleOnImagePicker,
intl,
isDraftSaved,
isDraftSaving,
draftId,
isEdit,
isLoggedIn,
isPostSending,
isReply,
isUploading,
post,
uploadedImage,
handleOnBackPress,
handleSchedulePress,
handleRewardChange,
handleShouldReblogChange,
currentAccount,
autoFocusText,
sharedSnippetText,
onLoadDraftPress,
thumbUrl,
uploadProgress,
rewardType,
setIsUploading,
} = this.props;
fields,
isPreviewActive,
wordsCount,
isFormValid,
isCommunitiesListModalOpen,
selectedCommunity,
selectedAccount,
scheduledFor,
} = state;
const rightButtonText = intl.formatMessage({
id: isEdit
? 'basic_header.update'
: isReply
? 'basic_header.reply'
: scheduledFor
? 'basic_header.schedule'
: 'basic_header.publish',
id: isEdit
? 'basic_header.update'
: isReply
? 'basic_header.reply'
: scheduledFor
? 'basic_header.schedule'
: 'basic_header.publish',
});
const _renderCommunityModal = () => {
return (
<Modal
isOpen={isCommunitiesListModalOpen}
animationType="animationType"
presentationStyle="pageSheet"
style={styles.modal}
>
<SelectCommunityModalContainer
onPressCommunity={this._handlePressCommunity}
currentAccount={currentAccount}
onCloseModal={() => {
this.setState({ isCommunitiesListModalOpen: false });
}}
/>
</Modal>
);
return (
<Modal
isOpen={isCommunitiesListModalOpen}
animationType="animationType"
presentationStyle="pageSheet"
style={styles.modal}
>
<SelectCommunityModalContainer
onPressCommunity={_handlePressCommunity}
currentAccount={currentAccount}
onCloseModal={() => {
setState({
...state,
isCommunitiesListModalOpen: false
});
}}
/>
</Modal>
);
};
return (
<View style={globalStyles.defaultContainer}>
<BasicHeader
handleSchedulePress={(date) => handleSchedulePress(date, fields)}
handleRewardChange={handleRewardChange}
handleOnBackPress={handleOnBackPress}
handleOnPressPreviewButton={this._handleOnPressPreviewButton}
handleOnSaveButtonPress={this._handleOnSaveButtonPress}
handleOnSubmit={this._handleOnSubmit}
isDraftSaved={isDraftSaved}
isDraftSaving={isDraftSaving}
draftId={draftId}
isEdit={isEdit}
isFormValid={isFormValid}
isHasIcons
isLoading={isPostSending || isUploading}
isLoggedIn={isLoggedIn}
isPreviewActive={isPreviewActive}
isReply={isReply}
quickTitle={wordsCount > 0 && `${wordsCount} words`}
rightButtonText={rightButtonText}
handleSettingsPress={this._handleSettingsPress}
/>
<PostForm
handleFormUpdate={this._handleFormUpdate}
handleBodyChange={this._setWordsCount}
handleOnSubmit={this._handleOnSubmit}
isFormValid={isFormValid}
isPreviewActive={isPreviewActive}
>
{!isReply && !isEdit && (
<SelectCommunityAreaView
selectedAccount={selectedAccount}
selectedCommunity={selectedCommunity}
// because of the bug in react-native-modal
// https://github.com/facebook/react-native/issues/26892
onPressOut={() => this.setState({ isCommunitiesListModalOpen: true })}
onPressIn={() => this.setState({ isCommunitiesListModalOpen: false })}
<View style={globalStyles.defaultContainer}>
<BasicHeader
handleSchedulePress={(date) => handleSchedulePress(date, fields)}
handleRewardChange={handleRewardChange}
handleOnBackPress={handleOnBackPress}
handleOnPressPreviewButton={_handleOnPressPreviewButton}
handleOnSaveButtonPress={_handleOnSaveButtonPress}
handleOnSubmit={_handleOnSubmit}
isDraftSaved={isDraftSaved}
isDraftSaving={isDraftSaving}
draftId={draftId}
isEdit={isEdit}
isFormValid={isFormValid}
isHasIcons
isLoading={isPostSending || isUploading}
isLoggedIn={isLoggedIn}
isPreviewActive={isPreviewActive}
isReply={isReply}
quickTitle={wordsCount > 0 && `${wordsCount} words`}
rightButtonText={rightButtonText}
handleSettingsPress={_handleSettingsPress}
/>
)}
<MarkdownEditor
paramFiles={paramFiles}
componentID="body"
draftBody={fields && fields.body}
isFormValid={isFormValid}
handleOpenImagePicker={handleOnImagePicker}
intl={intl}
uploadedImage={uploadedImage}
initialFields={this._initialFields}
isReply={isReply}
isLoading={isPostSending}
isUploading={isUploading}
isEdit={isEdit}
post={post}
fields={fields}
currentAccount={currentAccount}
onTagChanged={this._handleOnTagAdded}
onTitleChanged={this._handleChangeTitle}
getCommunity={this._getCommunity}
autoFocusText={autoFocusText}
sharedSnippetText={sharedSnippetText}
onLoadDraftPress={onLoadDraftPress}
uploadProgress={uploadProgress}
setIsUploading={setIsUploading}
/>
</PostForm>
<PostForm
handleFormUpdate={_handleFormUpdate}
handleBodyChange={_setWordsCount}
handleOnSubmit={_handleOnSubmit}
isFormValid={isFormValid}
isPreviewActive={isPreviewActive}
>
{!isReply && !isEdit && (
<SelectCommunityAreaView
selectedAccount={selectedAccount}
selectedCommunity={selectedCommunity}
// because of the bug in react-native-modal
// https://github.com/facebook/react-native/issues/26892
onPressOut={() => setState({ ...state, isCommunitiesListModalOpen: true })}
onPressIn={() => setState({ ...state, isCommunitiesListModalOpen: false })}
/>
)}
<MarkdownEditor
paramFiles={paramFiles}
componentID="body"
draftBody={fields && fields.body}
isFormValid={isFormValid}
handleOpenImagePicker={handleOnImagePicker}
intl={intl}
uploadedImage={uploadedImage}
initialFields={_initialFields}
isReply={isReply}
isLoading={isPostSending}
isUploading={isUploading}
isEdit={isEdit}
post={post}
fields={fields}
currentAccount={currentAccount}
onTagChanged={_handleOnTagAdded}
onTitleChanged={_handleChangeTitle}
getCommunity={_getCommunity}
autoFocusText={autoFocusText}
sharedSnippetText={sharedSnippetText}
onLoadDraftPress={onLoadDraftPress}
uploadProgress={uploadProgress}
setIsUploading={setIsUploading}
/>
</PostForm>
{_renderCommunityModal()}
{_renderCommunityModal()}
<PostOptionsModal
ref={(componentRef) => (this.postOptionsModalRef = componentRef)}
body={fields.body}
draftId={draftId}
thumbUrl={thumbUrl}
isEdit={isEdit}
isCommunityPost={selectedCommunity !== null}
rewardType={rewardType}
isUploading={isUploading}
handleThumbSelection={this._handleOnThumbSelection}
handleRewardChange={this._handleRewardChange}
handleScheduleChange={this._handleScheduleChange}
handleShouldReblogChange={handleShouldReblogChange}
handleFormUpdate={this._handleFormUpdate}
/>
</View>
<PostOptionsModal
ref={postOptionsModalRef}
body={fields.body}
draftId={draftId}
thumbUrl={thumbUrl}
isEdit={isEdit}
isCommunityPost={selectedCommunity !== null}
rewardType={rewardType}
isUploading={isUploading}
handleThumbSelection={_handleOnThumbSelection}
handleRewardChange={_handleRewardChange}
handleScheduleChange={_handleScheduleChange}
handleShouldReblogChange={handleShouldReblogChange}
handleFormUpdate={_handleFormUpdate}
/>
</View>
);
}
}
export default injectIntl(EditorScreen);
export default EditorScreen;

View File

@ -0,0 +1,513 @@
import React, { Component } from 'react';
import { Alert, View } from 'react-native';
import { injectIntl } from 'react-intl';
import { get, isNull, isEqual } from 'lodash';
// Utils
import { extractMetadata, getWordsCount, makeJsonMetadata } from '../../../utils/editor';
// Components
import {
BasicHeader,
PostForm,
MarkdownEditor,
SelectCommunityAreaView,
SelectCommunityModalContainer,
Modal,
} from '../../../components';
// dhive
import { getCommunity } from '../../../providers/hive/dhive';
// Styles
import globalStyles from '../../../globalStyles';
import { isCommunity } from '../../../utils/communityValidation';
import styles from './editorScreenStyles';
import PostOptionsModal from '../children/postOptionsModal';
class EditorScreen extends Component {
/* Props
* ------------------------------------------------
* @prop { type } name - Description....
*/
postOptionsModalRef = null;
constructor(props) {
super(props);
this.state = {
isFormValid: false,
isPreviewActive: false,
wordsCount: null,
isRemoveTag: false,
fields: {
title: (props.draftPost && props.draftPost.title) || '',
body: (props.draftPost && props.draftPost.body) || '',
tags: (props.draftPost && props.draftPost.tags) || props.tags || [],
community: props.community || [],
isValid: false,
},
isCommunitiesListModalOpen: false,
selectedCommunity: null,
selectedAccount: null,
scheduledFor: null,
};
}
// Component Life Cycles
componentDidMount() {
const { draftPost, currentAccount } = this.props;
if (draftPost) {
if (draftPost.tags?.length > 0 && isCommunity(draftPost.tags[0])) {
this._getCommunity(draftPost.tags[0]);
} else {
this.setState({
selectedAccount: currentAccount,
});
}
}
}
componentWillUnmount() {
const { isEdit } = this.props;
if (!isEdit) {
this._saveDraftToDB();
}
}
UNSAFE_componentWillReceiveProps = async (nextProps) => {
const { draftPost, isUploading, community, currentAccount } = this.props;
if (nextProps.draftPost && draftPost !== nextProps.draftPost) {
if (nextProps.draftPost.tags?.length > 0 && isCommunity(nextProps.draftPost.tags[0])) {
this._getCommunity(nextProps.draftPost.tags[0]);
} else {
this.setState({
selectedAccount: currentAccount,
});
}
await this.setState((prevState) => {
if (community && community.length > 0) {
nextProps.draftPost.tags = [...community, ...nextProps.draftPost.tags];
}
return {
fields: {
...prevState.fields,
...nextProps.draftPost,
},
};
});
}
if (isUploading !== nextProps) {
this._handleFormUpdate();
}
if (community && community.length > 0) {
this._getCommunity(community[0]);
this._handleOnTagAdded(community);
}
};
// Component Functions
_initialFields = () => {
const { initialEditor } = this.props;
this.setState({
fields: {
title: '',
body: '',
tags: [],
isValid: false,
},
isRemoveTag: true,
});
if (initialEditor) {
initialEditor();
}
};
_handleOnPressPreviewButton = () => {
const { isPreviewActive } = this.state;
this.setState({ isPreviewActive: !isPreviewActive }, () => {
this._handleIsFormValid();
});
};
_setWordsCount = (content) => {
const _wordsCount = getWordsCount(content);
const { wordsCount } = this.state;
if (_wordsCount !== wordsCount) {
this.setState({ wordsCount: _wordsCount });
}
};
_handleOnSaveButtonPress = () => {
const { draftId, intl } = this.props;
if (draftId) {
Alert.alert(intl.formatMessage({ id: 'editor.draft_save_title' }), '', [
{
text: intl.formatMessage({ id: 'editor.draft_update' }),
onPress: () => this._saveDraftToDB(),
},
{
text: intl.formatMessage({ id: 'editor.draft_save_new' }),
onPress: () => this._saveDraftToDB(true),
},
{
text: intl.formatMessage({ id: 'alert.cancel' }),
onPress: () => {},
style: 'cancel',
},
]);
return;
}
this._saveDraftToDB();
};
_saveCurrentDraft = (fields) => {
const { saveCurrentDraft, updateDraftFields } = this.props;
if (this.changeTimer) {
clearTimeout(this.changeTimer);
}
this.changeTimer = setTimeout(() => {
saveCurrentDraft(fields);
updateDraftFields(fields);
}, 300);
};
_handleOnSubmit = () => {
const { handleOnSubmit, handleSchedulePress } = this.props;
const { fields, scheduledFor } = this.state;
if (scheduledFor && handleSchedulePress) {
handleSchedulePress(scheduledFor, fields);
return;
}
if (handleOnSubmit) {
handleOnSubmit({ fields });
}
};
_handleOnThumbSelection = (url: string) => {
const { setThumbUrl } = this.props;
if (setThumbUrl) {
setThumbUrl(url);
}
};
_handleScheduleChange = (datetime: string | null) => {
this.setState({
scheduledFor: datetime,
});
};
_handleRewardChange = (value) => {
const { handleRewardChange } = this.props;
handleRewardChange(value);
};
_handleSettingsPress = () => {
if (this.postOptionsModalRef) {
this.postOptionsModalRef.show();
}
};
_handleIsFormValid = (bodyText) => {
const { fields } = this.state;
const { isReply, isLoggedIn } = this.props;
let isFormValid;
if (isReply) {
isFormValid = get(fields, 'body').length > 0;
} else {
isFormValid =
get(fields, 'title', '') &&
get(fields, 'title', '').length < 255 &&
(get(fields, 'body', '') || (bodyText && bodyText > 0)) &&
get(fields, 'tags', null) &&
get(fields, 'tags', null).length <= 10 &&
isLoggedIn;
}
this.setState({ isFormValid });
};
_handleFormUpdate = (componentID, content) => {
const { handleFormChanged, thumbUrl, rewardType, getBeneficiaries } = this.props;
const { fields: _fields } = this.state;
const fields = { ..._fields };
if (componentID === 'body') {
fields.body = content;
} else if (componentID === 'title') {
fields.title = content;
} else if (componentID === 'tag-area') {
fields.tags = content;
}
const meta = Object.assign({}, extractMetadata(fields.body, thumbUrl), {
tags: fields.tags,
beneficiaries: getBeneficiaries(),
rewardType,
});
const jsonMeta = makeJsonMetadata(meta, fields.tags);
fields.meta = jsonMeta;
if (
get(fields, 'body', '').trim() !== get(_fields, 'body', '').trim() ||
get(fields, 'title', '').trim() !== get(_fields, 'title', '').trim() ||
get(fields, 'tags') !== get(_fields, 'tags') ||
!isEqual(get(fields, 'meta'), get(_fields, 'meta'))
) {
console.log('jsonMeta : ', jsonMeta);
handleFormChanged();
this._saveCurrentDraft(fields);
}
this.setState({ fields }, () => {
this._handleIsFormValid();
});
};
_handleOnTagAdded = async (tags) => {
const { currentAccount } = this.props;
if (tags.length > 0) {
if (!isCommunity(tags[0])) {
this.setState({
selectedCommunity: null,
selectedAccount: currentAccount,
});
}
}
const { fields: _fields } = this.state;
const __tags = tags; // .map((t) => t.replace(/([^a-z0-9-]+)/gi, '').toLowerCase());
const __fields = { ..._fields, tags: __tags };
this.setState({ fields: __fields, isRemoveTag: false }, () => {
this._handleFormUpdate('tag-area', __fields.tags);
});
};
_handleChangeTitle = (text) => {
const { fields: _fields } = this.state;
_fields.title = text;
this.setState({ fields: _fields }, () => {
this._handleFormUpdate('title', _fields.title);
});
};
_handlePressCommunity = (community) => {
const { fields, selectedCommunity } = this.state;
const { currentAccount } = this.props;
const tags = [...fields.tags];
if (community == null) {
if (!isNull(selectedCommunity)) {
tags.shift();
}
} else {
if (!isNull(selectedCommunity)) {
tags.shift();
}
tags.unshift(community.name);
}
this.setState({
fields: { ...fields, tags },
isCommunitiesListModalOpen: false,
selectedCommunity: community,
selectedAccount: community ? null : currentAccount,
});
};
_getCommunity = (hive) => {
getCommunity(hive)
.then((community) => {
this.setState({ selectedCommunity: community });
})
.catch((error) => {
console.log(error);
});
};
_saveDraftToDB(saveAsNew?: boolean) {
const { saveDraftToDB } = this.props;
const { fields } = this.state;
// save draft only if any of field is valid
if (fields.body || fields.title) {
saveDraftToDB(fields, saveAsNew);
}
}
render() {
const {
fields,
isPreviewActive,
wordsCount,
isFormValid,
isCommunitiesListModalOpen,
selectedCommunity,
selectedAccount,
scheduledFor,
} = this.state;
const {
paramFiles,
handleOnImagePicker,
intl,
isDraftSaved,
isDraftSaving,
draftId,
isEdit,
isLoggedIn,
isPostSending,
isReply,
isUploading,
post,
uploadedImage,
handleOnBackPress,
handleSchedulePress,
handleRewardChange,
handleShouldReblogChange,
currentAccount,
autoFocusText,
sharedSnippetText,
onLoadDraftPress,
thumbUrl,
uploadProgress,
rewardType,
setIsUploading,
} = this.props;
const rightButtonText = intl.formatMessage({
id: isEdit
? 'basic_header.update'
: isReply
? 'basic_header.reply'
: scheduledFor
? 'basic_header.schedule'
: 'basic_header.publish',
});
const _renderCommunityModal = () => {
return (
<Modal
isOpen={isCommunitiesListModalOpen}
animationType="animationType"
presentationStyle="pageSheet"
style={styles.modal}
>
<SelectCommunityModalContainer
onPressCommunity={this._handlePressCommunity}
currentAccount={currentAccount}
onCloseModal={() => {
this.setState({ isCommunitiesListModalOpen: false });
}}
/>
</Modal>
);
};
return (
<View style={globalStyles.defaultContainer}>
<BasicHeader
handleSchedulePress={(date) => handleSchedulePress(date, fields)}
handleRewardChange={handleRewardChange}
handleOnBackPress={handleOnBackPress}
handleOnPressPreviewButton={this._handleOnPressPreviewButton}
handleOnSaveButtonPress={this._handleOnSaveButtonPress}
handleOnSubmit={this._handleOnSubmit}
isDraftSaved={isDraftSaved}
isDraftSaving={isDraftSaving}
draftId={draftId}
isEdit={isEdit}
isFormValid={isFormValid}
isHasIcons
isLoading={isPostSending || isUploading}
isLoggedIn={isLoggedIn}
isPreviewActive={isPreviewActive}
isReply={isReply}
quickTitle={wordsCount > 0 && `${wordsCount} words`}
rightButtonText={rightButtonText}
handleSettingsPress={this._handleSettingsPress}
/>
<PostForm
handleFormUpdate={this._handleFormUpdate}
handleBodyChange={this._setWordsCount}
handleOnSubmit={this._handleOnSubmit}
isFormValid={isFormValid}
isPreviewActive={isPreviewActive}
>
{!isReply && !isEdit && (
<SelectCommunityAreaView
selectedAccount={selectedAccount}
selectedCommunity={selectedCommunity}
// because of the bug in react-native-modal
// https://github.com/facebook/react-native/issues/26892
onPressOut={() => this.setState({ isCommunitiesListModalOpen: true })}
onPressIn={() => this.setState({ isCommunitiesListModalOpen: false })}
/>
)}
<MarkdownEditor
paramFiles={paramFiles}
componentID="body"
draftBody={fields && fields.body}
isFormValid={isFormValid}
handleOpenImagePicker={handleOnImagePicker}
intl={intl}
uploadedImage={uploadedImage}
initialFields={this._initialFields}
isReply={isReply}
isLoading={isPostSending}
isUploading={isUploading}
isEdit={isEdit}
post={post}
fields={fields}
currentAccount={currentAccount}
onTagChanged={this._handleOnTagAdded}
onTitleChanged={this._handleChangeTitle}
getCommunity={this._getCommunity}
autoFocusText={autoFocusText}
sharedSnippetText={sharedSnippetText}
onLoadDraftPress={onLoadDraftPress}
uploadProgress={uploadProgress}
setIsUploading={setIsUploading}
/>
</PostForm>
{_renderCommunityModal()}
<PostOptionsModal
ref={(componentRef) => (this.postOptionsModalRef = componentRef)}
body={fields.body}
draftId={draftId}
thumbUrl={thumbUrl}
isEdit={isEdit}
isCommunityPost={selectedCommunity !== null}
rewardType={rewardType}
isUploading={isUploading}
handleThumbSelection={this._handleOnThumbSelection}
handleRewardChange={this._handleRewardChange}
handleScheduleChange={this._handleScheduleChange}
handleShouldReblogChange={handleShouldReblogChange}
handleFormUpdate={this._handleFormUpdate}
/>
</View>
);
}
}
export default injectIntl(EditorScreen);