diff --git a/ui/component/claimPreview/index.js b/ui/component/claimPreview/index.js index 611e60e20..c35b0c8ce 100644 --- a/ui/component/claimPreview/index.js +++ b/ui/component/claimPreview/index.js @@ -15,6 +15,8 @@ import { doCollectionEdit, makeSelectUrlsForCollectionId, makeSelectIndexForUrlInCollection, + makeSelectTitleForUri, + makeSelectDateForUri, } from 'lbry-redux'; import { selectMutedChannels, makeSelectChannelIsMuted } from 'redux/selectors/blocked'; import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc'; @@ -22,32 +24,43 @@ import { selectShowMatureContent } from 'redux/selectors/settings'; import { makeSelectHasVisitedUri } from 'redux/selectors/content'; import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions'; import { selectModerationBlockList } from 'redux/selectors/comments'; -import ClaimPreview from './view'; -const select = (state, props) => ({ - pending: props.uri && makeSelectClaimIsPending(props.uri)(state), - claim: props.uri && makeSelectClaimForUri(props.uri)(state), - reflectingProgress: props.uri && makeSelectReflectingClaimForUri(props.uri)(state), - obscureNsfw: selectShowMatureContent(state) === false, - claimIsMine: props.uri && makeSelectClaimIsMine(props.uri)(state), - isResolvingUri: props.uri && makeSelectIsUriResolving(props.uri)(state), - isResolvingRepost: props.uri && makeSelectIsUriResolving(props.repostUrl)(state), - repostClaim: props.uri && makeSelectClaimForUri(props.uri)(state), - nsfw: props.uri && makeSelectClaimIsNsfw(props.uri)(state), - blackListedOutpoints: selectBlackListedOutpoints(state), - filteredOutpoints: selectFilteredOutpoints(state), - mutedUris: selectMutedChannels(state), - blockedUris: selectModerationBlockList(state), - hasVisitedUri: props.uri && makeSelectHasVisitedUri(props.uri)(state), - channelIsBlocked: props.uri && makeSelectChannelIsMuted(props.uri)(state), - isSubscribed: props.uri && makeSelectIsSubscribed(props.uri, true)(state), - streamingUrl: props.uri && makeSelectStreamingUrlForUri(props.uri)(state), - wasPurchased: props.uri && makeSelectClaimWasPurchased(props.uri)(state), - isLivestream: makeSelectClaimIsStreamPlaceholder(props.uri)(state), - isCollectionMine: makeSelectCollectionIsMine(props.collectionId)(state), - collectionUris: makeSelectUrlsForCollectionId(props.collectionId)(state), - collectionIndex: makeSelectIndexForUrlInCollection(props.uri, props.collectionId)(state), -}); +import ClaimPreview from './view'; +import formatMediaDuration from 'util/formatMediaDuration'; + +const select = (state, props) => { + const claim = props.uri && makeSelectClaimForUri(props.uri)(state); + const media = claim && claim.value && (claim.value.video || claim.value.audio); + const mediaDuration = media && media.duration && formatMediaDuration(media.duration, { screenReader: true }); + + return { + claim, + mediaDuration, + date: props.uri && makeSelectDateForUri(props.uri)(state), + title: props.uri && makeSelectTitleForUri(props.uri)(state), + pending: props.uri && makeSelectClaimIsPending(props.uri)(state), + reflectingProgress: props.uri && makeSelectReflectingClaimForUri(props.uri)(state), + obscureNsfw: selectShowMatureContent(state) === false, + claimIsMine: props.uri && makeSelectClaimIsMine(props.uri)(state), + isResolvingUri: props.uri && makeSelectIsUriResolving(props.uri)(state), + isResolvingRepost: props.uri && makeSelectIsUriResolving(props.repostUrl)(state), + repostClaim: props.uri && makeSelectClaimForUri(props.uri)(state), + nsfw: props.uri && makeSelectClaimIsNsfw(props.uri)(state), + blackListedOutpoints: selectBlackListedOutpoints(state), + filteredOutpoints: selectFilteredOutpoints(state), + mutedUris: selectMutedChannels(state), + blockedUris: selectModerationBlockList(state), + hasVisitedUri: props.uri && makeSelectHasVisitedUri(props.uri)(state), + channelIsBlocked: props.uri && makeSelectChannelIsMuted(props.uri)(state), + isSubscribed: props.uri && makeSelectIsSubscribed(props.uri, true)(state), + streamingUrl: props.uri && makeSelectStreamingUrlForUri(props.uri)(state), + wasPurchased: props.uri && makeSelectClaimWasPurchased(props.uri)(state), + isLivestream: makeSelectClaimIsStreamPlaceholder(props.uri)(state), + isCollectionMine: makeSelectCollectionIsMine(props.collectionId)(state), + collectionUris: makeSelectUrlsForCollectionId(props.collectionId)(state), + collectionIndex: makeSelectIndexForUrlInCollection(props.uri, props.collectionId)(state), + }; +}; const perform = (dispatch) => ({ resolveUri: (uri) => dispatch(doResolveUri(uri)), diff --git a/ui/component/claimPreview/view.jsx b/ui/component/claimPreview/view.jsx index 7f67b2774..2eaa6187d 100644 --- a/ui/component/claimPreview/view.jsx +++ b/ui/component/claimPreview/view.jsx @@ -27,6 +27,7 @@ import ClaimPreviewHidden from './claim-preview-no-mature'; import ClaimPreviewNoContent from './claim-preview-no-content'; import { ENABLE_NO_SOURCE_CLAIMS } from 'config'; import Button from 'component/button'; +import DateTime from 'component/dateTime'; import * as ICONS from 'constants/icons'; const AbandonedChannelPreview = lazyImport(() => @@ -84,6 +85,8 @@ type Props = { collectionUris: Array, collectionIndex?: number, disableNavigation?: boolean, + mediaDuration?: string, + date?: any, }; const ClaimPreview = forwardRef((props: Props, ref: any) => { @@ -98,8 +101,11 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { // claim properties // is the claim consider nsfw? nsfw, + date, + title, claimIsMine, streamingUrl, + mediaDuration, // user properties channelIsBlocked, hasVisitedUri, @@ -172,6 +178,23 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { const isCollection = claim && claim.value_type === 'collection'; const isChannelUri = isValid ? parseURI(uri).isChannel : false; const signingChannel = claim && claim.signing_channel; + const channelTitle = signingChannel && (signingChannel.value.title || signingChannel.name); + + // Aria-label value for claim preview + let ariaLabelData = title; + + if (!isChannelUri && channelTitle) { + ariaLabelData += ' ' + __('by %channelTitle%', { channelTitle }); + } + + if (date) { + ariaLabelData += ' ' + DateTime.getTimeAgoStr(date); + } + + if (mediaDuration) { + ariaLabelData += ', ' + mediaDuration; + } + let navigateUrl = formatLbryUrlForWeb((claim && claim.canonical_url) || uri || '/'); if (collectionId) { const collectionParams = new URLSearchParams(); @@ -310,7 +333,7 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { })} > {isChannelUri && claim ? ( - + ) : ( @@ -349,7 +372,7 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { {pending ? ( ) : ( - + )} diff --git a/ui/component/claimPreviewTile/view.jsx b/ui/component/claimPreviewTile/view.jsx index 3da1c308b..efb5e8aa4 100644 --- a/ui/component/claimPreviewTile/view.jsx +++ b/ui/component/claimPreviewTile/view.jsx @@ -268,7 +268,7 @@ function ClaimPreviewTile(props: Props) { ) : ( - + diff --git a/ui/component/uriIndicator/view.jsx b/ui/component/uriIndicator/view.jsx index ba3d36376..34c362fe2 100644 --- a/ui/component/uriIndicator/view.jsx +++ b/ui/component/uriIndicator/view.jsx @@ -19,6 +19,7 @@ type Props = { inline: boolean, external?: boolean, className?: string, + focusable: boolean, }; class UriIndicator extends React.PureComponent { @@ -45,10 +46,10 @@ class UriIndicator extends React.PureComponent { claim, children, inline, - hideAnonymous = false, + focusable = true, external = false, + hideAnonymous = false, className, - ...props } = this.props; if (!claim) { @@ -87,7 +88,13 @@ class UriIndicator extends React.PureComponent { if (children) { return ( - ); @@ -97,7 +104,8 @@ class UriIndicator extends React.PureComponent { className={classnames(className, 'button--uri-indicator')} navigate={channelLink} target={external ? '_blank' : undefined} - {...props} + aria-hidden={!focusable} + tabIndex={focusable ? 0 : -1} > {inner}