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: add buttons to compare criteria #1968

Draft
wants to merge 61 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 54 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
2cd6bac
[front] feat: (WIP) add buttons to compare criteria
GresilleSiffle May 7, 2024
dfa9ca9
front: disable buttons during a slide
GresilleSiffle May 7, 2024
0e34530
front: do not duplicate score_max declaration
GresilleSiffle May 7, 2024
2c67a6f
front: make buttons look like button
GresilleSiffle May 7, 2024
58d9874
front: add visual indications for criterion swipes
GresilleSiffle May 8, 2024
863fa24
front: prevent skipping the main criterion
GresilleSiffle May 8, 2024
dd5b825
front: remove extra margin and padding added around the sliders
GresilleSiffle May 8, 2024
61459f4
front: add a component display all given scores
GresilleSiffle May 14, 2024
06f6233
Merge branch 'main' into 1940-replace_sliders_by_buttons
GresilleSiffle May 14, 2024
7030f3c
front: reduce the mb of CollectiveGoalWeeklyProgress on mobile
GresilleSiffle May 14, 2024
d3bbb53
front: hide the success alerts more quickly on mobile
GresilleSiffle May 14, 2024
c8ecc86
front: remove sticky hover effect on mobile
GresilleSiffle May 14, 2024
e997354
front: fix previous commit
GresilleSiffle May 14, 2024
a2a3c54
front: order criteria in CriteriaButtonsScoreReview
GresilleSiffle May 14, 2024
fda8d25
front: refactor the Comparison layout to have more flexibility in the…
GresilleSiffle May 15, 2024
b07cec6
front: add a zoom transition when icons are displayed by CriteriaButt…
GresilleSiffle May 15, 2024
9092019
front: fix layout of Comparison
GresilleSiffle May 15, 2024
f24ef3a
front: add a component displaying different input strategies
GresilleSiffle May 15, 2024
5af7e47
front: display buttons on mobile
GresilleSiffle May 15, 2024
1d87053
front: move comparison buttons in their own folder
GresilleSiffle May 16, 2024
19c083d
front: add component ComparisonScoreReviewStrategy
GresilleSiffle May 16, 2024
3c0be85
front: delete ComparisonScoreReviewStrategy to simplify the code
GresilleSiffle May 16, 2024
4dddeda
front: better display of buttons on large screen
GresilleSiffle May 16, 2024
f04966d
front: order the criteria according to the user's preferences
GresilleSiffle May 16, 2024
9ad2a65
front: enable the swipe only on the criterion paper
GresilleSiffle May 16, 2024
ab2f7c0
front: add a help text when displaying a "mobile" comp on desktop
GresilleSiffle May 16, 2024
d353aaf
front: display "desktop" comp in read-only on mobile devices
GresilleSiffle May 21, 2024
eba21f1
front: rename ComparisonInputStrategy.tsx -> ComparisonInput.tsx
GresilleSiffle May 21, 2024
f9cfbd6
front: add missing doc
GresilleSiffle May 21, 2024
1d2cbcb
front: more explicit icons in inputs/CriterionButtons
GresilleSiffle May 22, 2024
6ecd8b0
front: disable submit btn when sliders are displayed in read-only mode
GresilleSiffle May 22, 2024
9c6c179
front: fix tutorial not working on mobile devices
GresilleSiffle May 22, 2024
56d17c8
front: adapt the first step of the tuto to the UI of mobile devices
GresilleSiffle May 22, 2024
a6395c0
front: add missing code to the previous commit
GresilleSiffle May 22, 2024
a9a8a11
front: allow to delete a given score (except for the main criterion)
GresilleSiffle May 23, 2024
ce88b7c
front: improve CriteriaButtons UX by removing slide effect on score c…
GresilleSiffle May 23, 2024
138f94d
front: use a media query instead of checking the user agent
GresilleSiffle May 23, 2024
4395526
front: replace isMobileDevice by useMediaQuery('(pointer:coarse)') in…
GresilleSiffle May 23, 2024
b3953e6
front: update the tutorial tip 2 for the mobile interface
GresilleSiffle May 23, 2024
1c1dec6
front: add missing code to the previous commit
GresilleSiffle May 23, 2024
8447d17
front: revert a change in the tip3
GresilleSiffle May 23, 2024
c1fe5c1
front: make comparison buttons slide behind the entity selectors
GresilleSiffle May 23, 2024
46ea0a8
front: also add the fade effect
GresilleSiffle May 23, 2024
6dc1acc
front: wrap the ComparisonSeries in a TutorialContext provider
GresilleSiffle May 27, 2024
1a59e92
front: add the hook useOrderedCriteria.ts
GresilleSiffle May 27, 2024
8eb0ef4
front: display comp buttons for keyboard devices
GresilleSiffle May 27, 2024
a24e3b4
run the fe e2e tests with headed=true
GresilleSiffle May 27, 2024
b050cc5
Merge branch 'main' into 1940-replace_sliders_by_buttons
GresilleSiffle Jun 1, 2024
210116b
front: make the snackbar width smaller on mobile
GresilleSiffle Jun 1, 2024
6fed8b0
fix the e2e tests
GresilleSiffle Jun 6, 2024
fce9417
Merge branch 'main' into 1940-replace_sliders_by_buttons
GresilleSiffle Jun 6, 2024
a4bc50c
front: restore the explicit success message on desktop
GresilleSiffle Jun 6, 2024
0045778
front: add EntitySelectorControls component
GresilleSiffle Jun 6, 2024
3225dda
front: fix criteria swipe on ios
GresilleSiffle Jun 6, 2024
bb05675
front: invite the users to change video
GresilleSiffle Jun 6, 2024
e3bed73
increase the slide effect speed
GresilleSiffle Jun 6, 2024
2b6e534
try overscroll-behavior-y on the html tag
GresilleSiffle Jun 6, 2024
05cad56
always keep space for the navigation buttons
GresilleSiffle Jun 6, 2024
2d4f7d0
restore overscroll-behavior-y on body
GresilleSiffle Jun 6, 2024
fcdd886
chore: add doc in the css
GresilleSiffle Jun 6, 2024
2fa29db
add the mention "items are similar" in the input CriteriaButtons.tsx
GresilleSiffle Jun 10, 2024
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
1 change: 1 addition & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ jobs:
with:
working-directory: tests
browser: chrome
headed: true
spec: "cypress/e2e/frontend/**/*"

