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 3 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
39 changes: 37 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,
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 @@ import EntitySelector, {
} 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,15 @@ const Comparison = ({
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 initializing = useRef(true);
const [isLoading, setIsLoading] = useState(true);

const [initialComparison, setInitialComparison] =
Expand Down Expand Up @@ -137,13 +148,34 @@ const Comparison = ({
.then((comparison) => {
setInitialComparison(comparison);
setIsLoading(false);
initializing.current = false;
})
.catch(() => {
setInitialComparison(null);
setIsLoading(false);
initializing.current = false;
});
}, [pollName, uidA, uidB, selectorA.uid, selectorB.uid]);

/**
* When the UI language changes, refresh the ratings to retrieve the
* corresponding localized entity contexts.
*/
useEffect(() => {
if (initializing.current || isLoading) {
return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why this check on initliazing.current is useful here 🤔

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked and it's not useful, the isLoading check seems to be enough.

}

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 +282,9 @@ const Comparison = ({
>
<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 question's answer"
onClick={() => {
GresilleSiffle marked this conversation as resolved.
Show resolved Hide resolved
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