Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[front] feat: display entity contexts on comparison page #1858

Merged
merged 6 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions frontend/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@
"inactivePollComparisonCannotBeSubmittedOrEdited": "No comparison can be submitted or modified.",
"comparisonCriteria": "Comparison criteria"
},
"entityContext": {
"entityAtheAssociationWouldLikeToGiveYouContext": "<0>{{entityName}} A</0> - The Tournesol association would like to give you some context.",
"entityBtheAssociationWouldLikeToGiveYouContext": "<0>{{entityName}} B</0> - The Tournesol association would like to give you some context.",
"theAssociationWouldLikeToGiveYouContext": "The Tournesol association would like to give you some context on this element.",
"about": "About"
},
"comparisonHelper": {
"hideHelp": "Hide the help",
"showHelp": "Show help for comparisons"
Expand All @@ -109,9 +115,6 @@
"entityContextTextList": {
"whyThisMessage": "Why this message?"
},
"contextsFromOrigin": {
"theAssociationWouldLikeToGiveYouContext": "The Tournesol association would like to give you some context on this element."
},
"entitySelector": {
"newVideo": "Select a new video automatically",
"letTournesolSelectAVideo": "Let Tournesol select a video",
Expand Down
9 changes: 6 additions & 3 deletions frontend/public/locales/fr/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@
"inactivePollComparisonCannotBeSubmittedOrEdited": "Aucune comparaison ne peut être ajoutée ou modifiée.",
"comparisonCriteria": "Critères de comparaison"
},
"entityContext": {
"entityAtheAssociationWouldLikeToGiveYouContext": "<0>{{entityName}} A</0> - L'association Tournesol souhaite vous apporter du contexte.",
"entityBtheAssociationWouldLikeToGiveYouContext": "<0>{{entityName}} B</0> - L'association Tournesol souhaite vous apporter du contexte.",
"theAssociationWouldLikeToGiveYouContext": "L'association Tournesol souhaite vous apporter du contexte sur cet élément.",
"about": "A propos de"
},
"comparisonHelper": {
"hideHelp": "Cacher l'aide",
"showHelp": "Montrer l'aide pour les comparaisons"
Expand All @@ -113,9 +119,6 @@
"entityContextTextList": {
"whyThisMessage": "Pourquoi ce message ?"
},
"contextsFromOrigin": {
"theAssociationWouldLikeToGiveYouContext": "L'association Tournesol souhaite vous apporter du contexte sur cet élément."
},
"entitySelector": {
"newVideo": "Sélectionner une nouvelle vidéo automatiquement",
"letTournesolSelectAVideo": "Laisser Tournesol choisir une vidéo",
Expand Down
36 changes: 34 additions & 2 deletions frontend/src/features/comparisons/Comparison.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import React, { useState, useEffect, useCallback, useMemo } from 'react';
import React, {
useCallback,
useEffect,
useMemo,
useRef,

Check warning on line 5 in frontend/src/features/comparisons/Comparison.tsx

View workflow job for this annotation

GitHub Actions / test

'useRef' is defined but never used

Check warning on line 5 in frontend/src/features/comparisons/Comparison.tsx

View workflow job for this annotation

GitHub Actions / test

'useRef' is defined but never used
useState,
} from 'react';
import { useHistory, useLocation } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Location } from 'history';
Expand All @@ -17,6 +23,7 @@
} from 'src/features/entity_selector/EntitySelector';
import { UID_YT_NAMESPACE } from 'src/utils/constants';
import { useCurrentPoll } from 'src/hooks/useCurrentPoll';
import ComparisonEntityContexts from './ComparisonEntityContexts';
import ComparisonHelper from './ComparisonHelper';

export const UID_PARAMS: { vidA: string; vidB: string } = {
Expand Down Expand Up @@ -72,11 +79,14 @@
autoFillSelectorA = false,
autoFillSelectorB = false,
}: Props) => {
const { t } = useTranslation();
const { t, i18n } = useTranslation();
const currentLang = i18n.resolvedLanguage;

const history = useHistory();
const location = useLocation();
const { showSuccessAlert, displayErrorsFrom } = useNotifications();
const { name: pollName } = useCurrentPoll();

const [isLoading, setIsLoading] = useState(true);

const [initialComparison, setInitialComparison] =
Expand Down Expand Up @@ -144,6 +154,25 @@
});
}, [pollName, uidA, uidB, selectorA.uid, selectorB.uid]);

