update master with some odysee changes

This commit is contained in:
zeppi 2021-03-29 19:05:18 -04:00 committed by Sean Yesmunt
parent bfcdbd575f
commit 9468f2b0f2
15 changed files with 424 additions and 310 deletions

View file

@ -142,7 +142,7 @@
"imagesloaded": "^4.1.4", "imagesloaded": "^4.1.4",
"json-loader": "^0.5.4", "json-loader": "^0.5.4",
"lbry-format": "https://github.com/lbryio/lbry-format.git", "lbry-format": "https://github.com/lbryio/lbry-format.git",
"lbry-redux": "lbryio/lbry-redux#8e74e3137ae78f5d99819b72ba48eecb51015622", "lbry-redux": "lbryio/lbry-redux#35088a6d1007f877a33baf62d67b37669edea043",
"lbryinc": "lbryio/lbryinc#7faea40d87b78ec91b901c62f501499dc4737025", "lbryinc": "lbryio/lbryinc#7faea40d87b78ec91b901c62f501499dc4737025",
"lint-staged": "^7.0.2", "lint-staged": "^7.0.2",
"localforage": "^1.7.1", "localforage": "^1.7.1",

View file

@ -20,6 +20,7 @@ import analytics from 'analytics';
import LbcSymbol from 'component/common/lbc-symbol'; import LbcSymbol from 'component/common/lbc-symbol';
import SUPPORTED_LANGUAGES from 'constants/supported_languages'; import SUPPORTED_LANGUAGES from 'constants/supported_languages';
import WalletSpendableBalanceHelp from 'component/walletSpendableBalanceHelp'; import WalletSpendableBalanceHelp from 'component/walletSpendableBalanceHelp';
import { SIMPLE_SITE } from 'config';
const LANG_NONE = 'none'; const LANG_NONE = 'none';
@ -358,7 +359,7 @@ function ChannelForm(props: Props) {
<Card <Card
body={ body={
<TagsSearch <TagsSearch
suggestMature suggestMature={!SIMPLE_SITE}
disableAutoFocus disableAutoFocus
limitSelect={MAX_TAG_SELECT} limitSelect={MAX_TAG_SELECT}
tagsPassedIn={params.tags || []} tagsPassedIn={params.tags || []}

View file

@ -248,106 +248,110 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
'claim-preview__wrapper--small': type === 'small', 'claim-preview__wrapper--small': type === 'small',
})} })}
> >
{!hideRepostLabel && <ClaimRepostAuthor uri={uri} />} <>
{!hideRepostLabel && <ClaimRepostAuthor uri={uri} />}
<div <div
className={classnames('claim-preview', { className={classnames('claim-preview', {
'claim-preview--small': type === 'small' || type === 'tooltip', 'claim-preview--small': type === 'small' || type === 'tooltip',
'claim-preview--large': type === 'large', 'claim-preview--large': type === 'large',
'claim-preview--inline': type === 'inline', 'claim-preview--inline': type === 'inline',
'claim-preview--tooltip': type === 'tooltip', 'claim-preview--tooltip': type === 'tooltip',
'claim-preview--channel': isChannelUri, 'claim-preview--channel': isChannelUri,
'claim-preview--visited': !isChannelUri && !claimIsMine && hasVisitedUri, 'claim-preview--visited': !isChannelUri && !claimIsMine && hasVisitedUri,
'claim-preview--pending': pending, 'claim-preview--pending': pending,
})} })}
> >
{isChannelUri && claim ? ( {isChannelUri && claim ? (
<UriIndicator uri={contentUri} link> <UriIndicator uri={contentUri} link>
<ChannelThumbnail uri={contentUri} /> <ChannelThumbnail uri={contentUri} />
</UriIndicator> </UriIndicator>
) : ( ) : (
<> <>
{!pending ? ( {!pending ? (
<NavLink {...navLinkProps}>
<FileThumbnail thumbnail={thumbnailUrl}>
{/* @if TARGET='app' */}
{claim && (
<div className="claim-preview__hover-actions">
<FileDownloadLink uri={canonicalUrl} hideOpenButton hideDownloadStatus />
</div>
)}
{/* @endif */}
{!isRepost && !isChannelUri && (
<div className="claim-preview__file-property-overlay">
<FileProperties uri={contentUri} small />
</div>
)}
</FileThumbnail>
</NavLink>
) : (
<FileThumbnail thumbnail={thumbnailUrl} />
)}
</>
)}
<div className="claim-preview__text">
<div className="claim-preview-metadata">
<div className="claim-preview-info">
{pending ? (
<ClaimPreviewTitle uri={contentUri} />
) : (
<NavLink {...navLinkProps}> <NavLink {...navLinkProps}>
<ClaimPreviewTitle uri={uri} /> <FileThumbnail thumbnail={thumbnailUrl}>
{/* @if TARGET='app' */}
{claim && (
<div className="claim-preview__hover-actions">
<FileDownloadLink uri={canonicalUrl} hideOpenButton hideDownloadStatus />
</div>
)}
{/* @endif */}
{!isRepost && !isChannelUri && !isLivestream && (
<div className="claim-preview__file-property-overlay">
<FileProperties uri={contentUri} small />
</div>
)}
</FileThumbnail>
</NavLink> </NavLink>
) : (
<FileThumbnail thumbnail={thumbnailUrl} />
)} )}
</>
)}
{/* {type !== 'small' && !isChannelUri && signingChannel && SIMPLE_SITE && ( <div className="claim-preview__text">
<div className="claim-preview-metadata">
<div className="claim-preview-info">
{pending ? (
<ClaimPreviewTitle uri={contentUri} />
) : (
<NavLink {...navLinkProps}>
<ClaimPreviewTitle uri={uri} />
</NavLink>
)}
{/* {type !== 'small' && !isChannelUri && signingChannel && SIMPLE_SITE && (
<ChannelThumbnail uri={signingChannel.permanent_url} /> <ChannelThumbnail uri={signingChannel.permanent_url} />
)} */} )} */}
</div>
<ClaimPreviewSubtitle uri={uri} type={type} />
{(pending || !!reflectingProgress) && <PublishPending uri={uri} />}
</div> </div>
<ClaimPreviewSubtitle uri={uri} type={type} /> {type !== 'small' && (
{(pending || !!reflectingProgress) && <PublishPending uri={uri} />} <div className="claim-preview__actions">
{!pending && (
<>
{renderActions && claim && renderActions(claim)}
{shouldHideActions || renderActions ? null : actions !== undefined ? (
actions
) : (
<div className="claim-preview__primary-actions">
{!isChannelUri && signingChannel && (
<div className="claim-preview__channel-staked">
<ChannelThumbnail uri={signingChannel.permanent_url} />
</div>
)}
{isChannelUri && !channelIsBlocked && !claimIsMine && (
<SubscribeButton
uri={contentUri.startsWith('lbry://') ? contentUri : `lbry://${contentUri}`}
/>
)}
{includeSupportAction && <ClaimSupportButton uri={uri} />}
</div>
)}
</>
)}
{claim && (
<React.Fragment>
{typeof properties === 'function' ? (
properties(claim)
) : properties !== undefined ? (
properties
) : (
<ClaimTags uri={uri} type={type} />
)}
</React.Fragment>
)}
</div>
)}
</div> </div>
{type !== 'small' && (
<div className="claim-preview__actions">
{!pending && (
<>
{renderActions && claim && renderActions(claim)}
{shouldHideActions || renderActions ? null : actions !== undefined ? (
actions
) : (
<div className="claim-preview__primary-actions">
{!isChannelUri && signingChannel && (
<div className="claim-preview__channel-staked">
<ChannelThumbnail uri={signingChannel.permanent_url} />
</div>
)}
{isChannelUri && !channelIsBlocked && !claimIsMine && (
<SubscribeButton uri={contentUri.startsWith('lbry://') ? contentUri : `lbry://${contentUri}`} />
)}
{includeSupportAction && <ClaimSupportButton uri={uri} />}
</div>
)}
</>
)}
{claim && (
<React.Fragment>
{typeof properties === 'function' ? (
properties(claim)
) : properties !== undefined ? (
properties
) : (
<ClaimTags uri={uri} type={type} />
)}
</React.Fragment>
)}
</div>
)}
</div> </div>
</div> {!hideMenu && <ClaimMenuList uri={uri} />}
{!hideMenu && <ClaimMenuList uri={uri} />} </>
</WrapperElement> </WrapperElement>
); );
}); });

