Skip to content

Commit

Permalink
Fact-checks list (#1958)
Browse files Browse the repository at this point in the history
List of fact-checks.

Reference: CV2-4145.
  • Loading branch information
caiosba authored and danielevalverde committed Jul 22, 2024
1 parent 66b28d0 commit 583498f
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 47 deletions.
22 changes: 22 additions & 0 deletions localization/react-intl/src/app/components/article/FactChecks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[
{
"id": "factChecks.sortTitle",
"description": "Label for sort criteria option displayed in a drop-down in the fact-checks page.",
"defaultMessage": "Title"
},
{
"id": "factChecks.sortLanguage",
"description": "Label for sort criteria option displayed in a drop-down in the fact-checks page.",
"defaultMessage": "Language"
},
{
"id": "factChecks.sortDate",
"description": "Label for sort criteria option displayed in a drop-down in the fact-checks page.",
"defaultMessage": "Updated (date)"
},
{
"id": "factChecks.title",
"description": "Title of the fact-checks page.",
"defaultMessage": "Claim & Fact-checks"
}
]
2 changes: 2 additions & 0 deletions src/app/components/Root.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import Sandbox from './Sandbox';
import SandboxCrash from './SandboxCrash';
import FeedPage from './feed/FeedPage';
import Explainers from './article/Explainers';
import FactChecks from './article/FactChecks';

class Root extends Component {
static logPageView() {
Expand Down Expand Up @@ -122,6 +123,7 @@ class Root extends Component {
<Route path=":team/spam(/:query)" component={Spam} />
<Route path=":team/trash(/:query)" component={Trash} />
<Route path=":team/articles/explainers" component={Explainers} />
<Route path=":team/articles/fact-checks" component={FactChecks} />
<Route path="*" component={NotFound} public />
</Route>
</Router>
Expand Down
75 changes: 47 additions & 28 deletions src/app/components/article/ArticleFilters.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import MultiSelectFilter from '../search/MultiSelectFilter';
import SearchFieldUser from '../search/SearchFields/SearchFieldUser';
import SearchFieldTag from '../search/SearchFields/SearchFieldTag';
import DateRangeFilter from '../search/DateRangeFilter';
import LanguageFilter from '../search/LanguageFilter';
import PersonIcon from '../../icons/person.svg';
import DescriptionIcon from '../../icons/description.svg';
import styles from '../search/SearchResults.module.css';
Expand Down Expand Up @@ -164,6 +165,21 @@ const ArticleFilters = ({
);
}

if (filter === 'language_filter') {
return (
<>
{connector}
<LanguageFilter
onChange={(newValue) => { handleOptionChange('language_filter', newValue); }}
value={value}
onRemove={() => handleRemoveFilter('language_filter')}
teamSlug={teamSlug}
optionsToHide={['request_language', 'language']}
/>
</>
);
}

return null;
})}
<AddFilterMenu
Expand All @@ -172,34 +188,37 @@ const ArticleFilters = ({
addedFields={Object.keys(filters)}
onSelect={handleAddFilter}
/>
<Divider orientation="vertical" flexItem style={{ margin: '0 8px' }} />
{ Object.keys(filters).length > 0 ?
<ButtonMain
className="int-search-fields__button--apply-articlefilter"
variant="contained"
size="default"
theme="lightValidation"
onClick={handleSubmit}
label={
<FormattedMessage id="articleFilters.applyFilters" defaultMessage="Apply" description="Button to perform query with specified filters" />
}
buttonProps={{
id: 'search-fields__submit-button',
}}
/> : null }
<ButtonMain
className="int-search-fields__button--reset-articlefilter"
variant="contained"
size="default"
theme="lightText"
onClick={handleClear}
label={
<FormattedMessage id="articleFilters.reset" defaultMessage="Reset" description="Tooltip for button to remove any applied filters" />
}
buttonProps={{
id: 'search-fields__clear-button',
}}
/>
{ Object.keys(filters).length > 1 && ( // Filter by article type is fixed
<>
<Divider orientation="vertical" flexItem style={{ margin: '0 8px' }} />
<ButtonMain
className="int-search-fields__button--apply-articlefilter"
variant="contained"
size="default"
theme="lightValidation"
onClick={handleSubmit}
label={
<FormattedMessage id="articleFilters.applyFilters" defaultMessage="Apply" description="Button to perform query with specified filters" />
}
buttonProps={{
id: 'search-fields__submit-button',
}}
/>
<ButtonMain
className="int-search-fields__button--reset-articlefilter"
variant="contained"
size="default"
theme="lightText"
onClick={handleClear}
label={
<FormattedMessage id="articleFilters.reset" defaultMessage="Reset" description="Tooltip for button to remove any applied filters" />
}
buttonProps={{
id: 'search-fields__clear-button',
}}
/>
</>
)}
</div>
</div>
);
Expand Down
84 changes: 66 additions & 18 deletions src/app/components/article/Articles.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ArticleCard from '../search/SearchResultsCards/ArticleCard';
import searchResultsStyles from '../search/SearchResults.module.css';
import Paginator from '../cds/inputs/Paginator';
import ListSort from '../cds/inputs/ListSort';
import { getStatus } from '../../helpers';
import MediasLoading from '../media/MediasLoading';
import ArticleFilters from './ArticleFilters';
import styles from './Articles.module.css';
Expand All @@ -29,6 +30,7 @@ const ArticlesComponent = ({
filterOptions,
filters,
onChangeSearchParams,
statuses,
articles,
articlesCount,
updateMutation,
Expand Down Expand Up @@ -142,21 +144,31 @@ const ArticlesComponent = ({
}

<div className={styles.articlesList}>
{articles.map(article => (
<ArticleCard
key={article.id}
variant={type}
title={article.title}
summary={article.description}
url={article.url}
languageCode={article.language}
date={article.updated_at}
tags={article.tags}
onChangeTags={(tags) => {
handleUpdateTags(article.id, tags);
}}
/>
))}
{articles.map((article) => {
let currentStatus = null;
if (article.claim_description?.project_media?.status) {
currentStatus = getStatus(statuses, article.claim_description.project_media.status);
}

return (
<ArticleCard
key={article.id}
variant={type}
title={article.title || article.claim_description?.description}
summary={article.description}
url={article.url}
languageCode={article.language !== 'und' ? article.language : null}
date={article.updated_at}
tags={article.tags}
statusColor={currentStatus ? currentStatus.style?.color : null}
statusLabel={currentStatus ? currentStatus.label : null}
publishedAt={article.claim_description?.project_media?.report_status === 'published' && article.claim_description?.project_media?.published ? parseInt(article.claim_description?.project_media?.published, 10) : null}
onChangeTags={(tags) => {
handleUpdateTags(article.id, tags);
}}
/>
);
})}
</div>
</div>
</React.Fragment>
Expand All @@ -170,6 +182,7 @@ ArticlesComponent.defaultProps = {
sortOptions: [],
filterOptions: [],
filters: {},
statuses: {},
articles: [],
articlesCount: 0,
};
Expand All @@ -190,6 +203,7 @@ ArticlesComponent.propTypes = {
value: PropTypes.string.isRequired,
label: PropTypes.string.isRequired, // Localizable string
})),
statuses: PropTypes.object,
articlesCount: PropTypes.number,
articles: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string.isRequired,
Expand All @@ -199,6 +213,14 @@ ArticlesComponent.propTypes = {
language: PropTypes.string,
updated_at: PropTypes.number,
tags: PropTypes.arrayOf(PropTypes.string),
claim_description: PropTypes.shape({
description: PropTypes.string,
project_media: PropTypes.shape({
status: PropTypes.string,
published: PropTypes.string, // Timestamp
report_status: PropTypes.string,
}),
}),
})),
};

