From 16bcaf7cebcc8c34c7d14343da4291f1e505dde7 Mon Sep 17 00:00:00 2001 From: jessop Date: Fri, 28 Jun 2019 13:00:29 -0400 Subject: [PATCH 1/2] updates in component state updates work asset upload works channelForm component --- src/ui/component/channelForm/index.js | 34 +++ src/ui/component/channelForm/view.jsx | 208 ++++++++++++++++++ src/ui/component/channelThumbnail/view.jsx | 7 +- src/ui/component/common/file-selector.jsx | 2 +- src/ui/component/selectAsset/index.js | 12 + .../selectAsset/thumbnail-broken.png | Bin 0 -> 6680 bytes .../selectAsset/thumbnail-missing.png | Bin 0 -> 2791 bytes src/ui/component/selectAsset/view.jsx | 142 ++++++++++++ src/ui/page/channel/view.jsx | 93 +++++--- src/ui/redux/actions/publish.js | 2 +- static/locales/pl.json | 7 +- 11 files changed, 467 insertions(+), 40 deletions(-) create mode 100644 src/ui/component/channelForm/index.js create mode 100644 src/ui/component/channelForm/view.jsx create mode 100644 src/ui/component/selectAsset/index.js create mode 100644 src/ui/component/selectAsset/thumbnail-broken.png create mode 100644 src/ui/component/selectAsset/thumbnail-missing.png create mode 100644 src/ui/component/selectAsset/view.jsx diff --git a/src/ui/component/channelForm/index.js b/src/ui/component/channelForm/index.js new file mode 100644 index 000000000..61742cd00 --- /dev/null +++ b/src/ui/component/channelForm/index.js @@ -0,0 +1,34 @@ +import { connect } from 'react-redux'; +import { + makeSelectTitleForUri, + makeSelectThumbnailForUri, + makeSelectCoverForUri, + selectCurrentChannelPage, + makeSelectMetadataItemForUri, + doUpdateChannel, + makeSelectAmountForUri, +} from 'lbry-redux'; +import ChannelPage from './view'; + +const select = (state, props) => ({ + title: makeSelectTitleForUri(props.uri)(state), + thumbnail: makeSelectThumbnailForUri(props.uri)(state), + cover: makeSelectCoverForUri(props.uri)(state), + page: selectCurrentChannelPage(state), + description: makeSelectMetadataItemForUri(props.uri, 'description')(state), + website: makeSelectMetadataItemForUri(props.uri, 'website_url')(state), + email: makeSelectMetadataItemForUri(props.uri, 'email')(state), + tags: makeSelectMetadataItemForUri(props.uri, 'tags')(state), + locations: makeSelectMetadataItemForUri(props.uri, 'locations')(state), + languages: makeSelectMetadataItemForUri(props.uri, 'languages')(state), + amount: makeSelectAmountForUri(props.uri)(state), +}); + +const perform = dispatch => ({ + updateChannel: params => dispatch(doUpdateChannel(params)), +}); + +export default connect( + select, + perform +)(ChannelPage); diff --git a/src/ui/component/channelForm/view.jsx b/src/ui/component/channelForm/view.jsx new file mode 100644 index 000000000..418615c06 --- /dev/null +++ b/src/ui/component/channelForm/view.jsx @@ -0,0 +1,208 @@ +// @flow +import React, { useState } from 'react'; +import { parseURI } from 'lbry-redux'; +import { Form, FormField } from 'component/common/form'; +import Button from 'component/button'; + +import SelectAsset from '../../component/selectAsset/view'; + +type Props = { + uri: string, + + title: ?string, + amount: string, + cover: ?string, + thumbnail: ?string, + location: { search: string }, + description: string, + website: string, + email: string, + balance: number, + tags: Array, + locations: Array, + languages: Array, + + updateChannel: any => void, + + updateThumb: string => void, + updateCover: string => void, + setEditing: boolean => void, +}; + +function ChannelForm(props: Props) { + const { + uri, + title, + cover, + description, + website, + email, + thumbnail, + tags, + locations, + languages, + amount, + updateChannel, + setEditing, + updateThumb, + updateCover, + } = props; + const { claimId } = parseURI(uri); + + // fill this in with sdk data + const channelParams = { + website: website, + email: email, + languages: languages || [], + cover: cover, + description: description, + locations: locations || [], + title: title, + thumbnail: thumbnail, + tags: tags || [], + claim_id: claimId, + amount: amount, + }; + + const [params, setParams] = useState(channelParams); + const [bidError, setBidError] = useState(''); + + const MINIMUM_PUBLISH_BID = 0.00000001; + // If a user changes tabs, update the url so it stays on the same page if they refresh. + // We don't want to use links here because we can't animate the tab change and using links + // would alter the Tab label's role attribute, which should stay role="tab" to work with keyboards/screen readers. + + const handleBidChange = (bid: number) => { + const { balance, amount } = props; + const totalAvailableBidAmount = parseFloat(amount) + parseFloat(balance); + setParams({ ...params, amount: bid }); + setBidError(''); + if (bid <= 0.0 || isNaN(bid)) { + setBidError(__('Deposit cannot be 0')); + } else if (totalAvailableBidAmount === bid) { + setBidError(__('Please decrease your deposit to account for transaction fees')); + } else if (totalAvailableBidAmount < bid) { + setBidError(__('Deposit cannot be higher than your balance')); + } else if (bid <= MINIMUM_PUBLISH_BID) { + setBidError(__('Your deposit must be higher')); + } + }; + + const handleThumbnailChange = (url: string) => { + setParams({ ...params, thumbnail: url }); + updateThumb(url); + }; + + const handleCoverChange = (url: string) => { + setParams({ ...params, cover: url }); + updateCover(url); + }; + // TODO clear and bail after submit + return ( +
+
+
+
+