- name: Print dev-env logs
Expand Down
19 changes: 16 additions & 3 deletions frontend/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@
"title": "Criteria scores distribution"
},
"comparison": {
"successfullySubmitted": "The comparison has been successfully submitted.",
"ok": "OK",
"successfullySubmitted": "Comparison successfully submitted.",
"itemsAreSimilar": "These two items are very similar, it is probably not useful to compare them.",
"removeOptionalCriterias": "Remove optional criteria",
"addOptionalCriterias": "Add optional criteria",
Expand All @@ -103,10 +104,18 @@
"hideHelp": "Hide the help",
"showHelp": "Show help for comparisons"
},
"comparisonInput": {
"thisComparisonWasMadeOnAMobileDevice": "This comparison was made on a mobile device. The mobile interface exceptionally replaces the usual interface.",
"thisComparisonWasMadeOnAComputer": "This comparison was made on a computer. It cannot be modified on a mobile device."
},
"comparisons": {
"goToComparison": "Go to comparison"
},
"submit": "submit",
"comparisonCriteriaButtons": {
"nextQualityCriterion": "Next quality criterion",
"previousQualityCriterion": "Previous quality criterion"
},
"comparisonSeries": {
"skipTheSeries": "Skip the series"
},
Expand Down Expand Up @@ -1148,12 +1157,16 @@
"title1": "Comparing two videos 🌻",
"message1": {
"p10": "After watching the videos, move the main handle to the video that should be largely recommended according to you. Save once you've made your choice.",
"p20": "The more your opinion is clear-cut on a criterion, the more the handle should be close to the slider extremity. If you find the videos similar, the handle should remain close to the center."
"p10mobile": "After watching the videos, vote for the video that should be largely recommended using the buttons below.",
"p20": "The more your opinion is clear-cut on a criterion, the more the handle should be close to the slider extremity. If you find the videos similar, the handle should remain close to the center.",
"p20mobile": "If your preference is strong, use the buttons at the ends. If you find the two videos similar, use the \"equal\" button in the middle."
},
"title2": "Making comparisons helps science 🔬",
"message2": {
"p10": "Each comparison helps Tournesol improve its recommendations and its open database. Try to unfold and use the optional criteria now.",
"p20": "You can always choose to not vote on an optional criterion by unchecking it. You will be able to edit all your comparisons from your page My comparisons."
"p10mobile": "Each comparison helps Tournesol improve its recommendations and its open database.",
"p20": "You can always choose to not vote on an optional criterion by unchecking it. You will be able to edit all your comparisons from the page My comparisons.",
"p20mobile": "After a few comparisons, new optional criteria will be displayed. You will also be able to edit all your comparisons from the page My Comparisons."
},
"title3": "How does Tournesol work? 🤖",
"message3": {
Expand Down
17 changes: 15 additions & 2 deletions frontend/public/locales/fr/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
"title": "Distribution des scores par critère"
},
"comparison": {
"ok": "OK",
"successfullySubmitted": "Comparaison bien enregistrée.",
"itemsAreSimilar": "Ces deux éléments sont très similaires, ce n'est probablement pas une bonne idée de les comparer.",
"removeOptionalCriterias": "Supprimer les critères optionnels",
Expand All @@ -107,10 +108,18 @@
"hideHelp": "Cacher l'aide",
"showHelp": "Montrer l'aide pour les comparaisons"
},
"comparisonInput": {
"thisComparisonWasMadeOnAMobileDevice": "Cette comparaison a été faite sur un périphérique mobile. L'interface mobile remplace exceptionnellement l'interface habituelle.",
"thisComparisonWasMadeOnAComputer": "Cette comparaison a été faite sur un ordinateur. Elle ne peut pas être modifiée sur un périphérique mobile."
},
"comparisons": {
"goToComparison": "Voir la comparaison"
},
"submit": "enregistrer",
"comparisonCriteriaButtons": {
"nextQualityCriterion": "Critère de qualité suivant",
"previousQualityCriterion": "Critère de qualité précédent"
},
"comparisonSeries": {
"skipTheSeries": "Passer la série"
},
Expand Down Expand Up @@ -1158,12 +1167,16 @@
"title1": "Comparer deux vidéos 🌻",
"message1": {
"p10": "Après avoir regardé les vidéos, déplacez le curseur principal vers la vidéo qui, selon vous, devrait être largement recommandée. Enregistrez quand votre choix est fait.",
"p20": "Plus votre avis est tranché sur un critère, plus le curseur devrait être proche de l'extrémité. Si vous trouvez les vidéos similaires, le curseur devrait se rapprocher du centre."
"p10mobile": "Après avoir regardé les vidéos, votez pour celle qui dévrait être largement recommandée en utilisant les boutons de comparaison au dessous.",
"p20": "Plus votre avis est tranché sur un critère, plus le curseur devrait être proche de l'extrémité. Si vous trouvez les vidéos similaires, le curseur devrait se rapprocher du centre.",
"p20mobile": "Si votre avis est tranché, utilisez les boutons aux extrémités. Si vous trouvez les deux vidéos similaires, utilisez le bouton \"égal\" du milieu."
},
"title2": "Faire des comparaisons aide la science 🔬",
"message2": {
"p10": "Chaque comparaison aide Tournesol à améliorer ses recommandations et sa base de données publique. Essayez maintenant de dérouler et d'utiliser les critères optionnels.",
"p20": "Vous pouvez choisir de ne pas vous exprimer sur un critère optionnel en le décochant. Il vous sera possible de modifier ultérieurement vos comparaisons depuis votre page Mes comparaisons."
"p10mobile": "Chaque comparaison aide Tournesol à améliorer ses recommandations et sa base de données publique.",
"p20": "Vous pouvez choisir de ne pas vous exprimer sur un critère optionnel en le décochant. Il vous sera possible de modifier ultérieurement vos comparaisons depuis votre page Mes comparaisons.",
"p20mobile": "Après quelques comparaisons de nouveaux critères optionnels vous serons proposés. Il vous sera aussi possible de modifier toutes vos comparaisons depuis votre page Mes comparaisons."
},
"title3": "Comment fonctionne Tournesol ? 🤖",
"message3": {
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/features/comparisonSeries/TutorialContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { createContext } from 'react';

const initialState = {
isActive: false,
};

export const TutorialContext = createContext(initialState);
204 changes: 124 additions & 80 deletions frontend/src/features/comparisons/Comparison.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,21 @@ import { useHistory, useLocation } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Location } from 'history';