Expand Down Expand Up @@ -235,6 +257,13 @@ const Articles = ({
// Adjust some filters
if (filters.range?.updated_at) {
filters.updatedAt = JSON.stringify(filters.range.updated_at);
} else {
delete filters.updatedAt;
}
if (filters.language_filter?.report_language) {
filters.language = filters.language_filter.report_language;
} else {
delete filters.language;
}

return (
Expand All @@ -244,13 +273,14 @@ const Articles = ({
query={graphql`
query ArticlesQuery(
$slug: String!, $type: String!, $pageSize: Int, $sort: String, $sortType: String, $offset: Int,
$users: [Int], $updatedAt: String, $tags: [String],
$users: [Int], $updatedAt: String, $tags: [String], $language: [String],
) {
team(slug: $slug) {
articles_count(article_type: $type)
verification_statuses
articles_count(article_type: $type, user_ids: $users, tags: $tags, updated_at: $updatedAt, language: $language)
articles(
first: $pageSize, article_type: $type, offset: $offset, sort: $sort, sort_type: $sortType,
user_ids: $users, tags: $tags, updated_at: $updatedAt,
user_ids: $users, tags: $tags, updated_at: $updatedAt, language: $language,
) {
edges {
node {
Expand All @@ -263,6 +293,23 @@ const Articles = ({
updated_at
tags
}
... on FactCheck {
id
title
description: summary
url
language
updated_at
tags
claim_description { # There will be no N + 1 problem here because the backend uses eager loading
description
project_media {
status
published
report_status
}
}
}
}
}
}
Expand Down Expand Up @@ -294,6 +341,7 @@ const Articles = ({
filters={filters}
articles={props.team.articles.edges.map(edge => edge.node)}
articlesCount={props.team.articles_count}
statuses={props.team.verification_statuses}
onChangeSearchParams={handleChangeSearchParams}
updateMutation={updateMutation}
/>
Expand Down
66 changes: 66 additions & 0 deletions src/app/components/article/FactChecks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React from 'react';
import { graphql } from 'react-relay/compat';
import PropTypes from 'prop-types';
import { FormattedMessage, defineMessages, injectIntl, intlShape } from 'react-intl';
import FactCheckIcon from '../../icons/fact_check.svg';
import Articles from './Articles';

const messages = defineMessages({
sortTitle: {
id: 'factChecks.sortTitle',
defaultMessage: 'Title',
description: 'Label for sort criteria option displayed in a drop-down in the fact-checks page.',
},
sortLanguage: {
id: 'factChecks.sortLanguage',
defaultMessage: 'Language',
description: 'Label for sort criteria option displayed in a drop-down in the fact-checks page.',
},
sortDate: {
id: 'factChecks.sortDate',
defaultMessage: 'Updated (date)',
description: 'Label for sort criteria option displayed in a drop-down in the fact-checks page.',
},
});

const FactChecks = ({ routeParams, intl }) => {
const sortOptions = [
{ value: 'title', label: intl.formatMessage(messages.sortTitle) },
{ value: 'language', label: intl.formatMessage(messages.sortLanguage) },
{ value: 'updated_at', label: intl.formatMessage(messages.sortDate) },
];

const updateMutation = graphql`
mutation FactChecksUpdateFactCheckMutation($input: UpdateFactCheckInput!) {
updateFactCheck(input: $input) {
fact_check {
id
tags
}
}
}
`;

return (
<Articles
type="fact-check"
title={<FormattedMessage id="factChecks.title" defaultMessage="Claim & Fact-checks" description="Title of the fact-checks page." />}
icon={<FactCheckIcon />}
teamSlug={routeParams.team}
sortOptions={sortOptions}
filterOptions={['users', 'tags', 'range', 'language_filter']}
updateMutation={updateMutation}
/>
);
};

FactChecks.defaultProps = {};

FactChecks.propTypes = {
routeParams: PropTypes.shape({
team: PropTypes.string.isRequired, // slug
}).isRequired,
intl: intlShape.isRequired,
};

export default injectIntl(FactChecks);
2 changes: 1 addition & 1 deletion src/app/components/cds/media-cards/ItemReportStatus.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,4 @@ ItemReportStatus.propTypes = {
publishedAt: PropTypes.instanceOf(Date), // Timestamp
};

export default ItemReportStatus;
export default ItemReportStatus;
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,10 @@
}
}
}

.cardRightTop {
display: flex;
gap: 3px;
justify-content: flex-end;
}
}

0 comments on commit 583498f

Please sign in to comment.