{__('Edit')}

+
+
+
+
+
updateChannel(channelParams)}> +
+ handleThumbnailChange(v)} + currentValue={params.thumbnail} + assetName={'Thumbnail'} + recommended={'(400x400)'} + /> + + handleCoverChange(v)} + currentValue={params.cover} + assetName={'Cover'} + recommended={'(1000x300)'} + /> + + setParams({ ...params, title: e.target.value })} + /> + handleBidChange(parseFloat(event.target.value))} + placeholder={0.1} + // helper={ + // + // } + /> + + setParams({ ...params, website: e.target.value })} + /> + + setParams({ ...params, email: e.target.value })} + /> + + setParams({ ...params, description: text })} + /> +
+
+
+
+
+
+ ); +} + +export default ChannelForm; diff --git a/src/ui/component/channelThumbnail/view.jsx b/src/ui/component/channelThumbnail/view.jsx index df3c24142..383be9ec1 100644 --- a/src/ui/component/channelThumbnail/view.jsx +++ b/src/ui/component/channelThumbnail/view.jsx @@ -8,10 +8,11 @@ type Props = { thumbnail: ?string, uri: string, className?: string, + thumbnailPreview: ?string, }; function ChannelThumbnail(props: Props) { - const { thumbnail, uri, className } = props; + const { thumbnail, uri, className, thumbnailPreview } = props; // Generate a random color class based on the first letter of the channel name const { channelName } = parseURI(uri); @@ -24,8 +25,8 @@ function ChannelThumbnail(props: Props) { [colorClassName]: !thumbnail, })} > - {!thumbnail && } - {thumbnail && } + {!thumbnail && } + {thumbnail && } ); } diff --git a/src/ui/component/common/file-selector.jsx b/src/ui/component/common/file-selector.jsx index 817c2412f..63e45338c 100644 --- a/src/ui/component/common/file-selector.jsx +++ b/src/ui/component/common/file-selector.jsx @@ -12,7 +12,7 @@ type FileFilters = { type Props = { type: string, - currentPath: ?string, + currentPath?: ?string, onFileChosen: (string, string) => void, label?: string, placeholder?: string, diff --git a/src/ui/component/selectAsset/index.js b/src/ui/component/selectAsset/index.js new file mode 100644 index 000000000..3aedb8b3e --- /dev/null +++ b/src/ui/component/selectAsset/index.js @@ -0,0 +1,12 @@ +import { connect } from 'react-redux'; +import { doOpenModal } from 'redux/actions/app'; +import SelectThumbnail from './view'; + +const perform = dispatch => ({ + openModal: (modal, props) => dispatch(doOpenModal(modal, props)), +}); + +export default connect( + null, + perform +)(SelectThumbnail); diff --git a/src/ui/component/selectAsset/thumbnail-broken.png b/src/ui/component/selectAsset/thumbnail-broken.png new file mode 100644 index 0000000000000000000000000000000000000000..f41613f8e40f564e96c1fc2a45b161508f0a80c1 GIT binary patch literal 6680 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 aRY?kut$qEAwvSKZL9B^SimQpqD)=`6pH_ST literal 0 HcmV?d00001 diff --git a/src/ui/component/selectAsset/view.jsx b/src/ui/component/selectAsset/view.jsx new file mode 100644 index 000000000..09a511a4d --- /dev/null +++ b/src/ui/component/selectAsset/view.jsx @@ -0,0 +1,142 @@ +// @flow + +import React, { useState } from 'react'; +import { FormField } from 'component/common/form'; +import FileSelector from 'component/common/file-selector'; +import Button from 'component/button'; +import fs from 'fs'; +import path from 'path'; +import { v4 as uuidv4 } from 'uuid'; + +const filters = [ + { + name: __('Thumbnail Image'), + extensions: ['png', 'jpg', 'jpeg', 'gif'], + }, +]; + +const SOURCE_URL = 'url'; +const SOURCE_UPLOAD = 'upload'; +const SPEECH_READY = 'READY'; +const SPEECH_UPLOADING = 'UPLOADING'; +type Props = { + assetName: string, + currentValue: ?string, + onUpdate: string => void, + recommended: string, +}; + +function SelectAsset(props: Props) { + const { onUpdate, assetName, currentValue, recommended } = props; + const [assetSource, setAssetSource] = useState(SOURCE_URL); + const [pathSelected, setPathSelected] = useState(''); + const [uploadStatus, setUploadStatus] = useState(SPEECH_READY); + + function doUploadAsset(filePath, thumbnailBuffer) { + let thumbnail, fileExt, fileName, fileType; + if (filePath) { + thumbnail = fs.readFileSync(filePath); + fileExt = path.extname(filePath); + fileName = path.basename(filePath); + fileType = `image/${fileExt.slice(1)}`; + } else if (thumbnailBuffer) { + thumbnail = thumbnailBuffer; + fileExt = '.png'; + fileName = 'thumbnail.png'; + fileType = 'image/png'; + } else { + return null; + } + + const uploadError = (error = '') => { + console.log('error', error); + }; + + const setUrl = path => { + setUploadStatus(SPEECH_READY); + onUpdate(path); + setAssetSource(SOURCE_URL); + }; + + setUploadStatus(SPEECH_UPLOADING); + + const data = new FormData(); + const name = uuidv4(); + const file = new File([thumbnail], fileName, { type: fileType }); + data.append('name', name); + data.append('file', file); + + return fetch('https://spee.ch/api/claim/publish', { + method: 'POST', + body: data, + }) + .then(response => response.json()) + .then(json => (json.success ? setUrl(`${json.data.serveUrl}`) : uploadError(json.message))) + .catch(err => uploadError(err.message)); + } + return ( + + + setAssetSource(e.target.value)} + label={__(assetName + ' source')} + > + + + + {assetSource === SOURCE_UPLOAD && ( + <> + {!pathSelected && ( + { + setPathSelected(path); + }} + filters={filters} + /> + )} + {pathSelected && ( +
+ {`...${pathSelected.slice(-18)}`} {uploadStatus}{' '} + {' '} + +
+ )} + + )} + {assetSource === SOURCE_URL && ( + { + onUpdate(e.target.value); + }} + /> + )} +
+
+ ); +} + +export default SelectAsset; diff --git a/src/ui/page/channel/view.jsx b/src/ui/page/channel/view.jsx index db88920ab..907dda7f5 100644 --- a/src/ui/page/channel/view.jsx +++ b/src/ui/page/channel/view.jsx @@ -1,15 +1,18 @@ // @flow -import React from 'react'; +import React, { useState } from 'react'; import { parseURI } from 'lbry-redux'; import Page from 'component/page'; import SubscribeButton from 'component/subscribeButton'; import ShareButton from 'component/shareButton'; import { Tabs, TabList, Tab, TabPanels, TabPanel } from 'component/common/tabs'; import { withRouter } from 'react-router'; +import Button from 'component/button'; import { formatLbryUriForWeb } from 'util/uri'; import ChannelContent from 'component/channelContent'; import ChannelAbout from 'component/channelAbout'; import ChannelThumbnail from 'component/channelThumbnail'; +import ChannelForm from 'component/channelForm'; +import * as ICONS from 'constants/icons'; const PAGE_VIEW_QUERY = `view`; const ABOUT_PAGE = `about`; @@ -23,15 +26,20 @@ type Props = { location: { search: string }, history: { push: string => void }, match: { params: { attribute: ?string } }, + channelIsMine: boolean, }; function ChannelPage(props: Props) { - const { uri, title, cover, history, location, page } = props; + const { uri, title, cover, history, location, page, channelIsMine, thumbnail } = props; const { channelName, claimName, claimId } = parseURI(uri); const { search } = location; const urlParams = new URLSearchParams(search); const currentView = urlParams.get(PAGE_VIEW_QUERY) || undefined; + const [editing, setEditing] = useState(false); + const [thumbPreview, setThumbPreview] = useState(thumbnail); + const [coverPreview, setCoverPreview] = useState(cover); + // If a user changes tabs, update the url so it stays on the same page if they refresh. // We don't want to use links here because we can't animate the tab change and using links // would alter the Tab label's role attribute, which should stay role="tab" to work with keyboards/screen readers. @@ -52,40 +60,59 @@ function ChannelPage(props: Props) {
- {cover && } - -
- - -
-

