import React from 'react'; import lbry from 'lbry'; import lbryuri from 'lbryuri'; import { FormField, FormRow } from 'component/form.js'; import Link from 'component/link'; import rewards from 'rewards'; import Modal from 'component/modal'; class PublishPage extends React.Component { constructor(props) { super(props); this._requiredFields = ['meta_title', 'name', 'bid', 'tos_agree']; this.state = { channels: null, rawName: '', name: '', bid: 10, hasFile: false, feeAmount: '', feeCurrency: 'USD', channel: 'anonymous', newChannelName: '@', newChannelBid: 10, nameResolved: null, myClaimExists: null, topClaimValue: 0.0, myClaimValue: 0.0, myClaimMetadata: null, copyrightNotice: '', otherLicenseDescription: '', otherLicenseUrl: '', uploadProgress: 0.0, uploaded: false, errorMessage: null, submitting: false, creatingChannel: false, modal: null }; } _updateChannelList(channel) { // Calls API to update displayed list of channels. If a channel name is provided, will select // that channel at the same time (used immediately after creating a channel) lbry.channel_list_mine().then(channels => { rewards.claimReward(rewards.TYPE_FIRST_CHANNEL).then(() => {}, () => {}); this.setState({ channels: channels, ...(channel ? { channel } : {}) }); }); } handleSubmit(event) { if (typeof event !== 'undefined') { event.preventDefault(); } this.setState({ submitting: true }); let checkFields = this._requiredFields; if (!this.state.myClaimExists) { checkFields.unshift('file'); } let missingFieldFound = false; for (let fieldName of checkFields) { const field = this.refs[fieldName]; if (field) { if (field.getValue() === '' || field.getValue() === false) { field.showRequiredError(); if (!missingFieldFound) { field.focus(); missingFieldFound = true; } } else { field.clearError(); } } } if (missingFieldFound) { this.setState({ submitting: false }); return; } if (this.state.nameIsMine) { // Pre-populate with existing metadata var metadata = Object.assign({}, this.state.myClaimMetadata); if (this.refs.file.getValue() !== '') { delete metadata.sources; } } else { var metadata = {}; } for (let metaField of [ 'title', 'description', 'thumbnail', 'license', 'license_url', 'language' ]) { var value = this.refs['meta_' + metaField].getValue(); if (value !== '') { metadata[metaField] = value; } } metadata.nsfw = parseInt(this.refs.meta_nsfw.getValue()) === 1; const licenseUrl = this.refs.meta_license_url.getValue(); if (licenseUrl) { metadata.license_url = licenseUrl; } var doPublish = () => { var publishArgs = { name: this.state.name, bid: parseFloat(this.state.bid), metadata: metadata, ...(this.state.channel != 'new' && this.state.channel != 'anonymous' ? { channel_name: this.state.channel } : {}) }; if (this.refs.file.getValue() !== '') { publishArgs.file_path = this.refs.file.getValue(); } lbry.publish( publishArgs, message => { this.handlePublishStarted(); }, null, error => { this.handlePublishError(error); } ); }; if (this.state.isFee) { lbry.wallet_unused_address().then(address => { metadata.fee = {}; metadata.fee[this.state.feeCurrency] = { amount: parseFloat(this.state.feeAmount), address: address }; doPublish(); }); } else { doPublish(); } } handlePublishStarted() { this.setState({ modal: 'publishStarted' }); } handlePublishStartedConfirmed() { this.props.navigate('/published'); } handlePublishError(error) { this.setState({ submitting: false, modal: 'error', errorMessage: error.message }); } handleNameChange(event) { var rawName = event.target.value; if (!rawName) { this.setState({ rawName: '', name: '', nameResolved: false }); return; } if (!lbryuri.isValidName(rawName, false)) { this.refs.name.showError( __('LBRY names must contain only letters, numbers and dashes.') ); return; } const name = rawName.toLowerCase(); this.setState({ rawName: rawName, name: name, nameResolved: null, myClaimExists: null }); const myClaimInfo = Object.values(this.props.myClaims).find( claim => claim.name === name ); this.setState({ myClaimExists: !!myClaimInfo }); lbry.resolve({ uri: name }).then( claimInfo => { if (name != this.state.name) { return; } if (!claimInfo) { this.setState({ nameResolved: false }); } else { const topClaimIsMine = myClaimInfo && myClaimInfo.amount >= claimInfo.amount; const newState = { nameResolved: true, topClaimValue: parseFloat(claimInfo.amount), myClaimExists: !!myClaimInfo, myClaimValue: myClaimInfo ? parseFloat(myClaimInfo.amount) : null, myClaimMetadata: myClaimInfo ? myClaimInfo.value : null, topClaimIsMine: topClaimIsMine }; if (topClaimIsMine) { newState.bid = myClaimInfo.amount; } else if (this.state.myClaimMetadata) { // Just changed away from a name we have a claim on, so clear pre-fill newState.bid = ''; } this.setState(newState); } }, () => { // Assume an error means the name is available this.setState({ name: name, nameResolved: false, myClaimExists: false }); } ); } handleBidChange(event) { this.setState({ bid: event.target.value }); } handleFeeAmountChange(event) { this.setState({ feeAmount: event.target.value }); } handleFeeCurrencyChange(event) { this.setState({ feeCurrency: event.target.value }); } handleFeePrefChange(feeEnabled) { this.setState({ isFee: feeEnabled }); } handleLicenseChange(event) { var licenseType = event.target.options[ event.target.selectedIndex ].getAttribute('data-license-type'); var newState = { copyrightChosen: licenseType == 'copyright', otherLicenseChosen: licenseType == 'other' }; if (licenseType == 'copyright') { newState.copyrightNotice = __('All rights reserved.'); } this.setState(newState); } handleCopyrightNoticeChange(event) { this.setState({ copyrightNotice: event.target.value }); } handleOtherLicenseDescriptionChange(event) { this.setState({ otherLicenseDescription: event.target.value }); } handleOtherLicenseUrlChange(event) { this.setState({ otherLicenseUrl: event.target.value }); } handleChannelChange(event) { const channel = event.target.value; this.setState({ channel: channel }); } handleNewChannelNameChange(event) { const newChannelName = event.target.value.startsWith('@') ? event.target.value : '@' + event.target.value; if ( newChannelName.length > 1 && !lbryuri.isValidName(newChannelName.substr(1), false) ) { this.refs.newChannelName.showError( __('LBRY channel names must contain only letters, numbers and dashes.') ); return; } else { this.refs.newChannelName.clearError(); } this.setState({ newChannelName: newChannelName }); } handleNewChannelBidChange(event) { this.setState({ newChannelBid: event.target.value }); } handleTOSChange(event) { this.setState({ TOSAgreed: event.target.checked }); } handleCreateChannelClick(event) { if (this.state.newChannelName.length < 5) { this.refs.newChannelName.showError( __('LBRY channel names must be at least 4 characters in length.') ); return; } this.setState({ creatingChannel: true }); const newChannelName = this.state.newChannelName; lbry .channel_new({ channel_name: newChannelName, amount: parseInt(this.state.newChannelBid) }) .then( () => { setTimeout(() => { this.setState({ creatingChannel: false }); this._updateChannelList(newChannelName); }, 5000); }, error => { // TODO: better error handling this.refs.newChannelName.showError( __('Unable to create channel due to an internal error.') ); this.setState({ creatingChannel: false }); } ); } getLicenseUrl() { if (!this.refs.meta_license) { return ''; } else if (this.state.otherLicenseChosen) { return this.state.otherLicenseUrl; } else { return ( this.refs.meta_license.getSelectedElement().getAttribute('data-url') || '' ); } } componentWillMount() { this._updateChannelList(); } onFileChange() { if (this.refs.file.getValue()) { this.setState({ hasFile: true }); } else { this.setState({ hasFile: false }); } } getNameBidHelpText() { if (!this.state.name) { return __('Select a URL for this publish.'); } else if (this.state.nameResolved === false) { return __('This URL is unused.'); } else if (this.state.myClaimExists) { return __( 'You have already used this URL. Publishing to it again will update your previous publish.' ); } else if (this.state.topClaimValue) { return ( {__n( 'A deposit of at least "%s" credit is required to win "%s". However, you can still get a permanent URL for any amount.', 'A deposit of at least "%s" credits is required to win "%s". However, you can still get a permanent URL for any amount.', this.state.topClaimValue /*pluralization param*/, this.state.topClaimValue, this.state.name /*regular params*/ )} ); } else { return ''; } } closeModal() { this.setState({ modal: null }); } render() { if (this.state.channels === null) { return null; } const lbcInputHelp = __( 'This LBC remains yours and the deposit can be undone at any time.' ); return (
{ this.handleSubmit(event); }} >

{__('Content')}

{__('What are you publishing?')}
{ this.onFileChange(event); }} helper={ this.state.myClaimExists ? __( "If you don't choose a file, the file from your existing claim will be used." ) : null } />
{!this.state.hasFile ? '' :
{/* */}
}

