Publish revamp (Part 1) (#1593)

* Rearrange fields

* Autocomplete title

* Fix class position

* Hide deposit behind advanced settings

* Redesign additional options

* Redesign price section

* Update price section

* Redesign tags section

* Fix title edit

* Make with dynamic

* Redesign thumbnail section

* Redesign description section

* Resedign file section

* Polish sections

* Adjust help text

* Clear title on form reset

* Adjust price section

* Fix help color in light theme

* Polish

* Mobile adjustments

* More mobile adjustments

* Remove border-bottom from publish rows

* Redesign date section

* Adjust some details

* Adjust clear button

* Adjust channel selector on mobile

* Adjust post save button position

* Adjust browse button color

* Adjust channel picker on mobile

* Eenable announcement page

* Remove file title, remove space, redesign licence section

* Fix edit form, existing claim warning, missing title warning

* Adjust light theme

* Adjust icon collor in button
This commit is contained in:
Rave | 図書館猫 2022-06-03 15:28:12 +02:00 committed by GitHub
parent 9dc03d816a
commit 81eddb2b5d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 806 additions and 451 deletions

View file

@ -78,7 +78,7 @@ class LicenseType extends React.PureComponent<Props> {
{licenseType === OTHER && ( {licenseType === OTHER && (
<fieldset> <fieldset>
<legend>{__('Provide a description and link to your license')}</legend> <div className="form-field__help">{__('Provide a description and link to your license')}</div>
<fieldset-group> <fieldset-group>
<FormField <FormField
label={__('License description')} label={__('License description')}

View file

@ -9,6 +9,7 @@ import LicenseType from './license-type';
import Card from 'component/common/card'; import Card from 'component/common/card';
import SUPPORTED_LANGUAGES from 'constants/supported_languages'; import SUPPORTED_LANGUAGES from 'constants/supported_languages';
import { sortLanguageMap } from 'util/default-languages'; import { sortLanguageMap } from 'util/default-languages';
import PublishBid from 'component/publishBid';
// @if TARGET='app' // @if TARGET='app'
// import ErrorText from 'component/common/error-text'; // import ErrorText from 'component/common/error-text';
@ -39,6 +40,7 @@ function PublishAdditionalOptions(props: Props) {
licenseUrl, licenseUrl,
updatePublishForm, updatePublishForm,
showSchedulingOptions, showSchedulingOptions,
disabled,
// user, // user,
// useLBRYUploader, // useLBRYUploader,
// needsYTAuth, // needsYTAuth,
@ -112,14 +114,20 @@ function PublishAdditionalOptions(props: Props) {
// @endif // @endif
return ( return (
<Card <>
className="card--enable-overflow" <h2 className="card__title">{__('Additional Options')}</h2>
actions={ <Card
<React.Fragment> className="card--enable-overflow card--publish-section card--additional-options"
{!hideSection && ( actions={
<div className={classnames({ 'card--disabled': !name })}> <React.Fragment>
{/* @if TARGET='app' */} {!hideSection && (
{/* {showLbryFirstCheckbox && ( <>
<div className="publish-row">
<PublishBid disabled={disabled} />
</div>
<div className={classnames({ 'card--disabled': !name })}>
{/* @if TARGET='app' */}
{/* {showLbryFirstCheckbox && (
<div className="section"> <div className="section">
<> <>
<FormField <FormField
@ -152,55 +160,57 @@ function PublishAdditionalOptions(props: Props) {
</> </>
</div> </div>
)} */} )} */}
{/* @endif */} {/* @endif */}
<div className="section"> <div className="section">
{!showSchedulingOptions && <PublishReleaseDate />} <div className="publish-row">{!showSchedulingOptions && <PublishReleaseDate />}</div>
<FormField <div className="publish-row">
label={__('Language')} <FormField
type="select" label={__('Language')}
name="content_language" type="select"
value={language} name="content_language"
onChange={(event) => updatePublishForm({ languages: [event.target.value] })} value={language}
> onChange={(event) => updatePublishForm({ languages: [event.target.value] })}
{sortLanguageMap(SUPPORTED_LANGUAGES).map(([langKey, langName]) => ( >
<option key={langKey} value={langKey}> {sortLanguageMap(SUPPORTED_LANGUAGES).map(([langKey, langName]) => (
{langName} <option key={langKey} value={langKey}>
</option> {langName}
))} </option>
</FormField> ))}
</FormField>
</div>
<LicenseType <div className="publish-row">
licenseType={licenseType} <LicenseType
otherLicenseDescription={otherLicenseDescription} licenseType={licenseType}
licenseUrl={licenseUrl} otherLicenseDescription={otherLicenseDescription}
handleLicenseChange={(newLicenseType, newLicenseUrl) => licenseUrl={licenseUrl}
updatePublishForm({ handleLicenseChange={(newLicenseType, newLicenseUrl) =>
licenseType: newLicenseType, updatePublishForm({
licenseUrl: newLicenseUrl, licenseType: newLicenseType,
}) licenseUrl: newLicenseUrl,
} })
handleLicenseDescriptionChange={(event) => }
updatePublishForm({ handleLicenseDescriptionChange={(event) =>
otherLicenseDescription: event.target.value, updatePublishForm({
}) otherLicenseDescription: event.target.value,
} })
handleLicenseUrlChange={(event) => updatePublishForm({ licenseUrl: event.target.value })} }
/> handleLicenseUrlChange={(event) => updatePublishForm({ licenseUrl: event.target.value })}
</div> />
</div>
</div>
</div>
</>
)}
<div className="section__actions">
<Button label={hideSection ? __('Show') : __('Hide')} button="link" onClick={toggleHideSection} />
</div> </div>
)} </React.Fragment>
}
<div className="section__actions"> />
<Button </>
label={hideSection ? __('Additional Options') : __('Hide')}
button="link"
onClick={toggleHideSection}
/>
</div>
</React.Fragment>
}
/>
); );
} }

View file

@ -20,26 +20,30 @@ function PublishDescription(props: Props) {
} }
return ( return (
<Card <>
actions={ {disabled && <h2 className="card__title card__title-disabled">{__('Description')}</h2>}
<FormField {!disabled && <h2 className="card__title">{__('Description')}</h2>}
type={!SIMPLE_SITE && advancedEditor ? 'markdown' : 'textarea'} <Card
name="content_description" className="card--description"
label={__('Description')} actions={
placeholder={__( <FormField
'What is your content about? Use this space to include any other relevant details you may like to share about your content and channel.' type={!SIMPLE_SITE && advancedEditor ? 'markdown' : 'textarea'}
)} name="content_description"
value={description} placeholder={__(
disabled={disabled} 'What is your content about? Use this space to include any other relevant details you may like to share about your content and channel.'
onChange={value => )}
updatePublishForm({ description: !SIMPLE_SITE && advancedEditor ? value : value.target.value }) value={description}
} disabled={disabled}
quickActionLabel={!SIMPLE_SITE && (advancedEditor ? __('Simple Editor') : __('Advanced Editor'))} onChange={(value) =>
quickActionHandler={toggleMarkdown} updatePublishForm({ description: !SIMPLE_SITE && advancedEditor ? value : value.target.value })
textAreaMaxLength={FF_MAX_CHARS_IN_DESCRIPTION} }
/> quickActionLabel={!SIMPLE_SITE && (advancedEditor ? __('Simple Editor') : __('Advanced Editor'))}
} quickActionHandler={toggleMarkdown}
/> textAreaMaxLength={FF_MAX_CHARS_IN_DESCRIPTION}
/>
}
/>
</>
); );
} }