View file

@ -8,6 +8,7 @@ import {
doFileGet, doFileGet,
makeSelectChannelForClaimUri, makeSelectChannelForClaimUri,
makeSelectClaimIsNsfw, makeSelectClaimIsNsfw,
makeSelectClaimIsStreamPlaceholder,
} from 'lbry-redux'; } from 'lbry-redux';
import { selectMutedChannels } from 'redux/selectors/blocked'; import { selectMutedChannels } from 'redux/selectors/blocked';
import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc'; import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
@ -25,6 +26,7 @@ const select = (state, props) => ({
blockedChannelUris: selectMutedChannels(state), blockedChannelUris: selectMutedChannels(state),
showMature: selectShowMatureContent(state), showMature: selectShowMatureContent(state),
isMature: makeSelectClaimIsNsfw(props.uri)(state), isMature: makeSelectClaimIsNsfw(props.uri)(state),
isLivestream: makeSelectClaimIsStreamPlaceholder(props.uri)(state),
}); });
const perform = (dispatch) => ({ const perform = (dispatch) => ({

View file

@ -20,6 +20,8 @@ type Props = {
defaultExpand?: boolean, defaultExpand?: boolean,
nag?: Node, nag?: Node,
smallTitle?: boolean, smallTitle?: boolean,
onClick?: () => void,
children?: any, // not sure how this works
}; };
export default function Card(props: Props) { export default function Card(props: Props) {
@ -37,12 +39,21 @@ export default function Card(props: Props) {
smallTitle = false, smallTitle = false,
defaultExpand, defaultExpand,
nag, nag,
onClick,
children,
} = props; } = props;
const [expanded, setExpanded] = useState(defaultExpand); const [expanded, setExpanded] = useState(defaultExpand);
const expandable = defaultExpand !== undefined; const expandable = defaultExpand !== undefined;
return ( return (
<section className={classnames(className, 'card')}> <section
className={classnames(className, 'card')}
onClick={() => {
if (onClick) {
onClick();
}
}}
>
{(title || subtitle) && ( {(title || subtitle) && (
<div <div
className={classnames('card__header--between', { className={classnames('card__header--between', {
@ -93,6 +104,7 @@ export default function Card(props: Props) {
</div> </div>
)} )}
{actions && <div className="card__main-actions">{actions}</div>} {actions && <div className="card__main-actions">{actions}</div>}
{children && <div className="card__main-actions">{children}</div>}
</> </>
)} )}

View file

@ -64,11 +64,12 @@ export default function LivestreamLink(props: Props) {
return null; return null;
} }
return ( // gonna pass the wrapper in so I don't have to rewrite the dmca/blocking logic in claimPreview.
<Card const element = (props: { children: any }) => (
className="livestream__channel-link" <Card className="livestream__channel-link" title={__('Live stream in progress')}>
title={__('Live stream in progress')} {props.children}
actions={<ClaimPreview uri={livestreamClaim.canonical_url} livestream type="inline" />} </Card>
/>
); );
return <ClaimPreview uri={livestreamClaim.canonical_url} wrapperElement={element} type="inline" />;
} }

View file

@ -256,6 +256,13 @@ function PublishForm(props: Props) {
} }
}, [name, activeChannelName, resolveUri, updatePublishForm, checkAvailability]); }, [name, activeChannelName, resolveUri, updatePublishForm, checkAvailability]);
useEffect(() => {
// because editingURI is lbry://channel_short/claim_long and that particular shape won't map to the claimId yet
if (editingURI) {
resolveUri(editingURI);
}
}, [editingURI, resolveUri]);
useEffect(() => { useEffect(() => {
updatePublishForm({ updatePublishForm({
isMarkdownPost: mode === PUBLISH_MODES.POST, isMarkdownPost: mode === PUBLISH_MODES.POST,
@ -440,7 +447,7 @@ function PublishForm(props: Props) {
{mode === PUBLISH_MODES.FILE && <PublishDescription disabled={formDisabled} />} {mode === PUBLISH_MODES.FILE && <PublishDescription disabled={formDisabled} />}
<Card actions={<SelectThumbnail />} /> <Card actions={<SelectThumbnail />} />
<TagsSelect <TagsSelect
suggestMature suggestMature={!SIMPLE_SITE}
disableAutoFocus disableAutoFocus
hideHeader hideHeader
label={__('Selected Tags')} label={__('Selected Tags')}

View file

@ -52,7 +52,7 @@ import YoutubeSyncPage from 'page/youtubeSync';
import { LINKED_COMMENT_QUERY_PARAM } from 'constants/comment'; import { LINKED_COMMENT_QUERY_PARAM } from 'constants/comment';
import { parseURI, isURIValid } from 'lbry-redux'; import { parseURI, isURIValid } from 'lbry-redux';
import { SITE_TITLE, WELCOME_VERSION } from 'config'; import { SITE_TITLE, WELCOME_VERSION, SIMPLE_SITE } from 'config';
// Tell the browser we are handling scroll restoration // Tell the browser we are handling scroll restoration
if ('scrollRestoration' in history) { if ('scrollRestoration' in history) {
@ -218,6 +218,7 @@ function AppRouter(props: Props) {
<Route path={`/`} exact component={HomePage} /> <Route path={`/`} exact component={HomePage} />
<Route path={`/$/${PAGES.DISCOVER}`} exact component={DiscoverPage} /> <Route path={`/$/${PAGES.DISCOVER}`} exact component={DiscoverPage} />
{SIMPLE_SITE && <Route path={`/$/${PAGES.WILD_WEST}`} exact component={DiscoverPage} />}
{/* $FlowFixMe */} {/* $FlowFixMe */}
{dynamicRoutes.map((dynamicRouteProps: RowDataItem) => ( {dynamicRoutes.map((dynamicRouteProps: RowDataItem) => (
<Route <Route

View file

@ -16,6 +16,8 @@ const select = (state) => ({
...selectPublishFormValues(state), ...selectPublishFormValues(state),
myChannels: selectMyChannelClaims(state), myChannels: selectMyChannelClaims(state),
isVid: makeSelectPublishFormValue('fileVid')(state), isVid: makeSelectPublishFormValue('fileVid')(state),
publishSuccess: makeSelectPublishFormValue('publishSuccess')(state),
publishing: makeSelectPublishFormValue('publishing')(state),
isStillEditing: selectIsStillEditing(state), isStillEditing: selectIsStillEditing(state),
ffmpegStatus: selectFfmpegStatus(state), ffmpegStatus: selectFfmpegStatus(state),
enablePublishPreview: makeSelectClientSetting(SETTINGS.ENABLE_PUBLISH_PREVIEW)(state), enablePublishPreview: makeSelectClientSetting(SETTINGS.ENABLE_PUBLISH_PREVIEW)(state),

View file

@ -41,17 +41,59 @@ type Props = {
setEnablePublishPreview: (boolean) => void, setEnablePublishPreview: (boolean) => void,
isStillEditing: boolean, isStillEditing: boolean,
myChannels: ?Array<ChannelClaim>, myChannels: ?Array<ChannelClaim>,
publishSuccess: boolean,
publishing: boolean,
}; };
class ModalPublishPreview extends React.PureComponent<Props> { // class ModalPublishPreview extends React.PureComponent<Props> {
onConfirmed() { const ModalPublishPreview = (props: Props) => {
const { filePath, publish, closeModal } = this.props; const {
filePath,
isMarkdownPost,
optimize,
title,
description,
channel,
bid,
uri,
contentIsFree,
fee,
language,
licenseType,
otherLicenseDescription,
licenseUrl,
tags,
isVid,
ffmpegStatus = {},
previewResponse,
enablePublishPreview,
setEnablePublishPreview,
isStillEditing,
myChannels,
publishSuccess,
publishing,
publish,
closeModal,
} = props;
const livestream =
// $FlowFixMe
previewResponse.outputs[0] && previewResponse.outputs[0].value && !previewResponse.outputs[0].value.source;
// leave the confirm modal up if we're not going straight to upload/reflecting
React.useEffect(() => {
if (publishing && IS_WEB && !livestream) {
closeModal();
} else if (publishSuccess) {
closeModal();
}
}, [publishSuccess, publishing, livestream]);
// const waitForSuccess = false;
function onConfirmed() {
// Publish for real: // Publish for real:
publish(this.resolveFilePathName(filePath), false); publish(getFilePathName(filePath), false);
closeModal();
} }
resolveFilePathName(filePath: string | WebFile) { function getFilePathName(filePath: string | WebFile) {
if (!filePath) { if (!filePath) {
return NO_FILE; return NO_FILE;
} }
@ -63,7 +105,7 @@ class ModalPublishPreview extends React.PureComponent<Props> {
} }
} }
createRow(label: string, value: any) { function createRow(label: string, value: any) {
return ( return (
<tr> <tr>
<td>{label}</td> <td>{label}</td>
@ -72,54 +114,20 @@ class ModalPublishPreview extends React.PureComponent<Props> {
); );
} }
togglePreviewEnabled() { const txFee = previewResponse ? previewResponse['total_fee'] : null;
const { enablePublishPreview, setEnablePublishPreview } = this.props; // $FlowFixMe add outputs[0] etc to PublishResponse type
setEnablePublishPreview(!enablePublishPreview); const isOptimizeAvail = filePath && filePath !== '' && isVid && ffmpegStatus.available;
let modalTitle;
if (isStillEditing) {
modalTitle = __('Confirm Edit');
} else if (livestream) {
modalTitle = __('Create Livestream');
} else {
modalTitle = __('Confirm Upload');
} }
render() { let confirmBtnText;
const { if (!publishing) {
filePath,
isMarkdownPost,
optimize,
title,
description,
channel,
bid,
uri,
contentIsFree,
fee,
language,
licenseType,
otherLicenseDescription,
licenseUrl,
tags,
isVid,
ffmpegStatus = {},
previewResponse,
closeModal,
enablePublishPreview,
setEnablePublishPreview,
isStillEditing,
myChannels,
} = this.props;
const txFee = previewResponse ? previewResponse['total_fee'] : null;
// $FlowFixMe add outputs[0] etc to PublishResponse type
const livestream =
// $FlowFixMe
previewResponse.outputs[0] && previewResponse.outputs[0].value && !previewResponse.outputs[0].value.source;
const isOptimizeAvail = filePath && filePath !== '' && isVid && ffmpegStatus.available;
let modalTitle;
if (isStillEditing) {
modalTitle = __('Confirm Edit');
} else if (livestream) {
modalTitle = __('Create Livestream');
} else {
modalTitle = __('Confirm Upload');
}
let confirmBtnText;
if (isStillEditing) { if (isStillEditing) {
confirmBtnText = __('Save'); confirmBtnText = __('Save');
} else if (livestream) { } else if (livestream) {
@ -127,110 +135,119 @@ class ModalPublishPreview extends React.PureComponent<Props> {
} else { } else {
confirmBtnText = __('Upload'); confirmBtnText = __('Upload');
} }
const descriptionValue = description ? ( } else {
<div className="media__info-text-preview"> if (isStillEditing) {
<MarkdownPreview content={description} simpleLinks /> confirmBtnText = __('Saving');
</div> } else if (livestream) {
) : null; confirmBtnText = __('Creating');
} else {
const licenseValue = confirmBtnText = __('Uploading');
licenseType === COPYRIGHT ? (
<p>© {otherLicenseDescription}</p>
) : licenseType === OTHER ? (
<p>
{otherLicenseDescription}
<br />
{licenseUrl}
</p>
) : (
<p>{licenseType}</p>
);
const tagsValue =
// Do nothing for onClick(). Setting to 'null' results in "View Tag" action -- we don't want to leave the modal.
tags.map((tag) => <Tag key={tag.name} title={tag.name} name={tag.name} type={'flow'} onClick={() => {}} />);
const depositValue = bid ? <LbcSymbol postfix={`${bid}`} size={14} /> : <p>---</p>;
let priceValue = __('Free');
if (!contentIsFree) {
if (fee.currency === 'LBC') {
priceValue = <LbcSymbol postfix={fee.amount} />;
} else {
priceValue = `${fee.amount} ${fee.currency}`;
}
} }
const channelValue = (channel) => {
const channelClaim = myChannels && myChannels.find((x) => x.name === channel);
return channel ? (
<div className="channel-value">
{channelClaim && <ChannelThumbnail uri={channelClaim.permanent_url} />}
{channel}
</div>
) : (
<div className="channel-value">
<Icon sectionIcon icon={ICONS.ANONYMOUS} />
<i>{__('Anonymous')}</i>
</div>
);
};
return (
<Modal isOpen contentLabel={modalTitle} type="card" onAborted={closeModal}>
<Form onSubmit={() => this.onConfirmed()}>
<Card
title={modalTitle}
body={
<>
<div className="section">
<table className="table table--condensed table--publish-preview">
<tbody>
{!livestream && !isMarkdownPost && this.createRow(__('File'), this.resolveFilePathName(filePath))}
{isOptimizeAvail && this.createRow(__('Transcode'), optimize ? __('Yes') : __('No'))}
{this.createRow(__('Title'), title)}
{this.createRow(__('Description'), descriptionValue)}
{this.createRow(__('Channel'), channelValue(channel))}
{this.createRow(__('URL'), uri)}
{this.createRow(__('Deposit'), depositValue)}
{this.createRow(__('Price'), priceValue)}
{this.createRow(__('Language'), language)}
{this.createRow(__('License'), licenseValue)}
{this.createRow(__('Tags'), tagsValue)}
</tbody>
</table>
</div>
{txFee && (
<div className="section" aria-label={__('Estimated transaction fee:')}>
<b>{__('Est. transaction fee:')}</b>&nbsp;&nbsp;
<em>
<LbcSymbol postfix={txFee} />
</em>
</div>
)}
</>
}
actions={
<>
<div className="section__actions">
<Button autoFocus button="primary" label={confirmBtnText} onClick={() => this.onConfirmed()} />
<Button button="link" label={__('Cancel')} onClick={closeModal} />
</div>
<p className="help">{__('Once the transaction is sent, it cannot be reversed.')}</p>
<FormField
type="checkbox"
name="sync_toggle"
label={__('Skip preview and confirmation')}
checked={!enablePublishPreview}
onChange={() => setEnablePublishPreview(!enablePublishPreview)}
/>
</>
}
/>
</Form>
</Modal>
);
} }
}
const descriptionValue = description ? (
<div className="media__info-text-preview">
<MarkdownPreview content={description} simpleLinks />
</div>
) : null;
const licenseValue =
licenseType === COPYRIGHT ? (
<p>© {otherLicenseDescription}</p>
) : licenseType === OTHER ? (
<p>
{otherLicenseDescription}
<br />
{licenseUrl}
</p>
) : (
<p>{licenseType}</p>
);
const tagsValue =
// Do nothing for onClick(). Setting to 'null' results in "View Tag" action -- we don't want to leave the modal.
tags.map((tag) => <Tag key={tag.name} title={tag.name} name={tag.name} type={'flow'} onClick={() => {}} />);
const depositValue = bid ? <LbcSymbol postfix={`${bid}`} size={14} /> : <p>---</p>;
let priceValue = __('Free');
if (!contentIsFree) {
if (fee.currency === 'LBC') {
priceValue = <LbcSymbol postfix={fee.amount} />;
} else {
priceValue = `${fee.amount} ${fee.currency}`;
}
}
const channelValue = (channel) => {
const channelClaim = myChannels && myChannels.find((x) => x.name === channel);
return channel ? (
<div className="channel-value">
{channelClaim && <ChannelThumbnail uri={channelClaim.permanent_url} />}
{channel}
</div>
) : (
<div className="channel-value">
<Icon sectionIcon icon={ICONS.ANONYMOUS} />
<i>{__('Anonymous')}</i>
</div>
);
};
return (
<Modal isOpen contentLabel={modalTitle} type="card" onAborted={closeModal}>
<Form onSubmit={onConfirmed}>
<Card
title={modalTitle}
body={
<>
<div className="section">
<table className="table table--condensed table--publish-preview">
<tbody>
{!livestream && !isMarkdownPost && createRow(__('File'), getFilePathName(filePath))}
{isOptimizeAvail && createRow(__('Transcode'), optimize ? __('Yes') : __('No'))}
{createRow(__('Title'), title)}
{createRow(__('Description'), descriptionValue)}
{createRow(__('Channel'), channelValue(channel))}
{createRow(__('URL'), uri)}
{createRow(__('Deposit'), depositValue)}
{createRow(__('Price'), priceValue)}
{createRow(__('Language'), language)}
{createRow(__('License'), licenseValue)}
{createRow(__('Tags'), tagsValue)}
</tbody>
</table>
</div>
{txFee && (
<div className="section" aria-label={__('Estimated transaction fee:')}>
<b>{__('Est. transaction fee:')}</b>&nbsp;&nbsp;
<em>
<LbcSymbol postfix={txFee} />
</em>
</div>
)}
</>
}
actions={
<>
<div className="section__actions">
<Button autoFocus button="primary" disabled={publishing} label={confirmBtnText} onClick={onConfirmed} />
<Button button="link" label={__('Cancel')} onClick={closeModal} />
</div>
<p className="help">{__('Once the transaction is sent, it cannot be reversed.')}</p>
<FormField
type="checkbox"
name="sync_toggle"
label={__('Skip preview and confirmation')}
checked={!enablePublishPreview}
onChange={() => setEnablePublishPreview(!enablePublishPreview)}
/>
</>
}
/>
</Form>
</Modal>
);
};
export default ModalPublishPreview; export default ModalPublishPreview;

View file

@ -8,6 +8,8 @@ import {
makeSelectClaimIsNsfw, makeSelectClaimIsNsfw,
SETTINGS, SETTINGS,
makeSelectTagInClaimOrChannelForUri, makeSelectTagInClaimOrChannelForUri,
makeSelectClaimIsMine,
makeSelectClaimIsStreamPlaceholder,
} from 'lbry-redux'; } from 'lbry-redux';
import { makeSelectCostInfoForUri, doFetchCostInfoForUri } from 'lbryinc'; import { makeSelectCostInfoForUri, doFetchCostInfoForUri } from 'lbryinc';
import { selectShowMatureContent, makeSelectClientSetting } from 'redux/selectors/settings'; import { selectShowMatureContent, makeSelectClientSetting } from 'redux/selectors/settings';
@ -32,14 +34,16 @@ const select = (state, props) => {
renderMode: makeSelectFileRenderModeForUri(props.uri)(state), renderMode: makeSelectFileRenderModeForUri(props.uri)(state),
videoTheaterMode: makeSelectClientSetting(SETTINGS.VIDEO_THEATER_MODE)(state), videoTheaterMode: makeSelectClientSetting(SETTINGS.VIDEO_THEATER_MODE)(state),
commentsDisabled: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_COMMENTS_TAG)(state), commentsDisabled: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_COMMENTS_TAG)(state),
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
isLivestream: makeSelectClaimIsStreamPlaceholder(props.uri)(state),
}; };
}; };
const perform = dispatch => ({ const perform = (dispatch) => ({
fetchFileInfo: uri => dispatch(doFetchFileInfo(uri)), fetchFileInfo: (uri) => dispatch(doFetchFileInfo(uri)),
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)), fetchCostInfo: (uri) => dispatch(doFetchCostInfoForUri(uri)),
setViewed: uri => dispatch(doSetContentHistoryItem(uri)), setViewed: (uri) => dispatch(doSetContentHistoryItem(uri)),
setPrimaryUri: uri => dispatch(doSetPrimaryUri(uri)), setPrimaryUri: (uri) => dispatch(doSetPrimaryUri(uri)),
}); });
export default withRouter(connect(select, perform)(FilePage)); export default withRouter(connect(select, perform)(FilePage));

View file

@ -4,6 +4,7 @@ import React from 'react';
import Page from 'component/page'; import Page from 'component/page';
import LivestreamLayout from 'component/livestreamLayout'; import LivestreamLayout from 'component/livestreamLayout';
import analytics from 'analytics'; import analytics from 'analytics';
import { Lbry } from 'lbry-redux';
type Props = { type Props = {
uri: string, uri: string,
@ -19,8 +20,41 @@ export default function LivestreamPage(props: Props) {
const [activeViewers, setActiveViewers] = React.useState(0); const [activeViewers, setActiveViewers] = React.useState(0);
const [isLive, setIsLive] = React.useState(false); const [isLive, setIsLive] = React.useState(false);
const livestreamChannelId = channelClaim && channelClaim.signing_channel && channelClaim.signing_channel.claim_id; const livestreamChannelId = channelClaim && channelClaim.signing_channel && channelClaim.signing_channel.claim_id;
const [hasLivestreamClaim, setHasLivestreamClaim] = React.useState(false);
const STREAMING_POLL_INTERVAL_IN_MS = 10000;
const LIVESTREAM_CLAIM_POLL_IN_MS = 60000;
// the component needs to check if the channel has published a new livestream, so we know if it should check
React.useEffect(() => {
let checkClaimsInterval;
function checkHasLivestreamClaim() {
Lbry.claim_search({
channel_ids: [livestreamChannelId],
has_no_source: true,
claim_type: ['stream'],
})
.then((res) => {
if (res && res.items && res.items.length > 0) {
setHasLivestreamClaim(true);
}
})
.catch(() => {});
}
if (livestreamChannelId && !isLive) {
if (!checkClaimsInterval) checkHasLivestreamClaim();
checkClaimsInterval = setInterval(checkHasLivestreamClaim, LIVESTREAM_CLAIM_POLL_IN_MS);
return () => {
if (checkClaimsInterval) {
clearInterval(checkClaimsInterval);
}
};
}
}, [livestreamChannelId, isLive]);
React.useEffect(() => { React.useEffect(() => {
let interval;
function checkIsLive() { function checkIsLive() {
// $FlowFixMe Bitwave's API can handle garbage // $FlowFixMe Bitwave's API can handle garbage
fetch(`${BITWAVE_API}/${livestreamChannelId}`) fetch(`${BITWAVE_API}/${livestreamChannelId}`)
@ -38,19 +72,17 @@ export default function LivestreamPage(props: Props) {
} }
}); });
} }
if (livestreamChannelId && hasLivestreamClaim) {
let interval;
if (livestreamChannelId) {
if (!interval) checkIsLive(); if (!interval) checkIsLive();
interval = setInterval(checkIsLive, 10 * 1000); interval = setInterval(checkIsLive, STREAMING_POLL_INTERVAL_IN_MS);
}
return () => { return () => {
if (interval) { if (interval) {
clearInterval(interval); clearInterval(interval);
} }
}; };
}, [livestreamChannelId]); }
}, [livestreamChannelId, hasLivestreamClaim]);
const stringifiedClaim = JSON.stringify(claim); const stringifiedClaim = JSON.stringify(claim);
React.useEffect(() => { React.useEffect(() => {

View file

@ -24,16 +24,34 @@ type Props = {
}; };
export default function LivestreamSetupPage(props: Props) { export default function LivestreamSetupPage(props: Props) {
const LIVESTREAM_CLAIM_POLL_IN_MS = 60000;
const { channels, fetchingChannels, activeChannelClaim, pendingClaims } = props; const { channels, fetchingChannels, activeChannelClaim, pendingClaims } = props;
const [sigData, setSigData] = React.useState({ signature: undefined, signing_ts: undefined }); const [sigData, setSigData] = React.useState({ signature: undefined, signing_ts: undefined });
const [showHelpTest, setShowHelpTest] = usePersistedState('livestream-help-seen', true); const [showHelpTest, setShowHelpTest] = usePersistedState('livestream-help-seen', true);
const [spin, setSpin] = React.useState(true); const [spin, setSpin] = React.useState(true);
const [livestreamClaims, setLivestreamClaims] = React.useState([]);
const hasChannels = channels && channels.length > 0; const hasChannels = channels && channels.length > 0;
const activeChannelClaimStr = JSON.stringify(activeChannelClaim); const activeChannelClaimStr = JSON.stringify(activeChannelClaim);
const streamKey = createStreamKey();
function createStreamKey() {
if (!activeChannelClaim || !sigData.signature || !sigData.signing_ts) return null;
return `${activeChannelClaim.claim_id}?d=${toHex(activeChannelClaim.name)}&s=${sigData.signature}&t=${
sigData.signing_ts
}`;
}
const streamKey = createStreamKey();
const pendingLiveStreamClaims = pendingClaims
? pendingClaims.filter(
(claim) =>
// $FlowFixMe
claim.value_type === 'stream' && !(claim.value && claim.value.source)
)
: [];
const pendingLength = pendingLiveStreamClaims.length;
const totalLivestreamClaims = pendingLiveStreamClaims.concat(livestreamClaims);
const helpText = ( const helpText = (
<div className="section__subtitle"> <div className="section__subtitle">
<p> <p>
@ -76,6 +94,7 @@ export default function LivestreamSetupPage(props: Props) {
<p>{__(`Click Save and you are done!`)}</p> <p>{__(`Click Save and you are done!`)}</p>
</div> </div>
); );
React.useEffect(() => { React.useEffect(() => {
if (activeChannelClaimStr) { if (activeChannelClaimStr) {
const channelClaim = JSON.parse(activeChannelClaimStr); const channelClaim = JSON.parse(activeChannelClaimStr);
@ -96,47 +115,44 @@ export default function LivestreamSetupPage(props: Props) {
} }
}, [activeChannelClaimStr, setSigData]); }, [activeChannelClaimStr, setSigData]);
function createStreamKey() {
if (!activeChannelClaim || !sigData.signature || !sigData.signing_ts) return null;
return `${activeChannelClaim.claim_id}?d=${toHex(activeChannelClaim.name)}&s=${sigData.signature}&t=${
sigData.signing_ts
}`;
}
const [livestreamClaims, setLivestreamClaims] = React.useState([]);
const pendingLiveStreamClaims =
// $FlowFixMe
pendingClaims ? pendingClaims.filter((claim) => !(claim && claim.value && claim.value.source)) : [];
const pendingLength = pendingLiveStreamClaims.length;
const totalLivestreamClaims = pendingLiveStreamClaims.concat(livestreamClaims);
React.useEffect(() => { React.useEffect(() => {
let checkClaimsInterval;
if (!activeChannelClaimStr) return; if (!activeChannelClaimStr) return;
const channelClaim = JSON.parse(activeChannelClaimStr); const channelClaim = JSON.parse(activeChannelClaimStr);
Lbry.claim_search({ function checkLivestreamClaims() {
channel_ids: [channelClaim.claim_id], Lbry.claim_search({
has_no_source: true, channel_ids: [channelClaim.claim_id],
claim_type: ['stream'], has_no_source: true,
}) claim_type: ['stream'],
.then((res) => {
if (res && res.items && res.items.length > 0) {
setLivestreamClaims(res.items.reverse());
} else {
setLivestreamClaims([]);
}
setSpin(false);
}) })
.catch(() => { .then((res) => {
setLivestreamClaims([]); if (res && res.items && res.items.length > 0) {
setSpin(false); setLivestreamClaims(res.items.reverse());
}); } else {
setLivestreamClaims([]);
}
setSpin(false);
})
.catch(() => {
setLivestreamClaims([]);
setSpin(false);
});
}
if (!checkClaimsInterval) {
checkLivestreamClaims();
checkClaimsInterval = setInterval(checkLivestreamClaims, LIVESTREAM_CLAIM_POLL_IN_MS);
}
return () => {
if (checkClaimsInterval) {
clearInterval(checkClaimsInterval);
}
};
}, [activeChannelClaimStr, pendingLength, setSpin]); }, [activeChannelClaimStr, pendingLength, setSpin]);
return ( return (
<Page> <Page>
{fetchingChannels && ( {(fetchingChannels || spin) && (
<div className="main--empty"> <div className="main--empty">
<Spinner delayed /> <Spinner delayed />
</div> </div>

View file

@ -9,6 +9,8 @@ import {
doCheckPendingClaims, doCheckPendingClaims,
doCheckReflectingFiles, doCheckReflectingFiles,
ACTIONS as LBRY_REDUX_ACTIONS, ACTIONS as LBRY_REDUX_ACTIONS,
makeSelectPublishFormValue,
makeSelectClaimForUri,
} from 'lbry-redux'; } from 'lbry-redux';
import { doError } from 'redux/actions/notifications'; import { doError } from 'redux/actions/notifications';
import { push } from 'connected-react-router'; import { push } from 'connected-react-router';
@ -24,7 +26,12 @@ export const doPublishDesktop = (filePath: string, preview?: boolean) => (dispat
); );
}; };
const noFile = !filePath || filePath === NO_FILE; const noFileParam = !filePath || filePath === NO_FILE;
const state = getState();
const editingUri = makeSelectPublishFormValue('editingURI')(state) || '';
const claim = makeSelectClaimForUri(editingUri)(state) || {};
const hasSourceFile = claim.value && claim.value.source;
const redirectToLivestream = noFileParam && !hasSourceFile;
const publishSuccess = (successResponse, lbryFirstError) => { const publishSuccess = (successResponse, lbryFirstError) => {
const state = getState(); const state = getState();
@ -41,6 +48,7 @@ export const doPublishDesktop = (filePath: string, preview?: boolean) => (dispat
actions.push({ actions.push({
type: ACTIONS.PUBLISH_SUCCESS, type: ACTIONS.PUBLISH_SUCCESS,
}); });
// We have to fake a temp claim until the new pending one is returned by claim_list_mine // We have to fake a temp claim until the new pending one is returned by claim_list_mine
// We can't rely on claim_list_mine because there might be some delay before the new claims are returned // We can't rely on claim_list_mine because there might be some delay before the new claims are returned
// Doing this allows us to show the pending claim immediately, it will get overwritten by the real one // Doing this allows us to show the pending claim immediately, it will get overwritten by the real one
@ -73,6 +81,11 @@ export const doPublishDesktop = (filePath: string, preview?: boolean) => (dispat
// @if TARGET='app' // @if TARGET='app'
dispatch(doCheckReflectingFiles()); dispatch(doCheckReflectingFiles());
// @endif // @endif
// @if TARGET='web'
if (redirectToLivestream) {
dispatch(push(`/$/${PAGES.LIVESTREAM}`));
}
// @endif
}; };
const publishFail = (error) => { const publishFail = (error) => {
@ -93,7 +106,9 @@ export const doPublishDesktop = (filePath: string, preview?: boolean) => (dispat
// on the publishes page. This doesn't exist on desktop so wait until we get a response // on the publishes page. This doesn't exist on desktop so wait until we get a response
// from the SDK // from the SDK
// @if TARGET='web' // @if TARGET='web'
dispatch(push(noFile ? `/$/${PAGES.LIVESTREAM}` : `/$/${PAGES.UPLOADS}`)); if (!redirectToLivestream) {
dispatch(push(`/$/${PAGES.UPLOADS}`));
}
// @endif // @endif
dispatch(doPublish(publishSuccess, publishFail)); dispatch(doPublish(publishSuccess, publishFail));

View file

@ -6950,9 +6950,9 @@ lazy-val@^1.0.4:
yargs "^13.2.2" yargs "^13.2.2"
zstd-codec "^0.1.1" zstd-codec "^0.1.1"
lbry-redux@lbryio/lbry-redux#8e74e3137ae78f5d99819b72ba48eecb51015622: lbry-redux@lbryio/lbry-redux#35088a6d1007f877a33baf62d67b37669edea043:
version "0.0.1" version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/8e74e3137ae78f5d99819b72ba48eecb51015622" resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/35088a6d1007f877a33baf62d67b37669edea043"
dependencies: dependencies:
proxy-polyfill "0.1.6" proxy-polyfill "0.1.6"
reselect "^3.0.0" reselect "^3.0.0"