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.PureComponent { 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 => { this.props.claimFirstChannelReward(); 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 = { currency: 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: parseFloat(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.props.fetchClaimListMine(); 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;