View file

@ -149,6 +149,12 @@ function PublishFile(props: Props) {
} }
}, [currentFileType, mode, isStillEditing, updatePublishForm]); }, [currentFileType, mode, isStillEditing, updatePublishForm]);
// Reset title when form gets cleared
useEffect(() => {
updatePublishForm({ title: title });
}, [filePath]);
// Initialize default file source state for each mode. // Initialize default file source state for each mode.
useEffect(() => { useEffect(() => {
setShowSourceSelector(false); setShowSourceSelector(false);
@ -365,9 +371,7 @@ function PublishFile(props: Props) {
} }
function handleTitleChange(event) { function handleTitleChange(event) {
const title = event.target.value; updatePublishForm({ title: event.target.value });
// Update title
updatePublishForm({ title });
} }
function handleFileReaderLoaded(event: ProgressEvent) { function handleFileReaderLoaded(event: ProgressEvent) {
@ -430,6 +434,10 @@ function PublishFile(props: Props) {
updateFileInfo(0, file.size, isVideo); updateFileInfo(0, file.size, isVideo);
} }
// Strip off extention and replace invalid characters
let fileName = name || (file.name && file.name.substr(0, file.name.lastIndexOf('.'))) || '';
autofillTitle(file);
if (isTextPost) { if (isTextPost) {
// Create reader // Create reader
const reader = new FileReader(); const reader = new FileReader();
@ -457,8 +465,6 @@ function PublishFile(props: Props) {
// File.path will be undefined from web due to browser security, so it will default to the File Object. // File.path will be undefined from web due to browser security, so it will default to the File Object.
filePath: file.path || file, filePath: file.path || file,
}; };
// Strip off extention and replace invalid characters
let fileName = name || (file.name && file.name.substr(0, file.name.lastIndexOf('.'))) || '';
if (!isStillEditing) { if (!isStillEditing) {
publishFormParams.name = parseName(fileName); publishFormParams.name = parseName(fileName);
@ -469,12 +475,19 @@ function PublishFile(props: Props) {
updatePublishForm(publishFormParams); updatePublishForm(publishFormParams);
} }
function autofillTitle(file) {
const newTitle = (file && file.name && file.name.substr(0, file.name.lastIndexOf('.'))) || name || '';
if (!title) updatePublishForm({ title: newTitle });
}
const showFileUpload = mode === PUBLISH_MODES.FILE || PUBLISH_MODES.LIVESTREAM; const showFileUpload = mode === PUBLISH_MODES.FILE || PUBLISH_MODES.LIVESTREAM;
const isPublishPost = mode === PUBLISH_MODES.POST; const isPublishPost = mode === PUBLISH_MODES.POST;
return ( return (
<Card <Card
className={disabled || balance === 0 ? 'card--disabled' : ''} className={classnames({
'card--disabled': disabled || balance === 0,
})}
title={ title={
<div> <div>
{header} {/* display mode buttons from parent */} {header} {/* display mode buttons from parent */}
@ -482,8 +495,8 @@ function PublishFile(props: Props) {
{inProgress && ( {inProgress && (
<div> <div>
<Button <Button
button="close" button="alt"
label={__('New --[clears Publish Form]--')} label={__('Clear --[clears Publish Form]--')}
icon={ICONS.REFRESH} icon={ICONS.REFRESH}
onClick={doClearPublish} onClick={doClearPublish}
/> />
@ -493,216 +506,227 @@ function PublishFile(props: Props) {
} }
subtitle={subtitle || (isStillEditing && __('You are currently editing your upload.'))} subtitle={subtitle || (isStillEditing && __('You are currently editing your upload.'))}
actions={ actions={
<React.Fragment> <>
<PublishName uri={uri} /> {/* <h2 className="card__title">{__('File')}</h2> */}
<FormField <div className="card--file">
type="text" <React.Fragment>
name="content_title" {/* Decide whether to show file upload or replay selector */}
label={__('Title')} {/* @if TARGET='web' */}
placeholder={__('Descriptive titles work best')}
disabled={disabled}
value={title}
onChange={handleTitleChange}
/>
{/* Decide whether to show file upload or replay selector */}
{/* @if TARGET='web' */}
<>
{showSourceSelector && (
<fieldset-section>
<div className="section__actions--between section__actions--align-bottom">
<div>
<label>{__('Replay video available')}</label>
<div className="button-group">
{fileSelectorModes.map((fmode) => (
<Button
key={fmode.label}
icon={fmode.icon || undefined}
iconSize={18}
label={fmode.label}
button="alt"
onClick={() => {
// $FlowFixMe
handleFileSource(fmode.actionName);
}}
className={classnames('button-toggle', {
'button-toggle--active': fileSource === fmode.actionName,
})}
/>
))}
</div>
</div>
{fileSource === SOURCE_SELECT && (
<Button
button="secondary"
label={__('Check for Replays')}
disabled={isCheckingLivestreams}
icon={ICONS.REFRESH}
onClick={() => checkLivestreams(channelId, channelName)}
/>
)}
</div>
</fieldset-section>
)}
{fileSource === SOURCE_UPLOAD && showFileUpload && (
<> <>
{showSourceSelector && (
<fieldset-section>
<div className="section__actions--between section__actions--align-bottom">
<div>
<label>{__('Replay video available')}</label>
<div className="button-group">
{fileSelectorModes.map((fmode) => (
<Button
key={fmode.label}
icon={fmode.icon || undefined}
iconSize={18}
label={fmode.label}
button="alt"
onClick={() => {
// $FlowFixMe
handleFileSource(fmode.actionName);
}}
className={classnames('button-toggle', {
'button-toggle--active': fileSource === fmode.actionName,
})}
/>
))}
</div>
</div>
{fileSource === SOURCE_SELECT && (
<Button
button="secondary"
label={__('Check for Replays')}
disabled={isCheckingLivestreams}
icon={ICONS.REFRESH}
onClick={() => checkLivestreams(channelId, channelName)}
/>
)}
</div>
</fieldset-section>
)}
{fileSource === SOURCE_UPLOAD && showFileUpload && (
<>
<FileSelector
disabled={disabled}
currentPath={currentFile}
onFileChosen={handleFileChange}
// https://stackoverflow.com/questions/19107685/safari-input-type-file-accept-video-ignores-mp4-files
accept={SIMPLE_SITE ? 'video/mp4,video/x-m4v,video/*,audio/*' : undefined}
placeholder={
SIMPLE_SITE ? __('Select video or audio file to upload') : __('Select a file to upload')
}
/>
{getUploadMessage()}
</>
)}
{fileSource === SOURCE_SELECT && showFileUpload && hasLivestreamData && !isCheckingLivestreams && (
<>
<fieldset-section>
<label>{__('Select Replay')}</label>
<div className="table__wrapper">
<table className="table table--livestream-data">
<tbody>
{livestreamData
.slice((currentPage - 1) * PAGE_SIZE, currentPage * PAGE_SIZE)
.map((item, i) => (
<tr
onClick={() => setSelectedFileIndex((currentPage - 1) * PAGE_SIZE + i)}
key={item.id}
className={classnames('livestream__data-row', {
'livestream__data-row--selected':
selectedFileIndex === (currentPage - 1) * PAGE_SIZE + i,
})}
>
<td>
<FormField
type="radio"
checked={selectedFileIndex === (currentPage - 1) * PAGE_SIZE + i}
label={null}
onClick={() => setSelectedFileIndex((currentPage - 1) * PAGE_SIZE + i)}
className="livestream__data-row-radio"
/>
</td>
<td>
<div className="livestream_thumb_container">
{item.data.thumbnails.slice(0, 3).map((thumb) => (
<img key={thumb} className="livestream___thumb" src={thumb} />
))}
</div>
</td>
<td>
{item.data.fileDuration && isNaN(item.data.fileDuration)
? item.data.fileDuration
: `${Math.floor(item.data.fileDuration / 60)} ${
Math.floor(item.data.fileDuration / 60) > 1 ? __('minutes') : __('minute')
}`}
<div className="table__item-label">
{`${moment(item.data.uploadedAt).from(moment())}`}
</div>
</td>
<td>
<CopyableText
primaryButton
copyable={normalizeUrlForProtocol(item.data.fileLocation)}
snackMessage={__('Url copied.')}
/>
</td>
</tr>
))}
</tbody>
</table>
</div>
</fieldset-section>
<fieldset-group class="fieldset-group--smushed fieldgroup--paginate">
<fieldset-section>
<ReactPaginate
pageCount={totalPages}
pageRangeDisplayed={2}
previousLabel=""
nextLabel=""
activeClassName="pagination__item--selected"
pageClassName="pagination__item"
previousClassName="pagination__item pagination__item--previous"
nextClassName="pagination__item pagination__item--next"
breakClassName="pagination__item pagination__item--break"
marginPagesDisplayed={2}
onPageChange={(e) => handlePaginateReplays(e.selected + 1)}
forcePage={currentPage - 1}
initialPage={currentPage - 1}
containerClassName="pagination"
/>
</fieldset-section>
</fieldset-group>
</>
)}
{fileSource === SOURCE_SELECT && showFileUpload && !hasLivestreamData && !isCheckingLivestreams && (
<div className="main--empty empty">
<Empty text={__('No replays found.')} />
</div>
)}
{fileSource === SOURCE_SELECT && showFileUpload && isCheckingLivestreams && (
<div className="main--empty empty">
<Spinner small />
</div>
)}
</>
<FormField
type="text"
name="content_title"
label={__('Title')}
placeholder={__('Descriptive titles work best')}
disabled={disabled}
value={title}
onChange={handleTitleChange}
className="fieldset-group"
/>
<PublishName uri={uri} />
{/* @endif */}
{/* @if TARGET='app' */}
{showFileUpload && (
<FileSelector <FileSelector
label={__('File')} label={__('File')}
disabled={disabled} disabled={disabled}
currentPath={currentFile} currentPath={currentFile}
onFileChosen={handleFileChange} onFileChosen={handleFileChange}
// https://stackoverflow.com/questions/19107685/safari-input-type-file-accept-video-ignores-mp4-files // https://stackoverflow.com/questions/19107685/safari-input-type-file-accept-video-ignores-mp4-files
accept={SIMPLE_SITE ? 'video/mp4,video/x-m4v,video/*,audio/*' : undefined} placeholder={__('Select file to upload')}
placeholder={SIMPLE_SITE ? __('Select video or audio file to upload') : __('Select a file to upload')}
/> />
{getUploadMessage()} )}
</> {showFileUpload && (
)} <FormField
{fileSource === SOURCE_SELECT && showFileUpload && hasLivestreamData && !isCheckingLivestreams && ( type="checkbox"
<> checked={userOptimize}
<fieldset-section> disabled={!optimizeAvail}
<label>{__('Select Replay')}</label> onChange={() => setUserOptimize(!userOptimize)}
<div className="table__wrapper"> label={__('Optimize and transcode video')}
<table className="table table--livestream-data"> name="optimize"
<tbody> />
{livestreamData.slice((currentPage - 1) * PAGE_SIZE, currentPage * PAGE_SIZE).map((item, i) => ( )}
<tr {showFileUpload && !ffmpegAvail && (
onClick={() => setSelectedFileIndex((currentPage - 1) * PAGE_SIZE + i)} <p className="help">
key={item.id} <I18nMessage
className={classnames('livestream__data-row', { tokens={{
'livestream__data-row--selected': selectedFileIndex === (currentPage - 1) * PAGE_SIZE + i, settings_link: <Button button="link" navigate="/$/settings" label={__('Settings')} />,
})} }}
> >
<td> FFmpeg not configured. More in %settings_link%.
<FormField </I18nMessage>
type="radio" </p>
checked={selectedFileIndex === (currentPage - 1) * PAGE_SIZE + i} )}
label={null} {showFileUpload && Boolean(size) && ffmpegAvail && optimize && isVid && (
onClick={() => setSelectedFileIndex((currentPage - 1) * PAGE_SIZE + i)} <p className="help">
className="livestream__data-row-radio" <I18nMessage
/> tokens={{
</td> size: Math.ceil(sizeInMB),
<td> processTime: getTimeForMB(sizeInMB),
<div className="livestream_thumb_container"> units: getUnitsForMB(sizeInMB),
{item.data.thumbnails.slice(0, 3).map((thumb) => ( }}
<img key={thumb} className="livestream___thumb" src={thumb} /> >
))} Transcoding this %size% MB file should take under %processTime% %units%.
</div> </I18nMessage>
</td> </p>
<td> )}
{item.data.fileDuration && isNaN(item.data.fileDuration) {/* @endif */}
? item.data.fileDuration {isPublishPost && (
: `${Math.floor(item.data.fileDuration / 60)} ${ <PostEditor
Math.floor(item.data.fileDuration / 60) > 1 ? __('minutes') : __('minute') label={__('Post --[noun, markdown post tab button]--')}
}`} uri={uri}
<div className="table__item-label"> disabled={disabled}
{`${moment(item.data.uploadedAt).from(moment())}`} fileMimeType={fileMimeType}
</div> setPrevFileText={setPrevFileText}
</td> setCurrentFileType={setCurrentFileType}
<td> />
<CopyableText )}
primaryButton </React.Fragment>
copyable={normalizeUrlForProtocol(item.data.fileLocation)} </div>
snackMessage={__('Url copied.')} </>
/>
</td>
</tr>
))}
</tbody>
</table>
</div>
</fieldset-section>
<fieldset-group class="fieldset-group--smushed fieldgroup--paginate">
<fieldset-section>
<ReactPaginate
pageCount={totalPages}
pageRangeDisplayed={2}
previousLabel=""
nextLabel=""
activeClassName="pagination__item--selected"
pageClassName="pagination__item"
previousClassName="pagination__item pagination__item--previous"
nextClassName="pagination__item pagination__item--next"
breakClassName="pagination__item pagination__item--break"
marginPagesDisplayed={2}
onPageChange={(e) => handlePaginateReplays(e.selected + 1)}
forcePage={currentPage - 1}
initialPage={currentPage - 1}
containerClassName="pagination"
/>
</fieldset-section>
</fieldset-group>
</>
)}
{fileSource === SOURCE_SELECT && showFileUpload && !hasLivestreamData && !isCheckingLivestreams && (
<div className="main--empty empty">
<Empty text={__('No replays found.')} />
</div>
)}
{fileSource === SOURCE_SELECT && showFileUpload && isCheckingLivestreams && (
<div className="main--empty empty">
<Spinner small />
</div>
)}
</>
{/* @endif */}
{/* @if TARGET='app' */}
{showFileUpload && (
<FileSelector
label={__('File')}
disabled={disabled}
currentPath={currentFile}
onFileChosen={handleFileChange}
// https://stackoverflow.com/questions/19107685/safari-input-type-file-accept-video-ignores-mp4-files
placeholder={__('Select file to upload')}
/>
)}
{showFileUpload && (
<FormField
type="checkbox"
checked={userOptimize}
disabled={!optimizeAvail}
onChange={() => setUserOptimize(!userOptimize)}
label={__('Optimize and transcode video')}
name="optimize"
/>
)}
{showFileUpload && !ffmpegAvail && (
<p className="help">
<I18nMessage
tokens={{
settings_link: <Button button="link" navigate="/$/settings" label={__('Settings')} />,
}}
>
FFmpeg not configured. More in %settings_link%.
</I18nMessage>
</p>
)}
{showFileUpload && Boolean(size) && ffmpegAvail && optimize && isVid && (
<p className="help">
<I18nMessage
tokens={{
size: Math.ceil(sizeInMB),
processTime: getTimeForMB(sizeInMB),
units: getUnitsForMB(sizeInMB),
}}
>
Transcoding this %size% MB file should take under %processTime% %units%.
</I18nMessage>
</p>
)}
{/* @endif */}
{isPublishPost && (
<PostEditor
label={__('Post --[noun, markdown post tab button]--')}
uri={uri}
disabled={disabled}
fileMimeType={fileMimeType}
setPrevFileText={setPrevFileText}
setCurrentFileType={setCurrentFileType}
/>
)}
</React.Fragment>
} }
/> />
); );

View file

@ -20,7 +20,6 @@ import TagsSelect from 'component/tagsSelect';
import PublishDescription from 'component/publishDescription'; import PublishDescription from 'component/publishDescription';
import PublishPrice from 'component/publishPrice'; import PublishPrice from 'component/publishPrice';
import PublishFile from 'component/publishFile'; import PublishFile from 'component/publishFile';
import PublishBid from 'component/publishBid';
import PublishAdditionalOptions from 'component/publishAdditionalOptions'; import PublishAdditionalOptions from 'component/publishAdditionalOptions';
import PublishFormErrors from 'component/publishFormErrors'; import PublishFormErrors from 'component/publishFormErrors';
import SelectThumbnail from 'component/selectThumbnail'; import SelectThumbnail from 'component/selectThumbnail';
@ -593,7 +592,7 @@ function PublishForm(props: Props) {
// Editing claim uri // Editing claim uri
return ( return (
<div className="card-stack uploadPage-wrapper"> <div className="card-stack">
<ChannelSelect hideAnon={isLivestreamMode} disabled={disabled} autoSet channelToSet={claimChannelId} /> <ChannelSelect hideAnon={isLivestreamMode} disabled={disabled} autoSet channelToSet={claimChannelId} />
<PublishFile <PublishFile
@ -635,15 +634,17 @@ function PublishForm(props: Props) {
} }
/> />
{mode !== PUBLISH_MODES.POST && <PublishDescription disabled={formDisabled} />}
{!publishing && ( {!publishing && (
<div className={classnames({ 'card--disabled': formDisabled })}> <div className={classnames({ 'card--disabled': formDisabled })}>
{showSchedulingOptions && <Card body={<PublishStreamReleaseDate />} />} {showSchedulingOptions && <Card body={<PublishStreamReleaseDate />} />}
{mode !== PUBLISH_MODES.POST && <PublishDescription disabled={formDisabled} />}
<Card actions={<SelectThumbnail livestreamData={livestreamData} />} /> <Card actions={<SelectThumbnail livestreamData={livestreamData} />} />
<label style={{ marginTop: 'var(--spacing-l)' }}>{__('Tags')}</label> <h2 className="card__title" style={{ marginTop: 'var(--spacing-l)' }}>
{__('Tags')}
</h2>
<TagsSelect <TagsSelect
suggestMature={!SIMPLE_SITE} suggestMature={!SIMPLE_SITE}
disableAutoFocus disableAutoFocus
@ -671,7 +672,6 @@ function PublishForm(props: Props) {
tagsChosen={tags} tagsChosen={tags}
/> />
<PublishBid disabled={isStillEditing || formDisabled} />
{!isLivestreamMode && <PublishPrice disabled={formDisabled} />} {!isLivestreamMode && <PublishPrice disabled={formDisabled} />}
<PublishAdditionalOptions disabled={formDisabled} showSchedulingOptions={showSchedulingOptions} /> <PublishAdditionalOptions disabled={formDisabled} showSchedulingOptions={showSchedulingOptions} />
@ -696,7 +696,7 @@ function PublishForm(props: Props) {
</div> </div>
<p className="help"> <p className="help">
{!formDisabled && !formValid ? ( {!formDisabled && !formValid ? (
<PublishFormErrors mode={mode} waitForFile={waitingForFile} overMaxBitrate={overMaxBitrate} /> <PublishFormErrors title={title} mode={mode} waitForFile={waitingForFile} overMaxBitrate={overMaxBitrate} />
) : ( ) : (
<I18nMessage <I18nMessage
tokens={{ tokens={{

View file

@ -72,13 +72,7 @@ function NameHelpText(props: Props) {
nameHelpText = <div className="error__text">{__('You already have a claim with this name.')}</div>; nameHelpText = <div className="error__text">{__('You already have a claim with this name.')}</div>;
} }
return ( return <React.Fragment>{nameHelpText && nameHelpText}</React.Fragment>;
<React.Fragment>
{nameHelpText || (
<span>{__('Create a URL for this content. Simpler names are easier to find and remember.')}</span>
)}
</React.Fragment>
);
} }
export default NameHelpText; export default NameHelpText;

View file

@ -70,7 +70,7 @@ function PublishName(props: Props) {
<> <>
<fieldset-group class="fieldset-group--smushed fieldset-group--disabled-prefix"> <fieldset-group class="fieldset-group--smushed fieldset-group--disabled-prefix">
<fieldset-section> <fieldset-section>
<label>{__('Name')}</label> <label>{__('URL')}</label>
<div className="form-field__prefix">{prefix}</div> <div className="form-field__prefix">{prefix}</div>
</fieldset-section> </fieldset-section>
<FormField <FormField
@ -83,6 +83,7 @@ function PublishName(props: Props) {
onBlur={() => setBlurred(true)} onBlur={() => setBlurred(true)}
/> />
</fieldset-group> </fieldset-group>
<div className="form-field__help"> <div className="form-field__help">
<NameHelpText <NameHelpText
uri={uri} uri={uri}

View file

@ -15,8 +15,9 @@ function PublishPrice(props: Props) {
return ( return (
<> <>
<label>{__('Price')}</label> <h2 className="card__title">{__('Price')}</h2>
<Card <Card
className="card--publish-section card--price"
actions={ actions={
<React.Fragment> <React.Fragment>
<FormField <FormField

View file

@ -39,9 +39,11 @@ const PublishReleaseDate = (props: Props) => {
const isNew = releaseTime === undefined; const isNew = releaseTime === undefined;
const isEdit = !isNew || allowDefault === false; const isEdit = !isNew || allowDefault === false;
const showEditBtn = isNew && releaseTimeEdited === undefined && allowDefault !== false; // const showEditBtn = isNew && releaseTimeEdited === undefined && allowDefault !== false;
const showEditBtn = false;
const showDefaultBtn = isNew && releaseTimeEdited !== undefined && allowDefault !== false; const showDefaultBtn = isNew && releaseTimeEdited !== undefined && allowDefault !== false;
const showDatePicker = isEdit || releaseTimeEdited !== undefined; // const showDatePicker = isEdit || releaseTimeEdited !== undefined;
const showDatePicker = true;
const onDateTimePickerChanged = (value) => { const onDateTimePickerChanged = (value) => {
if (value) { if (value) {

View file

@ -55,47 +55,50 @@ const PublishStreamReleaseDate = (props: Props) => {
); );
return ( return (
<div className=""> <>
<label htmlFor="date-picker-input">{__('When do you want to go live?')}</label> <h2 className="card__title">{__('Date')}</h2>
<div className="card--date">
<label htmlFor="date-picker-input">{__('When do you want to go live?')}</label>
<div className={'w-full flex flex-col mt-s md:mt-0 md:h-12 md:items-center md:flex-row'}> <div className={'w-full flex flex-col mt-s md:mt-0 md:h-12 md:items-center md:flex-row'}>
<FormField
type="radio"
name="anytime"
disabled={false}
onChange={handleToggle}
checked={!publishLater}
label={__('Anytime')}
/>
<div className={'md:ml-m mt-s md:mt-0'}>
<FormField <FormField
type="radio" type="radio"
name="scheduled_time" name="anytime"
disabled={false} disabled={false}
onChange={handleToggle} onChange={handleToggle}
checked={publishLater} checked={!publishLater}
label={__('Scheduled Time')} label={__('Anytime')}
/> />
</div>
{publishLater && ( <div className={'md:ml-m mt-s md:mt-0'}>
<div className="form-field-date-picker mb-0 controls md:ml-m"> <FormField
<DateTimePicker type="radio"
className="date-picker-input w-full md:w-auto mt-s md:mt-0" name="scheduled_time"
calendarClassName="form-field-calendar" disabled={false}
onChange={onDateTimePickerChanged} onChange={handleToggle}
value={date} checked={publishLater}
format="y-MM-dd h:mm a" label={__('Scheduled Time')}
disableClock
clearIcon={null}
minDate={moment().add('30', 'minutes').toDate()}
/> />
</div> </div>
)} {publishLater && (
</div> <div className="form-field-date-picker mb-0 controls md:ml-m">
<DateTimePicker
className="date-picker-input w-full md:w-auto mt-s md:mt-0"
calendarClassName="form-field-calendar"
onChange={onDateTimePickerChanged}
value={date}
format="y-MM-dd h:mm a"
disableClock
clearIcon={null}
minDate={moment().add('30', 'minutes').toDate()}
/>
</div>
)}
</div>
<p className={'form-field__hint mt-m'}>{helpText}</p> <p className={'form-field__hint mt-m'}>{helpText}</p>
</div> </div>
</>
); );
}; };

View file

@ -122,34 +122,35 @@ function SelectThumbnail(props: Props) {
return ( return (
<> <>
<h2 className="card__title">{__('Thumbnail')}</h2>
{status !== THUMBNAIL_STATUSES.IN_PROGRESS && ( {status !== THUMBNAIL_STATUSES.IN_PROGRESS && (
<> <div className="column card--thumbnail">
<label>{__('Thumbnail')}</label> {thumbPreview}
<div className="column"> {publishForm && thumbUploaded ? (
{thumbPreview} <div className="column__item">
{publishForm && thumbUploaded ? ( <p>{__('Upload complete.')}</p>
<div className="column__item"> <div className="section__actions">
<p>{__('Upload complete.')}</p> <Button button="link" label={__('New thumbnail')} onClick={resetThumbnailStatus} />
<div className="section__actions">
<Button button="link" label={__('New thumbnail')} onClick={resetThumbnailStatus} />
</div>
</div> </div>
) : ( </div>
<div className="column__item"> ) : (
{manualInput ? ( <div className="column__item">
{manualInput ? (
<>
<FormField <FormField
type="text" type="text"
name="content_thumbnail" name="content_thumbnail"
label="URL"
placeholder="https://images.fbi.gov/alien" placeholder="https://images.fbi.gov/alien"
value={thumbnail} value={thumbnail}
disabled={formDisabled} disabled={formDisabled}
onChange={handleThumbnailChange} onChange={handleThumbnailChange}
/> />
) : ( {!thumbUploaded && <p className="help">{__('Enter a URL for your thumbnail.')}</p>}
</>
) : (
<>
<FileSelector <FileSelector
currentPath={thumbnailPath} currentPath={thumbnailPath}
label={__('Thumbnail')}
placeholder={__('Choose an enticing thumbnail')} placeholder={__('Choose an enticing thumbnail')}
accept={accept} accept={accept}
onFileChosen={(file) => onFileChosen={(file) =>
@ -159,44 +160,42 @@ function SelectThumbnail(props: Props) {
}) })
} }
/> />
)} {!thumbUploaded && (
<div className="card__actions"> <p className="help">
{__('Upload your thumbnail to %domain%. Recommended ratio is 16:9, %max_size%MB max.', {
domain: DOMAIN,
max_size: THUMBNAIL_CDN_SIZE_LIMIT_BYTES / (1024 * 1024),
})}
</p>
)}
</>
)}
<div className="card__actions">
<Button
button="link"
label={manualInput ? __('Use thumbnail upload tool') : __('Enter a thumbnail URL')}
onClick={() =>
updatePublishForm({
uploadThumbnailStatus: manualInput ? THUMBNAIL_STATUSES.READY : THUMBNAIL_STATUSES.MANUAL,
})
}
/>
{status === THUMBNAIL_STATUSES.READY && isSupportedVideo && IS_WEB && (
// Disabled on desktop until this is resolved
// https://github.com/electron/electron/issues/20750#issuecomment-709505902
<Button <Button
button="link" button="link"
label={manualInput ? __('Use thumbnail upload tool') : __('Enter a thumbnail URL')} label={__('Take a snapshot from your video')}
onClick={() => onClick={() => openModal(MODALS.AUTO_GENERATE_THUMBNAIL, { filePath: actualFilePath })}
updatePublishForm({
uploadThumbnailStatus: manualInput ? THUMBNAIL_STATUSES.READY : THUMBNAIL_STATUSES.MANUAL,
})
}
/> />
{status === THUMBNAIL_STATUSES.READY && isSupportedVideo && IS_WEB && ( )}
// Disabled on desktop until this is resolved
// https://github.com/electron/electron/issues/20750#issuecomment-709505902
<Button
button="link"
label={__('Take a snapshot from your video')}
onClick={() => openModal(MODALS.AUTO_GENERATE_THUMBNAIL, { filePath: actualFilePath })}
/>
)}
</div>
</div> </div>
)} </div>
</div> )}
</> </div>
)} )}
{status === THUMBNAIL_STATUSES.IN_PROGRESS && <p>{__('Uploading thumbnail')}...</p>} {status === THUMBNAIL_STATUSES.IN_PROGRESS && <p>{__('Uploading thumbnail')}...</p>}
{!thumbUploaded && (
<p className="help">
{manualInput
? __('Enter a URL for your thumbnail.')
: __('Upload your thumbnail to %domain%. Recommended ratio is 16:9, %max_size%MB max.', {
domain: DOMAIN,
max_size: THUMBNAIL_CDN_SIZE_LIMIT_BYTES / (1024 * 1024),
})}
</p>
)}
</> </>
); );
} }

View file

@ -79,6 +79,7 @@ export default function TagsSelect(props: Props) {
return ( return (
((showClose && !hasClosed) || !showClose) && ( ((showClose && !hasClosed) || !showClose) && (
<Card <Card
className="card--tags"
icon={ICONS.TAG} icon={ICONS.TAG}
title={ title={
hideHeader ? null : ( hideHeader ? null : (

View file

@ -25,14 +25,7 @@ function PublishPage(props: Props) {
} }
return ( return (
<Page <Page className="uploadPage-wrapper" noFooter>
noFooter
noSideNavigation
backout={{
title: __('Upload'),
backLabel: __('Back'),
}}
>
{balance === 0 && <YrblWalletEmpty />} {balance === 0 && <YrblWalletEmpty />}
{balance !== 0 && fetchingChannels ? ( {balance !== 0 && fetchingChannels ? (
<div className="main--empty"> <div className="main--empty">

View file

@ -56,3 +56,12 @@
.spinnerArea--centered { .spinnerArea--centered {
text-align: center; text-align: center;
} }
.button--primary {
.spinner {
height: 30px;
.rect {
background-color: var(--color-primary-contrast);
}
}
}

View file

@ -425,6 +425,7 @@ img {
.error__text { .error__text {
color: var(--color-text-error); color: var(--color-text-error);
margin-left: 2px;
} }
.help--error { .help--error {
@ -480,8 +481,7 @@ img {
background-color: #fef1f6; background-color: #fef1f6;
color: var(--color-black); color: var(--color-black);
font-weight: bold; font-weight: bold;
help .button {
.button {
color: #fa6165; color: #fa6165;
} }
} }
@ -727,92 +727,397 @@ img {
} }
.uploadPage-wrapper { .uploadPage-wrapper {
&.card-stack { transition: width 0.4s;
.card:not(:last-of-type) { @media (min-width: $breakpoint-small) {
margin-bottom: var(--spacing-l) !important; width: 80% !important;
}
@media (min-width: $breakpoint-medium) {
width: 70% !important;
}
@media (min-width: $breakpoint-large) {
width: 60% !important;
}
@media (max-width: $breakpoint-small) {
.channel__selector {
margin-top: var(--spacing-m);
margin-bottom: var(--spacing-l);
button {
width: 100%;
.claim-preview__title {
// flex: 1;
text-align: left;
}
.comment__badge {
flex: 1;
text-align: left;
}
.icon--ChevronDown {
margin-left: auto;
}
}
}
}
.card__title-section {
width: 100%;
div:first-of-type:not(.card__subtitle) {
width: 100%;
}
h2 {
width: 100%;
div:first-of-type {
div {
width: unset !important;
float: right;
}
}
}
}
.card__header--between {
// margin-bottom: var(--spacing-xl);
margin-bottom: 0;
border-bottom: 1px solid var(--color-border);
@media (max-width: $breakpoint-small) {
margin-bottom: var(--spacing-l);
}
}
.card__title {
div {
display: inline-block;
margin-top: 0px;
right: 0;
}
.button {
margin-top: 0;
}
.button--alt:not(.button-toggle) {
font-size: var(--font-base);
} }
} }
.card__main-actions { .card__main-actions {
padding-top: var(--spacing-l); border: unset;
} }
label { label {
font-size: var(--font-medium); font-size: var(--font-body);
} }
.button--close { .form-field__prefix {
&:hover { box-shadow: unset !important;
color: var(--color-primary); border-width: 2px 2px 2px 0 !important;
background-color: unset; border-color: var(--color-border) !important;
} background-color: var(--color-border) !important;
} }
.icon__wrapper--Tag { .card--publish-section {
margin-right: var(--spacing-s) !important; background: rgba(var(--color-header-background-base), 0.4);
width: 1rem !important; border-radius: var(--border-radius);
height: 1rem !important;
}
.card:not(:first-of-type) {
.card__main-actions:not(:first-of-type) {
border-top: unset;
}
.card__subtitle {
margin-top: 0 !important;
}
}
.card:nth-last-child(2) {
.card__main-actions { .card__main-actions {
padding-top: var(--spacing-xxxs); padding: 0;
fieldset-section:not(:first-child) { }
margin-top: var(--spacing-s);
fieldset-section {
padding: var(--spacing-s);
}
fieldset-section.radio {
margin-top: 0;
padding-bottom: 0;
}
fieldset-section.radio:last-of-type {
padding-bottom: var(--spacing-s);
}
fieldset-group {
margin-top: 0;
fieldset-section {
padding-right: 0;
} }
.fieldset-group--smushed { .input--currency-select {
fieldset-section { padding-left: 2px;
margin-top: 0;
label {
margin-left: 0;
}
.input--currency-select {
margin-left: 2px !important;
}
}
}
@media (max-width: $breakpoint-small) {
margin-top: var(--spacing-xxxs);
} }
} }
.publish-row {
// border-bottom: 1px solid var(--color-border);
.form-field__help {
padding: var(--spacing-s);
padding-top: 0;
}
}
.form-field-date-picker {
padding: var(--spacing-s);
margin-bottom: 0;
}
}
.tags__input-wrapper {
background: rgba(var(--color-header-background-base), 0.4);
border-radius: var(--border-radius);
padding: var(--spacing-s);
} }
.help { .help {
opacity: 0.8;
margin-bottom: var(--spacing-l);
color: var(--color-text); color: var(--color-text);
} }
.form-field__two-column { .card--file {
display: flex; background: rgba(var(--color-header-background-base), 0.4);
justify-content: space-between;
}
.comment__char-count-mde {
min-width: 40px;
text-align: center;
margin-bottom: var(--spacing-xxxs);
padding-left: 4px;
padding-right: 4px;
padding-top: 5px;
border-radius: var(--border-radius); border-radius: var(--border-radius);
height: var(--height-input-slim); padding: var(--spacing-s);
input {
box-shadow: 0 0 0 2px var(--color-border) inset;
&:focus-visible {
box-shadow: 0 0 0 2px var(--color-primary) inset;
}
}
.button--secondary {
border-width: 2px 2px 2px 0;
border-color: var(--color-primary);
background-color: var(--color-primary) !important;
.icon {
stroke: var(--color-primary-contrast) !important;
}
.button__label {
color: var(--color-primary-contrast) !important;
}
}
label {
margin-left: 0 !important;
}
} }
.section__actions { .card__title-disabled {
.button { opacity: 0.3;
.button__content { }
.button__label {
margin-top: 0; .card--description {
label {
font-size: var(--font-large);
font-weight: var(--font-weight-bold);
}
.card__main-actions {
padding-top: 0;
margin-top: 0;
}
.MuiAutocomplete-root {
background: rgba(var(--color-header-background-base), 0.4);
border-radius: var(--border-radius);
padding: var(--spacing-s);
textarea {
box-shadow: 0 0 0 2px var(--color-border) inset;
&:focus-visible {
box-shadow: 0 0 0 2px var(--color-primary) inset;
} }
} }
@media (max-width: $breakpoint-small) {
.MuiFormControl-root {
border-radius: var(--border-radius);
box-shadow: 0 0 0 2px var(--color-border) inset;
}
textarea {
box-shadow: unset;
}
}
}
}
.card--thumbnail {
background: rgba(var(--color-header-background-base), 0.4);
border-radius: var(--border-radius);
padding: var(--spacing-s);
input {
box-shadow: 0 0 0 2px var(--color-border) inset;
}
.button--secondary {
border-width: 2px 2px 2px 0;
border-color: var(--color-primary);
background-color: var(--color-primary) !important;
.icon {
stroke: var(--color-primary-contrast) !important;
}
.button__label {
color: var(--color-primary-contrast) !important;
}
}
@media (max-width: $breakpoint-small) {
.thumbnail-picker__preview {
width: 100%;
height: unset;
aspect-ratio: 16 / 9;
}
.column__item:last-of-type {
margin-bottom: 0;
}
}
}
.card--tags {
input {
box-shadow: 0 0 0 2px var(--color-border) inset;
}
.icon__wrapper {
margin-right: var(--spacing-s) !important;
}
.card__subtitle {
margin-top: 5px;
}
.card__main-actions {
padding-top: 0;
border-top: unset;
}
.card__header--between {
margin-bottom: unset;
border: unset;
}
.tag {
background-color: var(--color-header-button);
}
@media (max-width: $breakpoint-small) {
.card__subtitle {
font-size: var(--font-small);
}
}
}
.card--price {
.input--currency-select {
label {
margin-left: 0 !important;
}
}
.form-field--price-amount,
select {
box-shadow: 0 0 0 2px var(--color-border) inset;
}
@media (max-width: $breakpoint-small) {
.card__main-actions {
margin-top: 0;
}
}
}
.card--additional-options {
margin-bottom: var(--spacing-l) !important;
.card {
fieldset-section {
padding-bottom: 0;
}
}
.card__main-actions {
//padding-top:var(--spacing-s);
padding-top: 0;
}
.section__actions {
padding: var(--spacing-s);
margin-top: 0;
}
input,
select {
box-shadow: 0 0 0 2px var(--color-border) inset;
&:focus-visible {
box-shadow: 0 0 0 2px var(--color-primary) inset;
}
}
.react-datetime-picker {
box-shadow: 0 0 0 2px var(--color-border) inset;
input {
box-shadow: unset;
}
.react-datetime-picker__inputGroup__amPm {
margin-top: 4px;
height: calc(100% - 8px);
box-shadow: unset;
&:focus-visible {
box-shadow: 0 0 0 2px var(--color-primary) inset;
}
}
}
.form-field__help {
margin-bottom: 0;
}
fieldset {
.form-field__help {
margin-top: 0;
}
}
fieldset-section {
margin-top: 0;
}
fieldset-group {
fieldset-section {
width: 50%;
&:last-of-type {
padding-right: var(--spacing-s);
}
}
}
@media (max-width: $breakpoint-small) {
button {
margin-bottom: 0;
}
border-top: unset;
}
}
.card--date {
background: rgba(var(--color-header-background-base), 0.4);
border-radius: var(--border-radius);
padding: var(--spacing-s);
.form-field__hint {
opacity: 0.8;
// margin-bottom: var(--spacing-l);
color: var(--color-text);
}
.react-datetime-picker {
box-shadow: 0 0 0 2px var(--color-border) inset;
input {
box-shadow: unset;
}
.react-datetime-picker__inputGroup__amPm {
margin-top: 4px;
height: calc(100% - 8px);
box-shadow: unset;
&:focus-visible {
box-shadow: 0 0 0 2px var(--color-primary) inset;
}
}
}
}
.EasyMDEContainer {
.editor-toolbar,
.CodeMirror-wrap {
border-width: 2px !important;
}
.editor-statusbar {
padding: 0;
padding-top: var(--spacing-s);
} }
} }
@ -842,6 +1147,13 @@ img {
} }
} }
[data-reach-menu-popover] {
@media (max-width: $breakpoint-small) {
width: calc(100% - var(--spacing-xs) * 2);
max-width: calc(100% - var(--spacing-xs) * 2);
}
}
.transactionsPage-wrapper { .transactionsPage-wrapper {
.card__header--between { .card__header--between {
align-items: unset; align-items: unset;

View file

@ -80,6 +80,7 @@
--color-input-label: var(--color-text); --color-input-label: var(--color-text);
--color-input-placeholder: #f4f4f5; --color-input-placeholder: #f4f4f5;
--color-input-bg: var(--color-header-background); --color-input-bg: var(--color-header-background);
--color-input-bg-secondary: #333333;
--color-input-bg-selected: var(--color-primary); --color-input-bg-selected: var(--color-primary);
--color-input-border: var(--color-border); --color-input-border: var(--color-border);
--color-input-toggle: var(--color-primary-alt-3); --color-input-toggle: var(--color-primary-alt-3);

View file

@ -88,6 +88,7 @@
--color-input-label: var(--color-text); --color-input-label: var(--color-text);
--color-input-placeholder: #212529; --color-input-placeholder: #212529;
--color-input-bg: var(--color-header-background); --color-input-bg: var(--color-header-background);
--color-input-bg-secondary: #ffffff;
--color-input-bg-selected: var(--color-primary); --color-input-bg-selected: var(--color-primary);
--color-input-border: var(--color-border); --color-input-border: var(--color-border);
--color-input-toggle: var(--color-secondary); --color-input-toggle: var(--color-secondary);