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.claim.amount >= claimInfo.claim.amount); const newState = { nameResolved: true, topClaimValue: parseFloat(claimInfo.claim.amount), myClaimExists: !!myClaimInfo, myClaimValue: myClaimInfo ? parseFloat(myClaimInfo.claim.amount) : null, myClaimMetadata: myClaimInfo ? myClaimInfo.value : null, topClaimIsMine: topClaimIsMine, }; if (topClaimIsMine) { newState.bid = myClaimInfo.claim.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;