{title || channelName}

-

- {claimName} - {claimId && `#${claimId}`} -

-
+ {!editing && cover && } + {editing && } + {/* component that offers select/upload */} +
+ {!editing && } + {editing && ( + + )} +

+ {title || channelName} + {channelIsMine && !editing && ( +

+

+ {claimName} + {claimId && `#${claimId}`} +

+ {!editing && ( + + + {__('Content')} + {__('About')} +
+ + +
+
- - - {__('Content')} - {__('About')} -
- - -
-
- - - - - - - - - -
+ + + + + + + + +
+ )} + {editing && ( + setThumbPreview(v)} + updateCover={v => setCoverPreview(v)} + /> + )}
); diff --git a/src/ui/redux/actions/publish.js b/src/ui/redux/actions/publish.js index 63d3263c8..663d26c7d 100644 --- a/src/ui/redux/actions/publish.js +++ b/src/ui/redux/actions/publish.js @@ -345,7 +345,7 @@ export const doPublish = () => (dispatch: Dispatch, getState: () => {}) => { dispatch({ type: ACTIONS.PUBLISH_FAIL }); dispatch(doError(error.message)); }; - + console.log('PP', publishPayload); return Lbry.publish(publishPayload).then(success, failure); }; diff --git a/static/locales/pl.json b/static/locales/pl.json index 617233669..85fb7d220 100644 --- a/static/locales/pl.json +++ b/static/locales/pl.json @@ -286,5 +286,8 @@ "Multi-language support is brand new and incomplete. Switching your language may have unintended consequences.": "Multi-language support is brand new and incomplete. Switching your language may have unintended consequences.", "Wallet": "Wallet", "Home": "Home", - "Following": "Following" -} \ No newline at end of file + "Following": "Following", + "Update ready to install": "Update ready to install", + "Install now": "Install now", + "Edit": "Edit" +} From 0c28dee85e271ba6ed181ba21fb4b4618f462540 Mon Sep 17 00:00:00 2001 From: Sean Yesmunt Date: Tue, 2 Jul 2019 13:54:42 -0400 Subject: [PATCH 2/2] improve channel edit styling --- package.json | 2 +- .../{channelForm => channelEdit}/index.js | 0 src/ui/component/channelEdit/view.jsx | 204 +++++++++++++++++ src/ui/component/channelForm/view.jsx | 208 ------------------ src/ui/component/selectAsset/view.jsx | 4 +- src/ui/page/channel/view.jsx | 59 +++-- src/ui/scss/component/_channel.scss | 6 + static/locales/en.json | 23 +- yarn.lock | 4 +- 9 files changed, 265 insertions(+), 245 deletions(-) rename src/ui/component/{channelForm => channelEdit}/index.js (100%) create mode 100644 src/ui/component/channelEdit/view.jsx delete mode 100644 src/ui/component/channelForm/view.jsx diff --git a/package.json b/package.json index a45af625f..e8569647a 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,7 @@ "jsmediatags": "^3.8.1", "json-loader": "^0.5.4", "lbry-format": "https://github.com/lbryio/lbry-format.git", - "lbry-redux": "lbryio/lbry-redux#9a676ee311d573b84d11f402d918aeee77be76e1", + "lbry-redux": "lbryio/lbry-redux#efccab44cb025a14fd81ec05ffca0314710c8529", "lbryinc": "lbryio/lbryinc#43d382d9b74d396a581a74d87e4c53105e04f845", "lint-staged": "^7.0.2", "localforage": "^1.7.1", diff --git a/src/ui/component/channelForm/index.js b/src/ui/component/channelEdit/index.js similarity index 100% rename from src/ui/component/channelForm/index.js rename to src/ui/component/channelEdit/index.js diff --git a/src/ui/component/channelEdit/view.jsx b/src/ui/component/channelEdit/view.jsx new file mode 100644 index 000000000..1ecae9291 --- /dev/null +++ b/src/ui/component/channelEdit/view.jsx @@ -0,0 +1,204 @@ +// @flow +import React, { useState } from 'react'; +import { parseURI } from 'lbry-redux'; +import { Form, FormField } from 'component/common/form'; +import Button from 'component/button'; + +import SelectAsset from '../selectAsset/view'; + +type Props = { + uri: string, + + title: ?string, + amount: string, + cover: ?string, + thumbnail: ?string, + location: { search: string }, + description: string, + website: string, + email: string, + balance: number, + tags: Array, + locations: Array, + languages: Array, + + updateChannel: any => void, + + updateThumb: string => void, + updateCover: string => void, + setEditing: boolean => void, +}; + +function ChannelForm(props: Props) { + const { + uri, + title, + cover, + description, + website, + email, + thumbnail, + tags, + locations, + languages, + amount, + updateChannel, + setEditing, + updateThumb, + updateCover, + } = props; + const { claimId } = parseURI(uri); + + // fill this in with sdk data + const channelParams = { + website: website, + email: email, + languages: languages || [], + cover: cover, + description: description, + locations: locations || [], + title: title, + thumbnail: thumbnail, + tags: tags || [], + claim_id: claimId, + amount: amount, + }; + + const [params, setParams] = useState(channelParams); + const [bidError, setBidError] = useState(''); + + const MINIMUM_PUBLISH_BID = 0.00000001; + // If a user changes tabs, update the url so it stays on the same page if they refresh. + // We don't want to use links here because we can't animate the tab change and using links + // would alter the Tab label's role attribute, which should stay role="tab" to work with keyboards/screen readers. + + const handleBidChange = (bid: number) => { + const { balance, amount } = props; + const totalAvailableBidAmount = parseFloat(amount) + parseFloat(balance); + setParams({ ...params, amount: bid }); + setBidError(''); + if (bid <= 0.0 || isNaN(bid)) { + setBidError(__('Deposit cannot be 0')); + } else if (totalAvailableBidAmount === bid) { + setBidError(__('Please decrease your deposit to account for transaction fees')); + } else if (totalAvailableBidAmount < bid) { + setBidError(__('Deposit cannot be higher than your balance')); + } else if (bid <= MINIMUM_PUBLISH_BID) { + setBidError(__('Your deposit must be higher')); + } + }; + + const handleThumbnailChange = (url: string) => { + setParams({ ...params, thumbnail: url }); + updateThumb(url); + }; + + const handleCoverChange = (url: string) => { + setParams({ ...params, cover: url }); + updateCover(url); + }; + // TODO clear and bail after submit + return ( +
+
+

