From 34a66f1968ab51beac9d431384d49c1089239a4c Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Tue, 21 Aug 2018 08:53:17 -0400 Subject: [PATCH 01/46] allow claims to be updated and abandoned --- client/src/actions/publish.js | 13 ++ client/src/actions/show.js | 7 + client/src/api/assetApi.js | 12 ++ client/src/app.js | 2 + client/src/channels/publish.js | 4 +- client/src/constants/publish_action_types.js | 2 + client/src/constants/publish_claim_states.js | 1 + client/src/constants/show_action_types.js | 2 + client/src/containers/AssetDisplay/index.js | 3 +- client/src/containers/AssetDisplay/view.jsx | 76 +++++---- client/src/containers/AssetInfo/index.js | 3 +- client/src/containers/AssetTitle/index.js | 8 +- client/src/containers/AssetTitle/view.jsx | 8 +- client/src/containers/PublishDetails/index.js | 19 ++- client/src/containers/PublishDetails/view.jsx | 70 +++++++- client/src/containers/PublishStatus/view.jsx | 7 + client/src/containers/PublishTool/index.js | 1 + client/src/containers/PublishTool/view.jsx | 9 +- client/src/pages/EditPage/index.js | 31 ++++ client/src/pages/EditPage/view.jsx | 42 +++++ .../pages/HomePage/{index.jsx => index.js} | 0 client/src/pages/HomePage/view.jsx | 19 ++- client/src/pages/ShowAssetDetails/index.js | 5 +- client/src/pages/ShowAssetDetails/view.jsx | 1 - client/src/reducers/publish.js | 7 +- client/src/reducers/show.js | 27 ++++ client/src/sagas/abandon.js | 30 ++++ client/src/sagas/publish.js | 57 +++++-- client/src/sagas/rootSaga.js | 2 + client/src/utils/publish.js | 6 +- server/controllers/api/claim/abandon/index.js | 44 +++++ server/controllers/api/claim/publish/index.js | 15 +- .../publish/parsePublishApiRequestFiles.js | 59 ++++++- .../controllers/api/claim/publish/publish.js | 119 ++++++-------- server/controllers/api/claim/update/index.js | 151 ++++++++++++++++++ .../api/claim/update/updatePublishParams.js | 56 +++++++ server/lbrynet/index.js | 15 ++ server/routes/api/index.js | 6 +- server/routes/pages/index.js | 1 + 39 files changed, 779 insertions(+), 161 deletions(-) create mode 100644 client/src/pages/EditPage/index.js create mode 100644 client/src/pages/EditPage/view.jsx rename client/src/pages/HomePage/{index.jsx => index.js} (100%) create mode 100644 client/src/sagas/abandon.js create mode 100644 server/controllers/api/claim/abandon/index.js create mode 100644 server/controllers/api/claim/update/index.js create mode 100644 server/controllers/api/claim/update/updatePublishParams.js diff --git a/client/src/actions/publish.js b/client/src/actions/publish.js index 552e2aff..fc291c1a 100644 --- a/client/src/actions/publish.js +++ b/client/src/actions/publish.js @@ -14,6 +14,12 @@ export function clearFile () { }; } +export function setUpdateTrue () { + return { + type: actions.SET_UPDATE_TRUE, + }; +} + export function updateMetadata (name, value) { return { type: actions.METADATA_UPDATE, @@ -31,6 +37,13 @@ export function updateClaim (value) { }; }; +export function abandonClaim (data) { + return { + type: actions.ABANDON_CLAIM, + data, + }; +}; + export function setPublishInChannel (channel) { return { type: actions.SET_PUBLISH_IN_CHANNEL, diff --git a/client/src/actions/show.js b/client/src/actions/show.js index 1bac1310..32c4d47c 100644 --- a/client/src/actions/show.js +++ b/client/src/actions/show.js @@ -105,6 +105,13 @@ export function updateAssetViewsInList (id, claimId, claimViews) { }; } +export function removeAsset (data) { + return { + type: actions.ASSET_REMOVE, + data, + }; +} + // channel actions export function addNewChannelToChannelList (id, name, shortId, longId, claimsData) { diff --git a/client/src/api/assetApi.js b/client/src/api/assetApi.js index 5290d19f..0750b572 100644 --- a/client/src/api/assetApi.js +++ b/client/src/api/assetApi.js @@ -42,3 +42,15 @@ export function getClaimViews (claimId) { const url = `/api/claim/views/${claimId}`; return Request(url); } + +export function doAbandonClaim (claimId) { + const params = { + method : 'POST', + body : JSON.stringify({claimId}), + headers: new Headers({ + 'Content-Type': 'application/json', + }), + credentials: 'include', + }; + return Request('/api/claim/abandon', params); +} diff --git a/client/src/app.js b/client/src/app.js index c4456ffe..e2bfd46e 100644 --- a/client/src/app.js +++ b/client/src/app.js @@ -10,6 +10,7 @@ import ContentPageWrapper from '@pages/ContentPageWrapper'; import FourOhFourPage from '@pages/FourOhFourPage'; import MultisitePage from '@pages/MultisitePage'; import PopularPage from '@pages/PopularPage'; +import EditPage from '@pages/EditPage'; const App = () => { return ( @@ -21,6 +22,7 @@ const App = () => { + diff --git a/client/src/channels/publish.js b/client/src/channels/publish.js index bdbfbf19..0a89feb4 100644 --- a/client/src/channels/publish.js +++ b/client/src/channels/publish.js @@ -1,8 +1,8 @@ import {buffers, END, eventChannel} from 'redux-saga'; -export const makePublishRequestChannel = (fd) => { +export const makePublishRequestChannel = (fd, isUpdate) => { return eventChannel(emitter => { - const uri = '/api/claim/publish'; + const uri = `/api/claim/${isUpdate ? 'update' : 'publish'}`; const xhr = new XMLHttpRequest(); // add event listeners const onLoadStart = () => { diff --git a/client/src/constants/publish_action_types.js b/client/src/constants/publish_action_types.js index 317f055a..dee25bd0 100644 --- a/client/src/constants/publish_action_types.js +++ b/client/src/constants/publish_action_types.js @@ -10,3 +10,5 @@ export const TOGGLE_METADATA_INPUTS = 'TOGGLE_METADATA_INPUTS'; export const THUMBNAIL_NEW = 'THUMBNAIL_NEW'; export const PUBLISH_START = 'PUBLISH_START'; export const CLAIM_AVAILABILITY = 'CLAIM_AVAILABILITY'; +export const SET_UPDATE_TRUE = 'SET_UPDATE_TRUE'; +export const ABANDON_CLAIM = 'ABANDON_CLAIM'; diff --git a/client/src/constants/publish_claim_states.js b/client/src/constants/publish_claim_states.js index b8f87f1d..8dff837b 100644 --- a/client/src/constants/publish_claim_states.js +++ b/client/src/constants/publish_claim_states.js @@ -3,3 +3,4 @@ export const LOADING = 'LOADING'; export const PUBLISHING = 'PUBLISHING'; export const SUCCESS = 'SUCCESS'; export const FAILED = 'FAILED'; +export const ABANDONING = 'ABANDONING'; diff --git a/client/src/constants/show_action_types.js b/client/src/constants/show_action_types.js index 50996b2c..a2cc511b 100644 --- a/client/src/constants/show_action_types.js +++ b/client/src/constants/show_action_types.js @@ -11,6 +11,8 @@ export const REQUEST_LIST_ADD = 'REQUEST_LIST_ADD'; // asset actions export const ASSET_ADD = 'ASSET_ADD'; export const ASSET_VIEWS_UPDATE = 'ASSET_VIEWS_UPDATE'; +export const ASSET_UPDATE_CLAIMDATA = 'ASSET_UPDATE_CLAIMDATA'; +export const ASSET_REMOVE = 'ASSET_REMOVE'; // channel actions export const CHANNEL_ADD = 'CHANNEL_ADD'; diff --git a/client/src/containers/AssetDisplay/index.js b/client/src/containers/AssetDisplay/index.js index 54acb4e6..02c03e83 100644 --- a/client/src/containers/AssetDisplay/index.js +++ b/client/src/containers/AssetDisplay/index.js @@ -3,7 +3,8 @@ import View from './view'; import { fileRequested } from '../../actions/show'; import { selectAsset } from '../../selectors/show'; -const mapStateToProps = ({ show }) => { +const mapStateToProps = (props) => { + const {show} = props; // select error and status const error = show.displayAsset.error; const status = show.displayAsset.status; diff --git a/client/src/containers/AssetDisplay/view.jsx b/client/src/containers/AssetDisplay/view.jsx index d5b37b82..258701a0 100644 --- a/client/src/containers/AssetDisplay/view.jsx +++ b/client/src/containers/AssetDisplay/view.jsx @@ -3,14 +3,49 @@ import Row from '@components/Row'; import ProgressBar from '@components/ProgressBar'; import { LOCAL_CHECK, UNAVAILABLE, ERROR, AVAILABLE } from '../../constants/asset_display_states'; +class AvailableContent extends React.Component { + render () { + const {contentType, sourceUrl, name, thumbnail} = this.props; + switch (contentType) { + case 'image/jpeg': + case 'image/jpg': + case 'image/png': + case 'image/gif': + return ( + {name} + ); + case 'video/mp4': + return ( + + ); + default: + return ( +

Unsupported content type

+ ); + } + } +} + class AssetDisplay extends React.Component { componentDidMount () { const { asset: { claimData: { name, claimId } } } = this.props; this.props.onFileRequest(name, claimId); } render () { - const { status, error, asset: { claimData: { name, claimId, contentType, fileExt, thumbnail } } } = this.props; - const sourceUrl = `/${claimId}/${name}.${fileExt}`; + const { status, error, asset: { name, claimData: { claimId, contentType, fileExt, thumbnail, outpoint } } } = this.props; + const sourceUrl = `/${claimId}/${name}.${fileExt}?${outpoint}`; return (
{(status === LOCAL_CHECK) && @@ -36,37 +71,12 @@ class AssetDisplay extends React.Component {
} {(status === AVAILABLE) && - (() => { - switch (contentType) { - case 'image/jpeg': - case 'image/jpg': - case 'image/png': - case 'image/gif': - return ( - {name} - ); - case 'video/mp4': - return ( - - ); - default: - return ( -

Unsupported content type

- ); - } - })() + } ); diff --git a/client/src/containers/AssetInfo/index.js b/client/src/containers/AssetInfo/index.js index e2eb3a68..aa42d3f5 100644 --- a/client/src/containers/AssetInfo/index.js +++ b/client/src/containers/AssetInfo/index.js @@ -2,7 +2,8 @@ import { connect } from 'react-redux'; import View from './view'; import { selectAsset } from '../../selectors/show'; -const mapStateToProps = ({ show }) => { +const mapStateToProps = (props) => { + const {show} = props; // select asset const asset = selectAsset(show); // return props diff --git a/client/src/containers/AssetTitle/index.js b/client/src/containers/AssetTitle/index.js index 8ff2c9ae..9e3be580 100644 --- a/client/src/containers/AssetTitle/index.js +++ b/client/src/containers/AssetTitle/index.js @@ -2,10 +2,14 @@ import { connect } from 'react-redux'; import View from './view'; import { selectAsset } from '../../selectors/show'; -const mapStateToProps = ({ show }) => { - const { claimData: { title } } = selectAsset(show); +const mapStateToProps = (props) => { + const { claimData: { title, claimId, name, channelName } } = selectAsset(props.show); + const editable = Boolean(props.channel.loggedInChannel.name === channelName); return { title, + claimId, + name, + editable, }; }; diff --git a/client/src/containers/AssetTitle/view.jsx b/client/src/containers/AssetTitle/view.jsx index a542c762..034b7c06 100644 --- a/client/src/containers/AssetTitle/view.jsx +++ b/client/src/containers/AssetTitle/view.jsx @@ -1,10 +1,14 @@ import React from 'react'; +import { Link } from 'react-router-dom'; import Row from '@components/Row'; -const AssetTitle = ({ title }) => { +const AssetTitle = ({ title, editable, claimId, name }) => { return ( -

{title}

+

+ {title} + {editable && ( (edit))} +

); }; diff --git a/client/src/containers/PublishDetails/index.js b/client/src/containers/PublishDetails/index.js index 7b880782..a095e8e5 100644 --- a/client/src/containers/PublishDetails/index.js +++ b/client/src/containers/PublishDetails/index.js @@ -1,16 +1,29 @@ import { connect } from 'react-redux'; -import { clearFile, startPublish } from '../../actions/publish'; +import { clearFile, startPublish, abandonClaim } from '../../actions/publish'; import View from './view'; -const mapStateToProps = ({ channel, publish }) => { +const mapStateToProps = ({ show, publish }) => { + // select request info + const requestId = show.request.id; + // select asset info + let asset; + const request = show.requestList[requestId] || null; + const assetList = show.assetList; + if (request && assetList) { + const assetKey = request.key; // note: just store this in the request + asset = assetList[assetKey] || null; + }; return { - file: publish.file, + file : publish.file, + isUpdate: publish.isUpdate, + asset, }; }; const mapDispatchToProps = { clearFile, startPublish, + abandonClaim, }; export default connect(mapStateToProps, mapDispatchToProps)(View); diff --git a/client/src/containers/PublishDetails/view.jsx b/client/src/containers/PublishDetails/view.jsx index ac55e226..42d37c77 100644 --- a/client/src/containers/PublishDetails/view.jsx +++ b/client/src/containers/PublishDetails/view.jsx @@ -1,12 +1,15 @@ import React from 'react'; -import { withRouter } from 'react-router-dom'; +import {Link, withRouter} from 'react-router-dom'; import PublishUrlInput from '@containers/PublishUrlInput'; import PublishThumbnailInput from '@containers/PublishThumbnailInput'; import PublishMetadataInputs from '@containers/PublishMetadataInputs'; import ChannelSelect from '@containers/ChannelSelect'; import Row from '@components/Row'; +import Label from '@components/Label'; +import RowLabeled from '@components/RowLabeled'; import ButtonPrimaryJumbo from '@components/ButtonPrimaryJumbo'; import ButtonTertiary from '@components/ButtonTertiary'; +import ButtonSecondary from '@components/ButtonSecondary'; import SpaceAround from '@components/SpaceAround'; import PublishFinePrint from '@components/PublishFinePrint'; @@ -14,22 +17,62 @@ class PublishDetails extends React.Component { constructor (props) { super(props); this.onPublishSubmit = this.onPublishSubmit.bind(this); + this.abandonClaim = this.abandonClaim.bind(this); } onPublishSubmit () { this.props.startPublish(this.props.history); } + abandonClaim () { + const {asset, history} = this.props; + if (asset) { + const {claimData} = asset; + this.props.abandonClaim({claimData, history}); + } + } render () { + const {file, isUpdate, asset} = this.props; return (
- - - + {isUpdate ? (asset && ( + + + + } + content={ + + {asset.claimData.channelName} + + } + /> + + + + } + content={ + + {asset.name} + + } + /> + + + )) : ( + + + + - - - + + + + + )} - { this.props.file.type === 'video/mp4' && ( + { file && file.type === 'video/mp4' && ( @@ -46,6 +89,17 @@ class PublishDetails extends React.Component { /> + {isUpdate && ( + + + + + + )} +
} + {status === publishStates.ABANDONING && +
+ +

Your claim is being abandoned.

+
+
+ } ); } diff --git a/client/src/containers/PublishTool/index.js b/client/src/containers/PublishTool/index.js index 9258d560..0a0b8684 100644 --- a/client/src/containers/PublishTool/index.js +++ b/client/src/containers/PublishTool/index.js @@ -6,6 +6,7 @@ const mapStateToProps = ({ publish }) => { disabled: publish.disabled, file : publish.file, status : publish.status.status, + isUpdate: publish.isUpdate, }; }; diff --git a/client/src/containers/PublishTool/view.jsx b/client/src/containers/PublishTool/view.jsx index b628911a..e9869a22 100644 --- a/client/src/containers/PublishTool/view.jsx +++ b/client/src/containers/PublishTool/view.jsx @@ -6,18 +6,19 @@ import PublishDisabledMessage from '@containers/PublishDisabledMessage'; class PublishTool extends React.Component { render () { - if (this.props.disabled) { + const {disabled, file, isUpdate, status} = this.props; + if (disabled) { return ( ); } else { - if (this.props.file) { - if (this.props.status) { + if (file || isUpdate) { + if (status) { return ( ); } else { - return ; + return ; } } return ; diff --git a/client/src/pages/EditPage/index.js b/client/src/pages/EditPage/index.js new file mode 100644 index 00000000..68e2cbef --- /dev/null +++ b/client/src/pages/EditPage/index.js @@ -0,0 +1,31 @@ +import { connect } from 'react-redux'; +import { setUpdateTrue, updateMetadata, clearFile } from '../../actions/publish'; +import { onHandleShowPageUri } from '../../actions/show'; +import View from './view'; + +const mapStateToProps = (props) => { + const { show } = props; + // select request info + const requestId = show.request.id; + // select asset info + let asset; + const request = show.requestList[requestId] || null; + const assetList = show.assetList; + if (request && assetList) { + const assetKey = request.key; // note: just store this in the request + asset = assetList[assetKey] || null; + } + return { + asset, + myChannel: props.channel.loggedInChannel.name, + }; +}; + +const mapDispatchToProps = { + updateMetadata, + onHandleShowPageUri, + setUpdateTrue, + clearFile, +}; + +export default connect(mapStateToProps, mapDispatchToProps)(View); diff --git a/client/src/pages/EditPage/view.jsx b/client/src/pages/EditPage/view.jsx new file mode 100644 index 00000000..c725d527 --- /dev/null +++ b/client/src/pages/EditPage/view.jsx @@ -0,0 +1,42 @@ +import React from 'react'; +import PageLayout from '@components/PageLayout'; +import { Redirect } from 'react-router-dom'; +import PublishTool from '@containers/PublishTool'; + +class EditPage extends React.Component { + componentDidMount () { + const {asset, match, onHandleShowPageUri, setUpdateTrue, updateMetadata} = this.props; + onHandleShowPageUri(match.params); + setUpdateTrue(); + if (asset) { + ['title', 'description', 'license', 'nsfw'].forEach(meta => updateMetadata(meta, asset.claimData[meta])); + } + } + componentWillUnmount () { + this.props.clearFile(); + } + render () { + const { myChannel, asset } = this.props; + // redirect if user does not own this claim + if ( + !myChannel || ( + asset && + asset.claimsData && + asset.claimsData.channelName && + asset.claimsData.channelName !== myChannel + ) + ) { + return (); + } + return ( + + + + ); + } +}; + +export default EditPage; diff --git a/client/src/pages/HomePage/index.jsx b/client/src/pages/HomePage/index.js similarity index 100% rename from client/src/pages/HomePage/index.jsx rename to client/src/pages/HomePage/index.js diff --git a/client/src/pages/HomePage/view.jsx b/client/src/pages/HomePage/view.jsx index 491d00a1..9096e734 100644 --- a/client/src/pages/HomePage/view.jsx +++ b/client/src/pages/HomePage/view.jsx @@ -4,16 +4,19 @@ import PublishTool from '@containers/PublishTool'; import ContentPageWrapper from '@pages/ContentPageWrapper'; class HomePage extends React.Component { - componentDidMount () { - this.props.onHandleShowHomepage(this.props.match.params); - } + // componentDidMount () { + // this.props.onHandleShowHomepage(this.props.match.params); + // } + // + // componentWillReceiveProps (nextProps) { + // if (nextProps.match.params !== this.props.match.params) { + // this.props.onHandleShowHomepage(nextProps.match.params); + // } + // } - componentWillReceiveProps (nextProps) { - if (nextProps.match.params !== this.props.match.params) { - this.props.onHandleShowHomepage(nextProps.match.params); - } + componentWillUnmount () { + this.props.clearFile(); } - render () { const { homeChannel } = this.props; return homeChannel ? ( diff --git a/client/src/pages/ShowAssetDetails/index.js b/client/src/pages/ShowAssetDetails/index.js index 0af0073c..64544b70 100644 --- a/client/src/pages/ShowAssetDetails/index.js +++ b/client/src/pages/ShowAssetDetails/index.js @@ -1,7 +1,8 @@ import { connect } from 'react-redux'; import View from './view'; -const mapStateToProps = ({ show }) => { +const mapStateToProps = (props) => { + const {show} = props; // select request info const requestId = show.request.id; // select asset info @@ -11,7 +12,7 @@ const mapStateToProps = ({ show }) => { if (request && assetList) { const assetKey = request.key; // note: just store this in the request asset = assetList[assetKey] || null; - }; + } // return props return { asset, diff --git a/client/src/pages/ShowAssetDetails/view.jsx b/client/src/pages/ShowAssetDetails/view.jsx index 98c09372..ed26c436 100644 --- a/client/src/pages/ShowAssetDetails/view.jsx +++ b/client/src/pages/ShowAssetDetails/view.jsx @@ -1,6 +1,5 @@ import React from 'react'; import PageLayout from '@components/PageLayout'; - import HorizontalSplit from '@components/HorizontalSplit'; import AssetTitle from '@containers/AssetTitle'; import AssetDisplay from '@containers/AssetDisplay'; diff --git a/client/src/reducers/publish.js b/client/src/reducers/publish.js index 29a31d7d..1489c711 100644 --- a/client/src/reducers/publish.js +++ b/client/src/reducers/publish.js @@ -41,6 +41,7 @@ const initialState = { license : '', nsfw : false, }, + isUpdate : false, thumbnail: null, thumbnailChannel, thumbnailChannelId, @@ -49,7 +50,7 @@ const initialState = { export default function (state = initialState, action) { switch (action.type) { case actions.FILE_SELECTED: - return Object.assign({}, initialState, { // note: clears to initial state + return Object.assign({}, state.isUpdate ? state : initialState, { // note: clears to initial state file: action.data, }); case actions.FILE_CLEAR: @@ -90,6 +91,10 @@ export default function (state = initialState, action) { return Object.assign({}, state, { thumbnail: action.data, }); + case actions.SET_UPDATE_TRUE: + return Object.assign({}, state, { + isUpdate: true, + }); default: return state; } diff --git a/client/src/reducers/show.js b/client/src/reducers/show.js index 91177370..ded05015 100644 --- a/client/src/reducers/show.js +++ b/client/src/reducers/show.js @@ -65,6 +65,33 @@ export default function (state = initialState, action) { }, }), }); + case actions.ASSET_REMOVE: + const claim = action.data; + const newAssetList = state.assetList; + delete newAssetList[`a#${claim.name}#${claim.claimId}`]; + + const channelId = `c#${claim.channelName}#${claim.certificateId}`; + const channelClaims = state.channelList[channelId].claimsData.claims; + const newClaimsData = channelClaims.filter(c => c.claimId !== claim.claimId); + + return Object.assign({}, state, { + assetList : newAssetList, + channelList: Object.assign({}, state.channelList, { + [channelId]: Object.assign({}, state.channelList[channelId], { + claimsData: Object.assign({}, state.channelList[channelId].claimsData, { + claims: newClaimsData, + }), + }), + }), + }); + case actions.ASSET_UPDATE_CLAIMDATA: + return Object.assign({}, state, { + assetList: Object.assign({}, state.assetList, { + [action.data.id]: Object.assign({}, state.assetList[action.data.id], { + claimData: Object.assign({}, state.assetList[action.data.id].claimData, action.data.claimData), + }), + }), + }); // channel data case actions.CHANNEL_ADD: return Object.assign({}, state, { diff --git a/client/src/sagas/abandon.js b/client/src/sagas/abandon.js new file mode 100644 index 00000000..ffdc34cc --- /dev/null +++ b/client/src/sagas/abandon.js @@ -0,0 +1,30 @@ +import { call, put, takeLatest } from 'redux-saga/effects'; +import * as actions from '../constants/publish_action_types'; +import * as publishStates from '../constants/publish_claim_states'; +import { updatePublishStatus, clearFile } from '../actions/publish'; +import { removeAsset } from '../actions/show'; +import { doAbandonClaim } from '../api/assetApi'; + +function * abandonClaim (action) { + const { claimData, history } = action.data; + const {claimId} = claimData; + + const confirm = window.confirm('Are you sure you want to abandon this claim? This action cannot be undone.'); + if (!confirm) return; + + yield put(updatePublishStatus(publishStates.ABANDONING, 'Your claim is being abandoned...')); + + try { + yield call(doAbandonClaim, claimId); + } catch (error) { + return console.log('abandon error:', error.message); + } + + yield put(clearFile()); + yield put(removeAsset(claimData)); + return history.push('/'); +} + +export function * watchAbandonClaim () { + yield takeLatest(actions.ABANDON_CLAIM, abandonClaim); +}; diff --git a/client/src/sagas/publish.js b/client/src/sagas/publish.js index 62157eca..e82dd121 100644 --- a/client/src/sagas/publish.js +++ b/client/src/sagas/publish.js @@ -5,37 +5,61 @@ import { updateError, updatePublishStatus, clearFile } from '../actions/publish' import { selectPublishState } from '../selectors/publish'; import { selectChannelState } from '../selectors/channel'; import { selectSiteState } from '../selectors/site'; +import { selectShowState, selectAsset } from '../selectors/show'; import { validateChannelSelection, validateNoPublishErrors } from '../utils/validate'; import { createPublishMetadata, createPublishFormData, createThumbnailUrl } from '../utils/publish'; import { makePublishRequestChannel } from '../channels/publish'; function * publishFile (action) { const { history } = action.data; - const { publishInChannel, selectedChannel, file, claim, metadata, thumbnailChannel, thumbnailChannelId, thumbnail, error: publishToolErrors } = yield select(selectPublishState); + const publishState = yield select(selectPublishState); + const { publishInChannel, selectedChannel, file, claim, metadata, thumbnailChannel, thumbnailChannelId, thumbnail, isUpdate, error: publishToolErrors } = publishState; const { loggedInChannel } = yield select(selectChannelState); const { host } = yield select(selectSiteState); + + let show, asset; + if (isUpdate) { + show = yield select(selectShowState); + asset = selectAsset(show); + } // validate the channel selection try { validateChannelSelection(publishInChannel, selectedChannel, loggedInChannel); } catch (error) { return yield put(updateError('channel', error.message)); - }; + } // validate publish parameters try { validateNoPublishErrors(publishToolErrors); } catch (error) { return console.log('publish error:', error.message); } - // create metadata - let publishMetadata = createPublishMetadata(claim, file, metadata, publishInChannel, selectedChannel); - if (thumbnail) { - // add thumbnail to publish metadata - publishMetadata['thumbnail'] = createThumbnailUrl(thumbnailChannel, thumbnailChannelId, claim, host); + + let publishMetadata, publishFormData, publishChannel; + if (isUpdate) { + publishMetadata = createPublishMetadata(asset.name, {type: asset.claimData.contentType}, metadata, publishInChannel, selectedChannel); + publishMetadata['channelName'] = asset.claimData.channelName; + if (thumbnail) { + // add thumbnail to publish metadata + publishMetadata['thumbnail'] = createThumbnailUrl(thumbnailChannel, thumbnailChannelId, claim, host); + } + // create form data for main publish + publishFormData = createPublishFormData(file, thumbnail, publishMetadata); + // make the publish request + publishChannel = yield call(makePublishRequestChannel, publishFormData, true); + } else { + // create metadata + publishMetadata = createPublishMetadata(claim, file, metadata, publishInChannel, selectedChannel); + if (thumbnail) { + // add thumbnail to publish metadata + publishMetadata['thumbnail'] = createThumbnailUrl(thumbnailChannel, thumbnailChannelId, claim, host); + } + // create form data for main publish + publishFormData = createPublishFormData(file, thumbnail, publishMetadata); + // make the publish request + publishChannel = yield call(makePublishRequestChannel, publishFormData); } - // create form data for main publish - const publishFormData = createPublishFormData(file, thumbnail, publishMetadata); - // make the publish request - const publishChannel = yield call(makePublishRequestChannel, publishFormData); + while (true) { const {loadStart, progress, load, success, error: publishError} = yield take(publishChannel); if (publishError) { @@ -43,6 +67,15 @@ function * publishFile (action) { } if (success) { yield put(clearFile()); + if (isUpdate) { + yield put({ + type: 'ASSET_UPDATE_CLAIMDATA', + data: { + id : `a#${success.data.name}#${success.data.claimId}`, + claimData: success.data.claimData, + }, + }); + } return history.push(`/${success.data.claimId}/${success.data.name}`); } if (loadStart) { @@ -55,7 +88,7 @@ function * publishFile (action) { yield put(updatePublishStatus(publishStates.PUBLISHING, null)); } } -}; +} export function * watchPublishStart () { yield takeLatest(actions.PUBLISH_START, publishFile); diff --git a/client/src/sagas/rootSaga.js b/client/src/sagas/rootSaga.js index 05a5c471..39481bca 100644 --- a/client/src/sagas/rootSaga.js +++ b/client/src/sagas/rootSaga.js @@ -10,6 +10,7 @@ import { watchUpdateChannelAvailability } from './updateChannelAvailability'; import { watchChannelCreate } from './createChannel'; import { watchChannelLoginCheck } from './checkForLoggedInChannel'; import { watchChannelLogout } from './logoutChannel'; +import { watchAbandonClaim } from './abandon'; export function * rootSaga () { yield all([ @@ -27,5 +28,6 @@ export function * rootSaga () { watchChannelLoginCheck(), watchChannelLogout(), watchUpdateAssetViews(), + watchAbandonClaim(), ]); } diff --git a/client/src/utils/publish.js b/client/src/utils/publish.js index ecce371f..00ac2240 100644 --- a/client/src/utils/publish.js +++ b/client/src/utils/publish.js @@ -16,7 +16,9 @@ export const createPublishMetadata = (claim, { type }, { title, description, lic export const createPublishFormData = (file, thumbnail, metadata) => { let fd = new FormData(); // append file - fd.append('file', file); + if (file) { + fd.append('file', file); + } // append thumbnail if (thumbnail) { fd.append('thumbnail', thumbnail); @@ -31,5 +33,5 @@ export const createPublishFormData = (file, thumbnail, metadata) => { }; export const createThumbnailUrl = (channel, channelId, claim, host) => { - return `${host}/${channel}:${channelId}/${claim}-thumb.png`; + return `${host}/${channel}:${channelId}/${claim}-thumb.jpg`; }; diff --git a/server/controllers/api/claim/abandon/index.js b/server/controllers/api/claim/abandon/index.js new file mode 100644 index 00000000..c557afe0 --- /dev/null +++ b/server/controllers/api/claim/abandon/index.js @@ -0,0 +1,44 @@ +const logger = require('winston'); +const db = require('../../../../models'); +const { abandonClaim } = require('../../../../lbrynet'); +const deleteFile = require('../publish/deleteFile.js'); +const authenticateUser = require('../publish/authentication.js'); + +/* + route to abandon a claim through the daemon +*/ + +const claimAbandon = async (req, res) => { + const {claimId} = req.body; + const {user} = req; + try { + const [channel, claim] = await Promise.all([ + authenticateUser(user.channelName, null, null, user), + db.Claim.findOne({where: {claimId}}), + ]); + + if (!claim) throw new Error('That channel does not exist'); + if (!channel.channelName) throw new Error('You don\'t own this channel'); + + await abandonClaim({claimId}); + const file = await db.File.findOne({where: {claimId}}); + await Promise.all([ + deleteFile(file.filePath), + db.File.destroy({where: {claimId}}), + db.Claim.destroy({where: {claimId}}), + ]); + logger.debug(`Claim abandoned: ${claimId}`); + res.status(200).json({ + success: true, + message: `Claim with id ${claimId} abandonded`, + }); + } catch (error) { + logger.error('abandon claim error:', error); + res.status(400).json({ + success: false, + message: error.message, + }); + } +}; + +module.exports = claimAbandon; diff --git a/server/controllers/api/claim/publish/index.js b/server/controllers/api/claim/publish/index.js index f51d4055..530d03c8 100644 --- a/server/controllers/api/claim/publish/index.js +++ b/server/controllers/api/claim/publish/index.js @@ -14,7 +14,7 @@ const publish = require('./publish.js'); const createPublishParams = require('./createPublishParams.js'); const createThumbnailPublishParams = require('./createThumbnailPublishParams.js'); const parsePublishApiRequestBody = require('./parsePublishApiRequestBody.js'); -const parsePublishApiRequestFiles = require('./parsePublishApiRequestFiles.js'); +const {parsePublishApiRequestFiles} = require('./parsePublishApiRequestFiles.js'); const authenticateUser = require('./authentication.js'); const CLAIM_TAKEN = 'CLAIM_TAKEN'; @@ -85,17 +85,18 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res) // publish the asset return publish(publishParams, fileName, fileType); }) - .then(result => { + .then(claimData => { + logger.debug('Publish success >', claimData); res.status(200).json({ success: true, message: 'publish completed successfully', data : { name, - claimId : result.claim_id, - url : `${host}/${result.claim_id}/${name}`, // for backwards compatability with app - showUrl : `${host}/${result.claim_id}/${name}`, - serveUrl: `${host}/${result.claim_id}/${name}${fileExtension}`, - lbryTx : result, + claimId : claimData.claimId, + url : `${host}/${claimData.claimId}/${name}`, // for backwards compatability with app + showUrl : `${host}/${claimData.claimId}/${name}`, + serveUrl: `${host}/${claimData.claimId}/${name}${fileExtension}`, + claimData, }, }); // record the publish end time and send to google analytics diff --git a/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js b/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js index 3e99ad20..4b18e5f7 100644 --- a/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js +++ b/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js @@ -42,4 +42,61 @@ const parsePublishApiRequestFiles = ({file, thumbnail}) => { }; }; -module.exports = parsePublishApiRequestFiles; +const parsePublishApiRequestFile = ({file, thumbnail}, isUpdate) => { + // make sure a file was provided + if (!file) { + if (isUpdate) { + return; + } + throw new Error('no file with key of [file] found in request'); + } + if (!file.path) { + throw new Error('no file path found'); + } + if (!file.type) { + throw new Error('no file type found'); + } + if (!file.size) { + throw new Error('no file size found'); + } + // validate the file name + if (!file.name) { + throw new Error('no file name found'); + } + if (file.name.indexOf('.') < 0) { + throw new Error('no file extension found in file name'); + } + if (file.name.indexOf('.') === 0) { + throw new Error('file name cannot start with "."'); + } + if (/'/.test(file.name)) { + throw new Error('apostrophes are not allowed in the file name'); + } + + // validate the file + validateFileTypeAndSize(file); + // return results + return { + fileName : file.name, + filePath : file.path, + fileExtension: path.extname(file.path), + fileType : file.type, + }; +}; + +const parsePublishApiRequestThumbnail = ({file, thumbnail}) => { + if (!thumbnail) { + return; + } + return { + thumbnailFileName: thumbnail.name, + thumbnailFilePath: thumbnail.path, + thumbnailFileType: thumbnail.type, + }; +}; + +module.exports = { + parsePublishApiRequestFiles, + parsePublishApiRequestFile, + parsePublishApiRequestThumbnail, +}; diff --git a/server/controllers/api/claim/publish/publish.js b/server/controllers/api/claim/publish/publish.js index 21de83bd..298f07f4 100644 --- a/server/controllers/api/claim/publish/publish.js +++ b/server/controllers/api/claim/publish/publish.js @@ -1,81 +1,62 @@ const logger = require('winston'); -const { publishClaim } = require('../../../../lbrynet'); const db = require('../../../../models'); +const { publishClaim } = require('../../../../lbrynet'); const { createFileRecordDataAfterPublish } = require('../../../../models/utils/createFileRecordData.js'); const { createClaimRecordDataAfterPublish } = require('../../../../models/utils/createClaimRecordData.js'); const deleteFile = require('./deleteFile.js'); -const publish = (publishParams, fileName, fileType) => { - return new Promise((resolve, reject) => { - let publishResults, certificateId, channelName; - // publish the file - return publishClaim(publishParams) - .then(result => { - logger.info(`Successfully published ${publishParams.name} ${fileName}`, result); +const publish = async (publishParams, fileName, fileType) => { + let publishResults; + let channel; + let fileRecord; + let filePath = publishParams.file_path; + let newFile = Boolean(filePath); - // Support new daemon, TODO: remove - publishResults = result.output && result.output.claim_id ? result.output : result; + try { + publishResults = await publishClaim(publishParams); + logger.info(`Successfully published ${publishParams.name} ${fileName}`, publishResults); - // get the channel information - if (publishParams.channel_name) { - logger.debug(`this claim was published in channel: ${publishParams.channel_name}`); - return db.Channel.findOne({ - where: { - channelName: publishParams.channel_name, - }, - }); - } else { - logger.debug('this claim was not published in a channel'); - return null; - } - }) - .then(channel => { - // set channel information - certificateId = null; - channelName = null; - if (channel) { - certificateId = channel.channelClaimId; - channelName = channel.channelName; - } - logger.debug(`certificateId: ${certificateId}`); - }) - .then(() => { - return Promise.all([ - createFileRecordDataAfterPublish(fileName, fileType, publishParams, publishResults), - createClaimRecordDataAfterPublish(certificateId, channelName, fileName, fileType, publishParams, publishResults), - ]); - }) - .then(([fileRecord, claimRecord]) => { - // upsert the records - const {name} = publishParams; - const {claim_id: claimId} = publishResults; - const upsertCriteria = { - name, - claimId, - }; - return Promise.all([ - db.upsert(db.File, fileRecord, upsertCriteria, 'File'), - db.upsert(db.Claim, claimRecord, upsertCriteria, 'Claim'), - ]); - }) - .then(([file, claim]) => { - logger.debug('File and Claim records successfully created'); - return Promise.all([ - file.setClaim(claim), - claim.setFile(file), - ]); - }) - .then(() => { - logger.debug('File and Claim records successfully associated'); - // resolve the promise with the result from lbryApi publishClaim; - resolve(publishResults); - }) - .catch(error => { - logger.error('PUBLISH ERROR', error); - deleteFile(publishParams.file_path); // delete the local file - reject(error); + // get the channel information + if (publishParams.channel_name) { + logger.debug(`this claim was published in channel: ${publishParams.channel_name}`); + channel = await db.Channel.findOne({ + where: { + channelName: publishParams.channel_name, + }, }); - }); + } else { + channel = null; + } + const certificateId = channel ? channel.channelClaimId : null; + const channelName = channel ? channel.channelName : null; + + const claimRecord = await createClaimRecordDataAfterPublish(certificateId, channelName, fileName, fileType, publishParams, publishResults); + const {claimId} = claimRecord; + const upsertCriteria = {name: publishParams.name, claimId}; + if (newFile) { + fileRecord = await createFileRecordDataAfterPublish(fileName, fileType, publishParams, publishResults); + } else { + fileRecord = await db.File.findOne({where: {claimId}}); + } + + const [file, claim] = await Promise.all([ + db.upsert(db.File, fileRecord, upsertCriteria, 'File'), + db.upsert(db.Claim, claimRecord, upsertCriteria, 'Claim'), + ]); + logger.info('File and Claim records successfully created'); + + await Promise.all([ + file.setClaim(claim), + claim.setFile(file), + ]); + logger.info('File and Claim records successfully associated'); + + return claimRecord; + } catch (err) { + logger.error('PUBLISH ERROR', err); + await deleteFile(filePath); + return err; + } }; module.exports = publish; diff --git a/server/controllers/api/claim/update/index.js b/server/controllers/api/claim/update/index.js new file mode 100644 index 00000000..e45d124b --- /dev/null +++ b/server/controllers/api/claim/update/index.js @@ -0,0 +1,151 @@ +const logger = require('winston'); +const db = require('../../../../models'); +const { details, publishing: { disabled, disabledMessage, primaryClaimAddress } } = require('@config/siteConfig'); +const { resolveUri } = require('../../../../lbrynet'); +const { sendGATimingEvent } = require('../../../../utils/googleAnalytics.js'); +const { handleErrorResponse } = require('../../../utils/errorHandlers.js'); +const publish = require('../publish/publish.js'); +const parsePublishApiRequestBody = require('../publish/parsePublishApiRequestBody'); +const {parsePublishApiRequestFile, parsePublishApiRequestThumbnail} = require('../publish/parsePublishApiRequestFiles.js'); +const authenticateUser = require('../publish/authentication.js'); +const createThumbnailPublishParams = require('../publish/createThumbnailPublishParams.js'); + +/* + route to update a claim through the daemon +*/ + +const updateMetadata = ({nsfw, license, title, description}) => { + const update = {}; + if (nsfw) update['nsfw'] = nsfw; + if (license) update['license'] = license; + if (title) update['title'] = title; + if (description) update['description'] = description; + return update; +}; + +const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res) => { + // logging + logger.info('Claim update request:', { + ip, + headers, + body, + files, + user, + }); + + // check for disabled publishing + if (disabled) { + return res.status(503).json({ + success: false, + message: disabledMessage, + }); + } + + // define variables + let channelName; + let channelId; + let channelPassword; + let description; + let fileName; + let filePath; + let fileType; + let gaStartTime; + let thumbnail; + let fileExtension; + let license; + let name; + let nsfw; + let thumbnailFileName; + let thumbnailFilePath; + let thumbnailFileType; + let title; + let claimRecord; + let metadata; + // record the start time of the request + gaStartTime = Date.now(); + + try { + ({name, nsfw, license, title, description, thumbnail} = parsePublishApiRequestBody(body)); + if (files.file) { + ({fileName, filePath, fileExtension, fileType} = parsePublishApiRequestFile(files)); + if (files.thumbnail) { + ({thumbnailFileName, thumbnailFilePath, thumbnailFileType} = parsePublishApiRequestThumbnail(files)); + } + } + ({channelName, channelId, channelPassword} = body); + } catch (error) { + return res.status(400).json({success: false, message: error.message}); + } + + // check channel authorization + authenticateUser(channelName, channelId, channelPassword, user) + .then(({ channelName, channelClaimId }) => { + return db.Claim.findOne({ + where: { + name, + channelName, + }, + }); + }) + .then(claim => { + return resolveUri(`${claim.name}#${claim.claimId}`); + }) + .then(fullClaim => { + claimRecord = fullClaim; + logger.info('fullClaim', fullClaim); + metadata = Object.assign({}, { + title : claimRecord.title, + description: claimRecord.description, + nsfw : claimRecord.nsfw, + license : claimRecord.license, + language : 'en', + author : details.title, + }, updateMetadata({title, description, nsfw, license})); + const publishParams = { + name, + bid : 0.01, + claim_address: primaryClaimAddress, + channel_name : channelName, + channel_id : channelId, + metadata, + }; + if (files.file) { + publishParams['file_path'] = filePath; + } else { + fileName = fullClaim.file_name; + fileType = fullClaim.mime_type; + publishParams['sources'] = fullClaim.claim.value.stream.source; + } + // publish the thumbnail, if one exists + if (thumbnailFileName) { + const thumbnailPublishParams = createThumbnailPublishParams(thumbnailFilePath, name, license, nsfw); + publish(thumbnailPublishParams, thumbnailFileName, thumbnailFileType); + publishParams['thumbnail'] = `${details.host}/${channelName}:${channelId}/${name}-thumb.jpg`; + } + + return publish(publishParams, fileName, fileType); + }) + .then(claimData => { + const {claimId} = claimData; + res.status(200).json({ + success: true, + message: 'update successful', + data : { + name, + channelName, + channelId: claimData.certificateId, + claimId, + url : `${details.host}/${claimId}/${name}`, // for backwards compatability with app + showUrl : `${details.host}/${claimId}/${name}`, + claimData, + }, + }); + // record the publish end time and send to google analytics + sendGATimingEvent('end-to-end', 'update', fileType, gaStartTime, Date.now()); + }) + .catch(error => { + handleErrorResponse(originalUrl, ip, error, res); + }); +}; + +module.exports = claimUpdate; diff --git a/server/controllers/api/claim/update/updatePublishParams.js b/server/controllers/api/claim/update/updatePublishParams.js new file mode 100644 index 00000000..45280708 --- /dev/null +++ b/server/controllers/api/claim/update/updatePublishParams.js @@ -0,0 +1,56 @@ +const path = require('path'); +const validateFileTypeAndSize = require('../publish/validateFileTypeAndSize.js'); + +const parseUpdateFile = ({file}) => { + // make sure a file was provided + if (!file) { + return false; + } + if (!file.path) { + throw new Error('no file path found'); + } + if (!file.type) { + throw new Error('no file type found'); + } + if (!file.size) { + throw new Error('no file size found'); + } + // validate the file name + if (!file.name) { + throw new Error('no file name found'); + } + if (file.name.indexOf('.') < 0) { + throw new Error('no file extension found in file name'); + } + if (file.name.indexOf('.') === 0) { + throw new Error('file name cannot start with "."'); + } + if (/'/.test(file.name)) { + throw new Error('apostrophes are not allowed in the file name'); + } + // validate the file + validateFileTypeAndSize(file); + // return results + return { + fileName : file.name, + filePath : file.path, + fileExtension: path.extname(file.path), + fileType : file.type, + }; +}; + +const parseUpdateThumbnail = ({thumbnail}) => { + if (!thumbnail) { + return false; + } + return { + thumbnailFileName: thumbnail.name, + thumbnailFilePath: thumbnail.path, + thumbnailFileType: thumbnail.type, + }; +}; + +module.exports = [ + parseUpdateFile, + parseUpdateThumbnail, +]; diff --git a/server/lbrynet/index.js b/server/lbrynet/index.js index 1b914682..c77edc59 100644 --- a/server/lbrynet/index.js +++ b/server/lbrynet/index.js @@ -46,6 +46,21 @@ module.exports = { }); }); }, + async abandonClaim ({claimId}) { + logger.debug(`lbryApi >> Abandon claim "${claimId}"`); + const gaStartTime = Date.now(); + try { + const abandon = await axios.post(lbrynetUri, { + method: 'claim_abandon', + params: { claim_id: claimId }, + }); + sendGATimingEvent('lbrynet', 'abandonClaim', 'ABANDON_CLAIM', gaStartTime, Date.now()); + return abandon.data; + } catch (error) { + logger.error(error); + return error; + } + }, getClaimList (claimName) { logger.debug(`lbryApi >> Getting claim_list for "${claimName}"`); const gaStartTime = Date.now(); diff --git a/server/routes/api/index.js b/server/routes/api/index.js index 71b673eb..26706bd5 100644 --- a/server/routes/api/index.js +++ b/server/routes/api/index.js @@ -13,6 +13,8 @@ const claimGet = require('../../controllers/api/claim/get'); const claimList = require('../../controllers/api/claim/list'); const claimLongId = require('../../controllers/api/claim/longId'); const claimPublish = require('../../controllers/api/claim/publish'); +const claimAbandon = require('../../controllers/api/claim/abandon'); +const claimUpdate = require('../../controllers/api/claim/update'); const claimResolve = require('../../controllers/api/claim/resolve'); const claimShortId = require('../../controllers/api/claim/shortId'); const claimViews = require('../../controllers/api/claim/views'); @@ -29,12 +31,10 @@ const getOEmbedData = require('../../controllers/api/oEmbed'); module.exports = { // homepage routes '/api/homepage/data/channels': { controller: [ torCheckMiddleware, channelData ] }, - // channel routes '/api/channel/availability/:name': { controller: [ torCheckMiddleware, channelAvailability ] }, '/api/channel/short-id/:longId/:name': { controller: [ torCheckMiddleware, channelShortId ] }, '/api/channel/data/:channelName/:channelClaimId': { controller: [ torCheckMiddleware, channelData ] }, - '/api/channel/data/:channelName/:channelClaimId': { controller: [ torCheckMiddleware, channelData ] }, '/api/channel/claims/:channelName/:channelClaimId/:page': { controller: [ torCheckMiddleware, channelClaims ] }, // sepcial routes @@ -47,6 +47,8 @@ module.exports = { '/api/claim/list/:name': { controller: [ torCheckMiddleware, claimList ] }, '/api/claim/long-id': { method: 'post', controller: [ torCheckMiddleware, claimLongId ] }, // note: should be a 'get' '/api/claim/publish': { method: 'post', controller: [ torCheckMiddleware, autoblockPublishMiddleware, multipartMiddleware, autoblockPublishBodyMiddleware, claimPublish ] }, + '/api/claim/update': { method: 'post', controller: [ torCheckMiddleware, multipartMiddleware, claimUpdate ] }, + '/api/claim/abandon': { method: 'post', controller: [ torCheckMiddleware, multipartMiddleware, claimAbandon ] }, '/api/claim/resolve/:name/:claimId': { controller: [ torCheckMiddleware, claimResolve ] }, '/api/claim/short-id/:longId/:name': { controller: [ torCheckMiddleware, claimShortId ] }, '/api/claim/views/:claimId': { controller: [ torCheckMiddleware, claimViews ] }, diff --git a/server/routes/pages/index.js b/server/routes/pages/index.js index fc742b42..858a1898 100644 --- a/server/routes/pages/index.js +++ b/server/routes/pages/index.js @@ -15,6 +15,7 @@ module.exports = { '/trending': { controller: redirect('/popular') }, '/popular': { controller: handlePageRequest }, '/new': { controller: handlePageRequest }, + '/edit/:claimId': { controller: handlePageRequest }, '/multisite': { controller: handlePageRequest }, '/video-embed/:name/:claimId/:config?': { controller: handleVideoEmbedRequest }, // for twitter }; From 3935715e2955114307f0c33d9b5209a57a0d39ca Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 17 Sep 2018 16:19:38 -0400 Subject: [PATCH 02/46] get channel data from publish params --- server/controllers/api/claim/publish/publish.js | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/server/controllers/api/claim/publish/publish.js b/server/controllers/api/claim/publish/publish.js index 298f07f4..ee007506 100644 --- a/server/controllers/api/claim/publish/publish.js +++ b/server/controllers/api/claim/publish/publish.js @@ -7,7 +7,6 @@ const deleteFile = require('./deleteFile.js'); const publish = async (publishParams, fileName, fileType) => { let publishResults; - let channel; let fileRecord; let filePath = publishParams.file_path; let newFile = Boolean(filePath); @@ -16,19 +15,8 @@ const publish = async (publishParams, fileName, fileType) => { publishResults = await publishClaim(publishParams); logger.info(`Successfully published ${publishParams.name} ${fileName}`, publishResults); - // get the channel information - if (publishParams.channel_name) { - logger.debug(`this claim was published in channel: ${publishParams.channel_name}`); - channel = await db.Channel.findOne({ - where: { - channelName: publishParams.channel_name, - }, - }); - } else { - channel = null; - } - const certificateId = channel ? channel.channelClaimId : null; - const channelName = channel ? channel.channelName : null; + const certificateId = publishParams.channel_id || null; + const channelName = publishParams.channel_name || null; const claimRecord = await createClaimRecordDataAfterPublish(certificateId, channelName, fileName, fileType, publishParams, publishResults); const {claimId} = claimRecord; From e25715f2cc19233b3e6917672618081dfe8765ce Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 24 Sep 2018 14:15:33 -0400 Subject: [PATCH 03/46] use selectAsset in mapStateToProps --- client/src/containers/PublishDetails/index.js | 13 ++----------- client/src/pages/EditPage/index.js | 13 ++----------- client/src/pages/ShowAssetDetails/index.js | 17 +++-------------- 3 files changed, 7 insertions(+), 36 deletions(-) diff --git a/client/src/containers/PublishDetails/index.js b/client/src/containers/PublishDetails/index.js index a095e8e5..ec49ba03 100644 --- a/client/src/containers/PublishDetails/index.js +++ b/client/src/containers/PublishDetails/index.js @@ -1,22 +1,13 @@ import { connect } from 'react-redux'; import { clearFile, startPublish, abandonClaim } from '../../actions/publish'; +import { selectAsset } from '../../selectors/show'; import View from './view'; const mapStateToProps = ({ show, publish }) => { - // select request info - const requestId = show.request.id; - // select asset info - let asset; - const request = show.requestList[requestId] || null; - const assetList = show.assetList; - if (request && assetList) { - const assetKey = request.key; // note: just store this in the request - asset = assetList[assetKey] || null; - }; return { file : publish.file, isUpdate: publish.isUpdate, - asset, + asset : selectAsset(show), }; }; diff --git a/client/src/pages/EditPage/index.js b/client/src/pages/EditPage/index.js index 68e2cbef..52ccdc1f 100644 --- a/client/src/pages/EditPage/index.js +++ b/client/src/pages/EditPage/index.js @@ -1,22 +1,13 @@ import { connect } from 'react-redux'; import { setUpdateTrue, updateMetadata, clearFile } from '../../actions/publish'; import { onHandleShowPageUri } from '../../actions/show'; +import { selectAsset } from '../../selectors/show'; import View from './view'; const mapStateToProps = (props) => { const { show } = props; - // select request info - const requestId = show.request.id; - // select asset info - let asset; - const request = show.requestList[requestId] || null; - const assetList = show.assetList; - if (request && assetList) { - const assetKey = request.key; // note: just store this in the request - asset = assetList[assetKey] || null; - } return { - asset, + asset : selectAsset(show), myChannel: props.channel.loggedInChannel.name, }; }; diff --git a/client/src/pages/ShowAssetDetails/index.js b/client/src/pages/ShowAssetDetails/index.js index 64544b70..cabb5046 100644 --- a/client/src/pages/ShowAssetDetails/index.js +++ b/client/src/pages/ShowAssetDetails/index.js @@ -1,21 +1,10 @@ import { connect } from 'react-redux'; +import { selectAsset } from '../../selectors/show'; import View from './view'; -const mapStateToProps = (props) => { - const {show} = props; - // select request info - const requestId = show.request.id; - // select asset info - let asset; - const request = show.requestList[requestId] || null; - const assetList = show.assetList; - if (request && assetList) { - const assetKey = request.key; // note: just store this in the request - asset = assetList[assetKey] || null; - } - // return props +const mapStateToProps = ({ show }) => { return { - asset, + asset: selectAsset(show), }; }; From b937a44e91436bc767e6375c114682abb19e6768 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 24 Sep 2018 15:42:57 -0400 Subject: [PATCH 04/46] use rest spread operator in reducers --- client/src/reducers/show.js | 40 +++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/client/src/reducers/show.js b/client/src/reducers/show.js index ded05015..da80ed75 100644 --- a/client/src/reducers/show.js +++ b/client/src/reducers/show.js @@ -74,24 +74,34 @@ export default function (state = initialState, action) { const channelClaims = state.channelList[channelId].claimsData.claims; const newClaimsData = channelClaims.filter(c => c.claimId !== claim.claimId); - return Object.assign({}, state, { + return { + ...state, assetList : newAssetList, - channelList: Object.assign({}, state.channelList, { - [channelId]: Object.assign({}, state.channelList[channelId], { - claimsData: Object.assign({}, state.channelList[channelId].claimsData, { + channelList: { + ...state.channelList, + [channelId]: { + ...state.channelList[channelId], + claimsData: { + ...state.channelList[channelId].claimsData, claims: newClaimsData, - }), - }), - }), - }); + }, + }, + }, + }; case actions.ASSET_UPDATE_CLAIMDATA: - return Object.assign({}, state, { - assetList: Object.assign({}, state.assetList, { - [action.data.id]: Object.assign({}, state.assetList[action.data.id], { - claimData: Object.assign({}, state.assetList[action.data.id].claimData, action.data.claimData), - }), - }), - }); + return { + ...state, + assetList: { + ...state.assetList, + [action.data.id]: { + ...state.assetList[action.data.id], + claimData: { + ...state.assetList[action.data.id].claimData, + ...action.data.claimData, + }, + }, + }, + }; // channel data case actions.CHANNEL_ADD: return Object.assign({}, state, { From dadb00576ef523cedf6e993b9f6221c45943f247 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 24 Sep 2018 15:49:27 -0400 Subject: [PATCH 05/46] deconstructor formatting --- client/src/sagas/abandon.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/sagas/abandon.js b/client/src/sagas/abandon.js index ffdc34cc..db290274 100644 --- a/client/src/sagas/abandon.js +++ b/client/src/sagas/abandon.js @@ -7,7 +7,7 @@ import { doAbandonClaim } from '../api/assetApi'; function * abandonClaim (action) { const { claimData, history } = action.data; - const {claimId} = claimData; + const { claimId } = claimData; const confirm = window.confirm('Are you sure you want to abandon this claim? This action cannot be undone.'); if (!confirm) return; From 2b02f06152a7f5bb2a69f907e955b06731103c28 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 24 Sep 2018 15:50:15 -0400 Subject: [PATCH 06/46] refactor publish saga --- client/src/sagas/publish.js | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/client/src/sagas/publish.js b/client/src/sagas/publish.js index e82dd121..1a9bd46f 100644 --- a/client/src/sagas/publish.js +++ b/client/src/sagas/publish.js @@ -36,29 +36,25 @@ function * publishFile (action) { } let publishMetadata, publishFormData, publishChannel; + // create metadata + publishMetadata = createPublishMetadata( + isUpdate ? asset.name : claim, + isUpdate ? {type: asset.claimData.contentType} : file, + metadata, + publishInChannel, + selectedChannel + ); if (isUpdate) { - publishMetadata = createPublishMetadata(asset.name, {type: asset.claimData.contentType}, metadata, publishInChannel, selectedChannel); publishMetadata['channelName'] = asset.claimData.channelName; - if (thumbnail) { - // add thumbnail to publish metadata - publishMetadata['thumbnail'] = createThumbnailUrl(thumbnailChannel, thumbnailChannelId, claim, host); - } - // create form data for main publish - publishFormData = createPublishFormData(file, thumbnail, publishMetadata); - // make the publish request - publishChannel = yield call(makePublishRequestChannel, publishFormData, true); - } else { - // create metadata - publishMetadata = createPublishMetadata(claim, file, metadata, publishInChannel, selectedChannel); - if (thumbnail) { - // add thumbnail to publish metadata - publishMetadata['thumbnail'] = createThumbnailUrl(thumbnailChannel, thumbnailChannelId, claim, host); - } - // create form data for main publish - publishFormData = createPublishFormData(file, thumbnail, publishMetadata); - // make the publish request - publishChannel = yield call(makePublishRequestChannel, publishFormData); } + if (thumbnail) { + // add thumbnail to publish metadata + publishMetadata['thumbnail'] = createThumbnailUrl(thumbnailChannel, thumbnailChannelId, claim, host); + } + // create form data for main publish + publishFormData = createPublishFormData(file, thumbnail, publishMetadata); + // make the publish request + publishChannel = yield call(makePublishRequestChannel, publishFormData, isUpdate); while (true) { const {loadStart, progress, load, success, error: publishError} = yield take(publishChannel); From f575edc9f3aaa78d5b382c3248ae36a284319b9c Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 24 Sep 2018 16:13:18 -0400 Subject: [PATCH 07/46] consolidate validation --- .../publish/parsePublishApiRequestFiles.js | 53 ++---------------- server/controllers/api/claim/update/index.js | 4 +- .../api/claim/update/updatePublishParams.js | 56 ------------------- 3 files changed, 6 insertions(+), 107 deletions(-) delete mode 100644 server/controllers/api/claim/update/updatePublishParams.js diff --git a/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js b/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js index 4b18e5f7..885dc8d7 100644 --- a/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js +++ b/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js @@ -1,53 +1,9 @@ const path = require('path'); const validateFileTypeAndSize = require('./validateFileTypeAndSize.js'); -const parsePublishApiRequestFiles = ({file, thumbnail}) => { +const parsePublishApiRequestFiles = ({file}, isUpdate) => { // make sure a file was provided - if (!file) { - throw new Error('no file with key of [file] found in request'); - } - if (!file.path) { - throw new Error('no file path found'); - } - if (!file.type) { - throw new Error('no file type found'); - } - if (!file.size) { - throw new Error('no file size found'); - } - // validate the file name - if (!file.name) { - throw new Error('no file name found'); - } - if (file.name.indexOf('.') < 0) { - throw new Error('no file extension found in file name'); - } - if (file.name.indexOf('.') === 0) { - throw new Error('file name cannot start with "."'); - } - if (/'/.test(file.name)) { - throw new Error('apostrophes are not allowed in the file name'); - } - // validate the file - validateFileTypeAndSize(file); - // return results - return { - fileName : file.name, - filePath : file.path, - fileExtension : path.extname(file.path), - fileType : file.type, - thumbnailFileName: (thumbnail ? thumbnail.name : null), - thumbnailFilePath: (thumbnail ? thumbnail.path : null), - thumbnailFileType: (thumbnail ? thumbnail.type : null), - }; -}; - -const parsePublishApiRequestFile = ({file, thumbnail}, isUpdate) => { - // make sure a file was provided - if (!file) { - if (isUpdate) { - return; - } + if (!file && !isUpdate) { throw new Error('no file with key of [file] found in request'); } if (!file.path) { @@ -74,7 +30,7 @@ const parsePublishApiRequestFile = ({file, thumbnail}, isUpdate) => { } // validate the file - validateFileTypeAndSize(file); + if (file) validateFileTypeAndSize(file); // return results return { fileName : file.name, @@ -84,7 +40,7 @@ const parsePublishApiRequestFile = ({file, thumbnail}, isUpdate) => { }; }; -const parsePublishApiRequestThumbnail = ({file, thumbnail}) => { +const parsePublishApiRequestThumbnail = ({thumbnail}) => { if (!thumbnail) { return; } @@ -97,6 +53,5 @@ const parsePublishApiRequestThumbnail = ({file, thumbnail}) => { module.exports = { parsePublishApiRequestFiles, - parsePublishApiRequestFile, parsePublishApiRequestThumbnail, }; diff --git a/server/controllers/api/claim/update/index.js b/server/controllers/api/claim/update/index.js index e45d124b..9a4437e2 100644 --- a/server/controllers/api/claim/update/index.js +++ b/server/controllers/api/claim/update/index.js @@ -6,7 +6,7 @@ const { sendGATimingEvent } = require('../../../../utils/googleAnalytics.js'); const { handleErrorResponse } = require('../../../utils/errorHandlers.js'); const publish = require('../publish/publish.js'); const parsePublishApiRequestBody = require('../publish/parsePublishApiRequestBody'); -const {parsePublishApiRequestFile, parsePublishApiRequestThumbnail} = require('../publish/parsePublishApiRequestFiles.js'); +const {parsePublishApiRequestFiles, parsePublishApiRequestThumbnail} = require('../publish/parsePublishApiRequestFiles.js'); const authenticateUser = require('../publish/authentication.js'); const createThumbnailPublishParams = require('../publish/createThumbnailPublishParams.js'); @@ -67,7 +67,7 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res) try { ({name, nsfw, license, title, description, thumbnail} = parsePublishApiRequestBody(body)); if (files.file) { - ({fileName, filePath, fileExtension, fileType} = parsePublishApiRequestFile(files)); + ({fileName, filePath, fileExtension, fileType} = parsePublishApiRequestFiles(files)); if (files.thumbnail) { ({thumbnailFileName, thumbnailFilePath, thumbnailFileType} = parsePublishApiRequestThumbnail(files)); } diff --git a/server/controllers/api/claim/update/updatePublishParams.js b/server/controllers/api/claim/update/updatePublishParams.js deleted file mode 100644 index 45280708..00000000 --- a/server/controllers/api/claim/update/updatePublishParams.js +++ /dev/null @@ -1,56 +0,0 @@ -const path = require('path'); -const validateFileTypeAndSize = require('../publish/validateFileTypeAndSize.js'); - -const parseUpdateFile = ({file}) => { - // make sure a file was provided - if (!file) { - return false; - } - if (!file.path) { - throw new Error('no file path found'); - } - if (!file.type) { - throw new Error('no file type found'); - } - if (!file.size) { - throw new Error('no file size found'); - } - // validate the file name - if (!file.name) { - throw new Error('no file name found'); - } - if (file.name.indexOf('.') < 0) { - throw new Error('no file extension found in file name'); - } - if (file.name.indexOf('.') === 0) { - throw new Error('file name cannot start with "."'); - } - if (/'/.test(file.name)) { - throw new Error('apostrophes are not allowed in the file name'); - } - // validate the file - validateFileTypeAndSize(file); - // return results - return { - fileName : file.name, - filePath : file.path, - fileExtension: path.extname(file.path), - fileType : file.type, - }; -}; - -const parseUpdateThumbnail = ({thumbnail}) => { - if (!thumbnail) { - return false; - } - return { - thumbnailFileName: thumbnail.name, - thumbnailFilePath: thumbnail.path, - thumbnailFileType: thumbnail.type, - }; -}; - -module.exports = [ - parseUpdateFile, - parseUpdateThumbnail, -]; From bd1424acffe94880db5577836348d8fc77275638 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Tue, 25 Sep 2018 09:53:45 -0400 Subject: [PATCH 08/46] fix selectAsset --- client/src/selectors/show.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/client/src/selectors/show.js b/client/src/selectors/show.js index b3b5ba92..d7358e8e 100644 --- a/client/src/selectors/show.js +++ b/client/src/selectors/show.js @@ -1,7 +1,13 @@ -export const selectAsset = (show) => { - const request = show.requestList[show.request.id]; - const assetKey = request.key; - return show.assetList[assetKey]; +export const selectAsset = show => { + const requestId = show.request.id; + let asset; + const request = show.requestList[requestId] || null; + const assetList = show.assetList; + if (request && assetList) { + const assetKey = request.key; // note: just store this in the request + asset = assetList[assetKey] || null; + } + return asset; }; export const selectShowState = (state) => { From 53676e6bc02a4d926e20603cc491fe48f0d60fe5 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Tue, 25 Sep 2018 09:57:42 -0400 Subject: [PATCH 09/46] undo assignment of certId and channelName --- server/controllers/api/claim/publish/publish.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/server/controllers/api/claim/publish/publish.js b/server/controllers/api/claim/publish/publish.js index ee007506..298f07f4 100644 --- a/server/controllers/api/claim/publish/publish.js +++ b/server/controllers/api/claim/publish/publish.js @@ -7,6 +7,7 @@ const deleteFile = require('./deleteFile.js'); const publish = async (publishParams, fileName, fileType) => { let publishResults; + let channel; let fileRecord; let filePath = publishParams.file_path; let newFile = Boolean(filePath); @@ -15,8 +16,19 @@ const publish = async (publishParams, fileName, fileType) => { publishResults = await publishClaim(publishParams); logger.info(`Successfully published ${publishParams.name} ${fileName}`, publishResults); - const certificateId = publishParams.channel_id || null; - const channelName = publishParams.channel_name || null; + // get the channel information + if (publishParams.channel_name) { + logger.debug(`this claim was published in channel: ${publishParams.channel_name}`); + channel = await db.Channel.findOne({ + where: { + channelName: publishParams.channel_name, + }, + }); + } else { + channel = null; + } + const certificateId = channel ? channel.channelClaimId : null; + const channelName = channel ? channel.channelName : null; const claimRecord = await createClaimRecordDataAfterPublish(certificateId, channelName, fileName, fileType, publishParams, publishResults); const {claimId} = claimRecord; From f710dffd5715487ffa8326500b53ab539b7a0adf Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Tue, 25 Sep 2018 10:26:25 -0400 Subject: [PATCH 10/46] SUCCESS -> SUCCEEDED --- client/src/actions/show.js | 2 +- client/src/constants/publish_claim_states.js | 2 +- client/src/constants/show_action_types.js | 2 +- client/src/containers/PublishStatus/view.jsx | 2 +- client/src/reducers/show.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/src/actions/show.js b/client/src/actions/show.js index 32c4d47c..807d4f02 100644 --- a/client/src/actions/show.js +++ b/client/src/actions/show.js @@ -136,7 +136,7 @@ export function onUpdateChannelClaims (channelKey, name, longId, page) { export function updateChannelClaims (channelListId, claimsData) { return { - type: actions.CHANNEL_CLAIMS_UPDATE_SUCCESS, + type: actions.CHANNEL_CLAIMS_UPDATE_SUCCEEDED, data: {channelListId, claimsData}, }; } diff --git a/client/src/constants/publish_claim_states.js b/client/src/constants/publish_claim_states.js index 8dff837b..048077a5 100644 --- a/client/src/constants/publish_claim_states.js +++ b/client/src/constants/publish_claim_states.js @@ -1,6 +1,6 @@ export const LOAD_START = 'LOAD_START'; export const LOADING = 'LOADING'; export const PUBLISHING = 'PUBLISHING'; -export const SUCCESS = 'SUCCESS'; +export const SUCCEEDED = 'SUCCEEDED'; export const FAILED = 'FAILED'; export const ABANDONING = 'ABANDONING'; diff --git a/client/src/constants/show_action_types.js b/client/src/constants/show_action_types.js index a2cc511b..41c2db23 100644 --- a/client/src/constants/show_action_types.js +++ b/client/src/constants/show_action_types.js @@ -18,7 +18,7 @@ export const ASSET_REMOVE = 'ASSET_REMOVE'; export const CHANNEL_ADD = 'CHANNEL_ADD'; export const CHANNEL_CLAIMS_UPDATE_ASYNC = 'CHANNEL_CLAIMS_UPDATE_ASYNC'; -export const CHANNEL_CLAIMS_UPDATE_SUCCESS = 'CHANNEL_CLAIMS_UPDATE_SUCCESS'; +export const CHANNEL_CLAIMS_UPDATE_SUCCEEDED = 'CHANNEL_CLAIMS_UPDATE_SUCCEEDED'; // asset/file display actions export const FILE_REQUESTED = 'FILE_REQUESTED'; diff --git a/client/src/containers/PublishStatus/view.jsx b/client/src/containers/PublishStatus/view.jsx index b3f27c0e..b7b82052 100644 --- a/client/src/containers/PublishStatus/view.jsx +++ b/client/src/containers/PublishStatus/view.jsx @@ -42,7 +42,7 @@ class PublishStatus extends React.Component { } - {status === publishStates.SUCCESS && + {status === publishStates.SUCCEEDED &&

Your publish is complete! You are being redirected to it now.

diff --git a/client/src/reducers/show.js b/client/src/reducers/show.js index da80ed75..a93da58b 100644 --- a/client/src/reducers/show.js +++ b/client/src/reducers/show.js @@ -114,7 +114,7 @@ export default function (state = initialState, action) { }, }), }); - case actions.CHANNEL_CLAIMS_UPDATE_SUCCESS: + case actions.CHANNEL_CLAIMS_UPDATE_SUCCEEDED: return Object.assign({}, state, { channelList: Object.assign({}, state.channelList, { [action.data.channelListId]: Object.assign({}, state.channelList[action.data.channelListId], { From 4c9608a964241ceed9dc0e53f3b5ad4cf9865e90 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Tue, 25 Sep 2018 10:42:52 -0400 Subject: [PATCH 11/46] add comment relating to issue #607 --- client/src/containers/AssetDisplay/view.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/src/containers/AssetDisplay/view.jsx b/client/src/containers/AssetDisplay/view.jsx index 258701a0..98901ee9 100644 --- a/client/src/containers/AssetDisplay/view.jsx +++ b/client/src/containers/AssetDisplay/view.jsx @@ -45,6 +45,8 @@ class AssetDisplay extends React.Component { } render () { const { status, error, asset: { name, claimData: { claimId, contentType, fileExt, thumbnail, outpoint } } } = this.props; + // the outpoint is added to force the browser to re-download the asset after an update + // issue: https://github.com/lbryio/spee.ch/issues/607 const sourceUrl = `/${claimId}/${name}.${fileExt}?${outpoint}`; return (
From 5d3f66d201fe54e3ce3345a94eae8fc781c1915d Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Tue, 25 Sep 2018 10:58:19 -0400 Subject: [PATCH 12/46] move edit link from AssetTitle to AssetInfo --- client/src/containers/AssetInfo/index.js | 2 ++ client/src/containers/AssetInfo/view.jsx | 16 +++++++++++++--- client/src/containers/AssetTitle/index.js | 6 +----- client/src/containers/AssetTitle/view.jsx | 7 ++----- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/client/src/containers/AssetInfo/index.js b/client/src/containers/AssetInfo/index.js index aa42d3f5..bc7a9229 100644 --- a/client/src/containers/AssetInfo/index.js +++ b/client/src/containers/AssetInfo/index.js @@ -6,9 +6,11 @@ const mapStateToProps = (props) => { const {show} = props; // select asset const asset = selectAsset(show); + const editable = Boolean(props.channel.loggedInChannel.name === asset.claimData.channelName); // return props return { asset, + editable, }; }; diff --git a/client/src/containers/AssetInfo/view.jsx b/client/src/containers/AssetInfo/view.jsx index af742617..be40f4b7 100644 --- a/client/src/containers/AssetInfo/view.jsx +++ b/client/src/containers/AssetInfo/view.jsx @@ -13,10 +13,11 @@ import createCanonicalLink from '../../../../utils/createCanonicalLink'; class AssetInfo extends React.Component { render () { - const { asset } = this.props; - const { claimViews, claimData: { channelName, channelShortId, description, name, fileExt, contentType, thumbnail, host } } = asset; + const { editable, asset } = this.props; + const { claimViews, claimData } = asset; + const { channelName, claimId, channelShortId, description, name, fileExt, contentType, host } = claimData; - const canonicalUrl = createCanonicalLink({ asset: { ...asset.claimData, shortId: asset.shortId }}); + const canonicalUrl = createCanonicalLink({ asset: { ...claimData, shortId: asset.shortId }}); const assetCanonicalUrl = `${host}${canonicalUrl}`; let channelCanonicalUrl; @@ -29,6 +30,15 @@ class AssetInfo extends React.Component { } return (
+ {editable && ( + + } + content={{name}} + /> + + )} + {channelName && ( { - const { claimData: { title, claimId, name, channelName } } = selectAsset(props.show); - const editable = Boolean(props.channel.loggedInChannel.name === channelName); + const { claimData: { title } } = selectAsset(props.show); return { title, - claimId, - name, - editable, }; }; diff --git a/client/src/containers/AssetTitle/view.jsx b/client/src/containers/AssetTitle/view.jsx index 034b7c06..87e1e661 100644 --- a/client/src/containers/AssetTitle/view.jsx +++ b/client/src/containers/AssetTitle/view.jsx @@ -2,13 +2,10 @@ import React from 'react'; import { Link } from 'react-router-dom'; import Row from '@components/Row'; -const AssetTitle = ({ title, editable, claimId, name }) => { +const AssetTitle = ({ title }) => { return ( -

- {title} - {editable && ( (edit))} -

+

{title}

); }; From 2778c427735be8e5f18a3de24c02619d940883cc Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Wed, 26 Sep 2018 12:00:42 -0400 Subject: [PATCH 13/46] clear publish and edit pages based on publish.isUpdate --- client/src/pages/EditPage/index.js | 1 + client/src/pages/EditPage/view.jsx | 16 ++++++++-------- client/src/pages/HomePage/index.js | 4 ++++ client/src/pages/HomePage/view.jsx | 16 ++++------------ 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/client/src/pages/EditPage/index.js b/client/src/pages/EditPage/index.js index 52ccdc1f..63d11541 100644 --- a/client/src/pages/EditPage/index.js +++ b/client/src/pages/EditPage/index.js @@ -9,6 +9,7 @@ const mapStateToProps = (props) => { return { asset : selectAsset(show), myChannel: props.channel.loggedInChannel.name, + isUpdate : props.publish.isUpdate, }; }; diff --git a/client/src/pages/EditPage/view.jsx b/client/src/pages/EditPage/view.jsx index c725d527..7a605e69 100644 --- a/client/src/pages/EditPage/view.jsx +++ b/client/src/pages/EditPage/view.jsx @@ -5,16 +5,16 @@ import PublishTool from '@containers/PublishTool'; class EditPage extends React.Component { componentDidMount () { - const {asset, match, onHandleShowPageUri, setUpdateTrue, updateMetadata} = this.props; - onHandleShowPageUri(match.params); - setUpdateTrue(); - if (asset) { - ['title', 'description', 'license', 'nsfw'].forEach(meta => updateMetadata(meta, asset.claimData[meta])); + const {asset, isUpdate, match, onHandleShowPageUri, clearFile, setUpdateTrue, updateMetadata} = this.props; + if (!isUpdate) { + clearFile(); + onHandleShowPageUri(match.params); + setUpdateTrue(); + if (asset) { + ['title', 'description', 'license', 'nsfw'].forEach(meta => updateMetadata(meta, asset.claimData[meta])); + } } } - componentWillUnmount () { - this.props.clearFile(); - } render () { const { myChannel, asset } = this.props; // redirect if user does not own this claim diff --git a/client/src/pages/HomePage/index.js b/client/src/pages/HomePage/index.js index bdff3cab..5a00f4f1 100644 --- a/client/src/pages/HomePage/index.js +++ b/client/src/pages/HomePage/index.js @@ -2,6 +2,7 @@ import { connect } from 'react-redux'; import { onHandleShowHomepage } from '../../actions/show'; import View from './view'; +<<<<<<< HEAD const mapStateToProps = ({ show, site, channel }) => { return { error : show.request.error, @@ -9,6 +10,9 @@ const mapStateToProps = ({ show, site, channel }) => { homeChannel: site.publishOnlyApproved && !channel.loggedInChannel.name ? `${site.approvedChannels[0].name}:${site.approvedChannels[0].longId}` : null, }; }; +======= +const mapStateToProps = props => ({ isUpdate: props.publish.isUpdate }); +>>>>>>> clear publish and edit pages based on publish.isUpdate const mapDispatchToProps = { onHandleShowHomepage, diff --git a/client/src/pages/HomePage/view.jsx b/client/src/pages/HomePage/view.jsx index 9096e734..4b163ec5 100644 --- a/client/src/pages/HomePage/view.jsx +++ b/client/src/pages/HomePage/view.jsx @@ -4,18 +4,10 @@ import PublishTool from '@containers/PublishTool'; import ContentPageWrapper from '@pages/ContentPageWrapper'; class HomePage extends React.Component { - // componentDidMount () { - // this.props.onHandleShowHomepage(this.props.match.params); - // } - // - // componentWillReceiveProps (nextProps) { - // if (nextProps.match.params !== this.props.match.params) { - // this.props.onHandleShowHomepage(nextProps.match.params); - // } - // } - - componentWillUnmount () { - this.props.clearFile(); + componentDidMount () { + if (this.props.isUpdate) { + this.props.clearFile(); + } } render () { const { homeChannel } = this.props; From 4deeaed4e69f28514c92d26e4cf020cc20143158 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 1 Oct 2018 08:57:53 -0400 Subject: [PATCH 14/46] change amount value to string --- server/controllers/api/claim/update/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/controllers/api/claim/update/index.js b/server/controllers/api/claim/update/index.js index 9a4437e2..2c1c5231 100644 --- a/server/controllers/api/claim/update/index.js +++ b/server/controllers/api/claim/update/index.js @@ -103,7 +103,7 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res) }, updateMetadata({title, description, nsfw, license})); const publishParams = { name, - bid : 0.01, + bid : '0.01', claim_address: primaryClaimAddress, channel_name : channelName, channel_id : channelId, From 7f3e97d33996143276489d469baed36b51732a0a Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Wed, 3 Oct 2018 09:53:29 -0400 Subject: [PATCH 15/46] show dimmed image preview for updates -- not working for videos yet --- .../components/DropzonePreviewImage/index.jsx | 11 ++++-- client/src/containers/Dropzone/index.js | 8 ++++- client/src/containers/Dropzone/view.jsx | 34 ++++++++++++------- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/client/src/components/DropzonePreviewImage/index.jsx b/client/src/components/DropzonePreviewImage/index.jsx index d94d976a..d62abaeb 100644 --- a/client/src/components/DropzonePreviewImage/index.jsx +++ b/client/src/components/DropzonePreviewImage/index.jsx @@ -10,7 +10,12 @@ class PublishPreview extends React.Component { }; } componentDidMount () { - this.setPreviewImageSource(this.props.file); + const { isUpdate, sourceUrl, file } = this.props; + if (isUpdate && sourceUrl) { + this.setState({ imgSource: sourceUrl }); + } else { + this.setPreviewImageSource(file); + } } componentWillReceiveProps (newProps) { if (newProps.file !== this.props.file) { @@ -54,8 +59,10 @@ class PublishPreview extends React.Component { PublishPreview.propTypes = { dimPreview: PropTypes.bool.isRequired, - file : PropTypes.object.isRequired, + file : PropTypes.object, thumbnail : PropTypes.object, + isUpdate : PropTypes.bool, + sourceUrl : PropTypes.string, }; export default PublishPreview; diff --git a/client/src/containers/Dropzone/index.js b/client/src/containers/Dropzone/index.js index 13b2e5e0..8ee8079f 100644 --- a/client/src/containers/Dropzone/index.js +++ b/client/src/containers/Dropzone/index.js @@ -1,12 +1,18 @@ import { connect } from 'react-redux'; import { selectFile, updateError, clearFile } from '../../actions/publish'; +import { selectAsset } from '../../selectors/show'; import View from './view'; -const mapStateToProps = ({ publish }) => { +const mapStateToProps = ({ publish, show }) => { + const asset = selectAsset(show); + const { name, claimData: { claimId, fileExt, outpoint } } = asset; + const sourceUrl = `/${claimId}/${name}.${fileExt}?${outpoint}`; return { file : publish.file, thumbnail: publish.thumbnail, fileError: publish.error.file, + isUpdate : publish.isUpdate, + sourceUrl, }; }; diff --git a/client/src/containers/Dropzone/view.jsx b/client/src/containers/Dropzone/view.jsx index 517e9d15..6de31276 100644 --- a/client/src/containers/Dropzone/view.jsx +++ b/client/src/containers/Dropzone/view.jsx @@ -81,6 +81,8 @@ class Dropzone extends React.Component { } } render () { + const { dragOver, mouseOver, dimPreview } = this.state; + const { file, thumbnail, fileError, isUpdate, sourceUrl } = this.props; return (
@@ -95,7 +97,7 @@ class Dropzone extends React.Component { />
- {this.props.file ? ( + {file || isUpdate ? (
- + {file ? ( + + ) : ( + + )}
- { this.state.dragOver ? : null } - { this.state.mouseOver ? ( + { dragOver ? : null } + { mouseOver ? ( ) : null }
) : ( - this.state.dragOver ? : ( + dragOver ? : ( ) )} From 48408a872d1773349660eff07adf1994d293f464 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Wed, 3 Oct 2018 10:28:08 -0400 Subject: [PATCH 16/46] refactor DropZone mapStateToProps --- client/src/containers/Dropzone/index.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/client/src/containers/Dropzone/index.js b/client/src/containers/Dropzone/index.js index 8ee8079f..96d30ca4 100644 --- a/client/src/containers/Dropzone/index.js +++ b/client/src/containers/Dropzone/index.js @@ -3,17 +3,20 @@ import { selectFile, updateError, clearFile } from '../../actions/publish'; import { selectAsset } from '../../selectors/show'; import View from './view'; -const mapStateToProps = ({ publish, show }) => { - const asset = selectAsset(show); - const { name, claimData: { claimId, fileExt, outpoint } } = asset; - const sourceUrl = `/${claimId}/${name}.${fileExt}?${outpoint}`; - return { - file : publish.file, - thumbnail: publish.thumbnail, - fileError: publish.error.file, - isUpdate : publish.isUpdate, - sourceUrl, - }; +const mapStateToProps = ({ show, publish: { file, thumbnail, fileError, isUpdate } }) => { + const obj = { file, thumbnail, fileError, isUpdate }; + let asset, name, claimId, fileExt, outpoint, sourceUrl; + if (isUpdate) { + asset = selectAsset(show); + if (asset) { + ({name, claimData: {claimId, fileExt, outpoint}} = asset); + sourceUrl = `/${claimId}/${name}.${fileExt}?${outpoint}`; + } + if (sourceUrl) { + obj.sourceUrl = sourceUrl; + } + } + return obj; }; const mapDispatchToProps = dispatch => { From 6aeee63d85bac92723a4b5fc80eed026c3494aca Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Thu, 4 Oct 2018 09:31:37 -0400 Subject: [PATCH 17/46] convert space strings (' ') to empty strings ('') --- client/src/components/PublishLicenseInput/index.jsx | 2 +- server/controllers/api/claim/publish/createPublishParams.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/components/PublishLicenseInput/index.jsx b/client/src/components/PublishLicenseInput/index.jsx index c4ee43f5..6ef59418 100644 --- a/client/src/components/PublishLicenseInput/index.jsx +++ b/client/src/components/PublishLicenseInput/index.jsx @@ -16,7 +16,7 @@ const PublishLicenseInput = ({ handleSelect }) => { className='select select--primary' onChange={handleSelect} > - + diff --git a/server/controllers/api/claim/publish/createPublishParams.js b/server/controllers/api/claim/publish/createPublishParams.js index 242f4312..364e831a 100644 --- a/server/controllers/api/claim/publish/createPublishParams.js +++ b/server/controllers/api/claim/publish/createPublishParams.js @@ -11,7 +11,7 @@ const createPublishParams = (filePath, name, title, description, license, nsfw, } // provide default for license if (license === null || license.trim() === '') { - license = ' '; // default to empty string + license = ''; // default to empty string } // create the basic publish params const publishParams = { From 593d748bc0b39c836b8f8f16fd2eff76938ac99f Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Thu, 4 Oct 2018 10:26:42 -0400 Subject: [PATCH 18/46] improve error handling; pass filePath to publish.js explicitly --- client/src/pages/HomePage/index.js | 7 ++----- client/src/sagas/publish.js | 7 ++++++- server/controllers/api/claim/publish/index.js | 2 +- .../controllers/api/claim/publish/publish.js | 18 +++++++++++++----- server/controllers/api/claim/update/index.js | 10 +++++++++- server/lbrynet/index.js | 9 ++++++++- server/models/utils/createFileRecordData.js | 2 +- 7 files changed, 40 insertions(+), 15 deletions(-) diff --git a/client/src/pages/HomePage/index.js b/client/src/pages/HomePage/index.js index 5a00f4f1..cd32268b 100644 --- a/client/src/pages/HomePage/index.js +++ b/client/src/pages/HomePage/index.js @@ -2,17 +2,14 @@ import { connect } from 'react-redux'; import { onHandleShowHomepage } from '../../actions/show'; import View from './view'; -<<<<<<< HEAD -const mapStateToProps = ({ show, site, channel }) => { +const mapStateToProps = ({ show, site, channel, publish }) => { return { error : show.request.error, requestType: show.request.type, homeChannel: site.publishOnlyApproved && !channel.loggedInChannel.name ? `${site.approvedChannels[0].name}:${site.approvedChannels[0].longId}` : null, + isUpdate : publish.isUpdate, }; }; -======= -const mapStateToProps = props => ({ isUpdate: props.publish.isUpdate }); ->>>>>>> clear publish and edit pages based on publish.isUpdate const mapDispatchToProps = { onHandleShowHomepage, diff --git a/client/src/sagas/publish.js b/client/src/sagas/publish.js index 1a9bd46f..fbc45338 100644 --- a/client/src/sagas/publish.js +++ b/client/src/sagas/publish.js @@ -72,7 +72,12 @@ function * publishFile (action) { }, }); } - return history.push(`/${success.data.claimId}/${success.data.name}`); + if (success.data.claimId) { + return history.push(`/${success.data.claimId}/${success.data.name}`); + } else { + // this returns to the homepage, needs work + return yield put(updatePublishStatus(publishStates.FAILED, 'ERROR')); + } } if (loadStart) { yield put(updatePublishStatus(publishStates.LOAD_START, null)); diff --git a/server/controllers/api/claim/publish/index.js b/server/controllers/api/claim/publish/index.js index 530d03c8..a849cfa7 100644 --- a/server/controllers/api/claim/publish/index.js +++ b/server/controllers/api/claim/publish/index.js @@ -83,7 +83,7 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res) publish(thumbnailPublishParams, thumbnailFileName, thumbnailFileType); } // publish the asset - return publish(publishParams, fileName, fileType); + return publish(publishParams, fileName, fileType, filePath); }) .then(claimData => { logger.debug('Publish success >', claimData); diff --git a/server/controllers/api/claim/publish/publish.js b/server/controllers/api/claim/publish/publish.js index 298f07f4..7af9beae 100644 --- a/server/controllers/api/claim/publish/publish.js +++ b/server/controllers/api/claim/publish/publish.js @@ -5,11 +5,10 @@ const { createFileRecordDataAfterPublish } = require('../../../../models/utils/c const { createClaimRecordDataAfterPublish } = require('../../../../models/utils/createClaimRecordData.js'); const deleteFile = require('./deleteFile.js'); -const publish = async (publishParams, fileName, fileType) => { +const publish = async (publishParams, fileName, fileType, filePath) => { let publishResults; let channel; let fileRecord; - let filePath = publishParams.file_path; let newFile = Boolean(filePath); try { @@ -53,9 +52,18 @@ const publish = async (publishParams, fileName, fileType) => { return claimRecord; } catch (err) { - logger.error('PUBLISH ERROR', err); - await deleteFile(filePath); - return err; + // parse daemon response when err is a string + // this needs work + logger.info('publish/publish err:', err); + const error = typeof err === 'string' ? JSON.parse(err) : err; + if (filePath) { + await deleteFile(filePath); + } + const message = error.error && error.error.message ? error.error.message : 'Unknown publish error'; + return { + error: true, + message, + }; } }; diff --git a/server/controllers/api/claim/update/index.js b/server/controllers/api/claim/update/index.js index 2c1c5231..803a5611 100644 --- a/server/controllers/api/claim/update/index.js +++ b/server/controllers/api/claim/update/index.js @@ -123,9 +123,17 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res) publishParams['thumbnail'] = `${details.host}/${channelName}:${channelId}/${name}-thumb.jpg`; } - return publish(publishParams, fileName, fileType); + const fp = files && files.file && files.file.path ? files.file.path : undefined; + return publish(publishParams, fileName, fileType, fp); }) .then(claimData => { + // this may need to happen in ../publish/index.js as well + if (claimData.error) { + res.status(400).json({ + success: false, + message: claimData.message, + }); + } const {claimId} = claimData; res.status(200).json({ success: true, diff --git a/server/lbrynet/index.js b/server/lbrynet/index.js index c77edc59..3b4eafcd 100644 --- a/server/lbrynet/index.js +++ b/server/lbrynet/index.js @@ -2,6 +2,7 @@ const axios = require('axios'); const logger = require('winston'); const { apiHost, apiPort, getTimeout } = require('@config/lbryConfig'); const lbrynetUri = 'http://' + apiHost + ':' + apiPort; +const db = require('../models'); const { chooseGaLbrynetPublishLabel, sendGATimingEvent } = require('../utils/googleAnalytics.js'); const handleLbrynetResponse = require('./utils/handleLbrynetResponse.js'); const { publishing } = require('@config/siteConfig'); @@ -90,7 +91,13 @@ module.exports = { }) .then(({ data }) => { sendGATimingEvent('lbrynet', 'resolveUri', 'RESOLVE', gaStartTime, Date.now()); - if (data.result[uri].error) { // check for errors + if (Object.keys(data.result).length === 0 && data.result.constructor === Object) { + // workaround for daemon returning empty result object + // https://github.com/lbryio/lbry/issues/1485 + db.Claim.findOne({ where: { claimId: uri.split('#')[1] } }) + .then(() => reject('This claim has not yet been confirmed on the LBRY blockchain')) + .catch(() => reject(`Claim ${uri} does not exist`)); + } else if (data.result[uri].error) { // check for errors reject(data.result[uri].error); } else { // if no errors, resolve resolve(data.result[uri]); diff --git a/server/models/utils/createFileRecordData.js b/server/models/utils/createFileRecordData.js index aa0802cf..fdcb9ce0 100644 --- a/server/models/utils/createFileRecordData.js +++ b/server/models/utils/createFileRecordData.js @@ -28,7 +28,7 @@ async function createFileRecordDataAfterGet (resolveResult, getResult) { filePath, fileType, }; -}; +} async function createFileRecordDataAfterPublish (fileName, fileType, publishParams, publishResults) { const { From 24626bcb02edef24df15696c20d405c185aa86b5 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 15 Oct 2018 17:11:59 -0400 Subject: [PATCH 19/46] notify user when abandoning publish; clear form --- client/src/constants/confirmation_messages.js | 1 + client/src/containers/PublishDetails/view.jsx | 14 +++++++++++++- client/src/containers/PublishTool/view.jsx | 13 +++++++++++-- client/src/pages/AboutPage/index.jsx | 3 ++- client/src/pages/EditPage/view.jsx | 16 ++++++++-------- client/src/pages/HomePage/index.js | 2 ++ client/src/pages/HomePage/view.jsx | 6 ++---- 7 files changed, 39 insertions(+), 16 deletions(-) create mode 100644 client/src/constants/confirmation_messages.js diff --git a/client/src/constants/confirmation_messages.js b/client/src/constants/confirmation_messages.js new file mode 100644 index 00000000..3ec79818 --- /dev/null +++ b/client/src/constants/confirmation_messages.js @@ -0,0 +1 @@ +export const SAVE = 'Everything not saved will be lost. Are you sure you want to leave this page?'; diff --git a/client/src/containers/PublishDetails/view.jsx b/client/src/containers/PublishDetails/view.jsx index 42d37c77..eb56b3b9 100644 --- a/client/src/containers/PublishDetails/view.jsx +++ b/client/src/containers/PublishDetails/view.jsx @@ -12,12 +12,14 @@ import ButtonTertiary from '@components/ButtonTertiary'; import ButtonSecondary from '@components/ButtonSecondary'; import SpaceAround from '@components/SpaceAround'; import PublishFinePrint from '@components/PublishFinePrint'; +import { SAVE } from '../../constants/confirmation_messages'; class PublishDetails extends React.Component { constructor (props) { super(props); this.onPublishSubmit = this.onPublishSubmit.bind(this); this.abandonClaim = this.abandonClaim.bind(this); + this.onCancel = this.onCancel.bind(this); } onPublishSubmit () { this.props.startPublish(this.props.history); @@ -29,6 +31,16 @@ class PublishDetails extends React.Component { this.props.abandonClaim({claimData, history}); } } + onCancel () { + const { isUpdate, clearFile, history } = this.props; + if (isUpdate) { + history.push('/'); + } else { + if (confirm(SAVE)) { + clearFile(); + } + } + } render () { const {file, isUpdate, asset} = this.props; return ( @@ -104,7 +116,7 @@ class PublishDetails extends React.Component { diff --git a/client/src/containers/PublishTool/view.jsx b/client/src/containers/PublishTool/view.jsx index e9869a22..6ba957fe 100644 --- a/client/src/containers/PublishTool/view.jsx +++ b/client/src/containers/PublishTool/view.jsx @@ -1,8 +1,10 @@ import React from 'react'; +import { withRouter, Prompt } from 'react-router'; import Dropzone from '@containers/Dropzone'; import PublishPreview from '@components/PublishPreview'; import PublishStatus from '@containers/PublishStatus'; import PublishDisabledMessage from '@containers/PublishDisabledMessage'; +import { SAVE } from '../../constants/confirmation_messages'; class PublishTool extends React.Component { render () { @@ -18,7 +20,14 @@ class PublishTool extends React.Component { ); } else { - return ; + return ( + + + + + ); } } return ; @@ -26,4 +35,4 @@ class PublishTool extends React.Component { } }; -export default PublishTool; +export default withRouter(PublishTool); diff --git a/client/src/pages/AboutPage/index.jsx b/client/src/pages/AboutPage/index.jsx index 887e17f9..621f27b7 100644 --- a/client/src/pages/AboutPage/index.jsx +++ b/client/src/pages/AboutPage/index.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import { withRouter } from 'react-router'; import PageLayout from '@components/PageLayout'; import HorizontalSplit from '@components/HorizontalSplit'; import AboutSpeechOverview from '@components/AboutSpeechOverview'; @@ -20,4 +21,4 @@ class AboutPage extends React.Component { } } -export default AboutPage; +export default withRouter(AboutPage); diff --git a/client/src/pages/EditPage/view.jsx b/client/src/pages/EditPage/view.jsx index 7a605e69..d60ffbbc 100644 --- a/client/src/pages/EditPage/view.jsx +++ b/client/src/pages/EditPage/view.jsx @@ -5,16 +5,16 @@ import PublishTool from '@containers/PublishTool'; class EditPage extends React.Component { componentDidMount () { - const {asset, isUpdate, match, onHandleShowPageUri, clearFile, setUpdateTrue, updateMetadata} = this.props; - if (!isUpdate) { - clearFile(); - onHandleShowPageUri(match.params); - setUpdateTrue(); - if (asset) { - ['title', 'description', 'license', 'nsfw'].forEach(meta => updateMetadata(meta, asset.claimData[meta])); - } + const {asset, match, onHandleShowPageUri, clearFile, setUpdateTrue, updateMetadata} = this.props; + onHandleShowPageUri(match.params); + setUpdateTrue(); + if (asset) { + ['title', 'description', 'license', 'nsfw'].forEach(meta => updateMetadata(meta, asset.claimData[meta])); } } + componentWillUnmount () { + this.props.clearFile(); + } render () { const { myChannel, asset } = this.props; // redirect if user does not own this claim diff --git a/client/src/pages/HomePage/index.js b/client/src/pages/HomePage/index.js index cd32268b..4695ef62 100644 --- a/client/src/pages/HomePage/index.js +++ b/client/src/pages/HomePage/index.js @@ -1,5 +1,6 @@ import { connect } from 'react-redux'; import { onHandleShowHomepage } from '../../actions/show'; +import { clearFile } from '../../actions/publish'; import View from './view'; const mapStateToProps = ({ show, site, channel, publish }) => { @@ -13,6 +14,7 @@ const mapStateToProps = ({ show, site, channel, publish }) => { const mapDispatchToProps = { onHandleShowHomepage, + clearFile, }; export default connect(mapStateToProps, mapDispatchToProps)(View); diff --git a/client/src/pages/HomePage/view.jsx b/client/src/pages/HomePage/view.jsx index 4b163ec5..36b8a78e 100644 --- a/client/src/pages/HomePage/view.jsx +++ b/client/src/pages/HomePage/view.jsx @@ -4,10 +4,8 @@ import PublishTool from '@containers/PublishTool'; import ContentPageWrapper from '@pages/ContentPageWrapper'; class HomePage extends React.Component { - componentDidMount () { - if (this.props.isUpdate) { - this.props.clearFile(); - } + componentWillUnmount () { + this.props.clearFile(); } render () { const { homeChannel } = this.props; From e784897afeac296367616a51f2983ecbca63cb41 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Tue, 16 Oct 2018 09:35:32 -0400 Subject: [PATCH 20/46] hide edit link for anonymous publishes --- client/src/containers/AssetInfo/index.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/src/containers/AssetInfo/index.js b/client/src/containers/AssetInfo/index.js index bc7a9229..c4f34eb6 100644 --- a/client/src/containers/AssetInfo/index.js +++ b/client/src/containers/AssetInfo/index.js @@ -6,7 +6,12 @@ const mapStateToProps = (props) => { const {show} = props; // select asset const asset = selectAsset(show); - const editable = Boolean(props.channel.loggedInChannel.name === asset.claimData.channelName); + const editable = Boolean( + asset && + asset.claimData && + asset.claimData.channelName && + props.channel.loggedInChannel.name === asset.claimData.channelName + ); // return props return { asset, From d9000dda0df6cff325d778e8699f5df71f096506 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Tue, 16 Oct 2018 10:30:21 -0400 Subject: [PATCH 21/46] fix loading message --- client/src/containers/PublishStatus/view.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/containers/PublishStatus/view.jsx b/client/src/containers/PublishStatus/view.jsx index b7b82052..73212fba 100644 --- a/client/src/containers/PublishStatus/view.jsx +++ b/client/src/containers/PublishStatus/view.jsx @@ -12,7 +12,7 @@ class PublishStatus extends React.Component { {status === publishStates.LOAD_START &&
-

le is loading to server

+

File is loading to server

0%

From 7ed43c645bc71b0c1363b5e421773e22924f1874 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Tue, 16 Oct 2018 10:33:03 -0400 Subject: [PATCH 22/46] adding 'editing uri' message above title --- client/src/components/PublishPreview/index.jsx | 2 ++ client/src/containers/PublishTool/index.js | 11 ++++++++++- client/src/containers/PublishTool/view.jsx | 4 ++-- client/src/utils/buildURI.js | 10 ++++++++++ 4 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 client/src/utils/buildURI.js diff --git a/client/src/components/PublishPreview/index.jsx b/client/src/components/PublishPreview/index.jsx index 9f7240cf..817b5c8c 100644 --- a/client/src/components/PublishPreview/index.jsx +++ b/client/src/components/PublishPreview/index.jsx @@ -7,9 +7,11 @@ import Row from '@components/Row'; class PublishPreview extends React.Component { render () { + const { isUpdate, uri } = this.props; return (
+ {isUpdate && uri && (

{`Editing ${uri}`}

)}
{ +const mapStateToProps = props => { + const { show, publish } = props; + const asset = selectAsset(show); + let uri; + if (asset) { + uri = `lbry://${buildURI(asset)}`; + } return { disabled: publish.disabled, file : publish.file, status : publish.status.status, isUpdate: publish.isUpdate, + uri, }; }; diff --git a/client/src/containers/PublishTool/view.jsx b/client/src/containers/PublishTool/view.jsx index 6ba957fe..d62e60b2 100644 --- a/client/src/containers/PublishTool/view.jsx +++ b/client/src/containers/PublishTool/view.jsx @@ -8,7 +8,7 @@ import { SAVE } from '../../constants/confirmation_messages'; class PublishTool extends React.Component { render () { - const {disabled, file, isUpdate, status} = this.props; + const {disabled, file, isUpdate, uri, status} = this.props; if (disabled) { return ( @@ -25,7 +25,7 @@ class PublishTool extends React.Component { - + ); } diff --git a/client/src/utils/buildURI.js b/client/src/utils/buildURI.js new file mode 100644 index 00000000..b6fb599c --- /dev/null +++ b/client/src/utils/buildURI.js @@ -0,0 +1,10 @@ +export const buildURI = asset => { + let channelName, certificateId, name, claimId; + if (asset.claimData) { + ({ channelName, certificateId, name, claimId } = asset.claimData); + } + if (channelName) { + return `${channelName}:${certificateId}/${name}`; + } + return `${claimId}/${name}`; +}; From 5747245e645907377c76f1218d8bcc5ae632e9fb Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Tue, 16 Oct 2018 10:38:49 -0400 Subject: [PATCH 23/46] remove asset label/row on edit page --- client/src/containers/PublishDetails/view.jsx | 38 ++++++------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/client/src/containers/PublishDetails/view.jsx b/client/src/containers/PublishDetails/view.jsx index eb56b3b9..b16a7dce 100644 --- a/client/src/containers/PublishDetails/view.jsx +++ b/client/src/containers/PublishDetails/view.jsx @@ -46,32 +46,18 @@ class PublishDetails extends React.Component { return (
{isUpdate ? (asset && ( - - - - } - content={ - - {asset.claimData.channelName} - - } - /> - - - - } - content={ - - {asset.name} - - } - /> - - + + + } + content={ + + {asset.claimData.channelName} + + } + /> + )) : ( From 41cc6f511843809ea21d829cfb2696338ef5af05 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Tue, 16 Oct 2018 11:56:17 -0400 Subject: [PATCH 24/46] always show metadata inputs on update --- .../containers/PublishMetadataInputs/index.js | 1 + .../containers/PublishMetadataInputs/view.jsx | 17 ++++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/client/src/containers/PublishMetadataInputs/index.js b/client/src/containers/PublishMetadataInputs/index.js index b00b6f09..1c5b462a 100644 --- a/client/src/containers/PublishMetadataInputs/index.js +++ b/client/src/containers/PublishMetadataInputs/index.js @@ -8,6 +8,7 @@ const mapStateToProps = ({ publish }) => { description : publish.metadata.description, license : publish.metadata.license, nsfw : publish.metadata.nsfw, + isUpdate : publish.isUpdate, }; }; diff --git a/client/src/containers/PublishMetadataInputs/view.jsx b/client/src/containers/PublishMetadataInputs/view.jsx index 10d8a793..d5bfb3d9 100644 --- a/client/src/containers/PublishMetadataInputs/view.jsx +++ b/client/src/containers/PublishMetadataInputs/view.jsx @@ -26,27 +26,30 @@ class PublishMetadataInputs extends React.Component { this.props.onMetadataChange(name, selectedOption); } render () { + const { showMetadataInputs, description, isUpdate, nsfw } = this.props; return (
- {this.props.showMetadataInputs && ( + {(showMetadataInputs || isUpdate) && (
)} - + {!isUpdate && ( + + )}
); } From 70ef8b9eed99826854454640cee0960eb8b9a00e Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Tue, 16 Oct 2018 12:08:25 -0400 Subject: [PATCH 25/46] display update when appropriate --- client/src/containers/PublishDetails/view.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/containers/PublishDetails/view.jsx b/client/src/containers/PublishDetails/view.jsx index b16a7dce..0d6ce2d3 100644 --- a/client/src/containers/PublishDetails/view.jsx +++ b/client/src/containers/PublishDetails/view.jsx @@ -82,7 +82,7 @@ class PublishDetails extends React.Component { From 3daf0ce2142fbc3ef2ef3c3445a4431750eefd13 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Wed, 17 Oct 2018 09:32:21 -0400 Subject: [PATCH 26/46] fix file parsing --- server/controllers/api/claim/publish/index.js | 2 +- .../publish/parsePublishApiRequestFiles.js | 24 +++++++------------ 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/server/controllers/api/claim/publish/index.js b/server/controllers/api/claim/publish/index.js index a849cfa7..38490050 100644 --- a/server/controllers/api/claim/publish/index.js +++ b/server/controllers/api/claim/publish/index.js @@ -14,7 +14,7 @@ const publish = require('./publish.js'); const createPublishParams = require('./createPublishParams.js'); const createThumbnailPublishParams = require('./createThumbnailPublishParams.js'); const parsePublishApiRequestBody = require('./parsePublishApiRequestBody.js'); -const {parsePublishApiRequestFiles} = require('./parsePublishApiRequestFiles.js'); +const parsePublishApiRequestFiles = require('./parsePublishApiRequestFiles.js'); const authenticateUser = require('./authentication.js'); const CLAIM_TAKEN = 'CLAIM_TAKEN'; diff --git a/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js b/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js index 885dc8d7..d5b447b8 100644 --- a/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js +++ b/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js @@ -1,7 +1,7 @@ const path = require('path'); const validateFileTypeAndSize = require('./validateFileTypeAndSize.js'); -const parsePublishApiRequestFiles = ({file}, isUpdate) => { +const parsePublishApiRequestFiles = ({file, thumbnail}, isUpdate) => { // make sure a file was provided if (!file && !isUpdate) { throw new Error('no file with key of [file] found in request'); @@ -32,26 +32,20 @@ const parsePublishApiRequestFiles = ({file}, isUpdate) => { // validate the file if (file) validateFileTypeAndSize(file); // return results - return { + const obj = { fileName : file.name, filePath : file.path, fileExtension: path.extname(file.path), fileType : file.type, }; -}; -const parsePublishApiRequestThumbnail = ({thumbnail}) => { - if (!thumbnail) { - return; + if (thumbnail) { + obj.thumbnailFileName = thumbnail.name; + obj.thumbnailFilePath = thumbnail.path; + obj.thumbnailFileType = thumbnail.type; } - return { - thumbnailFileName: thumbnail.name, - thumbnailFilePath: thumbnail.path, - thumbnailFileType: thumbnail.type, - }; + + return obj; }; -module.exports = { - parsePublishApiRequestFiles, - parsePublishApiRequestThumbnail, -}; +module.exports = parsePublishApiRequestFiles; From 97de13527a9c06312669f405fd70ee8cbb5af30d Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Wed, 17 Oct 2018 10:07:06 -0400 Subject: [PATCH 27/46] get thumbnail address for mp4 edits --- client/src/containers/Dropzone/index.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/client/src/containers/Dropzone/index.js b/client/src/containers/Dropzone/index.js index 96d30ca4..7270d172 100644 --- a/client/src/containers/Dropzone/index.js +++ b/client/src/containers/Dropzone/index.js @@ -2,6 +2,9 @@ import { connect } from 'react-redux'; import { selectFile, updateError, clearFile } from '../../actions/publish'; import { selectAsset } from '../../selectors/show'; import View from './view'; +import siteConfig from '@config/siteConfig.json'; + +const { assetDefaults: { thumbnail: defaultThumbnail } } = siteConfig; const mapStateToProps = ({ show, publish: { file, thumbnail, fileError, isUpdate } }) => { const obj = { file, thumbnail, fileError, isUpdate }; @@ -9,11 +12,12 @@ const mapStateToProps = ({ show, publish: { file, thumbnail, fileError, isUpdate if (isUpdate) { asset = selectAsset(show); if (asset) { - ({name, claimData: {claimId, fileExt, outpoint}} = asset); - sourceUrl = `/${claimId}/${name}.${fileExt}?${outpoint}`; - } - if (sourceUrl) { - obj.sourceUrl = sourceUrl; + if (asset.claimData.fileExt === 'mp4') { + obj.sourceUrl = asset.claimData.thumbnail ? asset.claimData.thumbnail : defaultThumbnail; + } else { + ({name, claimData: {claimId, fileExt, outpoint}} = asset); + obj.sourceUrl = `/${claimId}/${name}.${fileExt}?${outpoint}`; + } } } return obj; From 30eae51069c63bd65992e99f679b0c1aea6899d3 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 22 Oct 2018 09:52:17 -0400 Subject: [PATCH 28/46] parse {file, thumbnail} with parsePublishApiRequestFiles --- server/controllers/api/claim/update/index.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/server/controllers/api/claim/update/index.js b/server/controllers/api/claim/update/index.js index 803a5611..ab2e2f25 100644 --- a/server/controllers/api/claim/update/index.js +++ b/server/controllers/api/claim/update/index.js @@ -6,7 +6,7 @@ const { sendGATimingEvent } = require('../../../../utils/googleAnalytics.js'); const { handleErrorResponse } = require('../../../utils/errorHandlers.js'); const publish = require('../publish/publish.js'); const parsePublishApiRequestBody = require('../publish/parsePublishApiRequestBody'); -const {parsePublishApiRequestFiles, parsePublishApiRequestThumbnail} = require('../publish/parsePublishApiRequestFiles.js'); +const parsePublishApiRequestFiles = require('../publish/parsePublishApiRequestFiles.js'); const authenticateUser = require('../publish/authentication.js'); const createThumbnailPublishParams = require('../publish/createThumbnailPublishParams.js'); @@ -66,12 +66,7 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res) try { ({name, nsfw, license, title, description, thumbnail} = parsePublishApiRequestBody(body)); - if (files.file) { - ({fileName, filePath, fileExtension, fileType} = parsePublishApiRequestFiles(files)); - if (files.thumbnail) { - ({thumbnailFileName, thumbnailFilePath, thumbnailFileType} = parsePublishApiRequestThumbnail(files)); - } - } + ({fileName, filePath, fileExtension, fileType, thumbnailFileName, thumbnailFilePath, thumbnailFileType} = parsePublishApiRequestFiles(files)); ({channelName, channelId, channelPassword} = body); } catch (error) { return res.status(400).json({success: false, message: error.message}); From 7703dc4e19ba86455a97627f628468f89af9c893 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 22 Oct 2018 10:39:13 -0400 Subject: [PATCH 29/46] add outpoint to object returned by publish --- server/controllers/api/claim/publish/publish.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/controllers/api/claim/publish/publish.js b/server/controllers/api/claim/publish/publish.js index 7af9beae..900093e3 100644 --- a/server/controllers/api/claim/publish/publish.js +++ b/server/controllers/api/claim/publish/publish.js @@ -14,7 +14,7 @@ const publish = async (publishParams, fileName, fileType, filePath) => { try { publishResults = await publishClaim(publishParams); logger.info(`Successfully published ${publishParams.name} ${fileName}`, publishResults); - + const outpoint = `${publishResults.output.txid}:${publishResults.output.nout}`; // get the channel information if (publishParams.channel_name) { logger.debug(`this claim was published in channel: ${publishParams.channel_name}`); @@ -50,7 +50,7 @@ const publish = async (publishParams, fileName, fileType, filePath) => { ]); logger.info('File and Claim records successfully associated'); - return claimRecord; + return Object.assign({}, claimRecord, {outpoint}); } catch (err) { // parse daemon response when err is a string // this needs work From c7a7790c27797cec28f03247cb6f814980ea69d0 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 22 Oct 2018 12:44:38 -0400 Subject: [PATCH 30/46] use claimId as react component key --- client/src/containers/ChannelClaimsDisplay/view.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/containers/ChannelClaimsDisplay/view.jsx b/client/src/containers/ChannelClaimsDisplay/view.jsx index 1f29b65b..8d4fb25e 100644 --- a/client/src/containers/ChannelClaimsDisplay/view.jsx +++ b/client/src/containers/ChannelClaimsDisplay/view.jsx @@ -36,7 +36,7 @@ class ChannelClaimsDisplay extends React.Component { ))}
From 9ba4c4641e74bad56338b0942d31a2c83c1d5650 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 22 Oct 2018 15:36:57 -0400 Subject: [PATCH 31/46] ignore channelClaims if channelId has not been confirmed --- server/controllers/api/channel/claims/getChannelClaims.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/controllers/api/channel/claims/getChannelClaims.js b/server/controllers/api/channel/claims/getChannelClaims.js index d1ff3b96..90a9c2c7 100644 --- a/server/controllers/api/channel/claims/getChannelClaims.js +++ b/server/controllers/api/channel/claims/getChannelClaims.js @@ -5,7 +5,11 @@ const { returnPaginatedChannelClaims } = require('./channelPagination.js'); const getChannelClaims = async (channelName, channelShortId, page) => { const channelId = await chainquery.claim.queries.getLongClaimId(channelName, channelShortId); - const channelClaims = await chainquery.claim.queries.getAllChannelClaims(channelId); + + let channelClaims; + if (channelId) { + channelClaims = await chainquery.claim.queries.getAllChannelClaims(channelId); + } const processingChannelClaims = channelClaims ? channelClaims.map((claim) => getClaimData(claim)) : []; const processedChannelClaims = await Promise.all(processingChannelClaims); From eaab9bb21be05853a8687f4180a31bdad657aa0e Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 22 Oct 2018 18:16:38 -0400 Subject: [PATCH 32/46] fix update parsing issue --- .../api/claim/publish/parsePublishApiRequestFiles.js | 12 +++++++++++- server/controllers/api/claim/update/index.js | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js b/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js index d5b447b8..6e1f3409 100644 --- a/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js +++ b/server/controllers/api/claim/publish/parsePublishApiRequestFiles.js @@ -3,7 +3,17 @@ const validateFileTypeAndSize = require('./validateFileTypeAndSize.js'); const parsePublishApiRequestFiles = ({file, thumbnail}, isUpdate) => { // make sure a file was provided - if (!file && !isUpdate) { + if (!file) { + if (isUpdate) { + if (thumbnail) { + const obj = {}; + obj.thumbnailFileName = thumbnail.name; + obj.thumbnailFilePath = thumbnail.path; + obj.thumbnailFileType = thumbnail.type; + return obj; + } + return {}; + } throw new Error('no file with key of [file] found in request'); } if (!file.path) { diff --git a/server/controllers/api/claim/update/index.js b/server/controllers/api/claim/update/index.js index ab2e2f25..bd3a262d 100644 --- a/server/controllers/api/claim/update/index.js +++ b/server/controllers/api/claim/update/index.js @@ -66,7 +66,7 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res) try { ({name, nsfw, license, title, description, thumbnail} = parsePublishApiRequestBody(body)); - ({fileName, filePath, fileExtension, fileType, thumbnailFileName, thumbnailFilePath, thumbnailFileType} = parsePublishApiRequestFiles(files)); + ({fileName, filePath, fileExtension, fileType, thumbnailFileName, thumbnailFilePath, thumbnailFileType} = parsePublishApiRequestFiles(files, true)); ({channelName, channelId, channelPassword} = body); } catch (error) { return res.status(400).json({success: false, message: error.message}); From dec8ade055568dc1e0f8b40156946d60ea178837 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Thu, 25 Oct 2018 08:56:26 -0400 Subject: [PATCH 33/46] consolidate lets --- server/controllers/api/claim/update/index.js | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/server/controllers/api/claim/update/index.js b/server/controllers/api/claim/update/index.js index bd3a262d..1a7a3ccf 100644 --- a/server/controllers/api/claim/update/index.js +++ b/server/controllers/api/claim/update/index.js @@ -42,25 +42,7 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res) } // define variables - let channelName; - let channelId; - let channelPassword; - let description; - let fileName; - let filePath; - let fileType; - let gaStartTime; - let thumbnail; - let fileExtension; - let license; - let name; - let nsfw; - let thumbnailFileName; - let thumbnailFilePath; - let thumbnailFileType; - let title; - let claimRecord; - let metadata; + let channelName, channelId, channelPassword, description, fileName, filePath, fileType, gaStartTime, thumbnail, fileExtension, license, name, nsfw, thumbnailFileName, thumbnailFilePath, thumbnailFileType, title, claimRecord, metadata; // record the start time of the request gaStartTime = Date.now(); From e9c60f652fb2fd559a9d411fd84abeb6a139b14e Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Sun, 28 Oct 2018 14:00:31 -0400 Subject: [PATCH 34/46] use speech db over chainquery when more recently updated --- server/controllers/api/claim/data/index.js | 11 ++--------- server/utils/fetchClaimData.js | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 server/utils/fetchClaimData.js diff --git a/server/controllers/api/claim/data/index.js b/server/controllers/api/claim/data/index.js index 6efe4e83..95146c37 100644 --- a/server/controllers/api/claim/data/index.js +++ b/server/controllers/api/claim/data/index.js @@ -1,8 +1,8 @@ const { handleErrorResponse } = require('../../../utils/errorHandlers.js'); const getClaimData = require('server/utils/getClaimData'); +const fetchClaimData = require('server/utils/fetchClaimData'); const chainquery = require('chainquery'); const db = require('server/models'); - /* route to return data for a claim @@ -10,16 +10,9 @@ const db = require('server/models'); */ const claimData = async ({ ip, originalUrl, body, params }, res) => { - const claimName = params.claimName; - let claimId = params.claimId; - if (claimId === 'none') claimId = null; try { - let resolvedClaim = await chainquery.claim.queries.resolveClaim(claimName, claimId).catch(() => {}); - - if(!resolvedClaim) { - resolvedClaim = await db.Claim.resolveClaim(claimName, claimId); - } + const resolvedClaim = await fetchClaimData(params); if (!resolvedClaim) { return res.status(404).json({ diff --git a/server/utils/fetchClaimData.js b/server/utils/fetchClaimData.js new file mode 100644 index 00000000..d143062d --- /dev/null +++ b/server/utils/fetchClaimData.js @@ -0,0 +1,20 @@ +const chainquery = require('chainquery'); +const db = require('server/models'); + +const fetchClaimData = async (params) => { + const name = params.claimName; + let claimId = params.claimId; + if (claimId === 'none') claimId = null; + + const [cq, local] = await Promise.all([ + chainquery.claim.queries.resolveClaim(name, claimId).then(res => res.dataValues).catch(() => {}), + db.Claim.resolveClaim(name, claimId).catch(() => {}), + ]); + + if (!cq && !local) return null; + if (cq.name === name && !local) return cq; + if (local.name === name && !cq) return local; + return local.updatedAt > cq.modified_at ? local : cq; +}; + +module.exports = fetchClaimData; From 3378e4289f8791954dfb3378361049b502d17ac7 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Sun, 28 Oct 2018 18:25:53 -0400 Subject: [PATCH 35/46] track when publishState.hasChanged; only update when true --- client/src/actions/publish.js | 7 +++++ client/src/constants/publish_action_types.js | 1 + client/src/containers/PublishDetails/index.js | 5 ++-- client/src/containers/PublishDetails/view.jsx | 4 +-- client/src/containers/PublishTool/index.js | 5 ++-- client/src/containers/PublishTool/view.jsx | 3 +- client/src/pages/EditPage/index.js | 3 +- client/src/pages/EditPage/view.jsx | 3 +- client/src/reducers/publish.js | 28 ++++++++++++++----- 9 files changed, 43 insertions(+), 16 deletions(-) diff --git a/client/src/actions/publish.js b/client/src/actions/publish.js index fc291c1a..91e44fd5 100644 --- a/client/src/actions/publish.js +++ b/client/src/actions/publish.js @@ -20,6 +20,13 @@ export function setUpdateTrue () { }; } +export function setHasChanged (status) { + return { + type: actions.SET_HAS_CHANGED, + data: status, + }; +} + export function updateMetadata (name, value) { return { type: actions.METADATA_UPDATE, diff --git a/client/src/constants/publish_action_types.js b/client/src/constants/publish_action_types.js index dee25bd0..4ad26d3c 100644 --- a/client/src/constants/publish_action_types.js +++ b/client/src/constants/publish_action_types.js @@ -12,3 +12,4 @@ export const PUBLISH_START = 'PUBLISH_START'; export const CLAIM_AVAILABILITY = 'CLAIM_AVAILABILITY'; export const SET_UPDATE_TRUE = 'SET_UPDATE_TRUE'; export const ABANDON_CLAIM = 'ABANDON_CLAIM'; +export const SET_HAS_CHANGED = 'SET_HAS_CHANGED'; diff --git a/client/src/containers/PublishDetails/index.js b/client/src/containers/PublishDetails/index.js index ec49ba03..8c8eede0 100644 --- a/client/src/containers/PublishDetails/index.js +++ b/client/src/containers/PublishDetails/index.js @@ -5,9 +5,10 @@ import View from './view'; const mapStateToProps = ({ show, publish }) => { return { - file : publish.file, + file: publish.file, isUpdate: publish.isUpdate, - asset : selectAsset(show), + hasChanged: publish.hasChanged, + asset: selectAsset(show), }; }; diff --git a/client/src/containers/PublishDetails/view.jsx b/client/src/containers/PublishDetails/view.jsx index 0d6ce2d3..95a79edd 100644 --- a/client/src/containers/PublishDetails/view.jsx +++ b/client/src/containers/PublishDetails/view.jsx @@ -32,8 +32,8 @@ class PublishDetails extends React.Component { } } onCancel () { - const { isUpdate, clearFile, history } = this.props; - if (isUpdate) { + const { isUpdate, hasChanged, clearFile, history } = this.props; + if (isUpdate || !hasChanged) { history.push('/'); } else { if (confirm(SAVE)) { diff --git a/client/src/containers/PublishTool/index.js b/client/src/containers/PublishTool/index.js index a124a5f9..86a4b6b3 100644 --- a/client/src/containers/PublishTool/index.js +++ b/client/src/containers/PublishTool/index.js @@ -12,9 +12,10 @@ const mapStateToProps = props => { } return { disabled: publish.disabled, - file : publish.file, - status : publish.status.status, + file: publish.file, + status: publish.status.status, isUpdate: publish.isUpdate, + hasChanged: publish.hasChanged, uri, }; }; diff --git a/client/src/containers/PublishTool/view.jsx b/client/src/containers/PublishTool/view.jsx index d62e60b2..794c39d1 100644 --- a/client/src/containers/PublishTool/view.jsx +++ b/client/src/containers/PublishTool/view.jsx @@ -8,7 +8,7 @@ import { SAVE } from '../../constants/confirmation_messages'; class PublishTool extends React.Component { render () { - const {disabled, file, isUpdate, uri, status} = this.props; + const {disabled, file, isUpdate, hasChanged, uri, status} = this.props; if (disabled) { return ( @@ -23,6 +23,7 @@ class PublishTool extends React.Component { return ( diff --git a/client/src/pages/EditPage/index.js b/client/src/pages/EditPage/index.js index 63d11541..d7ca7b25 100644 --- a/client/src/pages/EditPage/index.js +++ b/client/src/pages/EditPage/index.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { setUpdateTrue, updateMetadata, clearFile } from '../../actions/publish'; +import { setUpdateTrue, setHasChanged, updateMetadata, clearFile } from '../../actions/publish'; import { onHandleShowPageUri } from '../../actions/show'; import { selectAsset } from '../../selectors/show'; import View from './view'; @@ -17,6 +17,7 @@ const mapDispatchToProps = { updateMetadata, onHandleShowPageUri, setUpdateTrue, + setHasChanged, clearFile, }; diff --git a/client/src/pages/EditPage/view.jsx b/client/src/pages/EditPage/view.jsx index d60ffbbc..60b07900 100644 --- a/client/src/pages/EditPage/view.jsx +++ b/client/src/pages/EditPage/view.jsx @@ -5,12 +5,13 @@ import PublishTool from '@containers/PublishTool'; class EditPage extends React.Component { componentDidMount () { - const {asset, match, onHandleShowPageUri, clearFile, setUpdateTrue, updateMetadata} = this.props; + const {asset, match, onHandleShowPageUri, setUpdateTrue, setHasChanged, updateMetadata} = this.props; onHandleShowPageUri(match.params); setUpdateTrue(); if (asset) { ['title', 'description', 'license', 'nsfw'].forEach(meta => updateMetadata(meta, asset.claimData[meta])); } + setHasChanged(false); } componentWillUnmount () { this.props.clearFile(); diff --git a/client/src/reducers/publish.js b/client/src/reducers/publish.js index 1489c711..3282a722 100644 --- a/client/src/reducers/publish.js +++ b/client/src/reducers/publish.js @@ -41,7 +41,8 @@ const initialState = { license : '', nsfw : false, }, - isUpdate : false, + isUpdate: false, + hasChanged: false, thumbnail: null, thumbnailChannel, thumbnailChannelId, @@ -52,6 +53,7 @@ export default function (state = initialState, action) { case actions.FILE_SELECTED: return Object.assign({}, state.isUpdate ? state : initialState, { // note: clears to initial state file: action.data, + hasChanged: true, }); case actions.FILE_CLEAR: return initialState; @@ -60,14 +62,17 @@ export default function (state = initialState, action) { metadata: Object.assign({}, state.metadata, { [action.data.name]: action.data.value, }), + hasChanged: true, }); case actions.CLAIM_UPDATE: return Object.assign({}, state, { claim: action.data, + hasChanged: true, }); case actions.SET_PUBLISH_IN_CHANNEL: return Object.assign({}, state, { publishInChannel: action.channel, + hasChanged: true, }); case actions.PUBLISH_STATUS_UPDATE: return Object.assign({}, state, { @@ -84,17 +89,26 @@ export default function (state = initialState, action) { selectedChannel: action.data, }); case actions.TOGGLE_METADATA_INPUTS: - return Object.assign({}, state, { + return { + ...state, showMetadataInputs: action.data, - }); + }; case actions.THUMBNAIL_NEW: - return Object.assign({}, state, { + return { + ...state, thumbnail: action.data, - }); + hasChanged: true, + }; case actions.SET_UPDATE_TRUE: - return Object.assign({}, state, { + return { + ...state, isUpdate: true, - }); + }; + case actions.SET_HAS_CHANGED: + return { + ...state, + hasChanged: action.data, + }; default: return state; } From 760edeff1f7e14aef44ec0bd4a48f72d3e2b0ed4 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Sun, 28 Oct 2018 18:30:48 -0400 Subject: [PATCH 36/46] do not prompt for leaving publish page to publish page --- client/src/containers/PublishTool/view.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/containers/PublishTool/view.jsx b/client/src/containers/PublishTool/view.jsx index 794c39d1..79d82a1f 100644 --- a/client/src/containers/PublishTool/view.jsx +++ b/client/src/containers/PublishTool/view.jsx @@ -24,7 +24,7 @@ class PublishTool extends React.Component { location.pathname === '/' ? true : SAVE} /> From a210644959501c33150b5fcc66c91ed1b78d13e1 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Sun, 28 Oct 2018 18:51:51 -0400 Subject: [PATCH 37/46] fix naming mistake --- server/utils/fetchClaimData.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/server/utils/fetchClaimData.js b/server/utils/fetchClaimData.js index d143062d..e278a2e8 100644 --- a/server/utils/fetchClaimData.js +++ b/server/utils/fetchClaimData.js @@ -2,8 +2,7 @@ const chainquery = require('chainquery'); const db = require('server/models'); const fetchClaimData = async (params) => { - const name = params.claimName; - let claimId = params.claimId; + let { claimId, claimName: name } = params; if (claimId === 'none') claimId = null; const [cq, local] = await Promise.all([ @@ -12,8 +11,8 @@ const fetchClaimData = async (params) => { ]); if (!cq && !local) return null; - if (cq.name === name && !local) return cq; - if (local.name === name && !cq) return local; + if (cq && cq.name === name && !local) return cq; + if (local && local.name === name && !cq) return local; return local.updatedAt > cq.modified_at ? local : cq; }; From 4b72d02c59854e1ee96ee8907fc299c973eb7121 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 5 Nov 2018 16:16:42 -0500 Subject: [PATCH 38/46] use canonical url in edit link --- client/src/containers/AssetInfo/view.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/containers/AssetInfo/view.jsx b/client/src/containers/AssetInfo/view.jsx index be40f4b7..625d3f82 100644 --- a/client/src/containers/AssetInfo/view.jsx +++ b/client/src/containers/AssetInfo/view.jsx @@ -34,7 +34,7 @@ class AssetInfo extends React.Component { } - content={{name}} + content={{name}} /> )} From 7329c763b920e38eefe3a04e946cdb63da364476 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 5 Nov 2018 16:32:29 -0500 Subject: [PATCH 39/46] use canonical url in sourceUrl --- client/src/containers/Dropzone/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/src/containers/Dropzone/index.js b/client/src/containers/Dropzone/index.js index 7270d172..842973f6 100644 --- a/client/src/containers/Dropzone/index.js +++ b/client/src/containers/Dropzone/index.js @@ -3,6 +3,7 @@ import { selectFile, updateError, clearFile } from '../../actions/publish'; import { selectAsset } from '../../selectors/show'; import View from './view'; import siteConfig from '@config/siteConfig.json'; +import createCanonicalLink from '../../../../utils/createCanonicalLink'; const { assetDefaults: { thumbnail: defaultThumbnail } } = siteConfig; @@ -15,8 +16,8 @@ const mapStateToProps = ({ show, publish: { file, thumbnail, fileError, isUpdate if (asset.claimData.fileExt === 'mp4') { obj.sourceUrl = asset.claimData.thumbnail ? asset.claimData.thumbnail : defaultThumbnail; } else { - ({name, claimData: {claimId, fileExt, outpoint}} = asset); - obj.sourceUrl = `/${claimId}/${name}.${fileExt}?${outpoint}`; + ({claimData: {fileExt, outpoint}} = asset); + obj.sourceUrl = `${createCanonicalLink({ asset: asset.claimData })}.${fileExt}?${outpoint}`; } } } From ffe8aaba47fb4d85d1ca35ac64c2d0116e8e1710 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Tue, 6 Nov 2018 09:42:49 -0500 Subject: [PATCH 40/46] use proper file exension --- client/src/containers/AssetDisplay/view.jsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/client/src/containers/AssetDisplay/view.jsx b/client/src/containers/AssetDisplay/view.jsx index 98901ee9..7d77f951 100644 --- a/client/src/containers/AssetDisplay/view.jsx +++ b/client/src/containers/AssetDisplay/view.jsx @@ -2,6 +2,7 @@ import React from 'react'; import Row from '@components/Row'; import ProgressBar from '@components/ProgressBar'; import { LOCAL_CHECK, UNAVAILABLE, ERROR, AVAILABLE } from '../../constants/asset_display_states'; +import createCanonicalLink from '../../../../utils/createCanonicalLink'; class AvailableContent extends React.Component { render () { @@ -44,10 +45,15 @@ class AssetDisplay extends React.Component { this.props.onFileRequest(name, claimId); } render () { - const { status, error, asset: { name, claimData: { claimId, contentType, fileExt, thumbnail, outpoint } } } = this.props; + const { status, error, asset } = this.props; + const { name, claimData: { claimId, contentType, thumbnail, outpoint } } = asset; // the outpoint is added to force the browser to re-download the asset after an update // issue: https://github.com/lbryio/spee.ch/issues/607 - const sourceUrl = `/${claimId}/${name}.${fileExt}?${outpoint}`; + let fileExt; + if (typeof contentType === 'string') { + fileExt = contentType.split('/')[1] || 'jpg'; + } + const sourceUrl = `${createCanonicalLink({ asset: asset.claimData })}.${fileExt}?${outpoint}`; return (
{(status === LOCAL_CHECK) && From 936ac2f43925dc15dcfc3a0ce97f1133294750e9 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Tue, 6 Nov 2018 17:40:38 -0500 Subject: [PATCH 41/46] return canonicalUrl on publish/update; fixes --- client/src/sagas/publish.js | 2 +- server/chainquery/bundle.js | 27 ++++++- server/chainquery/queries/claimQueries.js | 27 ++++++- server/controllers/api/claim/publish/index.js | 56 ++++++++++++-- server/controllers/api/claim/update/index.js | 75 ++++++++++++------- 5 files changed, 149 insertions(+), 38 deletions(-) diff --git a/client/src/sagas/publish.js b/client/src/sagas/publish.js index fbc45338..7e61cda7 100644 --- a/client/src/sagas/publish.js +++ b/client/src/sagas/publish.js @@ -73,7 +73,7 @@ function * publishFile (action) { }); } if (success.data.claimId) { - return history.push(`/${success.data.claimId}/${success.data.name}`); + return history.push(success.data.pushTo); } else { // this returns to the homepage, needs work return yield put(updatePublishStatus(publishStates.FAILED, 'ERROR')); diff --git a/server/chainquery/bundle.js b/server/chainquery/bundle.js index 2b51ddb8..e97ae49a 100644 --- a/server/chainquery/bundle.js +++ b/server/chainquery/bundle.js @@ -850,7 +850,7 @@ var claimQueries = (db, table, sequelize) => ({ }); }, - getShortClaimIdFromLongClaimId: async (claimId, claimName) => { + getShortClaimIdFromLongClaimId: async (claimId, claimName, pendingClaim) => { logger$1.debug(`claim.getShortClaimIdFromLongClaimId for ${claimName}#${claimId}`); return await table.findAll({ where: { name: claimName }, @@ -860,7 +860,12 @@ var claimQueries = (db, table, sequelize) => ({ throw new Error('No claim(s) found with that claim name'); } - return returnShortId(result, claimId); + let list = result.map(claim => claim.dataValues); + if (pendingClaim) { + list = list.concat(pendingClaim); + } + + return returnShortId(list, claimId); }); }, @@ -981,6 +986,24 @@ var claimQueries = (db, table, sequelize) => ({ }); }, + resolveClaimInChannel: async (claimName, channelId) => { + logger$1.debug(`Claim.resolveClaimByNames: ${claimName} in ${channelId}`); + return table.findAll({ + where: { + name: claimName, + publisher_id: channelId, + }, + }).then(claimArray => { + if (claimArray.length === 0) { + return null; + } else if (claimArray.length !== 1) { + logger$1.warn(`more than one record matches ${claimName} in ${channelId}`); + } + + return claimArray[0]; + }); + }, + getOutpoint: async (name, claimId) => { logger$1.debug(`finding outpoint for ${name}#${claimId}`); diff --git a/server/chainquery/queries/claimQueries.js b/server/chainquery/queries/claimQueries.js index d8deb21c..185959ae 100644 --- a/server/chainquery/queries/claimQueries.js +++ b/server/chainquery/queries/claimQueries.js @@ -49,7 +49,7 @@ export default (db, table, sequelize) => ({ }); }, - getShortClaimIdFromLongClaimId: async (claimId, claimName) => { + getShortClaimIdFromLongClaimId: async (claimId, claimName, pendingClaim) => { logger.debug(`claim.getShortClaimIdFromLongClaimId for ${claimName}#${claimId}`); return await table.findAll({ where: { name: claimName }, @@ -59,7 +59,12 @@ export default (db, table, sequelize) => ({ throw new Error('No claim(s) found with that claim name'); } - return returnShortId(result, claimId); + let list = result.map(claim => claim.dataValues); + if (pendingClaim) { + list = list.concat(pendingClaim); + } + + return returnShortId(list, claimId); }); }, @@ -180,6 +185,24 @@ export default (db, table, sequelize) => ({ }); }, + resolveClaimInChannel: async (claimName, channelId) => { + logger.debug(`Claim.resolveClaimByNames: ${claimName} in ${channelId}`); + return table.findAll({ + where: { + name: claimName, + publisher_id: channelId, + }, + }).then(claimArray => { + if (claimArray.length === 0) { + return null; + } else if (claimArray.length !== 1) { + logger.warn(`more than one record matches ${claimName} in ${channelId}`); + } + + return claimArray[0]; + }); + }, + getOutpoint: async (name, claimId) => { logger.debug(`finding outpoint for ${name}#${claimId}`); diff --git a/server/controllers/api/claim/publish/index.js b/server/controllers/api/claim/publish/index.js index 38490050..cee6c2bc 100644 --- a/server/controllers/api/claim/publish/index.js +++ b/server/controllers/api/claim/publish/index.js @@ -17,6 +17,9 @@ const parsePublishApiRequestBody = require('./parsePublishApiRequestBody.js'); const parsePublishApiRequestFiles = require('./parsePublishApiRequestFiles.js'); const authenticateUser = require('./authentication.js'); +const chainquery = require('chainquery'); +const createCanonicalLink = require('../../../../../utils/createCanonicalLink'); + const CLAIM_TAKEN = 'CLAIM_TAKEN'; const UNAPPROVED_CHANNEL = 'UNAPPROVED_CHANNEL'; @@ -42,7 +45,25 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res) }); } // define variables - let channelName, channelId, channelPassword, description, fileName, filePath, fileExtension, fileType, gaStartTime, license, name, nsfw, thumbnail, thumbnailFileName, thumbnailFilePath, thumbnailFileType, title; + let channelName, + channelId, + channelPassword, + description, + fileName, + filePath, + fileExtension, + fileType, + gaStartTime, + license, + name, + nsfw, + thumbnail, + thumbnailFileName, + thumbnailFilePath, + thumbnailFileType, + title, + claimData, + claimId; // record the start time of the request gaStartTime = Date.now(); // validate the body and files of the request @@ -64,6 +85,7 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res) }; throw error; } + return Promise.all([ checkClaimAvailability(name), createPublishParams(filePath, name, title, description, license, nsfw, thumbnail, channelName, channelClaimId), @@ -85,17 +107,37 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res) // publish the asset return publish(publishParams, fileName, fileType, filePath); }) - .then(claimData => { - logger.debug('Publish success >', claimData); + .then(publishResults => { + logger.info('Publish success >', publishResults); + claimData = publishResults; + ({claimId} = claimData); + + if (channelName) { + return chainquery.claim.queries.getShortClaimIdFromLongClaimId(claimData.certificateId, channelName); + } else { + return chainquery.claim.queries.getShortClaimIdFromLongClaimId(claimId, name, claimData).catch(error => { + return claimId.slice(0, 1); + }); + } + }) + .then(shortId => { + let canonicalUrl; + if (channelName) { + canonicalUrl = createCanonicalLink({ asset: { ...claimData, channelShortId: shortId } }); + } else { + canonicalUrl = createCanonicalLink({ asset: { ...claimData, shortId } }) + } + res.status(200).json({ success: true, message: 'publish completed successfully', data : { name, - claimId : claimData.claimId, - url : `${host}/${claimData.claimId}/${name}`, // for backwards compatability with app - showUrl : `${host}/${claimData.claimId}/${name}`, - serveUrl: `${host}/${claimData.claimId}/${name}${fileExtension}`, + claimId, + url : `${host}${canonicalUrl}`, // for backwards compatability with app + showUrl : `${host}${canonicalUrl}`, + serveUrl: `${host}${canonicalUrl}${fileExtension}`, + pushTo : canonicalUrl, claimData, }, }); diff --git a/server/controllers/api/claim/update/index.js b/server/controllers/api/claim/update/index.js index 1a7a3ccf..7f8588f8 100644 --- a/server/controllers/api/claim/update/index.js +++ b/server/controllers/api/claim/update/index.js @@ -9,6 +9,8 @@ const parsePublishApiRequestBody = require('../publish/parsePublishApiRequestBod const parsePublishApiRequestFiles = require('../publish/parsePublishApiRequestFiles.js'); const authenticateUser = require('../publish/authentication.js'); const createThumbnailPublishParams = require('../publish/createThumbnailPublishParams.js'); +const chainquery = require('chainquery'); +const createCanonicalLink = require('../../../../../utils/createCanonicalLink'); /* route to update a claim through the daemon @@ -42,7 +44,7 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res) } // define variables - let channelName, channelId, channelPassword, description, fileName, filePath, fileType, gaStartTime, thumbnail, fileExtension, license, name, nsfw, thumbnailFileName, thumbnailFilePath, thumbnailFileType, title, claimRecord, metadata; + let channelName, channelId, channelPassword, description, fileName, filePath, fileType, gaStartTime, thumbnail, fileExtension, license, name, nsfw, thumbnailFileName, thumbnailFilePath, thumbnailFileType, title, claimRecord, metadata, publishResult; // record the start time of the request gaStartTime = Date.now(); @@ -57,19 +59,22 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res) // check channel authorization authenticateUser(channelName, channelId, channelPassword, user) .then(({ channelName, channelClaimId }) => { - return db.Claim.findOne({ - where: { - name, - channelName, - }, - }); + return chainquery.claim.queries.resolveClaimInChannel(name, channelClaimId).then(claim => claim.dataValues); }) .then(claim => { - return resolveUri(`${claim.name}#${claim.claimId}`); + claimRecord = claim; + + if (!files.file) { + return Promise.all([ + db.File.findOne({ where: { name, claimId: claim.claim_id } }), + resolveUri(`${claim.name}#${claim.claim_id}`), + ]); + } + + return [null, null]; }) - .then(fullClaim => { - claimRecord = fullClaim; - logger.info('fullClaim', fullClaim); + .then(([fileResult, resolution]) => { + metadata = Object.assign({}, { title : claimRecord.title, description: claimRecord.description, @@ -89,9 +94,9 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res) if (files.file) { publishParams['file_path'] = filePath; } else { - fileName = fullClaim.file_name; - fileType = fullClaim.mime_type; - publishParams['sources'] = fullClaim.claim.value.stream.source; + fileName = fileResult.fileName; + fileType = fileResult.fileType; + publishParams['sources'] = resolution.claim.value.stream.source; } // publish the thumbnail, if one exists if (thumbnailFileName) { @@ -103,26 +108,44 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res) const fp = files && files.file && files.file.path ? files.file.path : undefined; return publish(publishParams, fileName, fileType, fp); }) - .then(claimData => { - // this may need to happen in ../publish/index.js as well - if (claimData.error) { - res.status(400).json({ - success: false, - message: claimData.message, + .then(result => { + publishResult = result; + + if (channelName) { + return chainquery.claim.queries.getShortClaimIdFromLongClaimId(result.certificateId, channelName); + } else { + return chainquery.claim.queries.getShortClaimIdFromLongClaimId(result.claimId, name, result).catch(error => { + return result.claimId.slice(0, 1); }); } - const {claimId} = claimData; + }) + .then(shortId => { + let canonicalUrl; + if (channelName) { + canonicalUrl = createCanonicalLink({ asset: { ...publishResult, channelShortId: shortId } }); + } else { + canonicalUrl = createCanonicalLink({ asset: { ...publishResult, shortId } }) + } + + if (publishResult.error) { + res.status(400).json({ + success: false, + message: publishResult.message, + }); + } + + const {claimId} = publishResult; res.status(200).json({ success: true, message: 'update successful', data : { name, - channelName, - channelId: claimData.certificateId, claimId, - url : `${details.host}/${claimId}/${name}`, // for backwards compatability with app - showUrl : `${details.host}/${claimId}/${name}`, - claimData, + url : `${details.host}${canonicalUrl}`, // for backwards compatability with app + showUrl : `${details.host}${canonicalUrl}`, + serveUrl: `${details.host}${canonicalUrl}${fileExtension}`, + pushTo : canonicalUrl, + claimData: publishResult, }, }); // record the publish end time and send to google analytics From d35f48a90e7ce450ed71da40013c10cca676b731 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Wed, 7 Nov 2018 10:06:03 -0500 Subject: [PATCH 42/46] fix prompt logic --- client/src/containers/PublishTool/view.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/containers/PublishTool/view.jsx b/client/src/containers/PublishTool/view.jsx index 79d82a1f..86e4c2b2 100644 --- a/client/src/containers/PublishTool/view.jsx +++ b/client/src/containers/PublishTool/view.jsx @@ -8,7 +8,7 @@ import { SAVE } from '../../constants/confirmation_messages'; class PublishTool extends React.Component { render () { - const {disabled, file, isUpdate, hasChanged, uri, status} = this.props; + const {disabled, file, isUpdate, hasChanged, uri, status, location: currentLocation} = this.props; if (disabled) { return ( @@ -24,7 +24,7 @@ class PublishTool extends React.Component { location.pathname === '/' ? true : SAVE} + message={(location) => location.pathname === currentLocation.pathname ? false : SAVE} /> From cb28ad5c6729cf322b072d9544aa26700327c76e Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Wed, 7 Nov 2018 17:51:06 -0500 Subject: [PATCH 43/46] fix video updates --- .../DropzoneInstructionsDisplay/index.jsx | 7 +- .../components/DropzonePreviewImage/index.jsx | 1 + client/src/containers/Dropzone/index.js | 3 +- client/src/containers/Dropzone/view.jsx | 108 +++++++++--------- .../controllers/api/claim/publish/publish.js | 20 ++-- server/controllers/api/claim/update/index.js | 60 ++++++++-- server/models/utils/createFileRecordData.js | 6 +- 7 files changed, 133 insertions(+), 72 deletions(-) diff --git a/client/src/components/DropzoneInstructionsDisplay/index.jsx b/client/src/components/DropzoneInstructionsDisplay/index.jsx index 3f13c69f..eb49ae98 100644 --- a/client/src/components/DropzoneInstructionsDisplay/index.jsx +++ b/client/src/components/DropzoneInstructionsDisplay/index.jsx @@ -2,11 +2,14 @@ import React from 'react'; import FormFeedbackDisplay from '@components/FormFeedbackDisplay'; import Row from '@components/Row'; -const DropzoneInstructionsDisplay = ({fileError}) => { +const DropzoneInstructionsDisplay = ({fileError, message}) => { + if (!message) { + message = 'Drag & drop image or video here to publish'; + } return (
-

Drag & drop image or video here to publish

+

{message}

OR

diff --git a/client/src/components/DropzonePreviewImage/index.jsx b/client/src/components/DropzonePreviewImage/index.jsx index d62abaeb..a3957646 100644 --- a/client/src/components/DropzonePreviewImage/index.jsx +++ b/client/src/components/DropzonePreviewImage/index.jsx @@ -12,6 +12,7 @@ class PublishPreview extends React.Component { componentDidMount () { const { isUpdate, sourceUrl, file } = this.props; if (isUpdate && sourceUrl) { + console.log('setting sourceUrl:', sourceUrl); this.setState({ imgSource: sourceUrl }); } else { this.setPreviewImageSource(file); diff --git a/client/src/containers/Dropzone/index.js b/client/src/containers/Dropzone/index.js index 842973f6..155f802f 100644 --- a/client/src/containers/Dropzone/index.js +++ b/client/src/containers/Dropzone/index.js @@ -13,7 +13,8 @@ const mapStateToProps = ({ show, publish: { file, thumbnail, fileError, isUpdate if (isUpdate) { asset = selectAsset(show); if (asset) { - if (asset.claimData.fileExt === 'mp4') { + obj.fileExt = asset.claimData.contentType.split('/')[1]; + if (obj.fileExt === 'mp4') { obj.sourceUrl = asset.claimData.thumbnail ? asset.claimData.thumbnail : defaultThumbnail; } else { ({claimData: {fileExt, outpoint}} = asset); diff --git a/client/src/containers/Dropzone/view.jsx b/client/src/containers/Dropzone/view.jsx index 6de31276..0e2e50d3 100644 --- a/client/src/containers/Dropzone/view.jsx +++ b/client/src/containers/Dropzone/view.jsx @@ -82,61 +82,65 @@ class Dropzone extends React.Component { } render () { const { dragOver, mouseOver, dimPreview } = this.state; - const { file, thumbnail, fileError, isUpdate, sourceUrl } = this.props; + const { file, thumbnail, fileError, isUpdate, sourceUrl, fileExt } = this.props; return ( -
-
- -
-
- {file || isUpdate ? ( -
- {file ? ( - - ) : ( - - )} -
- { dragOver ? : null } - { mouseOver ? ( - + {isUpdate && fileExt === 'mp4' && (

Video thumbnail:

)} +
+
+ +
+
+ {file || isUpdate ? ( +
+ {file ? ( + - ) : null } + ) : ( + + )} +
+ { dragOver ? : null } + { mouseOver ? ( + + ) : null } +
-
- ) : ( - dragOver ? : ( - - ) - )} + ) : ( + dragOver ? : ( + + ) + )} +
); diff --git a/server/controllers/api/claim/publish/publish.js b/server/controllers/api/claim/publish/publish.js index 900093e3..7fa7fbd5 100644 --- a/server/controllers/api/claim/publish/publish.js +++ b/server/controllers/api/claim/publish/publish.js @@ -5,11 +5,11 @@ const { createFileRecordDataAfterPublish } = require('../../../../models/utils/c const { createClaimRecordDataAfterPublish } = require('../../../../models/utils/createClaimRecordData.js'); const deleteFile = require('./deleteFile.js'); -const publish = async (publishParams, fileName, fileType, filePath) => { +const publish = async (publishParams, fileName, fileType) => { let publishResults; let channel; let fileRecord; - let newFile = Boolean(filePath); + let newFile = Boolean(publishParams.file_path); try { publishResults = await publishClaim(publishParams); @@ -33,22 +33,28 @@ const publish = async (publishParams, fileName, fileType, filePath) => { const {claimId} = claimRecord; const upsertCriteria = {name: publishParams.name, claimId}; if (newFile) { + // this is the problem + // fileRecord = await createFileRecordDataAfterPublish(fileName, fileType, publishParams, publishResults); } else { - fileRecord = await db.File.findOne({where: {claimId}}); + fileRecord = await db.File.findOne({where: {claimId}}).then(result => result.dataValues); } + logger.info('fileRecord:', fileRecord); + logger.info('claimRecord:', claimRecord); + logger.info('upsertCriteria:', upsertCriteria); + const [file, claim] = await Promise.all([ db.upsert(db.File, fileRecord, upsertCriteria, 'File'), db.upsert(db.Claim, claimRecord, upsertCriteria, 'Claim'), ]); - logger.info('File and Claim records successfully created'); + logger.info(`File and Claim records successfully created (${publishParams.name})`); await Promise.all([ file.setClaim(claim), claim.setFile(file), ]); - logger.info('File and Claim records successfully associated'); + logger.info(`File and Claim records successfully associated (${publishParams.name})`); return Object.assign({}, claimRecord, {outpoint}); } catch (err) { @@ -56,8 +62,8 @@ const publish = async (publishParams, fileName, fileType, filePath) => { // this needs work logger.info('publish/publish err:', err); const error = typeof err === 'string' ? JSON.parse(err) : err; - if (filePath) { - await deleteFile(filePath); + if (publishParams.file_path) { + await deleteFile(publishParams.file_path); } const message = error.error && error.error.message ? error.error.message : 'Unknown publish error'; return { diff --git a/server/controllers/api/claim/update/index.js b/server/controllers/api/claim/update/index.js index 7f8588f8..9cb82822 100644 --- a/server/controllers/api/claim/update/index.js +++ b/server/controllers/api/claim/update/index.js @@ -25,6 +25,13 @@ const updateMetadata = ({nsfw, license, title, description}) => { return update; }; +const rando = () => { + let text = ''; + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + for (let i = 0; i < 6; i += 1) text += possible.charAt(Math.floor(Math.random() * 62)); + return text; +}; + const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res) => { // logging logger.info('Claim update request:', { @@ -44,7 +51,27 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res) } // define variables - let channelName, channelId, channelPassword, description, fileName, filePath, fileType, gaStartTime, thumbnail, fileExtension, license, name, nsfw, thumbnailFileName, thumbnailFilePath, thumbnailFileType, title, claimRecord, metadata, publishResult; + let channelName, + channelId, + channelPassword, + description, + fileName, + filePath, + fileType, + gaStartTime, + thumbnail, + fileExtension, + license, + name, + nsfw, + thumbnailFileName, + thumbnailFilePath, + thumbnailFileType, + title, + claimRecord, + metadata, + publishResult, + thumbnailUpdate = false; // record the start time of the request gaStartTime = Date.now(); @@ -59,12 +86,19 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res) // check channel authorization authenticateUser(channelName, channelId, channelPassword, user) .then(({ channelName, channelClaimId }) => { + if (!channelId) { + channelId = channelClaimId; + } return chainquery.claim.queries.resolveClaimInChannel(name, channelClaimId).then(claim => claim.dataValues); }) .then(claim => { claimRecord = claim; + if (claimRecord.content_type === 'video/mp4' && files.file) { + thumbnailUpdate = true; + } + logger.info('claimRecord:', claimRecord); - if (!files.file) { + if (!files.file || thumbnailUpdate) { return Promise.all([ db.File.findOne({ where: { name, claimId: claim.claim_id } }), resolveUri(`${claim.name}#${claim.claim_id}`), @@ -91,18 +125,26 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res) channel_id : channelId, metadata, }; + if (files.file) { - publishParams['file_path'] = filePath; + if (thumbnailUpdate) { + // publish new thumbnail + const newThumbnailName = `${name}-${rando()}`; + const newThumbnailParams = createThumbnailPublishParams(filePath, newThumbnailName, license, nsfw); + newThumbnailParams['file_path'] = filePath; + logger.info('newThumbnailParams:', newThumbnailParams); + publish(newThumbnailParams, fileName, fileType); + + publishParams['sources'] = resolution.claim.value.stream.source; + publishParams['thumbnail'] = `${details.host}/${newThumbnailParams.channel_name}:${newThumbnailParams.channel_id}/${newThumbnailName}-thumb.jpg`; + } else { + publishParams['file_path'] = filePath; + } } else { fileName = fileResult.fileName; fileType = fileResult.fileType; publishParams['sources'] = resolution.claim.value.stream.source; - } - // publish the thumbnail, if one exists - if (thumbnailFileName) { - const thumbnailPublishParams = createThumbnailPublishParams(thumbnailFilePath, name, license, nsfw); - publish(thumbnailPublishParams, thumbnailFileName, thumbnailFileType); - publishParams['thumbnail'] = `${details.host}/${channelName}:${channelId}/${name}-thumb.jpg`; + publishParams['thumbnail'] = claimRecord.thumbnail_url; } const fp = files && files.file && files.file.path ? files.file.path : undefined; diff --git a/server/models/utils/createFileRecordData.js b/server/models/utils/createFileRecordData.js index fdcb9ce0..54d7fc46 100644 --- a/server/models/utils/createFileRecordData.js +++ b/server/models/utils/createFileRecordData.js @@ -47,7 +47,7 @@ async function createFileRecordDataAfterPublish (fileName, fileType, publishPara width: fileWidth, } = await getMediaDimensions(fileType, filePath); - return { + const obj = { name, claimId, outpoint: `${txid}:${nout}`, @@ -57,6 +57,10 @@ async function createFileRecordDataAfterPublish (fileName, fileType, publishPara filePath, fileType, }; + + console.log('createFileRecordDataAfterPublish return:', obj); + + return obj; } module.exports = { From e970bb9e0daaeb7fe126e530e93094e049c0c4ca Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Thu, 8 Nov 2018 11:15:45 -0500 Subject: [PATCH 44/46] get thumbnail_url in getClaimData --- server/utils/getClaimData.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/utils/getClaimData.js b/server/utils/getClaimData.js index f247a4d3..2e77a4ee 100644 --- a/server/utils/getClaimData.js +++ b/server/utils/getClaimData.js @@ -25,7 +25,7 @@ module.exports = async (data) => { claimId: data.claim_id || data.claimId, fileExt: data.generated_extension || data.fileExt, description: data.description, - thumbnail: data.generated_thumbnail || data.thumbnail, + thumbnail: data.generated_thumbnail || data.thumbnail_url || data.thumbnail, outpoint: data.transaction_hash_id || data.outpoint, host, }) From 0fa9bc831edaeb1c9498f3c797c2ad6468dc4ed8 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Thu, 8 Nov 2018 14:05:06 -0500 Subject: [PATCH 45/46] disable video thumbnail edits; remove console logs --- .../components/DropzonePreviewImage/index.jsx | 1 - client/src/containers/Dropzone/view.jsx | 111 +++++++++--------- .../controllers/api/claim/publish/publish.js | 4 - server/controllers/api/claim/update/index.js | 2 - server/models/utils/createFileRecordData.js | 6 +- 5 files changed, 58 insertions(+), 66 deletions(-) diff --git a/client/src/components/DropzonePreviewImage/index.jsx b/client/src/components/DropzonePreviewImage/index.jsx index a3957646..d62abaeb 100644 --- a/client/src/components/DropzonePreviewImage/index.jsx +++ b/client/src/components/DropzonePreviewImage/index.jsx @@ -12,7 +12,6 @@ class PublishPreview extends React.Component { componentDidMount () { const { isUpdate, sourceUrl, file } = this.props; if (isUpdate && sourceUrl) { - console.log('setting sourceUrl:', sourceUrl); this.setState({ imgSource: sourceUrl }); } else { this.setPreviewImageSource(file); diff --git a/client/src/containers/Dropzone/view.jsx b/client/src/containers/Dropzone/view.jsx index 0e2e50d3..436bac99 100644 --- a/client/src/containers/Dropzone/view.jsx +++ b/client/src/containers/Dropzone/view.jsx @@ -85,63 +85,66 @@ class Dropzone extends React.Component { const { file, thumbnail, fileError, isUpdate, sourceUrl, fileExt } = this.props; return (
- {isUpdate && fileExt === 'mp4' && (

Video thumbnail:

)} -
-
- -
-
- {file || isUpdate ? ( -
- {file ? ( - - ) : ( - - )} -
- { dragOver ? : null } - { mouseOver ? ( - Video updates are currently disabled. This feature will be available soon. You can edit metadata.

+ ) : ( +
+
+ +
+
+ {file || isUpdate ? ( +
+ {file ? ( + - ) : null } + ) : ( + + )} +
+ { dragOver ? : null } + { mouseOver ? ( + + ) : null } +
-
- ) : ( - dragOver ? : ( - - ) - )} + ) : ( + dragOver ? : ( + + ) + )} +
-
+ )}
); } diff --git a/server/controllers/api/claim/publish/publish.js b/server/controllers/api/claim/publish/publish.js index 7fa7fbd5..b0589ece 100644 --- a/server/controllers/api/claim/publish/publish.js +++ b/server/controllers/api/claim/publish/publish.js @@ -40,10 +40,6 @@ const publish = async (publishParams, fileName, fileType) => { fileRecord = await db.File.findOne({where: {claimId}}).then(result => result.dataValues); } - logger.info('fileRecord:', fileRecord); - logger.info('claimRecord:', claimRecord); - logger.info('upsertCriteria:', upsertCriteria); - const [file, claim] = await Promise.all([ db.upsert(db.File, fileRecord, upsertCriteria, 'File'), db.upsert(db.Claim, claimRecord, upsertCriteria, 'Claim'), diff --git a/server/controllers/api/claim/update/index.js b/server/controllers/api/claim/update/index.js index 9cb82822..8cc5743c 100644 --- a/server/controllers/api/claim/update/index.js +++ b/server/controllers/api/claim/update/index.js @@ -96,7 +96,6 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res) if (claimRecord.content_type === 'video/mp4' && files.file) { thumbnailUpdate = true; } - logger.info('claimRecord:', claimRecord); if (!files.file || thumbnailUpdate) { return Promise.all([ @@ -132,7 +131,6 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res) const newThumbnailName = `${name}-${rando()}`; const newThumbnailParams = createThumbnailPublishParams(filePath, newThumbnailName, license, nsfw); newThumbnailParams['file_path'] = filePath; - logger.info('newThumbnailParams:', newThumbnailParams); publish(newThumbnailParams, fileName, fileType); publishParams['sources'] = resolution.claim.value.stream.source; diff --git a/server/models/utils/createFileRecordData.js b/server/models/utils/createFileRecordData.js index 54d7fc46..fdcb9ce0 100644 --- a/server/models/utils/createFileRecordData.js +++ b/server/models/utils/createFileRecordData.js @@ -47,7 +47,7 @@ async function createFileRecordDataAfterPublish (fileName, fileType, publishPara width: fileWidth, } = await getMediaDimensions(fileType, filePath); - const obj = { + return { name, claimId, outpoint: `${txid}:${nout}`, @@ -57,10 +57,6 @@ async function createFileRecordDataAfterPublish (fileName, fileType, publishPara filePath, fileType, }; - - console.log('createFileRecordDataAfterPublish return:', obj); - - return obj; } module.exports = { From c75987ef9d1fd01bcd485c0e2d8744d5fa74a3c6 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Fri, 9 Nov 2018 09:49:03 -0500 Subject: [PATCH 46/46] cleanup --- client/src/containers/Dropzone/index.js | 9 +++++---- server/controllers/api/claim/abandon/index.js | 4 ++-- server/controllers/api/claim/update/index.js | 4 ++-- server/utils/fetchClaimData.js | 12 +++++++++--- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/client/src/containers/Dropzone/index.js b/client/src/containers/Dropzone/index.js index 155f802f..dfd0f8cd 100644 --- a/client/src/containers/Dropzone/index.js +++ b/client/src/containers/Dropzone/index.js @@ -12,13 +12,14 @@ const mapStateToProps = ({ show, publish: { file, thumbnail, fileError, isUpdate let asset, name, claimId, fileExt, outpoint, sourceUrl; if (isUpdate) { asset = selectAsset(show); + const { claimData } = asset; if (asset) { - obj.fileExt = asset.claimData.contentType.split('/')[1]; + obj.fileExt = claimData.contentType.split('/')[1]; if (obj.fileExt === 'mp4') { - obj.sourceUrl = asset.claimData.thumbnail ? asset.claimData.thumbnail : defaultThumbnail; + obj.sourceUrl = claimData.thumbnail ? claimData.thumbnail : defaultThumbnail; } else { - ({claimData: {fileExt, outpoint}} = asset); - obj.sourceUrl = `${createCanonicalLink({ asset: asset.claimData })}.${fileExt}?${outpoint}`; + ({fileExt, outpoint} = claimData); + obj.sourceUrl = `${createCanonicalLink({ asset: claimData })}.${fileExt}?${outpoint}`; } } } diff --git a/server/controllers/api/claim/abandon/index.js b/server/controllers/api/claim/abandon/index.js index c557afe0..08a6505f 100644 --- a/server/controllers/api/claim/abandon/index.js +++ b/server/controllers/api/claim/abandon/index.js @@ -1,6 +1,6 @@ const logger = require('winston'); -const db = require('../../../../models'); -const { abandonClaim } = require('../../../../lbrynet'); +const db = require('server/models'); +const { abandonClaim } = require('server/lbrynet'); const deleteFile = require('../publish/deleteFile.js'); const authenticateUser = require('../publish/authentication.js'); diff --git a/server/controllers/api/claim/update/index.js b/server/controllers/api/claim/update/index.js index 8cc5743c..c1df4fb8 100644 --- a/server/controllers/api/claim/update/index.js +++ b/server/controllers/api/claim/update/index.js @@ -1,7 +1,7 @@ const logger = require('winston'); -const db = require('../../../../models'); +const db = require('server/models'); const { details, publishing: { disabled, disabledMessage, primaryClaimAddress } } = require('@config/siteConfig'); -const { resolveUri } = require('../../../../lbrynet'); +const { resolveUri } = require('server/lbrynet'); const { sendGATimingEvent } = require('../../../../utils/googleAnalytics.js'); const { handleErrorResponse } = require('../../../utils/errorHandlers.js'); const publish = require('../publish/publish.js'); diff --git a/server/utils/fetchClaimData.js b/server/utils/fetchClaimData.js index e278a2e8..aeaec8f3 100644 --- a/server/utils/fetchClaimData.js +++ b/server/utils/fetchClaimData.js @@ -10,9 +10,15 @@ const fetchClaimData = async (params) => { db.Claim.resolveClaim(name, claimId).catch(() => {}), ]); - if (!cq && !local) return null; - if (cq && cq.name === name && !local) return cq; - if (local && local.name === name && !cq) return local; + if (!cq && !local) { + return null; + } + if (cq && cq.name === name && !local) { + return cq; + } + if (local && local.name === name && !cq) { + return local; + } return local.updatedAt > cq.modified_at ? local : cq; };