import { CircularProgress, Grid, Card } from '@mui/material';
import {
Box,
Card,
CircularProgress,
Grid,
useMediaQuery,
useTheme,
} from '@mui/material';

import { useNotifications } from 'src/hooks';
import {
UsersService,
ComparisonRequest,
ApiError,
} from 'src/services/openapi';
import ComparisonSliders from 'src/features/comparisons/ComparisonSliders';
import EntitySelector, {
SelectorValue,
} from 'src/features/entity_selector/EntitySelector';
Expand All @@ -27,6 +33,7 @@ import { UID_YT_NAMESPACE } from 'src/utils/constants';
import { useCurrentPoll } from 'src/hooks/useCurrentPoll';
import ComparisonEntityContexts from './ComparisonEntityContexts';
import ComparisonHelper from './ComparisonHelper';
import ComparisonInput from './ComparisonInput';

export const UID_PARAMS: { vidA: string; vidB: string } = {
vidA: 'uidA',
Expand All @@ -37,6 +44,8 @@ const LEGACY_PARAMS: { vidA: string; vidB: string } = {
vidB: 'videoB',
};

const COMPARISON_MAX_WIDTH = '880px';

const getUidsFromLocation = (location: Location) => {
const searchParams = new URLSearchParams(location.search);
let uidA = searchParams.get(UID_PARAMS.vidA);
Expand Down Expand Up @@ -84,6 +93,9 @@ const Comparison = ({
const { t, i18n } = useTranslation();
const currentLang = i18n.resolvedLanguage;

const theme = useTheme();
const smallScreen = useMediaQuery(theme.breakpoints.down('sm'));

const history = useHistory();
const location = useLocation();
const { showSuccessAlert, displayErrorsFrom } = useNotifications();
Expand Down Expand Up @@ -231,16 +243,30 @@ const Comparison = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentLang]);

const onSubmitComparison = async (c: ComparisonRequest) => {
const onSubmitComparison = async (
c: ComparisonRequest,
partialUpdate?: boolean
) => {
try {
if (initialComparison) {
const { entity_a, entity_b, criteria_scores, duration_ms } = c;
await UsersService.usersMeComparisonsUpdate({
pollName,
uidA: entity_a.uid,
uidB: entity_b.uid,
requestBody: { criteria_scores, duration_ms },
});

if (partialUpdate === true) {
const resp = await UsersService.usersMeComparisonsPartialUpdate({
pollName,
uidA: entity_a.uid,
uidB: entity_b.uid,
requestBody: { criteria_scores, duration_ms },
});
setInitialComparison(resp);
} else {
await UsersService.usersMeComparisonsUpdate({
pollName,
uidA: entity_a.uid,
uidB: entity_b.uid,
requestBody: { criteria_scores, duration_ms },
});
}
} else {
await UsersService.usersMeComparisonsCreate({
pollName,
Expand Down Expand Up @@ -275,84 +301,102 @@ const Comparison = ({
setSelectorB((value) => ({ ...value, ratingIsExpired: true }));
}

showSuccessAlert(t('comparison.successfullySubmitted'));
if (smallScreen) {
showSuccessAlert(t('comparison.ok'), 1200);
} else {
showSuccessAlert(t('comparison.successfullySubmitted'));
}
};

return (
<Grid container maxWidth="880px" gap={1}>
<Grid item xs display="flex" flexDirection="column" alignSelf="stretch">
<EntitySelector
alignment="left"
value={selectorA}
onChange={onChangeA}
otherUid={uidB}
history={selectorAHistory.current}
/>
</Grid>
<Grid item xs display="flex" flexDirection="column" alignSelf="stretch">
<EntitySelector
alignment="right"
value={selectorB}
onChange={onChangeB}
otherUid={uidA}
history={selectorBHistory.current}
/>
</Grid>
<>
<Grid
item
xs={12}
sx={{
mt: 1,
display: 'flex',
alignItems: 'center',
flexDirection: 'column',
'&:empty': {
display: 'none',
},
}}
component={Card}
elevation={2}
container
gap={1}
mb={1}
maxWidth={COMPARISON_MAX_WIDTH}
// Allow the CriterionButtons to slide behind the entity selectors.
zIndex={theme.zIndex.comparisonElevation1}
>
<ComparisonHelper />
<Grid item xs display="flex" flexDirection="column" alignSelf="stretch">
<EntitySelector
alignment="left"
value={selectorA}
onChange={onChangeA}
otherUid={uidB}
history={selectorAHistory.current}
/>
</Grid>
<Grid item xs display="flex" flexDirection="column" alignSelf="stretch">
<EntitySelector
alignment="right"
value={selectorB}
onChange={onChangeB}
otherUid={uidA}
history={selectorBHistory.current}
/>
</Grid>
</Grid>
<Grid item xs={12} sx={{ '&:empty': { display: 'none' } }}>
<ComparisonEntityContexts selectorA={selectorA} selectorB={selectorB} />
</Grid>
<Grid
item
xs={12}
sx={{
marginTop: 2,
display: 'flex',
alignItems: 'center',
flexDirection: 'column',
py: 3,
'&:empty': { display: 'none' },
}}
component={Card}
elevation={2}
>
{selectorA.rating && selectorB.rating ? (
isLoading ? (
<CircularProgress color="secondary" />
) : (
<ComparisonSliders
submit={onSubmitComparison}
initialComparison={initialComparison}
uidA={uidA || ''}
uidB={uidB || ''}
isComparisonPublic={
selectorA.rating.individual_rating.is_public &&
selectorB.rating.individual_rating.is_public
}
/>
)
) : selectorA.uid && selectorB.uid ? (
// Entities are selected but ratings are not loaded yet
<CircularProgress color="secondary" />
) : null}
<Grid container gap={1} maxWidth={COMPARISON_MAX_WIDTH}>
<Grid
item
xs={12}
display="flex"
alignItems="center"
flexDirection="column"
sx={{
'&:empty': {
display: 'none',
},
}}
component={Card}
elevation={2}
>
<ComparisonHelper />
</Grid>
<Grid item xs={12} sx={{ '&:empty': { display: 'none' } }}>
<ComparisonEntityContexts
selectorA={selectorA}
selectorB={selectorB}
/>
</Grid>
<Grid
item
xs={12}
display="flex"
alignItems="stretch"
flexDirection="column"
gap={1}
sx={{
'&:empty': { display: 'none' },
}}
>
{selectorA.rating && selectorB.rating ? (
isLoading ? (
<Box display="flex" justifyContent="center">
<CircularProgress color="secondary" />
</Box>
) : (
<ComparisonInput
uidA={uidA || ''}
uidB={uidB || ''}
onSubmit={onSubmitComparison}
initialComparison={initialComparison}
isComparisonPublic={
selectorA.rating.individual_rating.is_public &&
selectorB.rating.individual_rating.is_public
}
/>
)
) : selectorA.uid && selectorB.uid ? (
// Entities are selected but ratings are not loaded yet
<Box display="flex" justifyContent="center">
<CircularProgress color="secondary" />
</Box>
) : null}
</Grid>
</Grid>
</Grid>
</>
);
};

Expand Down
Loading
Loading