/**
* When the UI language changes, refresh the ratings to retrieve the
* corresponding localized entity contexts.
*/
useEffect(() => {
if (isLoading) {
return;
}

if (selectorA.uid) {
setSelectorA((value) => ({ ...value, ratingIsExpired: true }));
}

if (selectorB.uid) {
setSelectorB((value) => ({ ...value, ratingIsExpired: true }));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentLang]);

const onSubmitComparison = async (c: ComparisonRequest) => {
try {
if (initialComparison) {
Expand Down Expand Up @@ -250,6 +279,9 @@
>
<ComparisonHelper />
</Grid>
<Grid item xs={12}>
<ComparisonEntityContexts selectorA={selectorA} selectorB={selectorB} />
</Grid>
<Grid
item
xs={12}
Expand Down
105 changes: 105 additions & 0 deletions frontend/src/features/comparisons/ComparisonEntityContexts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import React from 'react';
import { Trans, useTranslation } from 'react-i18next';

import { Grid, useTheme } from '@mui/material';

import EntityContextBox from 'src/features/entity_context/EntityContextBox';
import { SelectorValue } from 'src/features/entity_selector/EntitySelector';
import { useCurrentPoll } from 'src/hooks';
import { getEntityName } from 'src/utils/constants';

interface EntityContextBoxProps {
selectorA: SelectorValue;
selectorB: SelectorValue;
}

const ComparisonEntityContextsItem = ({
selector,
altAssociationDisclaimer,
}: {
selector: SelectorValue;
altAssociationDisclaimer?: React.ReactElement;
}) => {
return (
<>
{selector.rating && selector.uid && selector.rating.entity_contexts && (
<EntityContextBox
uid={selector.uid}
contexts={selector.rating.entity_contexts}
entityName={selector.rating?.entity?.metadata?.name}
altAssociationDisclaimer={altAssociationDisclaimer}
collapsible={true}
/>
)}
</>
);
};

const ComparisonEntityContexts = ({
selectorA,
selectorB,
}: EntityContextBoxProps) => {
const theme = useTheme();

const { t } = useTranslation();
const { name: pollName } = useCurrentPoll();

const entityName = getEntityName(t, pollName);

return (
<Grid
container
spacing={1}
sx={{
'span.primary': {
color: theme.palette.secondary.main,
fontWeight: 'bold',
textTransform: 'capitalize',
},
}}
>
{selectorA.uid === selectorB.uid ? (
<Grid item xs={12}>
<ComparisonEntityContextsItem selector={selectorA} />
</Grid>
) : (
<>
<Grid item xs={12} sm={12} md={6}>
<ComparisonEntityContextsItem
selector={selectorA}
altAssociationDisclaimer={
<strong>
<Trans
t={t}
i18nKey="entityContext.entityAtheAssociationWouldLikeToGiveYouContext"
>
<span className="primary">{{ entityName }} A</span> - The
Tournesol association would like to give you some context.
</Trans>
</strong>
}
/>
</Grid>
<Grid item xs={12} sm={12} md={6}>
<ComparisonEntityContextsItem
selector={selectorB}
altAssociationDisclaimer={
<strong>
<Trans
t={t}
i18nKey="entityContext.entityBtheAssociationWouldLikeToGiveYouContext"
>
<span className="primary">{{ entityName }} B</span> - The
Tournesol association would like to give you some context.
</Trans>
</strong>
}
/>
</Grid>
</>
)}
</Grid>
);
};

export default ComparisonEntityContexts;
94 changes: 79 additions & 15 deletions frontend/src/features/entity_context/EntityContextBox.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import { Link as RouterLink } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import linkifyStr from 'linkify-string';
Expand All @@ -7,23 +7,39 @@ import {
Alert,
AlertTitle,
Box,
Collapse,
Divider,
IconButton,
Link,
SxProps,
Typography,
} from '@mui/material';
import { KeyboardArrowDown, KeyboardArrowUp } from '@mui/icons-material';

import { EntityContext, OriginEnum } from 'src/services/openapi';
import {
getCollapsedState,
setCollapsedState,
} from 'src/utils/entityContexts/collapsed';

interface EntityContextBoxProps {
uid: string;
contexts: Array<EntityContext>;
// If set, display the entity name before the contexts.
entityName?: string;
// If true the contexts can be collapsed.
collapsible?: boolean;
// Replace the default association disclaier.
altAssociationDisclaimer?: React.ReactElement;
}

interface EntityContextListProps {
uid: string;
origin_?: OriginEnum;
contexts: Array<EntityContext>;
entityName?: string;
collapsible?: boolean;
diclaimer?: React.ReactElement;
}

interface EntityContextTextListProps {
Expand Down Expand Up @@ -78,9 +94,21 @@ const EntityContextList = ({
uid,
contexts,
origin_,
diclaimer,
entityName,
collapsible,
}: EntityContextListProps) => {
const { t } = useTranslation();

const [displayText, setDisplayText] = useState(
collapsible ? !getCollapsedState(uid) : true
);

const toggleDisplayText = (previousState: boolean) => {
setCollapsedState(uid, previousState);
setDisplayText(!previousState);
};

const infos = contexts.filter((ctx) => !ctx.unsafe);
const warnings = contexts.filter((ctx) => ctx.unsafe);

Expand All @@ -96,25 +124,58 @@ const EntityContextList = ({
<Alert
id="entity-context"
severity={entityHasWarnings ? 'warning' : 'info'}
sx={alertSx}
sx={{
'.MuiAlert-message': { width: '100%' },
...alertSx,
}}
>
<AlertTitle>
<strong>
{origin_ === OriginEnum.ASSOCIATION &&
t('contextsFromOrigin.theAssociationWouldLikeToGiveYouContext')}
</strong>
</AlertTitle>

<EntityContextTextList
uid={uid}
origin_={origin_}
contexts={entityHasWarnings ? warnings : infos}
/>
<Box display="flex" justifyContent="space-between">
<AlertTitle>
{diclaimer ? (
diclaimer
) : (
<strong>
{origin_ === OriginEnum.ASSOCIATION &&
t('entityContext.theAssociationWouldLikeToGiveYouContext')}
</strong>
)}
</AlertTitle>

{collapsible && (
<IconButton
aria-label="Show context"
onClick={() => {
toggleDisplayText(displayText ? true : false);
}}
>
{displayText ? <KeyboardArrowUp /> : <KeyboardArrowDown />}
</IconButton>
)}
</Box>

<Collapse in={displayText}>
{entityName && (
<Typography paragraph variant="body2" fontStyle="italic">
{t('entityContext.about')} « {entityName} »
</Typography>
)}
<EntityContextTextList
uid={uid}
origin_={origin_}
contexts={entityHasWarnings ? warnings : infos}
/>
</Collapse>
</Alert>
);
};

const EntityContextBox = ({ uid, contexts }: EntityContextBoxProps) => {
const EntityContextBox = ({
uid,
contexts,
entityName,
altAssociationDisclaimer,
collapsible = false,
}: EntityContextBoxProps) => {
const associationContexts = contexts.filter(
(ctx) => ctx.origin === OriginEnum.ASSOCIATION
);
Expand All @@ -127,6 +188,9 @@ const EntityContextBox = ({ uid, contexts }: EntityContextBoxProps) => {
uid={uid}
origin_={OriginEnum.ASSOCIATION}
contexts={associationContexts}
diclaimer={altAssociationDisclaimer}
entityName={entityName}
collapsible={collapsible}
/>
</Box>
)}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/features/entity_selector/EntitySelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ const EntitySelectorInnerAuth = ({
<Typography
variant="h6"
color="secondary"
fontWeight="bold"
sx={{ '&:first-letter': { textTransform: 'capitalize' } }}
>
{title}
Expand Down
Loading
Loading