lbry-react-native/src/page/publish/view.js

1012 lines
33 KiB
JavaScript
Raw Normal View History

2019-07-09 01:54:32 +01:00
import React from 'react';
import {
ActivityIndicator,
Clipboard,
2019-07-30 15:38:46 +01:00
DeviceEventEmitter,
2019-07-09 01:54:32 +01:00
Image,
NativeModules,
Picker,
ScrollView,
Switch,
Text,
TextInput,
TouchableOpacity,
TouchableWithoutFeedback,
View,
} from 'react-native';
import { FlatGrid } from 'react-native-super-grid';
2019-08-10 07:55:12 +01:00
import {
isNameValid,
buildURI,
regexInvalidURI,
CLAIM_VALUES,
LICENSES,
MATURE_TAGS,
THUMBNAIL_STATUSES,
} from 'lbry-redux';
2019-07-09 01:54:32 +01:00
import { DocumentPicker, DocumentPickerUtil } from 'react-native-document-picker';
import { RNCamera } from 'react-native-camera';
import { generateCombination } from 'gfycat-style-urls';
import RNFS from 'react-native-fs';
import Button from 'component/button';
import ChannelSelector from 'component/channelSelector';
import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
2019-07-09 01:54:32 +01:00
import FastImage from 'react-native-fast-image';
import FloatingWalletBalance from 'component/floatingWalletBalance';
import Icon from 'react-native-vector-icons/FontAwesome5';
import Feather from 'react-native-vector-icons/Feather';
import Link from 'component/link';
import PublishRewardsDriver from 'component/publishRewardsDriver';
import Tag from 'component/tag';
import TagSearch from 'component/tagSearch';
import UriBar from 'component/uriBar';
import publishStyle from 'styles/publish';
import __ from 'utils/helper';
2019-07-09 01:54:32 +01:00
const languages = {
en: 'English',
zh: 'Chinese',
fr: 'French',
de: 'German',
jp: 'Japanese',
ru: 'Russian',
es: 'Spanish',
id: 'Indonesian',
it: 'Italian',
nl: 'Dutch',
tr: 'Turkish',
pl: 'Polish',
ms: 'Malay',
pt: 'Portuguese',
vi: 'Vietnamese',
th: 'Thai',
ar: 'Arabic',
cs: 'Czech',
hr: 'Croatian',
km: 'Cambodian',
ko: 'Korean',
no: 'Norwegian',
ro: 'Romanian',
hi: 'Hindi',
el: 'Greek',
};
class PublishPage extends React.PureComponent {
camera = null;
state = {
canUseCamera: false,
titleFocused: false,
descriptionFocused: false,
loadingVideos: false,
2019-07-09 01:54:32 +01:00
// gallery videos
videos: null,
allThumbnailsChecked: false,
checkedThumbnails: [],
2019-07-09 01:54:32 +01:00
// camera
cameraType: RNCamera.Constants.Type.back,
videoRecordingMode: false,
recordingVideo: false,
showCameraOverlay: false,
// paths and media
uploadsPath: null,
thumbnailPath: null,
currentMedia: null,
currentThumbnailUri: null,
updatingThumbnailUri: false,
currentPhase: Constants.PHASE_SELECTOR,
// publish
advancedMode: false,
anonymous: true,
channelName: CLAIM_VALUES.CHANNEL_ANONYMOUS,
priceSet: false,
// input data
bid: 0.1,
description: null,
title: null,
language: 'en',
license: LICENSES.NONE,
2019-08-12 13:18:47 +01:00
licenseUrl: '',
otherLicenseDescription: '',
2019-07-09 01:54:32 +01:00
mature: false,
name: null,
price: 0,
uri: null,
tags: [],
2019-08-11 00:06:10 +01:00
selectedChannel: null,
2019-07-09 01:54:32 +01:00
uploadedThumbnailUri: null,
// other
publishStarted: false,
};
didFocusListener;
componentWillMount() {
const { navigation } = this.props;
2019-08-10 07:55:12 +01:00
// this.didFocusListener = navigation.addListener('didFocus', this.onComponentFocused);
DeviceEventEmitter.addListener('onGalleryThumbnailChecked', this.handleGalleryThumbnailChecked);
DeviceEventEmitter.addListener('onAllGalleryThumbnailsChecked', this.handleAllGalleryThumbnailsChecked);
2019-07-09 01:54:32 +01:00
}
componentWillUnmount() {
if (this.didFocusListener) {
this.didFocusListener.remove();
}
DeviceEventEmitter.removeListener('onGalleryThumbnailChecked', this.handleGalleryThumbnailChecked);
DeviceEventEmitter.removeListener('onAllGalleryThumbnailsChecked', this.handleAllGalleryThumbnailsChecked);
2019-07-09 01:54:32 +01:00
}
handleGalleryThumbnailChecked = evt => {
const checkedThumbnails = [...this.state.checkedThumbnails];
const { id } = evt;
// using checked because we only want thumbnails that can be displayed
if (!checkedThumbnails.includes(id)) {
checkedThumbnails.push(id);
}
this.setState({ checkedThumbnails });
2019-07-30 15:38:46 +01:00
};
handleAllGalleryThumbnailsChecked = () => {
this.setState({ allThumbnailsChecked: true });
};
2019-07-26 17:12:28 +01:00
onComponentFocused = () => {
const { pushDrawerStack, setPlayerVisible } = this.props;
pushDrawerStack();
setPlayerVisible();
2019-08-25 16:35:51 +01:00
NativeModules.Firebase.setCurrentScreen('New publish').then(result => {
NativeModules.Gallery.canUseCamera().then(canUseCamera => this.setState({ canUseCamera }));
NativeModules.Gallery.getThumbnailPath().then(thumbnailPath => this.setState({ thumbnailPath }));
this.setState(
{
loadingVideos: true,
},
() => {
NativeModules.Gallery.getVideos().then(videos => this.setState({ videos, loadingVideos: false }));
}
);
});
2019-07-26 17:12:28 +01:00
};
2019-07-09 01:54:32 +01:00
getNewUri(name, channel) {
const { resolveUri } = this.props;
// If they are midway through a channel creation, treat it as anonymous until it completes
const channelName =
channel === CLAIM_VALUES.CHANNEL_ANONYMOUS || channel === CLAIM_VALUES.CHANNEL_NEW ? '' : channel;
// We are only going to store the full uri, but we need to resolve the uri with and without the channel name
let uri;
try {
uri = buildURI({ contentName: name, channelName });
} catch (e) {
// something wrong with channel or name
}
if (uri) {
if (channelName) {
// resolve without the channel name so we know the winning bid for it
const uriLessChannel = buildURI({ contentName: name });
resolveUri(uriLessChannel);
}
resolveUri(uri);
return uri;
}
return '';
}
handleModePressed = () => {
this.setState({ advancedMode: !this.state.advancedMode });
};
handlePublishPressed = () => {
const { notify, publish, updatePublishForm } = this.props;
2019-07-09 01:54:32 +01:00
const {
bid,
channelName,
currentMedia,
description,
language,
license,
2019-08-12 13:18:47 +01:00
licenseUrl,
otherLicenseDescription,
2019-07-09 01:54:32 +01:00
mature,
name,
price,
priceSet,
tags,
title,
uploadedThumbnailUri: thumbnail,
uri,
} = this.state;
if (!title || title.trim().length === 0) {
notify({ message: 'Please provide a title' });
return;
}
if (!name) {
notify({ message: 'Please specify an address where people can find your content.' });
return;
}
const publishParams = {
filePath: currentMedia.filePath,
bid: bid || 0.1,
title: title || '',
2019-08-11 00:06:10 +01:00
thumbnail,
2019-07-09 01:54:32 +01:00
description: description || '',
language,
license,
2019-08-12 13:18:47 +01:00
licenseUrl,
otherLicenseDescription,
2019-07-09 01:54:32 +01:00
name: name || undefined,
contentIsFree: !priceSet,
2019-08-12 13:18:47 +01:00
fee: { currency: 'LBC', amount: price },
2019-07-09 01:54:32 +01:00
uri: uri || undefined,
2019-08-11 00:06:10 +01:00
channel: CLAIM_VALUES.CHANNEL_ANONYMOUS === channelName ? null : channelName,
2019-07-09 01:54:32 +01:00
isStillEditing: false,
2019-08-11 00:06:10 +01:00
claim: {
value: {
tags,
2019-08-12 13:18:47 +01:00
release_time: Math.round(Date.now() / 1000), // set now as the release time
2019-08-11 00:06:10 +01:00
},
},
2019-07-09 01:54:32 +01:00
};
2019-08-21 04:53:35 +01:00
updatePublishForm(publishParams);
this.setState({ publishStarted: true }, () => publish(this.handlePublishSuccess, this.handlePublishFailure));
};
handlePublishSuccess = data => {
this.setState({ publishStarted: false, currentPhase: Constants.PHASE_PUBLISH });
};
handlePublishFailure = error => {
const { notify } = this.props;
notify({ message: __('Your content could not be published at this time. Please try again.') });
this.setState({ publishStarted: false });
2019-07-09 01:54:32 +01:00
};
componentDidMount() {
this.onComponentFocused();
}
componentWillReceiveProps(nextProps) {
const { currentRoute, publishFormValues } = nextProps;
2019-08-11 00:06:10 +01:00
const { currentRoute: prevRoute, notify } = this.props;
2019-07-09 01:54:32 +01:00
if (Constants.DRAWER_ROUTE_PUBLISH === currentRoute && currentRoute !== prevRoute) {
this.onComponentFocused();
}
if (publishFormValues) {
if (publishFormValues.thumbnail && !this.state.uploadedThumbnailUri) {
this.setState({
currentThumbnailUri: publishFormValues.thumbnail,
uploadedThumbnailUri: publishFormValues.thumbnail,
});
2019-07-09 01:54:32 +01:00
}
}
}
setCurrentMedia(media) {
const name = generateCombination(2, ' ', true);
2019-07-09 01:54:32 +01:00
this.setState(
{
currentMedia: media,
title: name,
name: this.formatNameForTitle(name),
2019-07-09 01:54:32 +01:00
currentPhase: Constants.PHASE_DETAILS,
},
() => this.handleNameChange(this.state.name)
);
}
formatNameForTitle = title => {
2019-08-11 00:06:10 +01:00
return title.replace(new RegExp(regexInvalidURI.source, regexInvalidURI.flags + 'g'), '-').toLowerCase();
2019-07-09 01:54:32 +01:00
};
showSelector() {
const { updatePublishForm } = this.props;
this.setState(
{
publishStarted: false,
currentMedia: null,
currentThumbnailUri: null,
currentPhase: Constants.PHASE_SELECTOR,
updatingThumbnailUri: false,
uploadThumbnailStarted: false,
// publish
advancedMode: false,
anonymous: true,
channelName: CLAIM_VALUES.CHANNEL_ANONYMOUS,
priceSet: false,
// input data
bid: 0.1,
description: null,
title: null,
language: 'en',
license: LICENSES.NONE,
2019-08-12 13:18:47 +01:00
licenseUrl: '',
otherLicenseDescription: '',
name: null,
price: 0,
uri: null,
tags: [],
2019-08-11 00:06:10 +01:00
selectedChannel: null,
uploadedThumbnailUri: null,
},
() => {
// reset thumbnail
updatePublishForm({ thumbnail: null });
}
);
2019-07-09 01:54:32 +01:00
}
handleRecordVideoPressed = () => {
if (!this.state.showCameraOverlay) {
this.setState({ canUseCamera: true, showCameraOverlay: true, videoRecordingMode: true });
}
};
handleTakePhotoPressed = () => {
if (!this.state.showCameraOverlay) {
this.setState({ canUseCamera: true, showCameraOverlay: true, videoRecordingMode: false });
}
};
handleCloseCameraPressed = () => {
this.setState({ showCameraOverlay: false, videoRecordingMode: false });
};
getFilePathFromUri = uri => {
return uri.substring('file://'.length);
};
handleCameraActionPressed = () => {
// check if it's video or photo mode
if (this.state.videoRecordingMode) {
if (this.state.recordingVideo) {
this.camera.stopRecording();
} else {
this.setState({ recordingVideo: true });
const options = { quality: RNCamera.Constants.VideoQuality['1080p'] };
this.camera.recordAsync(options).then(data => {
this.setState({ recordingVideo: false });
const currentMedia = {
id: -1,
filePath: this.getFilePathFromUri(data.uri),
type: 'video/mp4', // always MP4
duration: 0,
};
this.setCurrentMedia(currentMedia);
this.setState({
currentThumbnailUri: null,
updatingThumbnailUri: false,
currentPhase: Constants.PHASE_DETAILS,
showCameraOverlay: false,
videoRecordingMode: false,
recordingVideo: false,
});
});
}
} else {
const options = { quality: 0.7 };
this.camera.takePictureAsync(options).then(data => {
const currentMedia = {
id: -1,
filePath: this.getFilePathFromUri(data.uri),
name: generateCombination(2, ' ', true),
type: 'image/jpg', // always JPEG
duration: 0,
};
this.setCurrentMedia(currentMedia);
this.setState({
currentPhase: Constants.PHASE_DETAILS,
currentThumbnailUri: null,
updatingThumbnailUri: false,
showCameraOverlay: false,
videoRecordingMode: false,
});
});
}
};
handleSwitchCameraPressed = () => {
const { cameraType } = this.state;
this.setState({
cameraType:
cameraType === RNCamera.Constants.Type.back ? RNCamera.Constants.Type.front : RNCamera.Constants.Type.back,
});
};
handleUploadPressed = () => {
DocumentPicker.show(
{
filetype: [DocumentPickerUtil.allFiles()],
},
(error, res) => {
if (!error) {
// console.log(res);
2019-07-09 01:54:32 +01:00
}
}
);
};
getRandomFileId = () => {
// generate a random id for a photo or recorded video between 1 and 20 (for creating thumbnails)
const id = Math.floor(Math.random() * (20 - 2)) + 1;
return '_' + id;
};
handlePublishAgainPressed = () => {
this.showSelector();
};
handleBidChange = bid => {
this.setState({ bid });
};
handlePriceChange = price => {
this.setState({ price });
};
handleNameChange = name => {
const { notify } = this.props;
this.setState({ name });
if (!isNameValid(name, false)) {
notify({ message: 'Your content address contains invalid characters' });
return;
}
const uri = this.getNewUri(name, this.state.channelName);
this.setState({ uri });
};
2019-08-11 00:06:10 +01:00
handleChannelChange = channel => {
const { name } = this.state;
2019-08-20 10:11:35 +01:00
const uri = this.getNewUri(name, channel);
this.setState({ uri, channelName: channel, selectedChannel: channel });
2019-07-09 01:54:32 +01:00
};
handleAddTag = tag => {
if (!tag) {
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.`) });
2019-07-09 01:54:32 +01:00
}
};
handleRemoveTag = tag => {
if (!tag) {
return;
}
const newTags = this.state.tags.slice();
const index = newTags.indexOf(tag.toLowerCase());
if (index > -1) {
newTags.splice(index, 1);
this.setState({ tags: newTags });
}
};
updateThumbnailUriForMedia = media => {
if (this.state.updatingThumbnailUri) {
return;
}
const { notify, uploadThumbnail } = this.props;
const { thumbnailPath } = this.state;
this.setState({ updatingThumbnailUri: true });
if (media.type) {
const mediaType = media.type.substring(0, 5);
const tempId = this.getRandomFileId();
if (mediaType === 'video' && media.id > -1) {
2019-07-09 01:54:32 +01:00
const uri = `file://${thumbnailPath}/${media.id}.png`;
this.setState({ currentThumbnailUri: uri, updatingThumbnailUri: false });
// upload the thumbnail
if (!this.state.uploadedThumbnailUri) {
this.setState({ uploadThumbnailStarted: true }, () => uploadThumbnail(this.getFilePathFromUri(uri), RNFS));
}
} else if (mediaType === 'image' || mediaType === 'video') {
2019-07-09 01:54:32 +01:00
const create =
mediaType === 'image'
2019-07-09 01:54:32 +01:00
? NativeModules.Gallery.createImageThumbnail
: NativeModules.Gallery.createVideoThumbnail;
create(tempId, media.filePath)
.then(path => {
this.setState({ currentThumbnailUri: `file://${path}`, updatingThumbnailUri: false });
if (!this.state.uploadedThumbnailUri) {
this.setState({ uploadThumbnailStarted: true }, () => uploadThumbnail(path, RNFS));
}
})
.catch(err => {
notify({ message: err });
this.setState({ updatingThumbnailUri: false });
});
}
}
};
handleTitleChange = title => {
this.setState(
{
title,
name: this.formatNameForTitle(title),
},
() => {
this.handleNameChange(this.state.name);
}
);
};
handleDescriptionChange = description => {
this.setState({ description });
};
2019-08-12 13:18:47 +01:00
handleLanguageValueChange = language => {
this.setState({ language });
};
handleLicenseValueChange = license => {
const otherLicenseDescription = [LICENSES.COPYRIGHT, LICENSES.OTHER].includes(license)
? this.state.otherLicenseDescription
: '';
this.setState({
otherLicenseDescription,
license,
licenseUrl: LICENSES.CC_LICENSES.reduce((value, item) => {
if (typeof value === 'object') {
value = '';
}
if (license === item.value) {
value = item.url;
}
return value;
}),
});
};
handleChangeLicenseDescription = otherLicenseDescription => {
this.setState({ otherLicenseDescription });
};
2019-07-09 01:54:32 +01:00
render() {
const { balance, navigation, notify, publishFormValues } = this.props;
const {
allThumbnailsChecked,
canUseCamera,
currentPhase,
checkedThumbnails,
loadingVideos,
thumbnailPath,
videos,
} = this.state;
2019-07-09 01:54:32 +01:00
let content;
2019-07-30 15:38:46 +01:00
if (Constants.PHASE_SELECTOR === currentPhase) {
2019-07-09 01:54:32 +01:00
content = (
<View style={publishStyle.gallerySelector}>
<View style={publishStyle.actionsView}>
2019-07-30 15:38:46 +01:00
{canUseCamera && <RNCamera style={publishStyle.cameraPreview} type={RNCamera.Constants.Type.back} />}
2019-07-09 01:54:32 +01:00
<View style={publishStyle.actionsSubView}>
<TouchableOpacity
style={[
publishStyle.record,
canUseCamera ? publishStyle.transparentBackground : publishStyle.actionBackground,
]}
onPress={this.handleRecordVideoPressed}
>
2019-07-09 01:54:32 +01:00
<Icon name="video" size={48} color={Colors.White} />
<Text style={publishStyle.actionText}>Record</Text>
</TouchableOpacity>
<View style={publishStyle.subActions}>
<TouchableOpacity
style={[
publishStyle.photo,
canUseCamera ? publishStyle.transparentBackground : publishStyle.actionBackground,
]}
onPress={this.handleTakePhotoPressed}
>
2019-07-09 01:54:32 +01:00
<Icon name="camera" size={48} color={Colors.White} />
<Text style={publishStyle.actionText}>Take a photo</Text>
</TouchableOpacity>
{false && (
<TouchableOpacity style={publishStyle.upload} onPress={this.handleUploadPressed}>
<Icon name="file-upload" size={48} color={Colors.White} />
<Text style={publishStyle.actionText}>Upload a file</Text>
</TouchableOpacity>
)}
</View>
</View>
</View>
{(loadingVideos || !allThumbnailsChecked) && (
2019-07-09 01:54:32 +01:00
<View style={publishStyle.loadingView}>
<ActivityIndicator size="small" color={Colors.NextLbryGreen} />
<Text style={publishStyle.loadingText}>Please wait while we load your videos...</Text>
2019-07-09 01:54:32 +01:00
</View>
)}
{!loadingVideos && (!videos || videos.length === 0) && (
<View style={publishStyle.relativeCentered}>
2019-07-30 15:38:46 +01:00
<Text style={publishStyle.noVideos}>
We could not find any videos on your device. Take a photo or record a video to get started.
</Text>
</View>
)}
{videos && thumbnailPath && allThumbnailsChecked && (
2019-07-09 01:54:32 +01:00
<FlatGrid
style={publishStyle.galleryGrid}
2019-08-21 04:53:35 +01:00
initialNumToRender={18}
maxToRenderPerBatch={24}
removeClippedSubviews
2019-07-09 01:54:32 +01:00
itemDimension={134}
spacing={2}
items={videos.filter(video => checkedThumbnails.includes(video.id))}
2019-07-09 01:54:32 +01:00
renderItem={({ item, index }) => {
return (
<TouchableOpacity key={index} onPress={() => this.setCurrentMedia(item)}>
<FastImage
style={publishStyle.galleryGridImage}
resizeMode={FastImage.resizeMode.cover}
source={{ uri: `file://${thumbnailPath}/${item.id}.png` }}
/>
</TouchableOpacity>
);
}}
/>
)}
</View>
);
} else if (Constants.PHASE_DETAILS === this.state.currentPhase && this.state.currentMedia) {
const { currentMedia, currentThumbnailUri } = this.state;
if (!currentThumbnailUri) {
this.updateThumbnailUriForMedia(currentMedia);
}
content = (
<ScrollView style={publishStyle.publishDetails}>
{currentThumbnailUri && currentThumbnailUri.trim().length > 0 && (
<View style={publishStyle.mainThumbnailContainer}>
<FastImage
style={publishStyle.mainThumbnail}
resizeMode={FastImage.resizeMode.contain}
source={{ uri: currentThumbnailUri }}
/>
</View>
)}
{balance < 0.1 && <PublishRewardsDriver navigation={navigation} />}
{this.state.uploadThumbnailStarted && !this.state.uploadedThumbnailUri && (
<View style={publishStyle.thumbnailUploadContainer}>
<ActivityIndicator size={'small'} color={Colors.NextLbryGreen} />
2019-07-09 01:54:32 +01:00
<Text style={publishStyle.thumbnailUploadText}>Uploading thumbnail...</Text>
</View>
)}
<View style={publishStyle.card}>
<View style={publishStyle.textInputLayout}>
{(this.state.titleFocused || (this.state.title != null && this.state.title.trim().length > 0)) && (
<Text style={publishStyle.textInputTitle}>Title</Text>
)}
<TextInput
placeholder={this.state.titleFocused ? '' : 'Title'}
style={publishStyle.inputText}
value={this.state.title}
numberOfLines={1}
underlineColorAndroid={Colors.NextLbryGreen}
onChangeText={this.handleTitleChange}
onFocus={() => this.setState({ titleFocused: true })}
onBlur={() => this.setState({ titleFocused: false })}
/>
</View>
<View style={publishStyle.textInputLayout}>
{(this.state.descriptionFocused ||
(this.state.description != null && this.state.description.trim().length > 0)) && (
<Text style={publishStyle.textInputTitle}>Description</Text>
)}
<TextInput
placeholder={this.state.descriptionFocused ? '' : 'Description'}
style={publishStyle.inputText}
value={this.state.description}
underlineColorAndroid={Colors.NextLbryGreen}
onChangeText={this.handleDescriptionChange}
onFocus={() => this.setState({ descriptionFocused: true })}
onBlur={() => this.setState({ descriptionFocused: false })}
/>
</View>
</View>
<View style={publishStyle.card}>
<Text style={publishStyle.cardTitle}>Tags</Text>
<View style={publishStyle.tagList}>
{this.state.tags &&
this.state.tags.map(tag => (
<Tag
key={tag}
name={tag}
type={'remove'}
style={publishStyle.tag}
onRemovePress={this.handleRemoveTag}
/>
))}
</View>
2019-08-10 07:55:12 +01:00
<TagSearch handleAddTag={this.handleAddTag} selectedTags={this.state.tags} showNsfwTags />
2019-07-09 01:54:32 +01:00
</View>
<View style={publishStyle.card}>
<Text style={publishStyle.cardTitle}>Channel</Text>
<ChannelSelector onChannelChange={this.handleChannelChange} />
</View>
<View style={publishStyle.card}>
<View style={publishStyle.titleRow}>
<Text style={publishStyle.cardTitle}>Price</Text>
<View style={publishStyle.switchTitleRow}>
<Switch value={this.state.priceSet} onValueChange={value => this.setState({ priceSet: value })} />
</View>
</View>
{!this.state.priceSet && (
<Text style={publishStyle.cardText}>Your content will be free. Press the toggle to set a price.</Text>
)}
{this.state.priceSet && (
<View style={[publishStyle.inputRow, publishStyle.priceInputRow]}>
<TextInput
placeholder={'0.00'}
keyboardType={'number-pad'}
style={publishStyle.priceInput}
underlineColorAndroid={Colors.NextLbryGreen}
numberOfLines={1}
value={String(this.state.price)}
onChangeText={this.handlePriceChange}
/>
<Text style={publishStyle.currency}>LBC</Text>
</View>
)}
</View>
<View style={publishStyle.card}>
<Text style={publishStyle.cardTitle}>Content Address</Text>
<Text style={publishStyle.helpText}>
The address where people can find your content (ex. lbry://myvideo)
</Text>
<TextInput
placeholder={'lbry://'}
style={publishStyle.inputText}
underlineColorAndroid={Colors.NextLbryGreen}
numberOfLines={1}
value={this.state.name}
onChangeText={this.handleNameChange}
/>
<View style={publishStyle.inputRow}>
2019-07-09 01:54:32 +01:00
<TextInput
placeholder={'0.00'}
style={publishStyle.priceInput}
2019-07-09 01:54:32 +01:00
underlineColorAndroid={Colors.NextLbryGreen}
numberOfLines={1}
keyboardType={'numeric'}
value={String(this.state.bid)}
onChangeText={this.handleBidChange}
2019-07-09 01:54:32 +01:00
/>
<Text style={publishStyle.currency}>LBC</Text>
2019-07-09 01:54:32 +01:00
</View>
<Text style={publishStyle.helpText}>This LBC remains yours and the deposit can be undone at any time.</Text>
</View>
2019-07-09 01:54:32 +01:00
{this.state.advancedMode && (
<View style={publishStyle.card}>
<Text style={publishStyle.cardTitle}>Additional Options</Text>
<View>
<Text style={publishStyle.cardText}>Language</Text>
<Picker
selectedValue={this.state.language}
style={publishStyle.picker}
itemStyle={publishStyle.pickerItem}
onValueChange={this.handleLanguageValueChange}
>
{Object.keys(languages).map(lang => (
<Picker.Item label={languages[lang]} value={lang} key={lang} />
))}
</Picker>
</View>
<View>
<Text style={publishStyle.cardText}>License</Text>
<Picker
selectedValue={this.state.license}
style={publishStyle.picker}
itemStyle={publishStyle.pickerItem}
onValueChange={this.handleLicenseValueChange}
>
<Picker.Item label={'None'} value={LICENSES.NONE} key={LICENSES.NONE} />
<Picker.Item label={'Public Domain'} value={LICENSES.PUBLIC_DOMAIN} key={LICENSES.PUBLIC_DOMAIN} />
{LICENSES.CC_LICENSES.map(({ value, url }) => (
<Picker.Item label={value} value={value} key={value} />
))}
<Picker.Item label={'Copyrighted...'} value={LICENSES.COPYRIGHT} key={LICENSES.COPYRIGHT} />
<Picker.Item label={'Other...'} value={LICENSES.OTHER} key={LICENSES.OTHER} />
</Picker>
2019-08-12 13:18:47 +01:00
{[LICENSES.COPYRIGHT, LICENSES.OTHER].includes(this.state.license) && (
<TextInput
placeholder={'License description'}
style={publishStyle.inputText}
underlineColorAndroid={Colors.NextLbryGreen}
numberOfLines={1}
value={this.state.otherLicenseDescription}
onChangeText={this.handleChangeLicenseDescription}
/>
)}
2019-07-09 01:54:32 +01:00
</View>
</View>
)}
<View style={publishStyle.toggleContainer}>
<Link
text={this.state.advancedMode ? 'Hide extra fields' : 'Show extra fields'}
onPress={this.handleModePressed}
style={publishStyle.modeLink}
/>
</View>
2019-08-11 00:06:10 +01:00
<View style={publishStyle.warning}>
<Text style={publishStyle.warningText}>
Please ensure that you have filled everything correctly as you cannot edit published content in this
release. This feature will be available in a future release.
</Text>
</View>
2019-07-09 01:54:32 +01:00
<View style={publishStyle.actionButtons}>
{(this.state.publishStarted || publishFormValues.publishing) && (
<View style={publishStyle.progress}>
<ActivityIndicator size={'small'} color={Colors.LbryGreen} />
</View>
)}
{!publishFormValues.publishing && !this.state.publishStarted && (
<Link style={publishStyle.cancelLink} text="Cancel" onPress={() => this.showSelector()} />
2019-07-09 01:54:32 +01:00
)}
{!publishFormValues.publishing && !this.state.publishStarted && (
<View style={publishStyle.rightActionButtons}>
<Button
style={publishStyle.publishButton}
disabled={balance < 0.1 || !this.state.uploadedThumbnailUri}
text="Publish"
onPress={this.handlePublishPressed}
/>
</View>
)}
</View>
</ScrollView>
);
} else if (Constants.PHASE_PUBLISH === this.state.currentPhase) {
content = (
<ScrollView style={publishStyle.publishDetails}>
<View style={publishStyle.successContainer}>
<Text style={publishStyle.successTitle}>Success!</Text>
<Text style={publishStyle.successText}>Congratulations! Your content was successfully uploaded.</Text>
<View style={publishStyle.successRow}>
<Link style={publishStyle.successUrl} text={this.state.uri} href={this.state.uri} />
<TouchableOpacity
onPress={() => {
Clipboard.setString(this.state.uri);
notify({ message: 'Copied.' });
}}
>
<Icon name="clipboard" size={24} color={Colors.LbryGreen} />
</TouchableOpacity>
</View>
<Text style={publishStyle.successText}>
Your content will be live in a few minutes. In the mean time, feel free to publish more content or explore
the app.
</Text>
</View>
<View style={publishStyle.actionButtons}>
<Button style={publishStyle.publishButton} text="Publish again" onPress={this.handlePublishAgainPressed} />
</View>
</ScrollView>
);
}
return (
<View style={publishStyle.container}>
<UriBar navigation={navigation} />
{content}
{false && Constants.PHASE_SELECTOR !== this.state.currentPhase && (
<FloatingWalletBalance navigation={navigation} />
)}
{this.state.canUseCamera && this.state.showCameraOverlay && (
<View style={publishStyle.cameraOverlay}>
<RNCamera
2019-08-11 00:06:10 +01:00
captureAudio={this.state.videoRecordingMode}
2019-07-09 01:54:32 +01:00
style={publishStyle.fullCamera}
ref={ref => {
this.camera = ref;
}}
type={this.state.cameraType}
flashMode={RNCamera.Constants.FlashMode.off}
androidCameraPermissionOptions={{
title: 'Camera',
message: 'Please grant access to make use of your camera',
buttonPositive: 'OK',
buttonNegative: 'Cancel',
}}
androidRecordAudioPermissionOptions={{
title: 'Audio',
message: 'Please grant access to record audio',
buttonPositive: 'OK',
buttonNegative: 'Cancel',
}}
2019-08-11 00:06:10 +01:00
notAuthorizedView={
<View style={publishStyle.fullCentered}>
<Text style={publishStyle.cameraInfo}>Camera not authorized</Text>
</View>
}
2019-07-09 01:54:32 +01:00
/>
<View
style={[
publishStyle.cameraControls,
this.state.videoRecordingMode ? publishStyle.transparentControls : publishStyle.opaqueControls,
]}
>
<View style={publishStyle.controlsRow}>
2019-08-11 00:06:10 +01:00
<TouchableOpacity onPress={this.handleCloseCameraPressed} style={publishStyle.backButtonControl}>
2019-07-09 01:54:32 +01:00
<Icon name="arrow-left" size={28} color={Colors.White} />
</TouchableOpacity>
<View style={publishStyle.mainControlsRow}>
<TouchableOpacity style={publishStyle.switchCameraToggle} onPress={this.handleSwitchCameraPressed}>
<Feather name="rotate-cw" size={36} color={Colors.White} />
</TouchableOpacity>
<TouchableOpacity onPress={this.handleCameraActionPressed}>
<View style={publishStyle.cameraAction}>
<Feather style={publishStyle.cameraActionIcon} name="circle" size={72} color={Colors.White} />
{this.state.recordingVideo && (
<Icon style={publishStyle.recordingIcon} name="circle" solid size={44} color={Colors.Red} />
2019-07-09 01:54:32 +01:00
)}
</View>
</TouchableOpacity>
</View>
</View>
</View>
</View>
)}
</View>
);
}
}
export default PublishPage;