{__('We can explain...')}

+

+ {__( + "We know this page won't win any design awards, we just wanted to release a very very very basic version that just barely kinda works so people can use it right now. There is a much nicer version being worked on." + )} +

+
+
updateChannel(channelParams)}> +
+ handleThumbnailChange(v)} + currentValue={params.thumbnail} + assetName={'Thumbnail'} + recommended={'(400x400)'} + /> + + handleCoverChange(v)} + currentValue={params.cover} + assetName={'Cover'} + recommended={'(1000x300)'} + /> + + setParams({ ...params, title: e.target.value })} + /> + handleBidChange(parseFloat(event.target.value))} + placeholder={0.1} + /> + + setParams({ ...params, website: e.target.value })} + /> + + setParams({ ...params, email: e.target.value })} + /> + + setParams({ ...params, description: text })} + /> +
+
+
+
+
+ ); +} + +export default ChannelForm; diff --git a/src/ui/component/channelForm/view.jsx b/src/ui/component/channelForm/view.jsx deleted file mode 100644 index 418615c06..000000000 --- a/src/ui/component/channelForm/view.jsx +++ /dev/null @@ -1,208 +0,0 @@ -// @flow -import React, { useState } from 'react'; -import { parseURI } from 'lbry-redux'; -import { Form, FormField } from 'component/common/form'; -import Button from 'component/button'; - -import SelectAsset from '../../component/selectAsset/view'; - -type Props = { - uri: string, - - title: ?string, - amount: string, - cover: ?string, - thumbnail: ?string, - location: { search: string }, - description: string, - website: string, - email: string, - balance: number, - tags: Array, - locations: Array, - languages: Array, - - updateChannel: any => void, - - updateThumb: string => void, - updateCover: string => void, - setEditing: boolean => void, -}; - -function ChannelForm(props: Props) { - const { - uri, - title, - cover, - description, - website, - email, - thumbnail, - tags, - locations, - languages, - amount, - updateChannel, - setEditing, - updateThumb, - updateCover, - } = props; - const { claimId } = parseURI(uri); - - // fill this in with sdk data - const channelParams = { - website: website, - email: email, - languages: languages || [], - cover: cover, - description: description, - locations: locations || [], - title: title, - thumbnail: thumbnail, - tags: tags || [], - claim_id: claimId, - amount: amount, - }; - - const [params, setParams] = useState(channelParams); - const [bidError, setBidError] = useState(''); - - const MINIMUM_PUBLISH_BID = 0.00000001; - // If a user changes tabs, update the url so it stays on the same page if they refresh. - // We don't want to use links here because we can't animate the tab change and using links - // would alter the Tab label's role attribute, which should stay role="tab" to work with keyboards/screen readers. - - const handleBidChange = (bid: number) => { - const { balance, amount } = props; - const totalAvailableBidAmount = parseFloat(amount) + parseFloat(balance); - setParams({ ...params, amount: bid }); - setBidError(''); - if (bid <= 0.0 || isNaN(bid)) { - setBidError(__('Deposit cannot be 0')); - } else if (totalAvailableBidAmount === bid) { - setBidError(__('Please decrease your deposit to account for transaction fees')); - } else if (totalAvailableBidAmount < bid) { - setBidError(__('Deposit cannot be higher than your balance')); - } else if (bid <= MINIMUM_PUBLISH_BID) { - setBidError(__('Your deposit must be higher')); - } - }; - - const handleThumbnailChange = (url: string) => { - setParams({ ...params, thumbnail: url }); - updateThumb(url); - }; - - const handleCoverChange = (url: string) => { - setParams({ ...params, cover: url }); - updateCover(url); - }; - // TODO clear and bail after submit - return ( -
-
-
-
-