{__('Access')}

{__('How much does this content cost?')}
{ this.handleFeePrefChange(false); }} defaultChecked={!this.state.isFee} /> { this.handleFeePrefChange(true); }} defaultChecked={this.state.isFee} /> this.handleFeeAmountChange(event)} /> {' '} { this.handleFeeCurrencyChange(event); }} > {this.state.isFee ?
{__( 'If you choose to price this content in dollars, the number of credits charged will be adjusted based on the value of LBRY credits at the time of purchase.' )}
: ''} { this.handleLicenseChange(event); }} > {this.state.copyrightChosen ? { this.handleCopyrightNoticeChange(event); }} /> : null} {this.state.otherLicenseChosen ? { this.handleOtherLicenseDescriptionChange(); }} /> : null} {this.state.otherLicenseChosen ? { this.handleOtherLicenseUrlChange(event); }} /> : null}

{__('Identity')}

{__('Who created this content?')}
{ this.handleChannelChange(event); }} value={this.state.channel} > {this.state.channels.map(({ name }) => )}
{this.state.channel == 'new' ?
{ this.handleNewChannelNameChange(event); }} ref={newChannelName => { this.refs.newChannelName = newChannelName; }} value={this.state.newChannelName} /> { this.handleNewChannelBidChange(event); }} value={this.state.newChannelBid} />
{ this.handleCreateChannelClick(event); }} disabled={this.state.creatingChannel} />
: null}

{__('Address')}

{__('Where should this content permanently reside?')} {' '}.
{ this.handleNameChange(event); }} helper={this.getNameBidHelpText()} />
{this.state.rawName ?
{ this.handleBidChange(event); }} value={this.state.bid} placeholder={ this.state.nameResolved ? this.state.topClaimValue + 10 : 100 } helper={lbcInputHelp} />
: ''}

{__('Terms of Service')}

{__('I agree to the')} {' '} } type="checkbox" name="tos_agree" ref={field => { this.refs.tos_agree = field; }} onChange={event => { this.handleTOSChange(event); }} />
{ this.handleSubmit(event); }} disabled={this.state.submitting} />
{ this.handlePublishStartedConfirmed(event); }} >

{__('Your file has been published to LBRY at the address')} {' '}lbry://{this.state.name}!

{__( 'The file will take a few minutes to appear for other LBRY users. Until then it will be listed as "pending" under your published files.' )}

{ this.closeModal(event); }} > {__( 'The following error occurred when attempting to publish your file' )}: {this.state.errorMessage}
); } } export default PublishPage;