diff --git a/app/javascript/flavours/glitch/actions/importer/index.js b/app/javascript/flavours/glitch/actions/importer/index.js index 5fbc9bb5bb..63a28eb0ed 100644 --- a/app/javascript/flavours/glitch/actions/importer/index.js +++ b/app/javascript/flavours/glitch/actions/importer/index.js @@ -68,13 +68,17 @@ export function importFetchedStatuses(statuses) { status.filtered.forEach(result => pushUnique(filters, result.filter)); } - if (status.reblog && status.reblog.id) { + if (status.reblog?.id) { processStatus(status.reblog); } - if (status.poll && status.poll.id) { + if (status.poll?.id) { pushUnique(polls, normalizePoll(status.poll, getState().getIn(['polls', status.poll.id]))); } + + if (status.card?.author_account) { + pushUnique(accounts, status.card.author_account); + } } statuses.forEach(processStatus); diff --git a/app/javascript/flavours/glitch/actions/importer/normalizer.js b/app/javascript/flavours/glitch/actions/importer/normalizer.js index c2ad0f9908..8f5bda89b5 100644 --- a/app/javascript/flavours/glitch/actions/importer/normalizer.js +++ b/app/javascript/flavours/glitch/actions/importer/normalizer.js @@ -36,6 +36,10 @@ export function normalizeStatus(status, normalOldStatus, settings) { normalStatus.poll = status.poll.id; } + if (status.card?.author_account) { + normalStatus.card = { ...status.card, author_account: status.card.author_account.id }; + } + if (status.filtered) { normalStatus.filtered = status.filtered.map(normalizeFilterResult); } diff --git a/app/javascript/flavours/glitch/actions/trends.js b/app/javascript/flavours/glitch/actions/trends.js index 0b840b41ce..01089fccbb 100644 --- a/app/javascript/flavours/glitch/actions/trends.js +++ b/app/javascript/flavours/glitch/actions/trends.js @@ -1,6 +1,6 @@ import api, { getLinks } from '../api'; -import { importFetchedStatuses } from './importer'; +import { importFetchedStatuses, importFetchedAccounts } from './importer'; export const TRENDS_TAGS_FETCH_REQUEST = 'TRENDS_TAGS_FETCH_REQUEST'; export const TRENDS_TAGS_FETCH_SUCCESS = 'TRENDS_TAGS_FETCH_SUCCESS'; @@ -49,8 +49,11 @@ export const fetchTrendingLinks = () => (dispatch) => { dispatch(fetchTrendingLinksRequest()); api() - .get('/api/v1/trends/links') - .then(({ data }) => dispatch(fetchTrendingLinksSuccess(data))) + .get('/api/v1/trends/links', { params: { limit: 20 } }) + .then(({ data }) => { + dispatch(importFetchedAccounts(data.map(link => link.author_account).filter(account => !!account))); + dispatch(fetchTrendingLinksSuccess(data)); + }) .catch(err => dispatch(fetchTrendingLinksFail(err))); }; diff --git a/app/javascript/flavours/glitch/components/more_from_author.jsx b/app/javascript/flavours/glitch/components/more_from_author.jsx new file mode 100644 index 0000000000..4f20ae76bf --- /dev/null +++ b/app/javascript/flavours/glitch/components/more_from_author.jsx @@ -0,0 +1,19 @@ +import PropTypes from 'prop-types'; + +import { FormattedMessage } from 'react-intl'; + +import { AuthorLink } from 'flavours/glitch/features/explore/components/author_link'; + +export const MoreFromAuthor = ({ accountId }) => ( +
+ + + + + }} /> +
+); + +MoreFromAuthor.propTypes = { + accountId: PropTypes.string.isRequired, +}; diff --git a/app/javascript/flavours/glitch/features/explore/components/author_link.jsx b/app/javascript/flavours/glitch/features/explore/components/author_link.jsx new file mode 100644 index 0000000000..94061c0092 --- /dev/null +++ b/app/javascript/flavours/glitch/features/explore/components/author_link.jsx @@ -0,0 +1,20 @@ +import PropTypes from 'prop-types'; + +import { Avatar } from 'flavours/glitch/components/avatar'; +import { Permalink } from 'flavours/glitch/components/permalink'; +import { useAppSelector } from 'flavours/glitch/store'; + +export const AuthorLink = ({ accountId }) => { + const account = useAppSelector(state => state.getIn(['accounts', accountId])); + + return ( + + + + + ); +}; + +AuthorLink.propTypes = { + accountId: PropTypes.string.isRequired, +}; diff --git a/app/javascript/flavours/glitch/features/explore/components/story.jsx b/app/javascript/flavours/glitch/features/explore/components/story.jsx index 0e5ab92122..28a1d69f8a 100644 --- a/app/javascript/flavours/glitch/features/explore/components/story.jsx +++ b/app/javascript/flavours/glitch/features/explore/components/story.jsx @@ -1,61 +1,89 @@ import PropTypes from 'prop-types'; -import { PureComponent } from 'react'; +import { useState, useCallback } from 'react'; import { FormattedMessage } from 'react-intl'; import classNames from 'classnames'; + import { Blurhash } from 'flavours/glitch/components/blurhash'; -import { accountsCountRenderer } from 'flavours/glitch/components/hashtag'; import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp'; import { ShortNumber } from 'flavours/glitch/components/short_number'; import { Skeleton } from 'flavours/glitch/components/skeleton'; -export default class Story extends PureComponent { +import { AuthorLink } from './author_link'; - static propTypes = { - url: PropTypes.string, - title: PropTypes.string, - lang: PropTypes.string, - publisher: PropTypes.string, - publishedAt: PropTypes.string, - author: PropTypes.string, - sharedTimes: PropTypes.number, - thumbnail: PropTypes.string, - thumbnailDescription: PropTypes.string, - blurhash: PropTypes.string, - expanded: PropTypes.bool, - }; +const sharesCountRenderer = (displayNumber, pluralReady) => ( + {displayNumber}, + }} + /> +); - state = { - thumbnailLoaded: false, - }; +export const Story = ({ + url, + title, + lang, + publisher, + publishedAt, + author, + authorAccount, + sharedTimes, + thumbnail, + thumbnailDescription, + blurhash, + expanded +}) => { + const [thumbnailLoaded, setThumbnailLoaded] = useState(false); - handleImageLoad = () => this.setState({ thumbnailLoaded: true }); + const handleImageLoad = useCallback(() => { + setThumbnailLoaded(true); + }, [setThumbnailLoaded]); - render () { - const { expanded, url, title, lang, publisher, author, publishedAt, sharedTimes, thumbnail, thumbnailDescription, blurhash } = this.props; - - const { thumbnailLoaded } = this.state; - - return ( - -
-
{publisher ? {publisher} : }{publishedAt && <> · }
-
{title ? title : }
-
{author && <>{author} }} /> · }{typeof sharedTimes === 'number' ? : }
+ return ( +
+
+
+ {publisher ? {publisher} : }{publishedAt && <> · }
-
- {thumbnail ? ( - <> -
- {thumbnailDescription} - - ) : } +
+ {title ? title : } + + +
+ {author ? : {author} }} /> : } + {typeof sharedTimes === 'number' ? : }
+
+ + + {thumbnail ? ( + <> +
+ {thumbnailDescription} + + ) : }
- ); - } +
+ ); +}; -} +Story.propTypes = { + url: PropTypes.string, + title: PropTypes.string, + lang: PropTypes.string, + publisher: PropTypes.string, + publishedAt: PropTypes.string, + author: PropTypes.string, + authorAccount: PropTypes.string, + sharedTimes: PropTypes.number, + thumbnail: PropTypes.string, + thumbnailDescription: PropTypes.string, + blurhash: PropTypes.string, + expanded: PropTypes.bool, +}; diff --git a/app/javascript/flavours/glitch/features/explore/links.jsx b/app/javascript/flavours/glitch/features/explore/links.jsx index 5dee66d183..dc15030f72 100644 --- a/app/javascript/flavours/glitch/features/explore/links.jsx +++ b/app/javascript/flavours/glitch/features/explore/links.jsx @@ -13,7 +13,7 @@ import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; -import Story from './components/story'; +import { Story } from './components/story'; const mapStateToProps = state => ({ links: state.getIn(['trends', 'links', 'items']), @@ -75,6 +75,7 @@ class Links extends PureComponent { publisher={link.get('provider_name')} publishedAt={link.get('published_at')} author={link.get('author_name')} + authorAccount={link.getIn(['author_account', 'id'])} sharedTimes={link.getIn(['history', 0, 'accounts']) * 1 + link.getIn(['history', 1, 'accounts']) * 1} thumbnail={link.get('image')} thumbnailDescription={link.get('image_description')} diff --git a/app/javascript/flavours/glitch/features/status/components/card.jsx b/app/javascript/flavours/glitch/features/status/components/card.jsx index c6094d0beb..c9b0f7ebaf 100644 --- a/app/javascript/flavours/glitch/features/status/components/card.jsx +++ b/app/javascript/flavours/glitch/features/status/components/card.jsx @@ -11,10 +11,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import DescriptionIcon from '@/material-icons/400-24px/description-fill.svg?react'; import OpenInNewIcon from '@/material-icons/400-24px/open_in_new.svg?react'; import PlayArrowIcon from '@/material-icons/400-24px/play_arrow-fill.svg?react'; -import { Avatar } from 'flavours/glitch/components/avatar'; import { Blurhash } from 'flavours/glitch/components/blurhash'; import { Icon } from 'flavours/glitch/components/icon'; -import { Permalink } from 'flavours/glitch/components/permalink'; +import { MoreFromAuthor } from 'flavours/glitch/components/more_from_author'; import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp'; import { useBlurhash } from 'flavours/glitch/initial_state'; import { decode as decodeIDNA } from 'flavours/glitch/utils/idna'; @@ -48,20 +47,6 @@ const addAutoPlay = html => { return html; }; -const MoreFromAuthor = ({ author }) => ( -
- - - - - {author.get('display_name')} }} /> -
-); - -MoreFromAuthor.propTypes = { - author: ImmutablePropTypes.map, -}; - export default class Card extends PureComponent { static propTypes = { @@ -248,7 +233,7 @@ export default class Card extends PureComponent { {description} - {showAuthor && } + {showAuthor && } ); } diff --git a/app/javascript/flavours/glitch/styles/components.scss b/app/javascript/flavours/glitch/styles/components.scss index e82d7aadc7..32cca1a801 100644 --- a/app/javascript/flavours/glitch/styles/components.scss +++ b/app/javascript/flavours/glitch/styles/components.scss @@ -9379,43 +9379,80 @@ noscript { display: flex; align-items: center; color: $primary-text-color; - text-decoration: none; - padding: 15px; + padding: 16px; border-bottom: 1px solid var(--background-border-color); - gap: 15px; + gap: 16px; &:last-child { border-bottom: 0; } - &:hover, - &:active, - &:focus { - color: $highlight-text-color; - - .story__details__publisher, - .story__details__shared { - color: $highlight-text-color; - } - } - &__details { flex: 1 1 auto; &__publisher { color: $darker-text-color; margin-bottom: 8px; + font-size: 14px; + line-height: 20px; } &__title { + display: block; font-size: 19px; line-height: 24px; font-weight: 500; margin-bottom: 8px; + text-decoration: none; + color: $primary-text-color; + + &:hover, + &:active, + &:focus { + color: $highlight-text-color; + } } &__shared { + display: flex; + align-items: center; color: $darker-text-color; + gap: 8px; + justify-content: space-between; + font-size: 14px; + line-height: 20px; + + & > span { + display: flex; + align-items: center; + gap: 4px; + } + + &__pill { + background: var(--surface-variant-background-color); + border-radius: 4px; + color: inherit; + text-decoration: none; + padding: 4px 12px; + font-size: 12px; + font-weight: 500; + line-height: 16px; + } + + &__author-link { + display: inline-flex; + align-items: center; + gap: 4px; + color: $primary-text-color; + font-weight: 500; + text-decoration: none; + + &:hover, + &:active, + &:focus { + color: $highlight-text-color; + } + } } strong { @@ -10486,14 +10523,14 @@ noscript { color: inherit; text-decoration: none; padding: 4px 12px; - background: $ui-base-color; + background: var(--surface-variant-background-color); border-radius: 4px; font-weight: 500; &:hover, &:focus, &:active { - background: lighten($ui-base-color, 4%); + background: var(--surface-variant-active-background-color); } } diff --git a/app/javascript/flavours/glitch/styles/variables.scss b/app/javascript/flavours/glitch/styles/variables.scss index 54dc54ac02..3a12809483 100644 --- a/app/javascript/flavours/glitch/styles/variables.scss +++ b/app/javascript/flavours/glitch/styles/variables.scss @@ -112,4 +112,6 @@ $dismiss-overlay-width: 4rem; --background-color: #{darken($ui-base-color, 8%)}; --background-color-tint: #{rgba(darken($ui-base-color, 8%), 0.9)}; --surface-background-color: #{darken($ui-base-color, 4%)}; + --surface-variant-background-color: #{$ui-base-color}; + --surface-variant-active-background-color: #{lighten($ui-base-color, 4%)}; }