{__('Edit')}

-
-
-
-
-
updateChannel(channelParams)}> -
- handleThumbnailChange(v)} - currentValue={params.thumbnail} - assetName={'Thumbnail'} - recommended={'(400x400)'} - /> - - handleCoverChange(v)} - currentValue={params.cover} - assetName={'Cover'} - recommended={'(1000x300)'} - /> - - setParams({ ...params, title: e.target.value })} - /> - handleBidChange(parseFloat(event.target.value))} - placeholder={0.1} - // helper={ - // - // } - /> - - setParams({ ...params, website: e.target.value })} - /> - - setParams({ ...params, email: e.target.value })} - /> - - setParams({ ...params, description: text })} - /> -
-
-
-
-
-
- ); -} - -export default ChannelForm; diff --git a/src/ui/component/selectAsset/view.jsx b/src/ui/component/selectAsset/view.jsx index 09a511a4d..9596bd9d3 100644 --- a/src/ui/component/selectAsset/view.jsx +++ b/src/ui/component/selectAsset/view.jsx @@ -6,7 +6,7 @@ import FileSelector from 'component/common/file-selector'; import Button from 'component/button'; import fs from 'fs'; import path from 'path'; -import { v4 as uuidv4 } from 'uuid'; +import uuid from 'uuid/v4'; const filters = [ { @@ -61,7 +61,7 @@ function SelectAsset(props: Props) { setUploadStatus(SPEECH_UPLOADING); const data = new FormData(); - const name = uuidv4(); + const name = uuid(); const file = new File([thumbnail], fileName, { type: fileType }); data.append('name', name); data.append('file', file); diff --git a/src/ui/page/channel/view.jsx b/src/ui/page/channel/view.jsx index 907dda7f5..afcd2d665 100644 --- a/src/ui/page/channel/view.jsx +++ b/src/ui/page/channel/view.jsx @@ -11,7 +11,7 @@ import { formatLbryUriForWeb } from 'util/uri'; import ChannelContent from 'component/channelContent'; import ChannelAbout from 'component/channelAbout'; import ChannelThumbnail from 'component/channelThumbnail'; -import ChannelForm from 'component/channelForm'; +import ChannelEdit from 'component/channelEdit'; import * as ICONS from 'constants/icons'; const PAGE_VIEW_QUERY = `view`; @@ -43,7 +43,7 @@ function ChannelPage(props: Props) { // If a user changes tabs, update the url so it stays on the same page if they refresh. // We don't want to use links here because we can't animate the tab change and using links // would alter the Tab label's role attribute, which should stay role="tab" to work with keyboards/screen readers. - const tabIndex = currentView === ABOUT_PAGE ? 1 : 0; + const tabIndex = currentView === ABOUT_PAGE || editing ? 1 : 0; const onTabChange = newTabIndex => { let url = formatLbryUriForWeb(uri); let search = '?'; @@ -75,7 +75,7 @@ function ChannelPage(props: Props) {

{title || channelName} {channelIsMine && !editing && ( -

@@ -84,35 +84,34 @@ function ChannelPage(props: Props) {

- {!editing && ( - - - {__('Content')} - {__('About')} -
- - -
-
+ + + {__('Content')} + {editing ? __('Editing Your Channel') : __('About')} +
+ + +
+
- - - - - + + + + + + {editing ? ( + setThumbPreview(v)} + updateCover={v => setCoverPreview(v)} + /> + ) : ( - - -
- )} - {editing && ( - setThumbPreview(v)} - updateCover={v => setCoverPreview(v)} - /> - )} + )} + + +
); diff --git a/src/ui/scss/component/_channel.scss b/src/ui/scss/component/_channel.scss index 2d8d7782d..b81abb78e 100644 --- a/src/ui/scss/component/_channel.scss +++ b/src/ui/scss/component/_channel.scss @@ -86,6 +86,12 @@ $metadata-z-index: 1; font-size: 3rem; font-weight: 800; margin-right: var(--spacing-large); + + // Quick hack to get this to work + // We should have a generic style for "header with button next to it" + .button { + margin-left: var(--spacing-medium); + } } .channel__url { diff --git a/static/locales/en.json b/static/locales/en.json index 0572b956c..86ebc1126 100644 --- a/static/locales/en.json +++ b/static/locales/en.json @@ -460,5 +460,24 @@ "LBRY names cannot contain that symbol ($, #, @)": "LBRY names cannot contain that symbol ($, #, @)", "Path copied.": "Path copied.", "Open Folder": "Open Folder", - "Create Backup": "Create Backup" -} + "Create Backup": "Create Backup", + "Submit": "Submit", + "Website": "Website", + "aprettygoodsite.com": "aprettygoodsite.com", + "yourstruly@example.com": "yourstruly@example.com", + "Thumbnail source": "Thumbnail source", + "Thumbnail (400x400)": "Thumbnail (400x400)", + "https://example.com/image.png": "https://example.com/image.png", + "Cover source": "Cover source", + "Cover (1000x300)": "Cover (1000x300)", + "Editing": "Editing", + "Edit Your Channel": "Edit Your Channel", + "Editing Your Channel": "Editing Your Channel", + "We can explain... We know this page won't win any design awards, we have a cool idea for channel edits in the future. We just wanted to release a very very very basic version that just barely kinda works so people can use it.": "We can explain... We know this page won't win any design awards, we have a cool idea for channel edits in the future. We just wanted to release a very very very basic version that just barely kinda works so people can use it.", + "We can explain... \n\n We know this page won't win any design awards, we have a cool idea for channel edits in the future. We just wanted to release a very very very basic version that just barely kinda works so people can use it.": "We can explain... \n\n We know this page won't win any design awards, we have a cool idea for channel edits in the future. We just wanted to release a very very very basic version that just barely kinda works so people can use it.", + "We can explain...": "We can explain...", + "We know this page won't win any design awards, we have a cool idea for channel edits in the future. We just wanted to release a very very very basic version that just barely kinda works so people can use": "We know this page won't win any design awards, we have a cool idea for channel edits in the future. We just wanted to release a very very very basic version that just barely kinda works so people can use", + "We know this page won't win any design awards, we just wanted to release a very very very basic version that just barely kinda works so people can use": "We know this page won't win any design awards, we just wanted to release a very very very basic version that just barely kinda works so people can use", + "We know this page won't win any design awards, we just wanted to release a very very very basic version that just barely kinda works so people can use it right now. There is a much nicer version in the works.": "We know this page won't win any design awards, we just wanted to release a very very very basic version that just barely kinda works so people can use it right now. There is a much nicer version in the works.", + "We know this page won't win any design awards, we just wanted to release a very very very basic version that just barely kinda works so people can use it right now. There is a much nicer version being worked on.": "We know this page won't win any design awards, we just wanted to release a very very very basic version that just barely kinda works so people can use it right now. There is a much nicer version being worked on." +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 4225997c4..1cc8f1644 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6646,9 +6646,9 @@ lazy-val@^1.0.3, lazy-val@^1.0.4: yargs "^13.2.2" zstd-codec "^0.1.1" -lbry-redux@lbryio/lbry-redux#9a676ee311d573b84d11f402d918aeee77be76e1: +lbry-redux@lbryio/lbry-redux#efccab44cb025a14fd81ec05ffca0314710c8529: version "0.0.1" - resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/9a676ee311d573b84d11f402d918aeee77be76e1" + resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/efccab44cb025a14fd81ec05ffca0314710c8529" dependencies: proxy-polyfill "0.1.6" reselect "^3.0.0"