Skip to content

Commit

Permalink
Create a locked state for lessons.
Browse files Browse the repository at this point in the history
Next lesson unlocks when a previous lesson is completed.
  • Loading branch information
HamsterCoder committed Mar 1, 2024
1 parent 1833e5f commit 3123bc0
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 23 deletions.
21 changes: 20 additions & 1 deletion src/api/lessons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ interface SavedLessonProgress {

interface DisplayedLessonProgress {
progress: number;
locked: boolean;
}

export interface LessonListItem
Expand Down Expand Up @@ -161,6 +162,7 @@ export async function listLessons(
return {
...lesson,
progress: computeLessonProgress(progressData, lesson.id),
locked: false, // Temporary, will be updated below
};
});

Expand All @@ -178,7 +180,24 @@ export async function listLessons(
return {
name,
displayName,
lessons: sectionLessons,
lessons: sectionLessons.map((lesson, index) => {
let locked;

if (index === 0) {
// First lesson is always unlocked
locked = false;
} else {
// Previous lesson isn't completed
locked =
computeLessonProgress(
progressData,
sectionLessons[index - 1].id,
) < 100;
}

lesson.locked = locked;
return lesson;
}),
};
});
} catch (error) {
Expand Down
5 changes: 5 additions & 0 deletions src/components/I18N/I18N.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ const dictionary: Record<
en: 'Lessons',
ru: 'Уроки',
},
'lesson-list-locked-message': {
en: 'Complete the previous lesson to unlock this one. Fill the progress bar.',
ru: 'Сначала нужно полностью пройти предыдущий урок. Заполните весь прогресс-бар.',
},
'lesson-submit-button': {
en: 'Check',
ru: 'Проверить',
Expand Down Expand Up @@ -85,6 +89,7 @@ const dictionary: Record<
en: 'See other lessons',
ru: 'Посмотреть другие уроки',
},

'conversation-list-heading': {
en: 'Conversations',
ru: 'Разговоры',
Expand Down
114 changes: 92 additions & 22 deletions src/routes/LessonList.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,35 @@
import { styled } from 'styled-components';
import { Link } from 'react-router-dom';
import { CircularProgress, LinearProgress } from '@mui/material';
import {
CircularProgress,
LinearProgress,
linearProgressClasses,
} from '@mui/material';
import { useQuery } from '@tanstack/react-query';
import LockIcon from '@mui/icons-material/Lock';
import Tooltip from '@mui/material/Tooltip';

import { listLessons, LessonListItem } from '@api/lessons';
import { CardListItem, CardList } from '@components/CardList';
import { EllipsisTypography } from '@components/EllispsisTypography';
import { Text } from '@components/Text/Text';
import { Heading } from '@components/Heading';
import { I18N, I18NLangs } from '@components/I18N/I18N';

// TODO: scroll to current active lesson on mobile and current active section on desktop
// TODO current active section is the first section with a locked lesson

const LessonProgress = styled(LinearProgress)`
&& {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 0.5rem;
}
`;
interface LessonCardProps {
locked: boolean;
}

const CardLink = styled(Link)`
const LessonLink = styled(Link)<LessonCardProps>`
${(props) => props.locked && 'pointer-events: none;'}
display: block;
height: 100%;
text-decoration: none;
`;

const CustomCard = styled.div`
const LessonCard = styled.div<LessonCardProps>`
position: relative;
width: 100%;
Expand All @@ -44,28 +45,95 @@ const CustomCard = styled.div`
0px 1px 1px 0px rgba(0, 0, 0, 0.14),
0px 1px 3px 0px rgba(0, 0, 0, 0.12);
border-radius: 0.25rem;
background-color: rgba(217, 175, 207, 0.2);
color: var(--text-color);
background-color: ${(props) =>
props.locked ? 'rgba(208,208,208, 0.3)' : 'rgba(217, 175, 207, 0.2)'};
overflow: hidden;
`;

const LessonHeading = styled(Heading)`
&& {
display: flex;
align-items: center;
white-space: nowrap;
margin-bottom: 0.5rem;
}
`;

const LessonHeadingIcon = styled.span`
&:not(:empty) {
pointer-events: auto;
margin-bottom: -6px;
margin-right: 0.25rem;
}
`;

const LessonHeadingText = styled.span`
flex-shrink: 1;
overflow: hidden;
text-overflow: ellipsis;
`;

const LessonProgress = styled(LinearProgress)<LessonCardProps>`
&& {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 0.5rem;
}
&.${linearProgressClasses.colorPrimary} {
background-color: ${(props) =>
props.locked ? 'rgb(205, 205, 205)' : 'rgb(217, 175, 206)'};
}
`;

// TODO tooltip for locked and completed lessons when hovering over the icon
function renderLessonList(lessons: LessonListItem[]) {
return lessons.map((lesson) => (
<CardListItem key={lesson.id}>
<CardLink to={`/lessons/${lesson.id}`}>
<CustomCard>
<EllipsisTypography variant="heading_s" gutterBottom>
{lesson.displayTopic} &middot; {lesson.displayName}
</EllipsisTypography>
<LessonLink to={`/lessons/${lesson.id}`} locked={lesson.locked}>
<LessonCard locked={lesson.locked}>
<LessonHeading
size="s"
color={lesson.locked ? 'currentColor' : 'default'}
>
<Tooltip
title={
<I18N
textKey="lesson-list-locked-message"
lang={I18NLangs.RU}
></I18N>
}
enterTouchDelay={0}
enterDelay={100}
leaveDelay={100}
arrow
>
<LessonHeadingIcon>
{lesson.locked && (
<LockIcon fontSize="inherit" />
)}
</LessonHeadingIcon>
</Tooltip>

<LessonHeadingText>
{lesson.displayTopic} &middot; {lesson.displayName}
</LessonHeadingText>
</LessonHeading>
<Text type="primary" color="default" withMargin={false}>
{lesson.description}
</Text>
<LessonProgress
locked={lesson.locked}
variant="determinate"
value={lesson.progress}
></LessonProgress>
</CustomCard>
</CardLink>
</LessonCard>
</LessonLink>
</CardListItem>
));
}
Expand Down Expand Up @@ -103,6 +171,8 @@ export const LessonList = () => {
);
}

console.log(sections);

return sections.map((section) => (
<SectionContainer key={section.name}>
<SectionHeading size="m">{section.displayName}</SectionHeading>
Expand Down

0 comments on commit 3123bc0

Please sign in to comment.