channel creator updates. upload image assets.
This commit is contained in:
parent
27c0ac7585
commit
2f0014be08
7 changed files with 475 additions and 39 deletions
|
@ -12,7 +12,7 @@
|
|||
"base-64": "^0.1.0",
|
||||
"@expo/vector-icons": "^8.1.0",
|
||||
"gfycat-style-urls": "^1.0.3",
|
||||
"lbry-redux": "lbryio/lbry-redux#053aead0d92b02fdd192ab5288104ad6688d549f",
|
||||
"lbry-redux": "lbryio/lbry-redux#40bbf6afbdc66806d2f1a810d125bb290999c18b",
|
||||
"lbryinc": "lbryio/lbryinc#4c761084990557d379c85e1c998fcae7e5db143a",
|
||||
"lodash": ">=4.17.11",
|
||||
"merge": ">=1.2.1",
|
||||
|
|
|
@ -204,7 +204,11 @@ class ClaimList extends React.PureComponent {
|
|||
|
||||
const options = this.buildClaimSearchOptions();
|
||||
const claimSearchKey = createNormalizedClaimSearchKey(options);
|
||||
const uris = claimSearchByQuery[claimSearchKey];
|
||||
let uris = claimSearchByQuery[claimSearchKey];
|
||||
|
||||
if (uris) {
|
||||
uris = uris.filter(uri => uri && uri.length > 0);
|
||||
}
|
||||
|
||||
if (Constants.ORIENTATION_VERTICAL === orientation) {
|
||||
return (
|
||||
|
|
|
@ -67,11 +67,12 @@ export default class TagSearch extends React.PureComponent {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { name, style, type, selectedTags = [], showNsfwTags } = this.props;
|
||||
const { editable, name, style, type, selectedTags = [], showNsfwTags } = this.props;
|
||||
|
||||
return (
|
||||
<View>
|
||||
<TextInput
|
||||
editable={editable}
|
||||
style={tagStyle.searchInput}
|
||||
placeholder={'Search for more tags'}
|
||||
underlineColorAndroid={Colors.NextLbryGreen}
|
||||
|
|
|
@ -3,9 +3,12 @@ import {
|
|||
selectBalance,
|
||||
selectMyChannelClaims,
|
||||
selectFetchingMyChannels,
|
||||
selectUpdatingChannel,
|
||||
selectUpdateChannelError,
|
||||
doAbandonClaim,
|
||||
doFetchChannelListMine,
|
||||
doCreateChannel,
|
||||
doUpdateChannel,
|
||||
doToast,
|
||||
} from 'lbry-redux';
|
||||
import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
|
||||
|
@ -16,12 +19,15 @@ const select = state => ({
|
|||
channels: selectMyChannelClaims(state),
|
||||
fetchingChannels: selectFetchingMyChannels(state),
|
||||
balance: selectBalance(state),
|
||||
updatingChannel: selectUpdatingChannel(state),
|
||||
updateChannelError: selectUpdateChannelError(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
abandonClaim: (txid, nout) => dispatch(doAbandonClaim(txid, nout)),
|
||||
notify: data => dispatch(doToast(data)),
|
||||
createChannel: (name, amount, optionalParams) => dispatch(doCreateChannel(name, amount, optionalParams)),
|
||||
updateChannel: params => dispatch(doUpdateChannel(params)),
|
||||
fetchChannelListMine: () => dispatch(doFetchChannelListMine()),
|
||||
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_CHANNEL_CREATOR)),
|
||||
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
|
||||
|
|
|
@ -8,12 +8,13 @@ import {
|
|||
Image,
|
||||
NativeModules,
|
||||
Picker,
|
||||
ScrollView,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { navigateToUri } from 'utils/helper';
|
||||
import { navigateToUri, uploadImageAsset } from 'utils/helper';
|
||||
import Button from 'component/button';
|
||||
import ChannelIconItem from 'component/channelIconItem';
|
||||
import Colors from 'styles/colors';
|
||||
|
@ -21,6 +22,8 @@ import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
|||
import FloatingWalletBalance from 'component/floatingWalletBalance';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||
import Link from 'component/link';
|
||||
import Tag from 'component/tag';
|
||||
import TagSearch from 'component/tagSearch';
|
||||
import UriBar from 'component/uriBar';
|
||||
import channelCreatorStyle from 'styles/channelCreator';
|
||||
import channelIconStyle from 'styles/channelIcon';
|
||||
|
@ -28,6 +31,8 @@ import channelIconStyle from 'styles/channelIcon';
|
|||
export default class ChannelCreator extends React.PureComponent {
|
||||
state = {
|
||||
autoStyle: null,
|
||||
canSave: false,
|
||||
claimId: null,
|
||||
currentSelectedValue: Constants.ITEM_ANONYMOUS,
|
||||
currentPhase: Constants.PHASE_LIST,
|
||||
displayName: null,
|
||||
|
@ -43,12 +48,27 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
showCreateChannel: false,
|
||||
thumbnailUrl: null,
|
||||
coverImageUrl: null,
|
||||
|
||||
avatarImagePickerOpen: false,
|
||||
coverImagePickerOpen: false,
|
||||
uploadingImage: false,
|
||||
|
||||
autoStyles: [],
|
||||
editMode: false,
|
||||
selectionMode: false,
|
||||
selectedChannels: [],
|
||||
|
||||
currentChannelName: null, // if editing, the current channel name
|
||||
description: null,
|
||||
website: null,
|
||||
email: null,
|
||||
tags: [],
|
||||
|
||||
showOptionalFields: false,
|
||||
titleFocused: false,
|
||||
descriptionFocused: false,
|
||||
websiteFocused: false,
|
||||
emailFocused: false,
|
||||
};
|
||||
|
||||
didFocusListener;
|
||||
|
@ -75,17 +95,38 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
this.onComponentFocused();
|
||||
}
|
||||
|
||||
generateAutoStyles = size => {
|
||||
const autoStyles = [];
|
||||
for (let i = 0; i < size; i++) {
|
||||
autoStyles.push(
|
||||
ChannelIconItem.AUTO_THUMB_STYLES[Math.floor(Math.random() * ChannelIconItem.AUTO_THUMB_STYLES.length)]
|
||||
);
|
||||
}
|
||||
return autoStyles;
|
||||
};
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const { currentRoute } = nextProps;
|
||||
const { currentRoute: prevRoute } = this.props;
|
||||
const { currentRoute, updatingChannel, updateChannelError } = nextProps;
|
||||
const { currentRoute: prevRoute, notify } = this.props;
|
||||
|
||||
if (Constants.DRAWER_ROUTE_CHANNEL_CREATOR === currentRoute && currentRoute !== prevRoute) {
|
||||
this.onComponentFocused();
|
||||
}
|
||||
|
||||
if (this.state.updateChannelStarted && !updatingChannel) {
|
||||
if (updateChannelError && updateChannelError.length > 0) {
|
||||
notify({ message: `The channel could not be updated: ${updateChannelError}`, error: true });
|
||||
} else {
|
||||
// successful channel update
|
||||
notify({ message: 'The channel was successfully updated.' });
|
||||
this.showChannelList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onComponentFocused = () => {
|
||||
const {
|
||||
balance,
|
||||
channels,
|
||||
channelName,
|
||||
fetchChannelListMine,
|
||||
|
@ -100,14 +141,53 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
if (!fetchingChannels) {
|
||||
fetchChannelListMine();
|
||||
}
|
||||
if (balance > 0.1) {
|
||||
this.setState({ canSave: true });
|
||||
}
|
||||
|
||||
DeviceEventEmitter.addListener('onDocumentPickerFilePicked', this.onFilePicked);
|
||||
DeviceEventEmitter.addListener('onDocumentPickerCanceled', this.onPickerCanceled);
|
||||
});
|
||||
};
|
||||
|
||||
handleModePressed = () => {
|
||||
this.setState({ showOptionalFields: !this.state.showOptionalFields });
|
||||
};
|
||||
|
||||
onFilePicked = evt => {
|
||||
console.log(evt);
|
||||
const { notify } = this.props;
|
||||
|
||||
if (evt.path && evt.path.length > 0) {
|
||||
// check which image we're trying to upload
|
||||
// should only be one or the other, so just default to cover
|
||||
const isCover = this.state.coverImagePickerOpen;
|
||||
this.setState(
|
||||
{
|
||||
uploadingImage: true,
|
||||
avatarImagePickerOpen: false,
|
||||
coverImagePickerOpen: false,
|
||||
},
|
||||
() => {
|
||||
uploadImageAsset(
|
||||
`file://${evt.path}`,
|
||||
({ url }) => {
|
||||
if (isCover) {
|
||||
this.setState({ coverImageUrl: url, uploadingImage: false });
|
||||
} else {
|
||||
this.setState({ thumbnailUrl: url, uploadingImage: false });
|
||||
}
|
||||
},
|
||||
error => {
|
||||
notify({ message: `The image could not be uploaded: ${error}` });
|
||||
this.setState({ uploadingImage: false });
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// could not determine the file path
|
||||
notify({ message: 'We could not use the selected image. Please try a different image.' });
|
||||
}
|
||||
};
|
||||
|
||||
onPickerCanceled = () => {
|
||||
|
@ -115,9 +195,11 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
};
|
||||
|
||||
componentDidUpdate() {
|
||||
const { channelName } = this.props;
|
||||
if (this.state.currentSelectedValue !== channelName) {
|
||||
this.setState({ currentSelectedValue: channelName });
|
||||
const { channels } = this.props;
|
||||
if (channels && this.state.autoStyles.length !== channels.length) {
|
||||
this.setState({
|
||||
autoStyles: this.generateAutoStyles(channels.length),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,9 +236,21 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
}
|
||||
};
|
||||
|
||||
handleDescriptionChange = value => {
|
||||
this.setState({ description: value });
|
||||
};
|
||||
|
||||
handleWebsiteChange = value => {
|
||||
this.setState({ website: value });
|
||||
};
|
||||
|
||||
handleEmailChange = value => {
|
||||
this.setState({ email: value });
|
||||
};
|
||||
|
||||
handleNewChannelTitleChange = value => {
|
||||
this.setState({ newChannelTitle: value });
|
||||
if (value && !this.state.channelNameUserEdited) {
|
||||
if (value && !this.state.editMode && !this.state.channelNameUserEdited) {
|
||||
// build the channel name based on the title
|
||||
const channelName = value
|
||||
.replace(new RegExp(regexInvalidURI.source, regexInvalidURI.flags + 'g'), '')
|
||||
|
@ -211,15 +305,33 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
};
|
||||
|
||||
handleCreateChannelClick = () => {
|
||||
const { balance, createChannel, onChannelChange, notify } = this.props;
|
||||
const { newChannelBid, newChannelName, newChannelTitle } = this.state;
|
||||
const { balance, createChannel, onChannelChange, notify, updateChannel } = this.props;
|
||||
const {
|
||||
claimId,
|
||||
coverImageUrl,
|
||||
currentChannelName,
|
||||
editMode,
|
||||
newChannelBid,
|
||||
newChannelName,
|
||||
newChannelTitle,
|
||||
description,
|
||||
email,
|
||||
tags,
|
||||
thumbnailUrl,
|
||||
website,
|
||||
} = this.state;
|
||||
|
||||
if (newChannelName.trim().length === 0 || !isURIValid(newChannelName.substr(1), false)) {
|
||||
notify({ message: 'Your channel name contains invalid characters.' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.channelExists(newChannelName)) {
|
||||
// shouldn't do this check in edit mode
|
||||
if (
|
||||
(editMode && currentChannelName !== newChannelName && this.channelExists(newChannelName)) ||
|
||||
(!editMode && this.channelExists(newChannelName))
|
||||
) {
|
||||
// TODO: boolean check improvement?
|
||||
notify({ message: 'You have already created a channel with the same name.' });
|
||||
return;
|
||||
}
|
||||
|
@ -232,7 +344,6 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
const channelName = `@${newChannelName}`;
|
||||
|
||||
this.setState({
|
||||
creatingChannel: true,
|
||||
createChannelError: undefined,
|
||||
});
|
||||
|
||||
|
@ -262,9 +373,33 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
|
||||
const optionalParams = {
|
||||
title: newChannelTitle,
|
||||
description,
|
||||
email,
|
||||
tags: tags.map(tag => {
|
||||
return { name: tag };
|
||||
}),
|
||||
website,
|
||||
cover: coverImageUrl,
|
||||
thumbnail: thumbnailUrl,
|
||||
};
|
||||
|
||||
createChannel(channelName, newChannelBid, optionalParams).then(success, failure);
|
||||
if (this.state.editMode) {
|
||||
// updateChannel
|
||||
// TODO: Change updateChannel in lby-redux to match createChannel with success and failure callbacks?
|
||||
const params = Object.assign(
|
||||
{},
|
||||
{
|
||||
claim_id: claimId,
|
||||
amount: newChannelBid,
|
||||
},
|
||||
optionalParams
|
||||
);
|
||||
this.setState({ updateChannelStarted: true }, () => updateChannel(params));
|
||||
} else {
|
||||
this.setState({ creatingChannel: true }, () =>
|
||||
createChannel(channelName, newChannelBid, optionalParams).then(success, failure)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
channelExists = name => {
|
||||
|
@ -282,6 +417,12 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
};
|
||||
|
||||
onCoverImagePress = () => {
|
||||
const { notify } = this.props;
|
||||
if (this.state.uploadingImage) {
|
||||
notify({ message: 'There is an image upload in progress. Please wait for the upload to complete.' });
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState(
|
||||
{
|
||||
avatarImagePickerOpen: false,
|
||||
|
@ -292,6 +433,12 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
};
|
||||
|
||||
onAvatarImagePress = () => {
|
||||
const { notify } = this.props;
|
||||
if (this.state.uploadingImage) {
|
||||
notify({ message: 'There is an image upload in progress. Please wait for the upload to complete.' });
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState(
|
||||
{
|
||||
avatarImagePickerOpen: true,
|
||||
|
@ -316,6 +463,8 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
|
||||
resetChannelCreator = () => {
|
||||
this.setState({
|
||||
canSave: false,
|
||||
claimId: null,
|
||||
editMode: false,
|
||||
displayName: null,
|
||||
channelNameUserEdited: false,
|
||||
|
@ -332,6 +481,19 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
coverImageUrl: null,
|
||||
avatarImagePickerOpen: false,
|
||||
coverImagePickerOpen: false,
|
||||
|
||||
currentChannelName: null,
|
||||
description: null,
|
||||
email: null,
|
||||
tags: [],
|
||||
website: null,
|
||||
|
||||
showOptionalFields: false,
|
||||
titleFocused: false,
|
||||
descriptionFocused: false,
|
||||
websiteFocused: false,
|
||||
emailFocused: false,
|
||||
uploadingImage: false,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -351,11 +513,23 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
};
|
||||
|
||||
prepareEdit = channel => {
|
||||
const { value } = channel;
|
||||
|
||||
this.setState({
|
||||
claimId: channel.claim_id,
|
||||
currentPhase: Constants.PHASE_CREATE,
|
||||
editMode: true,
|
||||
coverImageUrl: value && value.cover ? value.cover.url : null,
|
||||
currentChannelName: channel.name.substring(1),
|
||||
newChannelName: channel.name.substring(1),
|
||||
newChannelTitle: channel.meta.title ? channel.meta.title : null,
|
||||
newChannelTitle: value ? value.title : null,
|
||||
newChannelBid: channel.amount,
|
||||
description: value ? value.description : null,
|
||||
email: value ? value.email : null,
|
||||
website: value ? value.website_url : null,
|
||||
tags: value ? value.tags : [],
|
||||
thumbnailUrl: value && value.thumbnail ? value.thumbnail.url : null,
|
||||
showOptionalFields: value && (value.description || value.email || value.website_url || value.tags),
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -387,6 +561,37 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
);
|
||||
};
|
||||
|
||||
handleAddTag = tag => {
|
||||
if (!tag || !this.state.canSave || this.state.creatingChannel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { notify } = this.props;
|
||||
const { tags } = this.state;
|
||||
const index = tags.indexOf(tag.toLowerCase());
|
||||
if (index === -1) {
|
||||
const newTags = tags.slice();
|
||||
newTags.push(tag);
|
||||
this.setState({ tags: newTags });
|
||||
} else {
|
||||
notify({ message: __(`You already added the "${tag}" tag.`) });
|
||||
}
|
||||
};
|
||||
|
||||
handleRemoveTag = tag => {
|
||||
if (!tag || !this.state.canSave || this.state.creatingChannel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newTags = this.state.tags.slice();
|
||||
const index = newTags.indexOf(tag.toLowerCase());
|
||||
|
||||
if (index > -1) {
|
||||
newTags.splice(index, 1);
|
||||
this.setState({ tags: newTags });
|
||||
}
|
||||
};
|
||||
|
||||
selectedChannelIndex = channel => {
|
||||
const { selectedChannels } = this.state;
|
||||
for (let i = 0; i < selectedChannels.length; i++) {
|
||||
|
@ -426,14 +631,15 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { fetchingChannels, channels = [], navigation } = this.props;
|
||||
|
||||
console.log(channels);
|
||||
const { fetchingChannels, updatingChannel, channels = [], navigation } = this.props;
|
||||
|
||||
const {
|
||||
autoStyle,
|
||||
autoStyles,
|
||||
coverImageUrl,
|
||||
currentPhase,
|
||||
canSave,
|
||||
editMode,
|
||||
newChannelName,
|
||||
newChannelNameError,
|
||||
newChannelBid,
|
||||
|
@ -445,6 +651,7 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
thumbnailUrl,
|
||||
selectionMode,
|
||||
selectedChannels,
|
||||
uploadingImage,
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
|
@ -492,9 +699,10 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
initialNumToRender={10}
|
||||
maxToRenderPerBatch={20}
|
||||
removeClippedSubviews
|
||||
renderItem={({ item }) => {
|
||||
const itemAutoStyle =
|
||||
ChannelIconItem.AUTO_THUMB_STYLES[Math.floor(Math.random() * ChannelIconItem.AUTO_THUMB_STYLES.length)];
|
||||
renderItem={({ item, index }) => {
|
||||
const itemAutoStyle = autoStyles.length > index ? autoStyles[index] : autoStyle; /* fallback */
|
||||
const value = item.value;
|
||||
const itemThumbnailUrl = value && value.thumbnail ? value.thumbnail.url : null;
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={channelCreatorStyle.channelListItem}
|
||||
|
@ -502,10 +710,19 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
onLongPress={() => this.handleChannelListItemLongPress(item)}
|
||||
>
|
||||
<View style={[channelCreatorStyle.channelListAvatar, itemAutoStyle]}>
|
||||
<Text style={channelIconStyle.autothumbCharacter}>{item.name.substring(1, 2).toUpperCase()}</Text>
|
||||
{itemThumbnailUrl && (
|
||||
<Image
|
||||
style={channelCreatorStyle.avatarImage}
|
||||
resizeMode={'cover'}
|
||||
source={{ uri: itemThumbnailUrl }}
|
||||
/>
|
||||
)}
|
||||
{!itemThumbnailUrl && (
|
||||
<Text style={channelIconStyle.autothumbCharacter}>{item.name.substring(1, 2).toUpperCase()}</Text>
|
||||
)}
|
||||
</View>
|
||||
<View style={channelCreatorStyle.channelListDetails}>
|
||||
{item.value && item.value.title && (
|
||||
{value && value.title && (
|
||||
<Text style={channelCreatorStyle.channelListTitle}>{item.value.title}</Text>
|
||||
)}
|
||||
<Text style={channelCreatorStyle.channelListName}>{item.name}</Text>
|
||||
|
@ -524,7 +741,7 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
)}
|
||||
|
||||
{currentPhase === Constants.PHASE_CREATE && (
|
||||
<View style={channelCreatorStyle.createChannelContainer}>
|
||||
<ScrollView style={channelCreatorStyle.createChannelContainer}>
|
||||
<View style={channelCreatorStyle.imageSelectors}>
|
||||
<TouchableOpacity style={channelCreatorStyle.coverImageTouchArea} onPress={this.onCoverImagePress}>
|
||||
<Image
|
||||
|
@ -536,6 +753,9 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
: require('../../assets/default_channel_cover.png')
|
||||
}
|
||||
/>
|
||||
<View style={channelCreatorStyle.infoOverlay}>
|
||||
<Text style={channelCreatorStyle.infoText}>Tap to change</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
|
||||
<View style={[channelCreatorStyle.avatarImageContainer, autoStyle]}>
|
||||
|
@ -556,19 +776,36 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
</View>
|
||||
</View>
|
||||
|
||||
{this.state.uploadingImage && (
|
||||
<View style={channelCreatorStyle.uploadProgress}>
|
||||
<ActivityIndicator size={'small'} color={Colors.NextLbryGreen} />
|
||||
<Text style={channelCreatorStyle.uploadText}>Uploading image...</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View style={channelCreatorStyle.card}>
|
||||
<TextInput
|
||||
style={channelCreatorStyle.channelTitleInput}
|
||||
value={this.state.newChannelTitle}
|
||||
onChangeText={this.handleNewChannelTitleChange}
|
||||
placeholder={'Title'}
|
||||
underlineColorAndroid={Colors.NextLbryGreen}
|
||||
/>
|
||||
<View style={channelCreatorStyle.textInputLayout}>
|
||||
{(this.state.titleFocused ||
|
||||
(this.state.newChannelTitle != null && this.state.newChannelTitle.trim().length > 0)) && (
|
||||
<Text style={channelCreatorStyle.textInputTitle}>Title</Text>
|
||||
)}
|
||||
<TextInput
|
||||
editable={canSave && !creatingChannel && !updatingChannel}
|
||||
style={channelCreatorStyle.inputText}
|
||||
value={this.state.newChannelTitle}
|
||||
onChangeText={this.handleNewChannelTitleChange}
|
||||
placeholder={this.state.titleFocused ? '' : 'Title'}
|
||||
underlineColorAndroid={Colors.NextLbryGreen}
|
||||
onFocus={() => this.setState({ titleFocused: true })}
|
||||
onBlur={() => this.setState({ titleFocused: false })}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={channelCreatorStyle.channelInputContainer}>
|
||||
<Text style={channelCreatorStyle.channelAt}>@</Text>
|
||||
|
||||
<TextInput
|
||||
editable={canSave && !creatingChannel && !updatingChannel}
|
||||
style={channelCreatorStyle.channelNameInput}
|
||||
value={this.state.newChannelName}
|
||||
onChangeText={value => this.handleNewChannelNameChange(value, true)}
|
||||
|
@ -582,6 +819,7 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
<View style={channelCreatorStyle.bidRow}>
|
||||
<Text style={channelCreatorStyle.label}>Deposit</Text>
|
||||
<TextInput
|
||||
editable={canSave && !creatingChannel && !updatingChannel}
|
||||
style={channelCreatorStyle.bidAmountInput}
|
||||
value={String(newChannelBid)}
|
||||
onChangeText={this.handleNewChannelBidChange}
|
||||
|
@ -596,24 +834,112 @@ export default class ChannelCreator extends React.PureComponent {
|
|||
</Text>
|
||||
</View>
|
||||
|
||||
{this.state.showOptionalFields && (
|
||||
<View style={channelCreatorStyle.card}>
|
||||
<View style={channelCreatorStyle.textInputLayout}>
|
||||
{(this.state.descriptionFocused ||
|
||||
(this.state.description != null && this.state.description.trim().length > 0)) && (
|
||||
<Text style={channelCreatorStyle.textInputTitle}>Description</Text>
|
||||
)}
|
||||
<TextInput
|
||||
editable={canSave && !creatingChannel && !updatingChannel}
|
||||
style={channelCreatorStyle.inputText}
|
||||
value={this.state.description}
|
||||
onChangeText={this.handleDescriptionChange}
|
||||
placeholder={this.state.descriptionFocused ? '' : 'Description'}
|
||||
underlineColorAndroid={Colors.NextLbryGreen}
|
||||
onFocus={() => this.setState({ descriptionFocused: true })}
|
||||
onBlur={() => this.setState({ descriptionFocused: false })}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={channelCreatorStyle.textInputLayout}>
|
||||
{(this.state.websiteFocused ||
|
||||
(this.state.website != null && this.state.website.trim().length > 0)) && (
|
||||
<Text style={channelCreatorStyle.textInputTitle}>Website</Text>
|
||||
)}
|
||||
<TextInput
|
||||
editable={canSave && !creatingChannel && !updatingChannel}
|
||||
style={channelCreatorStyle.inputText}
|
||||
value={this.state.website}
|
||||
onChangeText={this.handleWebsiteChange}
|
||||
placeholder={this.state.websiteFocused ? '' : 'Website'}
|
||||
underlineColorAndroid={Colors.NextLbryGreen}
|
||||
onFocus={() => this.setState({ websiteFocused: true })}
|
||||
onBlur={() => this.setState({ websiteFocused: false })}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={channelCreatorStyle.textInputLayout}>
|
||||
{(this.state.emailFocused || (this.state.email != null && this.state.email.trim().length > 0)) && (
|
||||
<Text style={channelCreatorStyle.textInputTitle}>Email</Text>
|
||||
)}
|
||||
<TextInput
|
||||
editable={canSave && !creatingChannel && !updatingChannel}
|
||||
style={channelCreatorStyle.inputText}
|
||||
value={this.state.email}
|
||||
onChangeText={this.handleEmailChange}
|
||||
placeholder={this.state.emailFocused ? '' : 'Email'}
|
||||
underlineColorAndroid={Colors.NextLbryGreen}
|
||||
onFocus={() => this.setState({ emailFocused: true })}
|
||||
onBlur={() => this.setState({ emailFocused: false })}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{this.state.showOptionalFields && (
|
||||
<View style={channelCreatorStyle.card}>
|
||||
<Text style={channelCreatorStyle.cardTitle}>Tags</Text>
|
||||
<View style={channelCreatorStyle.tagList}>
|
||||
{this.state.tags &&
|
||||
this.state.tags.map(tag => (
|
||||
<Tag
|
||||
key={tag}
|
||||
name={tag}
|
||||
type={'remove'}
|
||||
style={channelCreatorStyle.tag}
|
||||
onRemovePress={this.handleRemoveTag}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
<TagSearch
|
||||
editable={canSave && !creatingChannel && !updatingChannel}
|
||||
handleAddTag={this.handleAddTag}
|
||||
selectedTags={this.state.tags}
|
||||
showNsfwTags
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View style={channelCreatorStyle.toggleContainer}>
|
||||
<Link
|
||||
text={this.state.showOptionalFields ? 'Hide optional fields' : 'Show optional fields'}
|
||||
onPress={this.handleModePressed}
|
||||
style={channelCreatorStyle.modeLink}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={channelCreatorStyle.buttonContainer}>
|
||||
{creatingChannel && <ActivityIndicator size={'small'} color={Colors.NextLbryGreen} />}
|
||||
{!creatingChannel && (
|
||||
{(creatingChannel || updatingChannel) && (
|
||||
<ActivityIndicator size={'small'} color={Colors.NextLbryGreen} />
|
||||
)}
|
||||
{!creatingChannel && !updatingChannel && (
|
||||
<View style={channelCreatorStyle.buttons}>
|
||||
<Link style={channelCreatorStyle.cancelLink} text="Cancel" onPress={this.handleCreateCancel} />
|
||||
<Button
|
||||
style={channelCreatorStyle.createButton}
|
||||
disabled={!(newChannelName.trim().length > 0 && newChannelBid > 0)}
|
||||
text="Create"
|
||||
disabled={!canSave || uploadingImage || !newChannelName || newChannelName.trim().length === 0}
|
||||
text={editMode ? 'Update' : 'Create'}
|
||||
onPress={this.handleCreateChannelClick}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
)}
|
||||
|
||||
<FloatingWalletBalance navigation={navigation} />
|
||||
{Constants.PHASE_CREATE !== this.state.currentPhase && <FloatingWalletBalance navigation={navigation} />}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ const channelCreatorStyle = StyleSheet.create({
|
|||
},
|
||||
buttonContainer: {
|
||||
marginTop: 24,
|
||||
marginBottom: 16,
|
||||
},
|
||||
buttons: {
|
||||
marginLeft: 16,
|
||||
|
@ -174,6 +175,65 @@ const channelCreatorStyle = StyleSheet.create({
|
|||
marginTop: 16,
|
||||
marginBottom: 16,
|
||||
},
|
||||
textInputLayout: {
|
||||
marginBottom: 4,
|
||||
},
|
||||
textInputTitle: {
|
||||
fontFamily: 'Inter-UI-Regular',
|
||||
fontSize: 12,
|
||||
marginBottom: -10,
|
||||
marginLeft: 4,
|
||||
},
|
||||
inputText: {
|
||||
fontFamily: 'Inter-UI-Regular',
|
||||
fontSize: 16,
|
||||
},
|
||||
toggleContainer: {
|
||||
marginTop: 24,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
modeLink: {
|
||||
color: Colors.LbryGreen,
|
||||
alignSelf: 'flex-end',
|
||||
marginRight: 16,
|
||||
},
|
||||
cardTitle: {
|
||||
fontFamily: 'Inter-UI-Regular',
|
||||
fontSize: 20,
|
||||
marginBottom: 8,
|
||||
},
|
||||
tag: {
|
||||
marginRight: 4,
|
||||
marginBottom: 4,
|
||||
},
|
||||
tagList: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
infoOverlay: {
|
||||
position: 'absolute',
|
||||
padding: 4,
|
||||
left: 4,
|
||||
bottom: 4,
|
||||
backgroundColor: '#00000077',
|
||||
},
|
||||
infoText: {
|
||||
color: Colors.White,
|
||||
fontFamily: 'Inter-UI-SemiBold',
|
||||
fontSize: 12,
|
||||
},
|
||||
uploadProgress: {
|
||||
flexDirection: 'row',
|
||||
marginTop: 16,
|
||||
marginLeft: 16,
|
||||
marginRight: 16,
|
||||
},
|
||||
uploadText: {
|
||||
fontFamily: 'Inter-UI-Regular',
|
||||
fontSize: 14,
|
||||
marginLeft: 8,
|
||||
},
|
||||
});
|
||||
|
||||
export default channelCreatorStyle;
|
||||
|
|
|
@ -250,3 +250,42 @@ export function getOrderBy(item) {
|
|||
export function __(str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
export function uploadImageAsset(filePath, success, failure) {
|
||||
const makeid = () => {
|
||||
let text = '';
|
||||
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
for (let i = 0; i < 24; i += 1) text += possible.charAt(Math.floor(Math.random() * 62));
|
||||
return text;
|
||||
};
|
||||
|
||||
const fileName = filePath.substring(filePath.lastIndexOf('/') + 1);
|
||||
let fileExt = fileName.indexOf('.') > -1 ? fileName.substring(fileName.lastIndexOf('.') + 1) : 0;
|
||||
if (!fileExt || fileExt.trim().length === 0) {
|
||||
fileExt = 'jpg'; // default to jpg
|
||||
}
|
||||
const fileType = `image/${fileExt}`;
|
||||
|
||||
const data = new FormData();
|
||||
const name = makeid();
|
||||
data.append('name', name);
|
||||
data.append('file', { uri: 'file://' + filePath, type: fileType, name: fileName });
|
||||
|
||||
return fetch('https://spee.ch/api/claim/publish', {
|
||||
method: 'POST',
|
||||
body: data,
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
if (json.success) {
|
||||
if (success) {
|
||||
success({ url: `${json.data.url}.${fileExt}` });
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
if (failure) {
|
||||
failure(err.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue