diff --git a/app/javascript/mastodon/features/explore/components/story.jsx b/app/javascript/mastodon/features/explore/components/story.jsx index 42b65e7dc..92eb41cff 100644 --- a/app/javascript/mastodon/features/explore/components/story.jsx +++ b/app/javascript/mastodon/features/explore/components/story.jsx @@ -1,10 +1,13 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; +import { FormattedMessage } from 'react-intl'; + import classNames from 'classnames'; import { Blurhash } from 'mastodon/components/blurhash'; import { accountsCountRenderer } from 'mastodon/components/hashtag'; +import { RelativeTimestamp } from 'mastodon/components/relative_timestamp'; import { ShortNumber } from 'mastodon/components/short_number'; import { Skeleton } from 'mastodon/components/skeleton'; @@ -15,9 +18,12 @@ export default class Story extends PureComponent { title: PropTypes.string, lang: PropTypes.string, publisher: PropTypes.string, + publishedAt: PropTypes.string, + author: PropTypes.string, sharedTimes: PropTypes.number, thumbnail: PropTypes.string, blurhash: PropTypes.string, + expanded: PropTypes.bool, }; state = { @@ -27,16 +33,16 @@ export default class Story extends PureComponent { handleImageLoad = () => this.setState({ thumbnailLoaded: true }); render () { - const { url, title, lang, publisher, sharedTimes, thumbnail, blurhash } = this.props; + const { expanded, url, title, lang, publisher, author, publishedAt, sharedTimes, thumbnail, blurhash } = this.props; const { thumbnailLoaded } = this.state; return ( - <a className='story' href={url} target='blank' rel='noopener'> + <a className={classNames('story', { expanded })} href={url} target='blank' rel='noopener'> <div className='story__details'> - <div className='story__details__publisher' lang={lang}>{publisher ? publisher : <Skeleton width={50} />}</div> + <div className='story__details__publisher'>{publisher ? <span lang={lang}>{publisher}</span> : <Skeleton width={50} />}{publishedAt && <> · <RelativeTimestamp timestamp={publishedAt} /></>}</div> <div className='story__details__title' lang={lang}>{title ? title : <Skeleton />}</div> - <div className='story__details__shared'>{typeof sharedTimes === 'number' ? <ShortNumber value={sharedTimes} renderer={accountsCountRenderer} /> : <Skeleton width={100} />}</div> + <div className='story__details__shared'>{author && <><FormattedMessage id='link_preview.author' defaultMessage='By {name}' values={{ name: <strong>{author}</strong> }} /> · </>}{typeof sharedTimes === 'number' ? <ShortNumber value={sharedTimes} renderer={accountsCountRenderer} /> : <Skeleton width={100} />}</div> </div> <div className='story__thumbnail'> diff --git a/app/javascript/mastodon/features/explore/links.jsx b/app/javascript/mastodon/features/explore/links.jsx index 6f402decb..489ab6dd6 100644 --- a/app/javascript/mastodon/features/explore/links.jsx +++ b/app/javascript/mastodon/features/explore/links.jsx @@ -55,13 +55,16 @@ class Links extends PureComponent { <div className='explore__links'> {banner} - {isLoading ? (<LoadingIndicator />) : links.map(link => ( + {isLoading ? (<LoadingIndicator />) : links.map((link, i) => ( <Story key={link.get('id')} + expanded={i === 0} lang={link.get('language')} url={link.get('url')} title={link.get('title')} publisher={link.get('provider_name')} + publishedAt={link.get('published_at')} + author={link.get('author_name')} sharedTimes={link.getIn(['history', 0, 'accounts']) * 1 + link.getIn(['history', 1, 'accounts']) * 1} thumbnail={link.get('image')} blurhash={link.get('blurhash')} diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index e75e55fb3..2d1d4aa47 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -8164,8 +8164,9 @@ noscript { align-items: center; color: $primary-text-color; text-decoration: none; - padding: 15px 0; + padding: 15px; border-bottom: 1px solid lighten($ui-base-color, 8%); + gap: 15px; &:last-child { border-bottom: 0; @@ -8174,33 +8175,40 @@ noscript { &:hover, &:active, &:focus { - background-color: lighten($ui-base-color, 4%); + color: $highlight-text-color; + + .story__details__publisher, + .story__details__shared { + color: $highlight-text-color; + } } &__details { - padding: 0 15px; flex: 1 1 auto; &__publisher { color: $darker-text-color; - margin-bottom: 4px; + margin-bottom: 8px; } &__title { font-size: 19px; line-height: 24px; font-weight: 500; - margin-bottom: 4px; + margin-bottom: 8px; } &__shared { color: $darker-text-color; } + + strong { + font-weight: 500; + } } &__thumbnail { flex: 0 0 auto; - margin: 0 15px; position: relative; width: 120px; height: 120px; @@ -8211,7 +8219,7 @@ noscript { } img { - border-radius: 4px; + border-radius: 8px; display: block; margin: 0; width: 100%; @@ -8220,7 +8228,7 @@ noscript { } &__preview { - border-radius: 4px; + border-radius: 8px; display: block; margin: 0; width: 100%; @@ -8236,6 +8244,23 @@ noscript { } } } + + &.expanded { + flex-direction: column; + + .story__thumbnail { + order: 1; + width: 100%; + height: auto; + aspect-ratio: 1.91 / 1; + } + + .story__details { + order: 2; + width: 100%; + flex: 0 0 auto; + } + } } .server-banner {