diff --git a/src/components/Fieldset.tsx b/src/components/Fieldset.tsx
index 959f701f..65a432ed 100644
--- a/src/components/Fieldset.tsx
+++ b/src/components/Fieldset.tsx
@@ -7,5 +7,6 @@ export const Fieldset = styled.fieldset`
min-width: 0;
legend {
display: table;
+ width: 100%;
}
`;
diff --git a/src/routing/AllRoutes.tsx b/src/routing/AllRoutes.tsx
index a7241a47..d3a01c7b 100644
--- a/src/routing/AllRoutes.tsx
+++ b/src/routing/AllRoutes.tsx
@@ -1,14 +1,12 @@
import { Navigate, Route, Routes } from "react-router-dom";
import { App } from "../App";
-import { LessonArchive } from "../views/lessons/LessonArchive";
+import { LessonHistoryPage } from "../views/lessons/LessonHistoryPage";
import { ViewLesson } from "../views/lessons/ViewLesson";
import { PracticeLesson } from "../views/practice/PracticeLesson";
-import { MyTerms } from "../views/terms/MyTerms";
import { CoursesPage } from "../views/vocabulary/CoursesPage";
-import { MySets } from "../views/vocabulary/MySets";
import { ViewCollection } from "../views/vocabulary/ViewCollection";
import { ViewSet } from "../views/vocabulary/ViewSet";
-import { Settings } from "../views/settings/Settings";
+import { SettingsPage } from "../views/settings/SettingsPage";
import { SignInPage } from "../views/signin/SignInPage";
import { CreateAccountPage } from "../views/signin/CreateAccountPage";
import { ForgotPasswordPage } from "../views/signin/ForgotPasswordPage";
@@ -54,14 +52,14 @@ export function AllRoutes() {
{/* }> */}
{/* } /> */}
- } />
+ } />
} />
} />
}>
- } />
+ } />
diff --git a/src/views/lessons/LessonArchive.tsx b/src/views/lessons/LessonArchive.tsx
deleted file mode 100644
index 7e5e913d..00000000
--- a/src/views/lessons/LessonArchive.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-import React, { ReactElement } from "react";
-import { Duration } from "luxon";
-import { SmallLoader } from "../../components/Loader";
-import { SectionHeading } from "../../components/SectionHeading";
-import { StyledLink } from "../../components/StyledLink";
-import { StyledTable } from "../../components/StyledTable";
-import { VisuallyHidden } from "../../components/VisuallyHidden";
-import { useAuth } from "../../firebase/AuthProvider";
-import {
- useAnalyticsPageName,
- useFirebaseAllLessonMetadata,
-} from "../../firebase/hooks";
-import { Lesson, nameForLesson } from "../../state/reducers/lessons";
-import { ViewLessonPath } from "../../routing/paths";
-
-type FinishedLesson = Lesson & { completedAt: number };
-
-export function LessonArchive(): ReactElement {
- useAnalyticsPageName("Lesson archive");
- const { user } = useAuth();
-
- const [firebaseResult, _] = useFirebaseAllLessonMetadata(user);
-
- if (!firebaseResult.ready)
- return (
-
-
-
- );
-
- const lessons = firebaseResult.data ?? {};
-
- const finishedLessons = Object.values(lessons)
- .filter((l): l is FinishedLesson => Boolean(l.completedAt))
- // most recent first
- .sort((a, b) => b.completedAt - a.completedAt);
- return (
-
-
Lessons archive
-
- Here, you can review lessons you've already completed. This can help you
- know how long lessons of different sizes take for you to complete.
-
- {finishedLessons.length ? (
-
-
-
- Name
- Number of challenges
- Duration
-
- Link to view lesson details
-
-
-
-
- {finishedLessons.map((lesson, idx) => (
-
- ))}
-
-
- ) : (
-
- You have not completed any lessons. Head over to the{" "}
- dashboard to start learning!
-
- )}
-
- );
-}
-
-function FinishedLessonRow({ lesson }: { lesson: FinishedLesson }) {
- return (
-
- {nameForLesson(lesson)}
- {lesson.numChallenges || "Unknown number of challenges"}
-
- {lesson.startedAt
- ? Duration.fromObject({
- milliseconds: lesson.completedAt - lesson.startedAt,
- })
- .shiftTo("minutes", "seconds", "milliseconds")
- .mapUnits((x, u) => (u === "milliseconds" ? 0 : x))
- .shiftTo("minutes", "seconds")
- .toHuman()
- : "--"}
-
-
- Details
-
-
- );
-}
diff --git a/src/views/lessons/LessonHistoryPage.tsx b/src/views/lessons/LessonHistoryPage.tsx
new file mode 100644
index 00000000..fae187bf
--- /dev/null
+++ b/src/views/lessons/LessonHistoryPage.tsx
@@ -0,0 +1,91 @@
+import React, { ReactElement } from "react";
+import { Duration } from "luxon";
+import { SmallLoader } from "../../components/Loader";
+import { SectionHeading } from "../../components/SectionHeading";
+import { StyledLink } from "../../components/StyledLink";
+import { StyledTable } from "../../components/StyledTable";
+import { VisuallyHidden } from "../../components/VisuallyHidden";
+import { useAuth } from "../../firebase/AuthProvider";
+import {
+ useAnalyticsPageName,
+ useFirebaseAllLessonMetadata,
+} from "../../firebase/hooks";
+import { Lesson, nameForLesson } from "../../state/reducers/lessons";
+import { ViewLessonPath } from "../../routing/paths";
+import { HanehldaView } from "../../components/HanehldaView";
+import { DefaultNav } from "../../components/HanehldaView/HanehldaNav";
+import { Button, ButtonLink } from "../../components/Button";
+import { Link } from "react-router-dom";
+
+type FinishedLesson = Lesson & { completedAt: number };
+
+export function LessonHistoryPage(): ReactElement {
+ useAnalyticsPageName("Lesson archive");
+ const { user } = useAuth();
+
+ const [firebaseResult, _] = useFirebaseAllLessonMetadata(user);
+
+ if (!firebaseResult.ready)
+ return (
+
+
+
+ );
+
+ const lessons = firebaseResult.data ?? {};
+
+ const finishedLessons = Object.values(lessons)
+ .filter((l): l is FinishedLesson => Boolean(l.completedAt))
+ // most recent first
+ .sort((a, b) => b.completedAt - a.completedAt);
+ return (
+ } collapseNav>
+
+
Lessons archive
+
+ Here, you can review lessons you've already completed. This can help
+ you know how long lessons of different sizes take for you to complete.
+
+ {finishedLessons.length ? (
+
+
+
+ Date completed
+
+ Link to view lesson details
+
+
+
+
+ {finishedLessons.map((lesson, idx) => (
+
+ ))}
+
+
+ ) : (
+
+ You have not completed any lessons. Head over to the{" "}
+ dashboard to start learning!
+
+ )}
+
+
+ );
+}
+
+function FinishedLessonRow({ lesson }: { lesson: FinishedLesson }) {
+ return (
+
+ {new Date(lesson.createdFor).toDateString()}
+
+
+ Details
+
+
+
+ );
+}
diff --git a/src/views/lessons/ViewLesson.tsx b/src/views/lessons/ViewLesson.tsx
index 2a91f295..8a2dcc54 100644
--- a/src/views/lessons/ViewLesson.tsx
+++ b/src/views/lessons/ViewLesson.tsx
@@ -1,5 +1,5 @@
import React, { ReactElement } from "react";
-import { Navigate, useNavigate, useParams } from "react-router-dom";
+import { Link, Navigate, useNavigate, useParams } from "react-router-dom";
import { Card, cards, keyForCard } from "../../data/cards";
import { nameForLesson } from "../../state/reducers/lessons";
import { ReviewResult } from "../../state/reducers/leitnerBoxes";
@@ -9,7 +9,15 @@ import { SectionHeading } from "../../components/SectionHeading";
import { CardTable } from "../../components/CardTable";
import { StyledLink } from "../../components/StyledLink";
import { useAnalyticsPageName } from "../../firebase/hooks";
-import { LessonsPath } from "../../routing/paths";
+import { DashboardPath, LessonsPath } from "../../routing/paths";
+import { BlueEm } from "../settings/SettingsPage";
+import { HanehldaView } from "../../components/HanehldaView";
+import { DefaultNav } from "../../components/HanehldaView/HanehldaNav";
+import styled from "styled-components";
+import { LearnPage } from "../learn/LearnPage";
+import { Button } from "../../components/Button";
+import { theme } from "../../theme";
+import { Hr } from "../setup/common";
export function ViewLesson(): ReactElement {
useAnalyticsPageName("View lesson");
@@ -32,6 +40,18 @@ const reviewResultNames: Record = {
REPEAT_MISTAKE: "Multiple mistakes",
};
+const ContentWrapper = styled.div`
+ padding: 5px 20px;
+`;
+
+const ReviewedCardsWrapper = styled.div`
+ h3 {
+ text-align: left;
+ margin: 10px;
+ font-size: ${theme.fontSizes.md};
+ }
+`;
+
export function _ViewLesson(): ReactElement {
const { lesson, reviewedTerms } = useLesson();
const reviewedCards = useCardsForTerms(
@@ -54,25 +74,40 @@ export function _ViewLesson(): ReactElement {
}
);
return (
-
-
Lesson debrief - {nameForLesson(lesson)}
-
- Take a look at how you did! If you were confused by a term, go ahead and
- listen to all the alternate pronouncations by hitting the "More" button.
-
-
- Head back to the dashboard to keep
- learning!
-
- {Object.entries(reviewedCardsByStatus).map(
- ([result, cards]) =>
- cards.length > 0 && (
-
-
{reviewResultNames[result as ReviewResult]}
-
-
- )
- )}
-
+ } collapseNav>
+
+
+ Lesson debrief
+
+ {nameForLesson(lesson)}
+
+
+ Take a look at how you did! If you were confused by a term, go ahead
+ and listen to all the alternate pronouncations by hitting the "More"
+ button.
+
+
+ Keep learning
+
+
+
+
+ {Object.entries(reviewedCardsByStatus).map(
+ ([result, cards]) =>
+ cards.length > 0 && (
+
+
{reviewResultNames[result as ReviewResult]}
+
+
+ )
+ )}
+
+
+
+ Keep learning
+
+
+
+
);
}
diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx
deleted file mode 100644
index b605bbf3..00000000
--- a/src/views/settings/Settings.tsx
+++ /dev/null
@@ -1,202 +0,0 @@
-import { ChangeEvent, useId, useState } from "react";
-import { useNavigate } from "react-router-dom";
-import styled from "styled-components";
-import { Button } from "../../components/Button";
-import { SectionHeading } from "../../components/SectionHeading";
-import { useAnalyticsPageName } from "../../firebase/hooks";
-import { lessonKey } from "../../state/reducers/lessons";
-import {
- isPhoneticsPreference,
- PREFERENCE_LITERATES,
-} from "../../state/reducers/phoneticsPreference";
-import { useUserStateContext } from "../../providers/UserStateProvider";
-import { UserState } from "../../state/useUserState";
-import { useAuth } from "../../firebase/AuthProvider";
-import { signOut } from "firebase/auth";
-import { auth } from "../../firebase";
-import { HanehldaView } from "../../components/HanehldaView";
-import { DefaultNav } from "../../components/HanehldaView/HanehldaNav";
-
-interface ExportedLessonData {
- lessonId: string;
- reviewedTerms: string | null;
- timings: string | null;
-}
-
-export function Settings() {
- const {
- config: { userEmail },
- } = useUserStateContext();
- const { user } = useAuth();
- useAnalyticsPageName("Settings");
- return (
- } collapseNav>
-
-
-
-
User identity
-
User id: {user.uid}
-
- We have your email on file as: {userEmail}
-
- {!user.isAnonymous && (
-
signOut(auth)}>Sign out
- )}
-
-
- Wrong address? Contact the maintainer at{" "}
-
- charliemcvicker@protonmail.com
-
-
-
-
-
-
-
-
- Settings below this point might not be much use to you unless a
- maintainer of this website contacted you.
-
-
-
-
-
- );
-}
-
-const PreferencesForm = styled.form`
- display: grid;
- grid-template-columns: auto 1fr;
- grid-gap: 8px;
-`;
-
-function Preferences() {
- const {
- setPhoneticsPreference,
- config: { phoneticsPreference },
- } = useUserStateContext();
- const phoneticsPreferenceId = useId();
-
- function onPhoneticsPreferenceChanged(event: ChangeEvent) {
- event.preventDefault();
- const newPreference = event.target.value;
- if (isPhoneticsPreference(newPreference)) {
- setPhoneticsPreference(newPreference);
- }
- }
-
- return (
-
-
Preferences
-
- Phonetics preference
-
- {Object.entries(PREFERENCE_LITERATES).map(([value, literate], i) => (
-
- {literate}
-
- ))}
-
-
-
- );
-}
-
-function ImportExportDataConsole() {
- const userState = useUserStateContext();
- const [fileToLoad, setFileToLoad] = useState(null);
- const navigate = useNavigate();
-
- function downloadAllData() {
- // this will need to be updated as data is added to user state. if you get
- // a type error here, you probably added a new top level field to user
- // state, and need to add that key here.
- const fieldsToSave: Record = {
- leitnerBoxes: null,
- // lessons: null,
- config: null,
- };
-
- const stateToSave = Object.keys(fieldsToSave).reduce(
- (obj, key) => ({ ...obj, [key]: userState[key as keyof UserState] }),
- {}
- );
-
- // const lessonData: ExportedLessonData[] = Object.keys(userState).map(
- // (lessonId) => ({
- // lessonId,
- // reviewedTerms: window.localStorage.getItem(
- // lessonKey(lessonId) + "/reviewed-terms"
- // ),
- // timings: window.localStorage.getItem(lessonKey(lessonId) + "/timings"),
- // })
- // );
-
- const dataStr =
- "data:text/json;charset=utf-8," +
- encodeURIComponent(JSON.stringify({ ...stateToSave /** lessonData */ }));
- const dlAnchorElem = document.createElement("a");
-
- dlAnchorElem.setAttribute("href", dataStr);
- dlAnchorElem.setAttribute("download", "cherokeeLanguageExercisesData.json");
-
- dlAnchorElem.click();
- }
-
- function onLoadFileChanged(event: ChangeEvent) {
- const files = event.target.files;
- if (!files || files.length !== 1) {
- setFileToLoad(null);
- } else {
- setFileToLoad(files[0]);
- }
- }
-
- function loadData() {
- if (fileToLoad)
- fileToLoad.text().then((data) => {
- const { lessonData, ...state } = JSON.parse(data);
- // load lesson data
- (lessonData ?? []).forEach((exported: ExportedLessonData) => {
- if (exported.reviewedTerms) {
- window.localStorage.setItem(
- lessonKey(exported.lessonId) + "/reviewed-terms",
- exported.reviewedTerms
- );
- }
-
- if (exported.timings) {
- window.localStorage.setItem(
- lessonKey(exported.lessonId) + "/timings",
- exported.timings
- );
- }
- });
-
- // load larger user state
- // userState.loadState(state);
- localStorage.setItem("user-state", JSON.stringify(state));
-
- navigate("/");
- });
- }
- return (
-
- Export data
- Download all data
-
-
- Import data
-
-
- );
-}
diff --git a/src/views/settings/SettingsPage.tsx b/src/views/settings/SettingsPage.tsx
new file mode 100644
index 00000000..0d567583
--- /dev/null
+++ b/src/views/settings/SettingsPage.tsx
@@ -0,0 +1,164 @@
+import {
+ ChangeEvent,
+ FormEvent,
+ ReactElement,
+ useId,
+ useMemo,
+ useState,
+} from "react";
+import { Link, useNavigate } from "react-router-dom";
+import styled from "styled-components";
+import { Button } from "../../components/Button";
+import { useAnalyticsPageName } from "../../firebase/hooks";
+import { lessonKey } from "../../state/reducers/lessons";
+import { PhoneticsPreference } from "../../state/reducers/phoneticsPreference";
+import { useUserStateContext } from "../../providers/UserStateProvider";
+import { UserState } from "../../state/useUserState";
+import { useAuth } from "../../firebase/AuthProvider";
+import { signOut } from "firebase/auth";
+import { auth } from "../../firebase";
+import { HanehldaView } from "../../components/HanehldaView";
+import { DefaultNav } from "../../components/HanehldaView/HanehldaNav";
+import { RadioBar } from "../../components/RadioBar";
+import { cards } from "../../data/cards";
+import { getPhonetics } from "../../utils/phonetics";
+import { Hr } from "../setup/common";
+import { theme } from "../../theme";
+import { LessonsPath } from "../../routing/paths";
+
+interface ExportedLessonData {
+ lessonId: string;
+ reviewedTerms: string | null;
+ timings: string | null;
+}
+
+export function SettingsPage() {
+ useAnalyticsPageName("Settings");
+ return (
+ } collapseNav>
+
+
+
+
+
+
+
+
+
+ If you have any questions please contact a maintainer at{" "}
+
+ charliemcvicker@protonmail.com
+
+
+
+
+
+ );
+}
+
+const PreferencesForm = styled.form`
+ display: grid;
+ grid-template-columns: auto 1fr;
+ grid-gap: 8px;
+ text-align: center;
+`;
+
+function Preferences() {
+ const {
+ setPhoneticsPreference,
+ config: { phoneticsPreference },
+ } = useUserStateContext();
+
+ const showTone = useMemo(
+ () => phoneticsPreference === PhoneticsPreference.Detailed,
+ [phoneticsPreference]
+ );
+
+ function onShowToneChanged(newValue: string) {
+ const newPhoneticsPreference =
+ newValue === "yes"
+ ? PhoneticsPreference.Detailed
+ : PhoneticsPreference.Simple;
+ setPhoneticsPreference(newPhoneticsPreference);
+ }
+
+ const demoCard = useMemo(
+ () => cards.find((c) => c.syllabary === "ᎠᏴᏓᏆᎶᏍᎩ")!,
+ []
+ );
+
+ const phoneticsPreview = useMemo(
+ () => getPhonetics(demoCard, phoneticsPreference),
+ [phoneticsPreference]
+ );
+
+ return (
+
+
Preferences
+
+
+
+
+ Example: {phoneticsPreview}
+
+
+
+
+ Tone is an important aspect of Cherokee, but can be daunting when
+ you're just getting started. You can always change this setting
+ later.
+
+
+
+
+
+ );
+}
+
+export const BlueEm = styled.em`
+ font-style: normal;
+ color: ${theme.hanehldaColors.DARK_BLUE};
+`;
+
+function UserIdentity(): ReactElement {
+ const {
+ config: { userEmail },
+ } = useUserStateContext();
+ const { user } = useAuth();
+ return (
+
+
User identity
+
+ User id: {user.uid}
+
+
+ We have your email on file as: {userEmail}
+
+ {!user.isAnonymous && (
+
signOut(auth)}>
+ Sign out
+
+ )}
+
+ );
+}
+
+function LessonArchiveLink(): ReactElement {
+ return (
+
+
Lesson history
+
You can browse previously completed lessons below.
+
+ View history
+
+
+ );
+}