mirror of
https://github.com/ecency/ecency-mobile.git
synced 2024-12-24 13:53:23 +03:00
Merge pull request #2102 from ecency/nt/editor-settings-modal
Nt/editor settings modal
This commit is contained in:
commit
2ab58fc861
@ -772,4 +772,4 @@ SPEC CHECKSUMS:
|
||||
|
||||
PODFILE CHECKSUM: 9c48318ea254e2c78005a7a0c2d8bfc14ddd783d
|
||||
|
||||
COCOAPODS: 1.10.1
|
||||
COCOAPODS: 1.10.2
|
||||
|
@ -34,11 +34,9 @@ const BasicHeaderView = ({
|
||||
isHasIcons,
|
||||
isHasSearch,
|
||||
isLoading,
|
||||
isLoggedIn,
|
||||
isModalHeader,
|
||||
isPreviewActive,
|
||||
isReply,
|
||||
isEdit,
|
||||
quickTitle,
|
||||
rightButtonText,
|
||||
rightIconName,
|
||||
@ -49,6 +47,7 @@ const BasicHeaderView = ({
|
||||
handleRewardChange,
|
||||
handleBeneficiaries,
|
||||
enableViewModeToggle,
|
||||
handleSettingsPress,
|
||||
showThumbSelectionModal,
|
||||
}) => {
|
||||
const [isInputVisible, setIsInputVisible] = useState(false);
|
||||
@ -173,13 +172,13 @@ const BasicHeaderView = ({
|
||||
onPress={() => (isModalHeader ? handleOnPressClose() : handleOnPressBackButton())}
|
||||
disabled={disabled}
|
||||
/>
|
||||
{isHasIcons && !isReply && !isEdit && (
|
||||
{isHasIcons && !isReply && (
|
||||
<IconButton
|
||||
style={{ marginHorizontal: 20 }}
|
||||
iconStyle={[styles.backIcon, isModalHeader && styles.closeIcon]}
|
||||
iconType="MaterialIcons"
|
||||
name="settings"
|
||||
onPress={() => settingMenuRef.current.show()}
|
||||
onPress={handleSettingsPress && handleSettingsPress}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { PureComponent, Fragment } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { withNavigation } from 'react-navigation';
|
||||
import { Share, Alert } from 'react-native';
|
||||
import { Share } from 'react-native';
|
||||
import ActionSheet from 'react-native-actionsheet';
|
||||
import { injectIntl } from 'react-intl';
|
||||
import get from 'lodash/get';
|
||||
|
@ -281,7 +281,8 @@
|
||||
"publish": "Publish",
|
||||
"search": "Search",
|
||||
"update": "Update",
|
||||
"reply": "Reply"
|
||||
"reply": "Reply",
|
||||
"schedule":"Schedule"
|
||||
},
|
||||
"editor": {
|
||||
"title": "Title",
|
||||
@ -303,6 +304,7 @@
|
||||
"limited_lastchar": "Tag must end with letter or number",
|
||||
"setting_schedule": "Scheduling Time",
|
||||
"setting_reward": "Reward",
|
||||
"setting_reblog": "Reblog",
|
||||
"setting_beneficiary": "Beneficiary",
|
||||
"setting_thumb": "Set Thumbnail",
|
||||
"reward_default": "Default 50% / 50%",
|
||||
@ -328,7 +330,11 @@
|
||||
"draft_save_success":"Draft Saved",
|
||||
"draft_save_fail":"Failed to save draft",
|
||||
"select_thumb":"Select Post Thumbnail",
|
||||
"two_thumbs_required":"Add more images in your post before setting thumbnail"
|
||||
"two_thumbs_required":"Add more images in your post before setting thumbnail",
|
||||
"scheduled_for":"Scheduled For",
|
||||
"scheduled_immediate":"Immediate",
|
||||
"scheduled_later":"Later",
|
||||
"settings_title":"Post Settings"
|
||||
},
|
||||
"snippets":{
|
||||
"label_no_snippets":"No Snippets Found",
|
||||
@ -671,7 +677,8 @@
|
||||
"percent": "Percent",
|
||||
"username": "Username",
|
||||
"addAccount": "Add Account",
|
||||
"save": "Save"
|
||||
"save": "Save",
|
||||
"cancel":"Cancel"
|
||||
},
|
||||
"welcome":{
|
||||
"label":"Welcome to",
|
||||
|
291
src/screens/editor/children/beneficiarySelectionContent.tsx
Normal file
291
src/screens/editor/children/beneficiarySelectionContent.tsx
Normal file
@ -0,0 +1,291 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { View, FlatList, Text } from 'react-native';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { isArray, debounce } from 'lodash';
|
||||
|
||||
import styles from './styles';
|
||||
import EStyleSheet from 'react-native-extended-stylesheet';
|
||||
|
||||
import { useAppSelector } from '../../../hooks';
|
||||
import { FormInput, IconButton, TextButton } from '../../../components';
|
||||
import { Beneficiary } from '../../../redux/reducers/editorReducer';
|
||||
import { lookupAccounts } from '../../../providers/hive/dhive';
|
||||
|
||||
interface BeneficiarySelectionContent {
|
||||
draftId:string,
|
||||
handleOnSaveBeneficiaries:()=>void
|
||||
}
|
||||
|
||||
const BeneficiarySelectionContent = ({handleOnSaveBeneficiaries, draftId }) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const beneficiariesMap = useAppSelector(state => state.editor.beneficiariesMap)
|
||||
const username = useAppSelector(state=>state.account.currentAccount.name)
|
||||
|
||||
const [beneficiaries, setBeneficiaries] = useState<Beneficiary[]>([
|
||||
{ account: username, weight: 10000, isValid: true},
|
||||
]);
|
||||
|
||||
const [newUsername, setNewUsername] = useState('');
|
||||
const [newWeight, setNewWeight] = useState(0);
|
||||
const [isUsernameValid, setIsUsernameValid] = useState(false);
|
||||
const [isWeightValid, setIsWeightValid] = useState(false);
|
||||
const [newEditable, setNewEditable] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
readTempBeneficiaries();
|
||||
}, [draftId]);
|
||||
|
||||
|
||||
const readTempBeneficiaries = async () => {
|
||||
if(beneficiariesMap){
|
||||
const tempBeneficiaries = beneficiariesMap[draftId || 'temp-beneficiaries'];
|
||||
|
||||
if (isArray(tempBeneficiaries) && tempBeneficiaries.length > 0) {
|
||||
let othersWeight = 0;
|
||||
tempBeneficiaries.forEach((item, index) => {
|
||||
item.isValid = true;
|
||||
if(index > 0){
|
||||
othersWeight += item.weight
|
||||
}
|
||||
});
|
||||
tempBeneficiaries[0].weight = 10000 - othersWeight;
|
||||
setBeneficiaries(tempBeneficiaries);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
const _onSavePress = () => {
|
||||
if(newEditable){
|
||||
beneficiaries.push({
|
||||
account:newUsername,
|
||||
weight:newWeight
|
||||
})
|
||||
}
|
||||
handleOnSaveBeneficiaries(beneficiaries);
|
||||
_resetInputs();
|
||||
}
|
||||
|
||||
|
||||
const _addAccount = () => {
|
||||
|
||||
if(isUsernameValid && isWeightValid){
|
||||
beneficiaries.push({
|
||||
account:newUsername,
|
||||
weight:newWeight,
|
||||
})
|
||||
setBeneficiaries([...beneficiaries]);
|
||||
}
|
||||
|
||||
setIsUsernameValid(false);
|
||||
setIsWeightValid(false);
|
||||
setNewWeight(0);
|
||||
setNewUsername('');
|
||||
setNewEditable(true);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const _onWeightInputChange = (value) => {
|
||||
let _value = (parseInt(value, 10) || 0) * 100;
|
||||
const _diff = _value - newWeight;
|
||||
beneficiaries[0].weight = beneficiaries[0].weight - _diff;
|
||||
setNewWeight(_value)
|
||||
setIsWeightValid(_value > 0 && _value <= 10000)
|
||||
setBeneficiaries([...beneficiaries]);
|
||||
};
|
||||
|
||||
|
||||
const _lookupAccounts = debounce((username) => {
|
||||
|
||||
lookupAccounts(username).then((res) => {
|
||||
const isValid = res.includes(username)
|
||||
//check if username duplicates else lookup contacts, done here to avoid debounce and post call mismatch
|
||||
const notExistAlready = !beneficiaries.find((item)=>item.account === username);
|
||||
setIsUsernameValid(isValid && notExistAlready)
|
||||
});
|
||||
}, 1000)
|
||||
|
||||
|
||||
|
||||
const _onUsernameInputChange = (value) => {
|
||||
setNewUsername(value);
|
||||
_lookupAccounts(value);
|
||||
};
|
||||
|
||||
const _resetInputs = () => {
|
||||
if(newWeight){
|
||||
beneficiaries[0].weight = beneficiaries[0].weight + newWeight;
|
||||
setBeneficiaries([...beneficiaries])
|
||||
setNewWeight(0);
|
||||
}
|
||||
setNewEditable(false);
|
||||
setIsWeightValid(false);
|
||||
setIsUsernameValid(false);
|
||||
setNewUsername('');
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const _renderHeader = () => (
|
||||
<View style={styles.inputWrapper}>
|
||||
<View style={{...styles.weightInput, marginTop:4}}>
|
||||
<Text style={styles.contentLabel}>
|
||||
{intl.formatMessage({
|
||||
id: 'beneficiary_modal.percent',
|
||||
})}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={{...styles.usernameInput, marginTop:4, marginLeft:28}}>
|
||||
<Text style={styles.contentLabel}>
|
||||
{intl.formatMessage({
|
||||
id: 'beneficiary_modal.username',
|
||||
})}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
|
||||
|
||||
|
||||
const _renderInput = () => {
|
||||
|
||||
return (
|
||||
<View style={styles.inputWrapper}>
|
||||
<View style={styles.weightInput}>
|
||||
<FormInput
|
||||
isValid={isWeightValid}
|
||||
value={`${newWeight / 100}`}
|
||||
inputStyle={styles.weightFormInput}
|
||||
wrapperStyle={styles.weightFormInputWrapper}
|
||||
onChange={(value) => _onWeightInputChange(value)}
|
||||
onBlur={() => {}}//_onBlur(item)}
|
||||
keyboardType='numeric'
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={styles.usernameInput}>
|
||||
<FormInput
|
||||
rightIconName="at"
|
||||
iconType="MaterialCommunityIcons"
|
||||
isValid={isUsernameValid}
|
||||
onChange={(value) => _onUsernameInputChange(value.trim())}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'beneficiary_modal.username',
|
||||
})}
|
||||
type="username"
|
||||
isFirstImage
|
||||
value={newUsername}
|
||||
inputStyle={styles.usernameInput}
|
||||
wrapperStyle={styles.usernameFormInputWrapper}
|
||||
/>
|
||||
</View>
|
||||
|
||||
{isWeightValid && isUsernameValid ? (
|
||||
<IconButton
|
||||
name="check"
|
||||
iconType="MaterialCommunityIcons"
|
||||
color={EStyleSheet.value('$primaryBlack')}
|
||||
size={24}
|
||||
iconStyle={{paddingLeft:8}}
|
||||
onPress={_onSavePress}
|
||||
/>
|
||||
) : <View style={{width:28}}/>}
|
||||
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const _renderFooter = () => (
|
||||
<>
|
||||
{newEditable && _renderInput()}
|
||||
<View style={{marginTop: 20, marginBottom:32}}>
|
||||
<TextButton
|
||||
text={newEditable?intl.formatMessage({
|
||||
id: 'beneficiary_modal.cancel'
|
||||
}):intl.formatMessage({
|
||||
id: 'beneficiary_modal.addAccount',
|
||||
})}
|
||||
onPress={newEditable?_resetInputs:_addAccount}
|
||||
textStyle={{
|
||||
color:EStyleSheet.value('$primaryBlue'),
|
||||
fontWeight:'bold'
|
||||
}}
|
||||
/>
|
||||
|
||||
</View>
|
||||
</>
|
||||
|
||||
)
|
||||
|
||||
|
||||
const _renderItem = ({ item, index }) => {
|
||||
const _isCurrentUser = item.account === username;
|
||||
|
||||
const _onRemovePress = () => {
|
||||
beneficiaries[0].weight = beneficiaries[0].weight + item.weight;
|
||||
beneficiaries.splice(index, 1);
|
||||
setBeneficiaries([...beneficiaries]);
|
||||
handleOnSaveBeneficiaries(beneficiaries);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<View style={styles.inputWrapper}>
|
||||
<View style={styles.weightInput}>
|
||||
<FormInput
|
||||
isValid={true}
|
||||
isEditable={false}
|
||||
value={`${item.weight / 100}`}
|
||||
inputStyle={styles.weightFormInput}
|
||||
wrapperStyle={styles.weightFormInputWrapper}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={styles.usernameInput}>
|
||||
<FormInput
|
||||
isValid={true}
|
||||
isEditable={false}
|
||||
type="username"
|
||||
isFirstImage
|
||||
value={item.account}
|
||||
inputStyle={styles.usernameInput}
|
||||
wrapperStyle={styles.usernameFormInputWrapper}
|
||||
/>
|
||||
</View>
|
||||
{!_isCurrentUser ? (
|
||||
<IconButton
|
||||
name="close"
|
||||
iconType="MaterialCommunityIcons"
|
||||
size={24}
|
||||
color={EStyleSheet.value('$primaryBlack')}
|
||||
iconStyle={{paddingLeft:8}}
|
||||
onPress={_onRemovePress}
|
||||
/>
|
||||
):(
|
||||
<View style={{width:30}} />
|
||||
)}
|
||||
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.settingLabel}>{intl.formatMessage({id:'editor.beneficiaries'})}</Text>
|
||||
<FlatList
|
||||
data={beneficiaries}
|
||||
renderItem={_renderItem}
|
||||
ListHeaderComponent={_renderHeader}
|
||||
showsVerticalScrollIndicator={false}
|
||||
/>
|
||||
{_renderFooter()}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default BeneficiarySelectionContent;
|
209
src/screens/editor/children/editorSettingsModal.tsx
Normal file
209
src/screens/editor/children/editorSettingsModal.tsx
Normal file
@ -0,0 +1,209 @@
|
||||
import React, { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { View } from 'react-native';
|
||||
|
||||
import { DateTimePicker, Modal, SettingsItem } from '../../../components';
|
||||
import styles from './editorSettingsModalStyles';
|
||||
import ThumbSelectionContent from './thumbSelectionContent';
|
||||
import {View as AnimatedView} from 'react-native-animatable';
|
||||
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
|
||||
import BeneficiarySelectionContent from './beneficiarySelectionContent';
|
||||
import { Beneficiary } from '../../../redux/reducers/editorReducer';
|
||||
|
||||
const REWARD_TYPES = [
|
||||
{
|
||||
key:'default',
|
||||
intlId:'editor.reward_default'
|
||||
},
|
||||
{
|
||||
key:'sp',
|
||||
intlId:'editor.reward_power_up'
|
||||
},
|
||||
{
|
||||
key:'dp',
|
||||
intlId:'editor.reward_decline'
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
|
||||
export interface EditorSettingsModalRef {
|
||||
showModal:()=>void;
|
||||
}
|
||||
|
||||
|
||||
interface EditorSettingsModalProps {
|
||||
body:string;
|
||||
draftId:string;
|
||||
isEdit:boolean;
|
||||
isCommunityPost:boolean;
|
||||
handleRewardChange:(rewardType:string)=>void;
|
||||
handleThumbSelection:(index:number)=>void;
|
||||
handleScheduleChange:(datetime:string|null)=>void;
|
||||
handleBeneficiariesChange:(beneficiaries:Beneficiary[])=>void;
|
||||
handleShouldReblogChange:(shouldReblog:boolean)=>void;
|
||||
}
|
||||
|
||||
const EditorSettingsModal = forwardRef(({
|
||||
body,
|
||||
draftId,
|
||||
isEdit,
|
||||
isCommunityPost,
|
||||
handleRewardChange,
|
||||
handleThumbSelection,
|
||||
handleScheduleChange,
|
||||
handleBeneficiariesChange,
|
||||
handleShouldReblogChange,
|
||||
}: EditorSettingsModalProps, ref) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [rewardTypeIndex, setRewardTypeIndex] = useState(0);
|
||||
const [thumbIndex, setThumbIndex] = useState(0);
|
||||
const [scheduleLater, setScheduleLater] = useState(false)
|
||||
const [shouldReblog, setShouldReblog] = useState(false);
|
||||
const [scheduledFor, setScheduledFor] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if(handleThumbSelection){
|
||||
handleThumbSelection(thumbIndex);
|
||||
}
|
||||
}, [thumbIndex])
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
if(!scheduleLater){
|
||||
handleScheduleChange(null)
|
||||
}else if(scheduledFor) {
|
||||
handleScheduleChange(scheduledFor)
|
||||
}
|
||||
}, [scheduleLater, scheduledFor])
|
||||
|
||||
useEffect(() => {
|
||||
handleShouldReblogChange(shouldReblog)
|
||||
}, [shouldReblog])
|
||||
|
||||
useEffect(() => {
|
||||
if(!isCommunityPost && shouldReblog){
|
||||
setShouldReblog(false);
|
||||
}
|
||||
}, [isCommunityPost])
|
||||
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
show: () => {
|
||||
setShowModal(true);
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
const _handleRewardChange = (index:number) => {
|
||||
setRewardTypeIndex(index)
|
||||
const rewardTypeKey = REWARD_TYPES[index].key
|
||||
if (handleRewardChange) {
|
||||
handleRewardChange(rewardTypeKey);
|
||||
}
|
||||
}
|
||||
|
||||
const _handleDatePickerChange = (date:string) => {
|
||||
setScheduledFor(date);
|
||||
}
|
||||
|
||||
|
||||
const _renderContent = (
|
||||
<KeyboardAwareScrollView contentContainerStyle={{flex:1}} >
|
||||
<View style={styles.container}>
|
||||
{!isEdit && (
|
||||
<>
|
||||
<SettingsItem
|
||||
title={intl.formatMessage({id:'editor.scheduled_for'}) }
|
||||
type="dropdown"
|
||||
actionType="reward"
|
||||
options={[
|
||||
intl.formatMessage({id:"editor.scheduled_immediate"}),
|
||||
intl.formatMessage({id:"editor.scheduled_later"}),
|
||||
]}
|
||||
selectedOptionIndex={scheduleLater ? 1 : 0}
|
||||
handleOnChange={(index)=>{
|
||||
setScheduleLater(index === 1)
|
||||
}}
|
||||
/>
|
||||
|
||||
{scheduleLater && (
|
||||
<AnimatedView animation="flipInX" duration={700}>
|
||||
<DateTimePicker
|
||||
type="datetime"
|
||||
onChanged={_handleDatePickerChange}
|
||||
disabled={true}
|
||||
/>
|
||||
</AnimatedView>
|
||||
|
||||
)}
|
||||
|
||||
<SettingsItem
|
||||
title={intl.formatMessage({
|
||||
id: 'editor.setting_reward',
|
||||
})}
|
||||
type="dropdown"
|
||||
actionType="reward"
|
||||
options={
|
||||
REWARD_TYPES.map((type)=>intl.formatMessage({ id: type.intlId}))
|
||||
}
|
||||
selectedOptionIndex={rewardTypeIndex}
|
||||
handleOnChange={_handleRewardChange}
|
||||
/>
|
||||
|
||||
|
||||
{isCommunityPost && (
|
||||
<SettingsItem
|
||||
title={intl.formatMessage({
|
||||
id: 'editor.setting_reblog',
|
||||
})}
|
||||
type="toggle"
|
||||
actionType="reblog"
|
||||
isOn={shouldReblog}
|
||||
handleOnChange={setShouldReblog}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
<ThumbSelectionContent
|
||||
body={body}
|
||||
thumbIndex={thumbIndex}
|
||||
onThumbSelection={setThumbIndex}
|
||||
/>
|
||||
|
||||
{!isEdit && (
|
||||
<BeneficiarySelectionContent
|
||||
handleOnSaveBeneficiaries={handleBeneficiariesChange}
|
||||
draftId={draftId}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
</View>
|
||||
</KeyboardAwareScrollView>
|
||||
|
||||
)
|
||||
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={showModal}
|
||||
handleOnModalClose={() => setShowModal(false)}
|
||||
isFullScreen
|
||||
isCloseButton
|
||||
presentationStyle="formSheet"
|
||||
title={intl.formatMessage({id:"editor.settings_title"})}
|
||||
animationType="slide"
|
||||
style={styles.modalStyle}
|
||||
>
|
||||
{_renderContent}
|
||||
</Modal>
|
||||
|
||||
);
|
||||
});
|
||||
|
||||
export default EditorSettingsModal
|
106
src/screens/editor/children/editorSettingsModalStyles.ts
Normal file
106
src/screens/editor/children/editorSettingsModalStyles.ts
Normal file
@ -0,0 +1,106 @@
|
||||
import { TextStyle, StyleSheet, ViewStyle, Dimensions, ImageStyle } from 'react-native';
|
||||
import EStyleSheet from 'react-native-extended-stylesheet';
|
||||
|
||||
const gridItemWidth = ((Dimensions.get('window').width/2) - 32);
|
||||
const gridItemHeight = (gridItemWidth * 500)/600
|
||||
|
||||
export default EStyleSheet.create({
|
||||
modalStyle: {
|
||||
flex: 1,
|
||||
backgroundColor: '$primaryBackgroundColor',
|
||||
margin: 0,
|
||||
paddingTop: 32,
|
||||
paddingBottom: 16,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 32,
|
||||
},
|
||||
bodyWrapper: {
|
||||
flex: 3,
|
||||
paddingHorizontal:16
|
||||
},
|
||||
floatingContainer:{
|
||||
position:'absolute',
|
||||
bottom:0,
|
||||
right:20,
|
||||
justifyContent:'flex-end',
|
||||
zIndex:10
|
||||
} as ViewStyle,
|
||||
|
||||
mediaItem:{
|
||||
margin:8,
|
||||
height:gridItemHeight,
|
||||
width:gridItemWidth,
|
||||
borderRadius:16,
|
||||
backgroundColor:'$primaryLightGray'
|
||||
} as ImageStyle,
|
||||
|
||||
inputContainer:{
|
||||
flex:1
|
||||
} as ViewStyle,
|
||||
titleInput:{
|
||||
color: '$primaryBlack',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 18,
|
||||
textAlignVertical: 'top',
|
||||
paddingVertical: 0,
|
||||
backgroundColor:'$primaryBackgroundColor',
|
||||
borderBottomWidth:StyleSheet.hairlineWidth,
|
||||
borderBottomColor:'$primaryDarkGray'
|
||||
} as TextStyle,
|
||||
dateTimeModa:{
|
||||
backgroundColor: 'white',
|
||||
alignItems: 'center',
|
||||
} as ViewStyle,
|
||||
title: {
|
||||
fontWeight: '700',
|
||||
flex:1,
|
||||
fontSize:16,
|
||||
color:'$primaryBlack'
|
||||
} as TextStyle,
|
||||
|
||||
btnText:{
|
||||
color:'$pureWhite'
|
||||
} as TextStyle,
|
||||
saveButton:{
|
||||
|
||||
backgroundColor:'$primaryBlue',
|
||||
width:150,
|
||||
paddingVertical:16,
|
||||
borderRadius:32,
|
||||
justifyContent:'center',
|
||||
alignItems:'center'
|
||||
} as ViewStyle,
|
||||
closeButton:{
|
||||
marginRight:16,
|
||||
paddingVertical:8,
|
||||
borderRadius:16,
|
||||
justifyContent:'center',
|
||||
alignItems:'center'
|
||||
} as ViewStyle,
|
||||
actionPanel:{
|
||||
flexDirection:'row',
|
||||
justifyContent:'flex-end',
|
||||
alignItems:'center',
|
||||
marginBottom:16
|
||||
} as ViewStyle,
|
||||
|
||||
itemIcon:{
|
||||
color:'$white',
|
||||
} as ViewStyle,
|
||||
|
||||
itemIconWrapper:{
|
||||
justifyContent:'center',
|
||||
alignItems:'center',
|
||||
backgroundColor:'$primaryRed',
|
||||
|
||||
} as ViewStyle,
|
||||
|
||||
removeItemContainer:{
|
||||
position:'absolute',
|
||||
top:16,
|
||||
right:16
|
||||
} as ViewStyle
|
||||
})
|
53
src/screens/editor/children/styles.ts
Normal file
53
src/screens/editor/children/styles.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import EStyleSheet from 'react-native-extended-stylesheet';
|
||||
import { getBottomSpace } from 'react-native-iphone-x-helper';
|
||||
|
||||
export default EStyleSheet.create({
|
||||
|
||||
sheetContent: {
|
||||
backgroundColor: '$primaryBackgroundColor',
|
||||
position:'absolute',
|
||||
bottom:0,
|
||||
left:0,
|
||||
right:0,
|
||||
zIndex:999
|
||||
},
|
||||
thumbStyle:{
|
||||
width:72,
|
||||
height:72,
|
||||
marginVertical:8,
|
||||
marginRight:8,
|
||||
borderRadius:12,
|
||||
backgroundColor:'$primaryLightGray'
|
||||
},
|
||||
selectedStyle:{
|
||||
borderWidth:4,
|
||||
borderColor:'$primaryBlack'
|
||||
},
|
||||
settingLabel:{
|
||||
color: '$primaryDarkGray',
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold',
|
||||
flexGrow: 1,
|
||||
},
|
||||
listContainer:{
|
||||
paddingBottom:getBottomSpace() + 16,
|
||||
},
|
||||
container:{
|
||||
paddingVertical:16
|
||||
},
|
||||
bodyWrapper: { flex: 1, paddingTop: 20, paddingBottom:20},
|
||||
inputWrapper: { flexDirection: 'row', alignItems: 'center' },
|
||||
contentLabel: { color: '$iconColor', marginTop:4 },
|
||||
weightInput: {width:80},
|
||||
weightFormInput: { textAlign: 'center', color: '$primaryBlack' },
|
||||
weightFormInputWrapper: { marginTop: 8 },
|
||||
usernameInput: { flex:1, color: '$primaryBlack', marginLeft: 16 },
|
||||
usernameFormInputWrapper: { marginTop: 8 },
|
||||
footerWrapper: { paddingTop:16 },
|
||||
saveButton: {
|
||||
width: 140,
|
||||
height: 44,
|
||||
alignSelf: 'flex-end',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
76
src/screens/editor/children/thumbSelectionContent.tsx
Normal file
76
src/screens/editor/children/thumbSelectionContent.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { Alert, Text, View } from 'react-native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import { FlatList, TouchableOpacity } from 'react-native-gesture-handler';
|
||||
import { extractImageUrls } from '../../../utils/editor';
|
||||
import styles from './styles';
|
||||
|
||||
interface ThumbSelectionContentProps {
|
||||
body:string;
|
||||
thumbIndex:number;
|
||||
onThumbSelection:(index:number)=>void;
|
||||
}
|
||||
|
||||
const ThumbSelectionContent = ({body, thumbIndex, onThumbSelection}: ThumbSelectionContentProps) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const [imageUrls, setImageUrls] = useState<string[]>([]);
|
||||
const [needMore, setNeedMore] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const urls = extractImageUrls({body});
|
||||
|
||||
if(urls.length < 2){
|
||||
setNeedMore(true);
|
||||
onThumbSelection(0);
|
||||
setImageUrls([])
|
||||
}else{
|
||||
setNeedMore(false);
|
||||
setImageUrls(urls)
|
||||
}
|
||||
}, [body])
|
||||
|
||||
|
||||
//VIEW_RENDERERS
|
||||
const _renderImageItem = ({item, index}:{item:string, index:number}) => {
|
||||
const _onPress = () => {
|
||||
onThumbSelection(index);
|
||||
}
|
||||
|
||||
const selectedStyle = index === thumbIndex ? styles.selectedStyle : null
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={_onPress} >
|
||||
<FastImage
|
||||
source={{uri:item}}
|
||||
style={{...styles.thumbStyle, ...selectedStyle}}
|
||||
resizeMode='cover'
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<View style={{marginTop:12}}>
|
||||
<Text style={styles.settingLabel}>{intl.formatMessage({id:'editor.select_thumb'})}</Text>
|
||||
{
|
||||
needMore ? (
|
||||
<Text style={styles.contentLabel}>Add more images to post</Text>
|
||||
):(
|
||||
<FlatList
|
||||
data={imageUrls}
|
||||
renderItem={_renderImageItem}
|
||||
keyExtractor={(item, index)=>`${item}-${index}`}
|
||||
horizontal={true}
|
||||
contentContainerStyle={styles.listContainer}
|
||||
showsHorizontalScrollIndicator={false}/>
|
||||
)
|
||||
}
|
||||
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThumbSelectionContent;
|
@ -2,13 +2,14 @@ import React, { useImperativeHandle, useRef, useState } from 'react';
|
||||
import { FlatList, TouchableOpacity } from 'react-native-gesture-handler';
|
||||
import ActionSheet from 'react-native-actions-sheet';
|
||||
import EStyleSheet from 'react-native-extended-stylesheet';
|
||||
import styles from './thumbSelectionModalStyles';
|
||||
import styles from './styles';
|
||||
import { extractImageUrls } from '../../../utils/editor';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import { forwardRef } from 'react';
|
||||
import { View, Text, Alert } from 'react-native';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
|
||||
export interface ThumbSelectionModalProps {
|
||||
thumbIndex:number;
|
||||
onThumbSelection:(index:number)=>void;
|
||||
@ -58,7 +59,6 @@ const ThumbSelectionModal = ({ onThumbSelection, thumbIndex }:ThumbSelectionModa
|
||||
|
||||
const selectedStyle = index === thumbIndex ? styles.selectedStyle : null
|
||||
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={_onPress} >
|
||||
<FastImage
|
||||
|
@ -1,36 +0,0 @@
|
||||
import EStyleSheet from 'react-native-extended-stylesheet';
|
||||
import { getBottomSpace } from 'react-native-iphone-x-helper';
|
||||
|
||||
export default EStyleSheet.create({
|
||||
|
||||
sheetContent: {
|
||||
backgroundColor: '$primaryBackgroundColor',
|
||||
position:'absolute',
|
||||
bottom:0,
|
||||
left:0,
|
||||
right:0,
|
||||
zIndex:999
|
||||
},
|
||||
thumbStyle:{
|
||||
width:100,
|
||||
height:100,
|
||||
margin:8,
|
||||
borderRadius:12,
|
||||
backgroundColor:'$primaryLightGray'
|
||||
},
|
||||
selectedStyle:{
|
||||
borderWidth:4,
|
||||
borderColor:'$primaryBlack'
|
||||
},
|
||||
title:{
|
||||
color: '$primaryBlack',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 18,
|
||||
padding: 16,
|
||||
textAlign:'center'
|
||||
},
|
||||
listContainer:{
|
||||
paddingHorizontal:8,
|
||||
paddingBottom:getBottomSpace() + 16,
|
||||
}
|
||||
});
|
@ -22,6 +22,7 @@ import {
|
||||
getPurePost,
|
||||
grantPostingPermission,
|
||||
signImage,
|
||||
reblog,
|
||||
} from '../../../providers/hive/dhive';
|
||||
import { setDraftPost, getDraftPost } from '../../../realm/realm';
|
||||
|
||||
@ -74,6 +75,7 @@ class EditorContainer extends Component {
|
||||
sharedSnippetText: null,
|
||||
onLoadDraftPress: false,
|
||||
thumbIndex: 0,
|
||||
shouldReblog:false
|
||||
};
|
||||
}
|
||||
|
||||
@ -573,6 +575,7 @@ class EditorContainer extends Component {
|
||||
};
|
||||
|
||||
_submitPost = async ({ fields, scheduleDate }: { fields: any, scheduleDate?: string }) => {
|
||||
|
||||
const {
|
||||
currentAccount,
|
||||
dispatch,
|
||||
@ -581,7 +584,9 @@ class EditorContainer extends Component {
|
||||
pinCode,
|
||||
// isDefaultFooter,
|
||||
} = this.props;
|
||||
const { rewardType, beneficiaries, isPostSending, thumbIndex, draftId } = this.state;
|
||||
const { rewardType, beneficiaries, isPostSending, thumbIndex, draftId, shouldReblog} = this.state;
|
||||
|
||||
|
||||
|
||||
if (isPostSending) {
|
||||
return;
|
||||
@ -644,7 +649,24 @@ class EditorContainer extends Component {
|
||||
options,
|
||||
voteWeight,
|
||||
)
|
||||
.then(async () => {
|
||||
.then((response) => {
|
||||
|
||||
console.log(response);
|
||||
|
||||
//reblog if flag is active
|
||||
if(shouldReblog){
|
||||
reblog(
|
||||
currentAccount,
|
||||
pinCode,
|
||||
author,
|
||||
permlink
|
||||
).then((resp)=>{
|
||||
console.log("Successfully reblogged post", resp)
|
||||
}).catch((err)=>{
|
||||
console.warn("Failed to reblog post", err)
|
||||
})
|
||||
}
|
||||
|
||||
//post publish updates
|
||||
setDraftPost(
|
||||
{
|
||||
@ -736,7 +758,7 @@ class EditorContainer extends Component {
|
||||
|
||||
_submitEdit = async (fields) => {
|
||||
const { currentAccount, pinCode } = this.props;
|
||||
const { post, isEdit, isPostSending } = this.state;
|
||||
const { post, isEdit, isPostSending, thumbIndex } = this.state;
|
||||
|
||||
if (isPostSending) {
|
||||
return;
|
||||
@ -762,7 +784,7 @@ class EditorContainer extends Component {
|
||||
newBody = patch;
|
||||
}
|
||||
|
||||
const meta = extractMetadata(fields.body);
|
||||
const meta = extractMetadata(fields.body, thumbIndex);
|
||||
|
||||
let jsonMeta = {};
|
||||
|
||||
@ -858,6 +880,7 @@ class EditorContainer extends Component {
|
||||
const { isReply, isEdit } = this.state;
|
||||
const { intl } = this.props;
|
||||
|
||||
|
||||
if (isReply && !isEdit) {
|
||||
this._submitReply(form.fields);
|
||||
} else if (isEdit) {
|
||||
@ -925,7 +948,8 @@ class EditorContainer extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
_handleDatePickerChange = async (datePickerValue, fields) => {
|
||||
|
||||
_handleSchedulePress = async (datePickerValue, fields) => {
|
||||
const { currentAccount, pinCode, intl } = this.props;
|
||||
|
||||
if (fields.title === '' || fields.body === '') {
|
||||
@ -1059,6 +1083,13 @@ class EditorContainer extends Component {
|
||||
dispatch(setBeneficiaries(draftId || 'temp-beneficiaries', value));
|
||||
};
|
||||
|
||||
_handleShouldReblogChange = (value:boolean) => {
|
||||
this.setState({
|
||||
shouldReblog:value
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
_handleSetThumbIndex = (index: number) => {
|
||||
this.setState({
|
||||
thumbIndex: index
|
||||
@ -1094,7 +1125,8 @@ class EditorContainer extends Component {
|
||||
draftPost={draftPost}
|
||||
handleRewardChange={this._handleRewardChange}
|
||||
handleBeneficiaries={this._handleBeneficiaries}
|
||||
handleDatePickerChange={this._handleDatePickerChange}
|
||||
handleShouldReblogChange={this._handleShouldReblogChange}
|
||||
handleSchedulePress={this._handleSchedulePress}
|
||||
handleFormChanged={this._handleFormChanged}
|
||||
handleOnBackPress={() => { }}
|
||||
handleOnImagePicker={this._handleRoutingAction}
|
||||
|
@ -32,6 +32,7 @@ import { isCommunity } from '../../../utils/communityValidation';
|
||||
|
||||
import styles from './editorScreenStyles';
|
||||
import ThumbSelectionModal from '../children/thumbSelectionModal';
|
||||
import EditorSettingsModal from '../children/editorSettingsModal';
|
||||
|
||||
class EditorScreen extends Component {
|
||||
/* Props
|
||||
@ -39,6 +40,7 @@ class EditorScreen extends Component {
|
||||
* @prop { type } name - Description....
|
||||
*/
|
||||
thumbSelectionModalRef = null;
|
||||
editorSettingsModalRef = null;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -57,6 +59,7 @@ class EditorScreen extends Component {
|
||||
isCommunitiesListModalOpen: false,
|
||||
selectedCommunity: null,
|
||||
selectedAccount: null,
|
||||
scheduledFor:null
|
||||
};
|
||||
}
|
||||
|
||||
@ -164,8 +167,13 @@ class EditorScreen extends Component {
|
||||
};
|
||||
|
||||
_handleOnSubmit = () => {
|
||||
const { handleOnSubmit } = this.props;
|
||||
const { fields } = this.state;
|
||||
const { handleOnSubmit, handleSchedulePress } = this.props;
|
||||
const { fields, scheduledFor } = this.state;
|
||||
|
||||
if(scheduledFor && handleSchedulePress){
|
||||
handleSchedulePress(scheduledFor, fields);
|
||||
return;
|
||||
}
|
||||
|
||||
if (handleOnSubmit) {
|
||||
handleOnSubmit({ fields });
|
||||
@ -186,6 +194,18 @@ class EditorScreen extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
_handleScheduleChange = (datetime:string|null) => {
|
||||
this.setState({
|
||||
scheduledFor:datetime
|
||||
})
|
||||
}
|
||||
|
||||
_handleSettingsPress = () => {
|
||||
if(this.editorSettingsModalRef){
|
||||
this.editorSettingsModalRef.show();
|
||||
}
|
||||
}
|
||||
|
||||
_handleIsFormValid = (bodyText) => {
|
||||
const { fields } = this.state;
|
||||
const { isReply, isLoggedIn } = this.props;
|
||||
@ -313,10 +333,10 @@ class EditorScreen extends Component {
|
||||
isPreviewActive,
|
||||
wordsCount,
|
||||
isFormValid,
|
||||
isRemoveTag,
|
||||
isCommunitiesListModalOpen,
|
||||
selectedCommunity,
|
||||
selectedAccount,
|
||||
scheduledFor,
|
||||
} = this.state;
|
||||
const {
|
||||
handleOnImagePicker,
|
||||
@ -332,19 +352,22 @@ class EditorScreen extends Component {
|
||||
post,
|
||||
uploadedImage,
|
||||
handleOnBackPress,
|
||||
handleDatePickerChange,
|
||||
handleSchedulePress,
|
||||
handleRewardChange,
|
||||
handleBeneficiaries,
|
||||
handleShouldReblogChange,
|
||||
currentAccount,
|
||||
autoFocusText,
|
||||
sharedSnippetText,
|
||||
onLoadDraftPress,
|
||||
thumbIndex,
|
||||
} = this.props;
|
||||
|
||||
const rightButtonText = intl.formatMessage({
|
||||
id: isEdit ? 'basic_header.update' : isReply ? 'basic_header.reply' : 'basic_header.publish',
|
||||
id: isEdit ? 'basic_header.update' : isReply ? 'basic_header.reply' : scheduledFor ? 'basic_header.schedule' : 'basic_header.publish',
|
||||
});
|
||||
|
||||
|
||||
const _renderCommunityModal = () => {
|
||||
return (
|
||||
<Modal
|
||||
@ -367,7 +390,7 @@ class EditorScreen extends Component {
|
||||
return (
|
||||
<View style={globalStyles.defaultContainer}>
|
||||
<BasicHeader
|
||||
handleDatePickerChange={(date) => handleDatePickerChange(date, fields)}
|
||||
handleSchedulePress={(date) => handleSchedulePress(date, fields)}
|
||||
handleRewardChange={handleRewardChange}
|
||||
handleBeneficiaries={handleBeneficiaries}
|
||||
handleOnBackPress={handleOnBackPress}
|
||||
@ -387,6 +410,7 @@ class EditorScreen extends Component {
|
||||
quickTitle={wordsCount > 0 && `${wordsCount} words`}
|
||||
rightButtonText={rightButtonText}
|
||||
showThumbSelectionModal={this._showThumbSelectionModal}
|
||||
handleSettingsPress={this._handleSettingsPress}
|
||||
/>
|
||||
<PostForm
|
||||
handleFormUpdate={this._handleFormUpdate}
|
||||
@ -436,6 +460,18 @@ class EditorScreen extends Component {
|
||||
thumbIndex={thumbIndex}
|
||||
onThumbSelection={this._handleOnThumbSelection}
|
||||
/>
|
||||
<EditorSettingsModal
|
||||
ref={(componentRef) => (this.editorSettingsModalRef = componentRef)}
|
||||
body={fields.body}
|
||||
draftId={draftId}
|
||||
isEdit={isEdit}
|
||||
isCommunityPost={selectedCommunity !== null}
|
||||
handleThumbSelection={this._handleOnThumbSelection}
|
||||
handleRewardChange={handleRewardChange}
|
||||
handleScheduleChange={this._handleScheduleChange}
|
||||
handleBeneficiariesChange={handleBeneficiaries}
|
||||
handleShouldReblogChange={handleShouldReblogChange}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user