From 0d535978ccc77d5a1827ce73f61a368fb32cb38b Mon Sep 17 00:00:00 2001 From: Charlie McVicker Date: Sun, 22 Jan 2023 21:11:07 -0500 Subject: [PATCH 01/14] starter code for new onboarding workflow --- .../GettingStartedModal/FakeStep.tsx | 27 ++++ .../GroupRegistrationStep.tsx | 59 +++++++++ src/components/GettingStartedModal/index.tsx | 119 ++++++++++++++++++ src/components/GroupRegistrationModal.tsx | 47 ------- src/state/UserStateProvider.tsx | 6 +- 5 files changed, 207 insertions(+), 51 deletions(-) create mode 100644 src/components/GettingStartedModal/FakeStep.tsx create mode 100644 src/components/GettingStartedModal/GroupRegistrationStep.tsx create mode 100644 src/components/GettingStartedModal/index.tsx delete mode 100644 src/components/GroupRegistrationModal.tsx diff --git a/src/components/GettingStartedModal/FakeStep.tsx b/src/components/GettingStartedModal/FakeStep.tsx new file mode 100644 index 00000000..eb6afa95 --- /dev/null +++ b/src/components/GettingStartedModal/FakeStep.tsx @@ -0,0 +1,27 @@ +import { ReactElement } from "react"; +import { NavigationButtons, Step, StepProps } from "."; +import { Button } from "../Button"; + +export const FakeStep: Step = { + title: "Fake final step", + commitState: () => {}, + Component: FakeStepComponent, +}; + +function FakeStepComponent({ + wizardState, + goToNextStep, + goToPreviousStep, +}: StepProps): ReactElement { + return ( +
+

Debug final step!

+

{JSON.stringify(wizardState)}

+ goToNextStep()}>Finish!} + /> +
+ ); +} diff --git a/src/components/GettingStartedModal/GroupRegistrationStep.tsx b/src/components/GettingStartedModal/GroupRegistrationStep.tsx new file mode 100644 index 00000000..3481dd31 --- /dev/null +++ b/src/components/GettingStartedModal/GroupRegistrationStep.tsx @@ -0,0 +1,59 @@ +import { ChangeEvent, FormEvent } from "react"; +import { NavigationButtons, Step, StepProps } from "."; +import { GROUPS } from "../../state/reducers/groupId"; +import { Button } from "../Button"; + +export const GroupRegistrationStep: Step = { + title: "Select your group...", + Component: GroupRegistrationForm, + commitState: ({ groupId }, { registerGroup }) => { + registerGroup(groupId); + }, +}; + +export function GroupRegistrationForm({ + wizardState: { groupId }, + setWizardState, + goToNextStep, + goToPreviousStep, +}: StepProps) { + function onRadioChanged(e: ChangeEvent) { + const groupId = e.target.value; + setWizardState((s) => ({ ...s, groupId })); + } + function onSubmit(e: FormEvent) { + e.preventDefault(); + if (groupId) goToNextStep(); + } + return ( + <> +

+ Please select the group you are registered with to use the app. If you + are not part of any group, please choose the "Open beta" option. +

+
+
+ Group + {Object.entries(GROUPS).map(([id, group]) => ( +
+ + +
+ ))} +
+ } + /> + + + ); +} diff --git a/src/components/GettingStartedModal/index.tsx b/src/components/GettingStartedModal/index.tsx new file mode 100644 index 00000000..48b01c79 --- /dev/null +++ b/src/components/GettingStartedModal/index.tsx @@ -0,0 +1,119 @@ +import { + Dispatch, + ReactElement, + ReactNode, + SetStateAction, + useState, +} from "react"; +import { + UserInteractors, + useUserStateContext, +} from "../../state/UserStateProvider"; +import { Button } from "../Button"; +import { Modal } from "../Modal"; +import { FakeStep } from "./FakeStep"; +import { GroupRegistrationStep } from "./GroupRegistrationStep"; + +export interface WizardState { + groupId: string; + fakeOtherField: number; +} + +export interface StepProps { + // these props let us keep track of the user's data + // partial means any field can be undefined + wizardState: Partial; + setWizardState: Dispatch>>; + + // these functions let us navigate through the steps + // undefiend if on first step + goToPreviousStep?: () => void; + goToNextStep: () => void; +} + +export interface Step { + title: string; + Component: (props: StepProps) => ReactElement; + /** + * This function says how we should actually update user state. + */ + commitState: (state: WizardState, interactors: UserInteractors) => void; +} + +/** + * This is the list of steps for the getting started modal. + * + * You will need to add more steps here. + */ +const steps: Step[] = [GroupRegistrationStep, FakeStep]; + +export function GettingStartedModal() { + const userStateContext = useUserStateContext(); + + // keep track of which step of workflow we are on (start on first step) + const [stepNumber, setStepNumber] = useState(0); + // keep track of what data the user has filled out + const [wizardState, setWizardState] = useState>({}); + + /** + * Take all the data the user input and run actions for each step using it + */ + function finish() { + // at the end of the workflow + if (stepNumber >= steps.length) { + // any part of wizard state could be undefined, so unpack it all + const { groupId, fakeOtherField } = wizardState; + // add checks here to make sure fields are defined + if (groupId !== undefined && fakeOtherField !== undefined) { + // reassemble state here (without Partial<>) + const fullState: WizardState = { groupId, fakeOtherField }; + // dispatch the actions for each step + steps.forEach((step) => step.commitState(fullState, userStateContext)); + } + } + } + + // render current step of workflow + const currentStep = steps[stepNumber]; + return ( + {}}> + {/* Rendering a component from a variable! This is how we change the content from step to step */} + { + setStepNumber(stepNumber - 1); + } + } + goToNextStep={() => { + const nextStep = stepNumber + 1; + if (stepNumber < steps.length) { + setStepNumber(nextStep); + } else { + finish(); + } + }} + setWizardState={setWizardState} + wizardState={wizardState} + /> + + ); +} + +export function NavigationButtons({ + goToPreviousStep, + goToNextStep, + customNext, +}: Pick & { + customNext?: ReactNode; +}) { + return ( +
+ {goToPreviousStep && ( + + )} + {customNext || } +
+ ); +} diff --git a/src/components/GroupRegistrationModal.tsx b/src/components/GroupRegistrationModal.tsx deleted file mode 100644 index c58d28da..00000000 --- a/src/components/GroupRegistrationModal.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import React, { ChangeEvent, FormEvent, useState } from "react"; -import { GROUPS } from "../state/reducers/groupId"; -import { Button } from "./Button"; -import { Modal } from "./Modal"; - -export function GroupRegistrationModal({ - registerGroup, -}: { - registerGroup: (id: string) => void; -}) { - const [groupId, setGroupId] = useState(undefined); - function onRadioChanged(e: ChangeEvent) { - setGroupId(e.target.value); - } - function onSubmit(e: FormEvent) { - e.preventDefault(); - if (groupId) registerGroup(groupId); - } - return ( - {}} title="Group registration"> -

- Please select the group you are registered with to use the app. If you - are not part of any group, please choose the "Open beta" option. -

-
-
- Group - {Object.entries(GROUPS).map(([id, group]) => ( -
- - -
- ))} -
-
-
-
-
- ); -} diff --git a/src/state/UserStateProvider.tsx b/src/state/UserStateProvider.tsx index 5b1064b3..28aedca7 100644 --- a/src/state/UserStateProvider.tsx +++ b/src/state/UserStateProvider.tsx @@ -28,7 +28,7 @@ import { import { UserStateAction } from "./actions"; import { LessonCreationError } from "./reducers/lessons/createNewLesson"; import { GroupId, GROUPS, isGroupId, reduceGroupId } from "./reducers/groupId"; -import { GroupRegistrationModal } from "../components/GroupRegistrationModal"; +import { GettingStartedModal } from "../components/GettingStartedModal"; import { PhoneticsPreference } from "./reducers/phoneticsPreference"; export interface UserStateProps { @@ -263,9 +263,7 @@ export function UserStateProvider({ return ( {children} - {state.groupId === undefined && ( - - )} + {state.groupId === undefined && } ); } From 34b38ac9bbb1db3903eb38dc36288698ac74cf29 Mon Sep 17 00:00:00 2001 From: Charlie McVicker Date: Mon, 23 Jan 2023 14:40:49 -0500 Subject: [PATCH 02/14] oop --- src/components/GettingStartedModal/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/GettingStartedModal/index.tsx b/src/components/GettingStartedModal/index.tsx index 48b01c79..14d6bea9 100644 --- a/src/components/GettingStartedModal/index.tsx +++ b/src/components/GettingStartedModal/index.tsx @@ -88,7 +88,7 @@ export function GettingStartedModal() { } goToNextStep={() => { const nextStep = stepNumber + 1; - if (stepNumber < steps.length) { + if (nextStep < steps.length) { setStepNumber(nextStep); } else { finish(); From 9d2ea5fdfd3b271cf9cd16ed1b068ed6727bf37c Mon Sep 17 00:00:00 2001 From: Charlie McVicker Date: Mon, 23 Jan 2023 14:44:03 -0500 Subject: [PATCH 03/14] oops pt2 --- src/components/GettingStartedModal/index.tsx | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/components/GettingStartedModal/index.tsx b/src/components/GettingStartedModal/index.tsx index 14d6bea9..e1c99518 100644 --- a/src/components/GettingStartedModal/index.tsx +++ b/src/components/GettingStartedModal/index.tsx @@ -16,7 +16,6 @@ import { GroupRegistrationStep } from "./GroupRegistrationStep"; export interface WizardState { groupId: string; - fakeOtherField: number; } export interface StepProps { @@ -59,17 +58,14 @@ export function GettingStartedModal() { * Take all the data the user input and run actions for each step using it */ function finish() { - // at the end of the workflow - if (stepNumber >= steps.length) { - // any part of wizard state could be undefined, so unpack it all - const { groupId, fakeOtherField } = wizardState; - // add checks here to make sure fields are defined - if (groupId !== undefined && fakeOtherField !== undefined) { - // reassemble state here (without Partial<>) - const fullState: WizardState = { groupId, fakeOtherField }; - // dispatch the actions for each step - steps.forEach((step) => step.commitState(fullState, userStateContext)); - } + // any part of wizard state could be undefined, so unpack it all + const { groupId } = wizardState; + // add checks here to make sure fields are defined + if (groupId !== undefined) { + // reassemble state here (without Partial<>) + const fullState: WizardState = { groupId }; + // dispatch the actions for each step + steps.forEach((step) => step.commitState(fullState, userStateContext)); } } From b960c45ba3d4ca2ff0afaec6aa635f92f03fa4a1 Mon Sep 17 00:00:00 2001 From: Charlie McVicker <5622360+CharlieMcVicker@users.noreply.github.com> Date: Mon, 6 Feb 2023 14:11:42 -0500 Subject: [PATCH 04/14] Phonetics Preference Implementation (#64) Co-authored-by: Neil Daterao --- .../GettingStartedModal/PhoneticsStep.tsx | 35 +++++++++++++++++++ src/components/GettingStartedModal/index.tsx | 3 +- src/views/settings/Settings.tsx | 2 +- 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 src/components/GettingStartedModal/PhoneticsStep.tsx diff --git a/src/components/GettingStartedModal/PhoneticsStep.tsx b/src/components/GettingStartedModal/PhoneticsStep.tsx new file mode 100644 index 00000000..79b5d28d --- /dev/null +++ b/src/components/GettingStartedModal/PhoneticsStep.tsx @@ -0,0 +1,35 @@ +import { ReactElement } from "react"; +import { NavigationButtons, Step, StepProps } from "."; +import { Button } from "../Button"; +import { Preferences } from "../../views/settings/Settings"; + + + + +export const PhoneticsStep: Step = { + title: "Phonetics", + commitState: () => {}, + Component: PhoneticsStepComponent, + }; + + + function PhoneticsStepComponent({ + wizardState, + goToNextStep, + goToPreviousStep, + }: StepProps): ReactElement { + return ( +
+

Select your phonetics preference!

+ + + + goToNextStep()}>Next} + /> +
+ ); + } + \ No newline at end of file diff --git a/src/components/GettingStartedModal/index.tsx b/src/components/GettingStartedModal/index.tsx index e1c99518..252f7e8a 100644 --- a/src/components/GettingStartedModal/index.tsx +++ b/src/components/GettingStartedModal/index.tsx @@ -12,6 +12,7 @@ import { import { Button } from "../Button"; import { Modal } from "../Modal"; import { FakeStep } from "./FakeStep"; +import { PhoneticsStep } from "./PhoneticsStep"; import { GroupRegistrationStep } from "./GroupRegistrationStep"; export interface WizardState { @@ -44,7 +45,7 @@ export interface Step { * * You will need to add more steps here. */ -const steps: Step[] = [GroupRegistrationStep, FakeStep]; +const steps: Step[] = [GroupRegistrationStep, PhoneticsStep, FakeStep]; export function GettingStartedModal() { const userStateContext = useUserStateContext(); diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index 88fa3064..d95dee86 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -39,7 +39,7 @@ const PreferencesForm = styled.form` grid-gap: 8px; `; -function Preferences() { +export function Preferences() { const { setPhoneticsPreference, phoneticsPreference } = useUserStateContext(); const phoneticsPreferenceId = useId(); From 02760a72e635f9835790b78f4490abd789dd2714 Mon Sep 17 00:00:00 2001 From: Charlie McVicker Date: Mon, 6 Feb 2023 14:47:55 -0500 Subject: [PATCH 05/14] keep state in wizard state; use radio buttons; implement default phonetics preference --- .../GettingStartedModal/PhoneticsStep.tsx | 97 ++++++++++++++----- src/components/GettingStartedModal/index.tsx | 10 +- src/views/settings/Settings.tsx | 2 +- 3 files changed, 80 insertions(+), 29 deletions(-) diff --git a/src/components/GettingStartedModal/PhoneticsStep.tsx b/src/components/GettingStartedModal/PhoneticsStep.tsx index 79b5d28d..6eb5c6fc 100644 --- a/src/components/GettingStartedModal/PhoneticsStep.tsx +++ b/src/components/GettingStartedModal/PhoneticsStep.tsx @@ -1,35 +1,84 @@ -import { ReactElement } from "react"; +import { ChangeEvent, FormEvent, ReactElement, useEffect } from "react"; import { NavigationButtons, Step, StepProps } from "."; import { Button } from "../Button"; -import { Preferences } from "../../views/settings/Settings"; - +import { + isPhoneticsPreference, + PhoneticsPreference, + PREFERENCE_LITERATES, +} from "../../state/reducers/phoneticsPreference"; +import { GROUPS, isGroupId } from "../../state/reducers/groupId"; +export const PhoneticsStep: Step = { + title: "Phonetics", + commitState: ({ phoneticsPreference }, { setPhoneticsPreference }) => { + setPhoneticsPreference(phoneticsPreference); + }, + Component: PhoneticsStepComponent, +}; +function PhoneticsStepComponent({ + wizardState: { phoneticsPreference, groupId }, + setWizardState, + goToNextStep, + goToPreviousStep, +}: StepProps): ReactElement { + function setWizardStatePhoneticsPreference( + newPhoneticsPreference: PhoneticsPreference + ) { + setWizardState((s) => ({ + ...s, + phoneticsPreference: newPhoneticsPreference, + })); + } + function onRadioChanged(e: ChangeEvent) { + const phoneticsPreference = e.target.value; + if (isPhoneticsPreference(phoneticsPreference)) + setWizardStatePhoneticsPreference(phoneticsPreference); + } + function onSubmit(e: FormEvent) { + e.preventDefault(); + if (phoneticsPreference) goToNextStep(); + } -export const PhoneticsStep: Step = { - title: "Phonetics", - commitState: () => {}, - Component: PhoneticsStepComponent, - }; + useEffect(() => { + if (!phoneticsPreference) { + const defaultPhoneticsPreference = + groupId && isGroupId(groupId) + ? GROUPS[groupId].phoneticsPreference + : undefined; + if (defaultPhoneticsPreference) { + setWizardStatePhoneticsPreference(defaultPhoneticsPreference); + } + } + }, [phoneticsPreference]); + return ( +
+

Select your phonetics preference!

- function PhoneticsStepComponent({ - wizardState, - goToNextStep, - goToPreviousStep, - }: StepProps): ReactElement { - return ( -
-

Select your phonetics preference!

- - - +
+
+ Phonetics preference + {Object.entries(PREFERENCE_LITERATES).map(([value, literate], i) => ( +
+ + +
+ ))} +
goToNextStep()}>Next} + customNext={
- ); - } - \ No newline at end of file + +
+ ); +} diff --git a/src/components/GettingStartedModal/index.tsx b/src/components/GettingStartedModal/index.tsx index 252f7e8a..c0ec9af3 100644 --- a/src/components/GettingStartedModal/index.tsx +++ b/src/components/GettingStartedModal/index.tsx @@ -14,9 +14,11 @@ import { Modal } from "../Modal"; import { FakeStep } from "./FakeStep"; import { PhoneticsStep } from "./PhoneticsStep"; import { GroupRegistrationStep } from "./GroupRegistrationStep"; +import { PhoneticsPreference } from "../../state/reducers/phoneticsPreference"; export interface WizardState { groupId: string; + phoneticsPreference: PhoneticsPreference; } export interface StepProps { @@ -60,11 +62,11 @@ export function GettingStartedModal() { */ function finish() { // any part of wizard state could be undefined, so unpack it all - const { groupId } = wizardState; + const { groupId, phoneticsPreference } = wizardState; // add checks here to make sure fields are defined - if (groupId !== undefined) { + if (groupId !== undefined && phoneticsPreference !== undefined) { // reassemble state here (without Partial<>) - const fullState: WizardState = { groupId }; + const fullState: WizardState = { groupId, phoneticsPreference }; // dispatch the actions for each step steps.forEach((step) => step.commitState(fullState, userStateContext)); } @@ -110,7 +112,7 @@ export function NavigationButtons({ {goToPreviousStep && ( )} - {customNext || } + {customNext || } ); } diff --git a/src/views/settings/Settings.tsx b/src/views/settings/Settings.tsx index d95dee86..88fa3064 100644 --- a/src/views/settings/Settings.tsx +++ b/src/views/settings/Settings.tsx @@ -39,7 +39,7 @@ const PreferencesForm = styled.form` grid-gap: 8px; `; -export function Preferences() { +function Preferences() { const { setPhoneticsPreference, phoneticsPreference } = useUserStateContext(); const phoneticsPreferenceId = useId(); From a4465c7db8a5e1813a073d82ac5630716620f4f4 Mon Sep 17 00:00:00 2001 From: Fiona Shyne Date: Thu, 9 Feb 2023 19:45:10 -0500 Subject: [PATCH 06/14] Added a step to add a starting collection --- .../GettingStartedModal/ChooseSetStep.tsx | 110 ++++++++++++++++++ src/components/GettingStartedModal/index.tsx | 13 ++- 2 files changed, 118 insertions(+), 5 deletions(-) create mode 100644 src/components/GettingStartedModal/ChooseSetStep.tsx diff --git a/src/components/GettingStartedModal/ChooseSetStep.tsx b/src/components/GettingStartedModal/ChooseSetStep.tsx new file mode 100644 index 00000000..5f72980c --- /dev/null +++ b/src/components/GettingStartedModal/ChooseSetStep.tsx @@ -0,0 +1,110 @@ +import { ChangeEvent, FormEvent, ReactElement, useEffect } from "react"; +import { NavigationButtons, Step, StepProps } from "."; +import { Button } from "../Button"; +import { + isPhoneticsPreference, + PhoneticsPreference, + PREFERENCE_LITERATES, +} from "../../state/reducers/phoneticsPreference"; +import { GROUPS, isGroupId } from "../../state/reducers/groupId"; +import { SectionHeading } from "../SectionHeading"; +import { CollectionDetails } from "../CollectionDetails"; + +import { collections, VocabSet } from "../../data/vocabSets"; +import { useUserStateContext } from "../../state/UserStateProvider"; +import { TermsByProficiencyLevelChart } from "../TermsByProficiencyLevelChart"; + +export const ChooseSetStep: Step = { + + + title: "Phonetics", + commitState: ({ collectionId }, {setUpstreamCollection}) => { + setUpstreamCollection(collectionId); + }, + Component: PhoneticsStepComponent, +}; + +function PhoneticsStepComponent({ + wizardState: { collectionId, groupId }, + setWizardState, + goToNextStep, + goToPreviousStep, +}: StepProps): ReactElement { + function setWizardStateCollectionId( + newCollectionId: string + ) { + setWizardState((s) => ({ + ...s, + collectionId: newCollectionId, + })); + } + function onRadioChanged(e: ChangeEvent) { + const collectionId = e.target.value; + setWizardStateCollectionId(collectionId); + } + function onSubmit(e: FormEvent) { + e.preventDefault(); + if (collectionId) goToNextStep(); + } + + + function totalTerms(vocab: VocabSet[]){ + var t = 0; + + vocab.map((vocabSet) => ( + t += vocabSet.terms.length + )) + + return t; + } + + + + useEffect(() => { + if (!collectionId) { + const defaultCollectionId = + groupId && isGroupId(groupId) + ? GROUPS[groupId].defaultCollectionId + : undefined; + if (defaultCollectionId) { + setWizardStateCollectionId(defaultCollectionId) + } + } + }, [collectionId]); + + return ( +
+ +

Choose your first set

+ +
+
+ Choose your collection + + {Object.values(collections).map((collection, idx) => ( +
+ + + + +
+ + ))} + +
+ } + /> + +
+ ); +} diff --git a/src/components/GettingStartedModal/index.tsx b/src/components/GettingStartedModal/index.tsx index c0ec9af3..6ce4b69e 100644 --- a/src/components/GettingStartedModal/index.tsx +++ b/src/components/GettingStartedModal/index.tsx @@ -15,10 +15,13 @@ import { FakeStep } from "./FakeStep"; import { PhoneticsStep } from "./PhoneticsStep"; import { GroupRegistrationStep } from "./GroupRegistrationStep"; import { PhoneticsPreference } from "../../state/reducers/phoneticsPreference"; +import { ChooseSetStep } from "./ChooseSetStep"; export interface WizardState { groupId: string; phoneticsPreference: PhoneticsPreference; + collectionId: string; + } export interface StepProps { @@ -47,14 +50,14 @@ export interface Step { * * You will need to add more steps here. */ -const steps: Step[] = [GroupRegistrationStep, PhoneticsStep, FakeStep]; +const steps: Step[] = [GroupRegistrationStep, PhoneticsStep, ChooseSetStep, FakeStep]; export function GettingStartedModal() { const userStateContext = useUserStateContext(); // keep track of which step of workflow we are on (start on first step) const [stepNumber, setStepNumber] = useState(0); - // keep track of what data the user has filled out + // keep track of what data the user has filled out const [wizardState, setWizardState] = useState>({}); /** @@ -62,11 +65,11 @@ export function GettingStartedModal() { */ function finish() { // any part of wizard state could be undefined, so unpack it all - const { groupId, phoneticsPreference } = wizardState; + const { groupId, phoneticsPreference, collectionId } = wizardState; // add checks here to make sure fields are defined - if (groupId !== undefined && phoneticsPreference !== undefined) { + if (groupId !== undefined && phoneticsPreference !== undefined && collectionId != null) { // reassemble state here (without Partial<>) - const fullState: WizardState = { groupId, phoneticsPreference }; + const fullState: WizardState = { groupId, phoneticsPreference, collectionId }; // dispatch the actions for each step steps.forEach((step) => step.commitState(fullState, userStateContext)); } From 07596be08770783500c02e52f305248785c76e88 Mon Sep 17 00:00:00 2001 From: Fiona Shyne Date: Sun, 26 Feb 2023 22:36:10 -0500 Subject: [PATCH 07/14] edited formatting of intro steps --- src/components/Button.tsx | 7 +++ .../GettingStartedModal/ChooseSetStep.tsx | 8 ++- .../GettingStartedModal/FakeStep.tsx | 2 +- .../GroupRegistrationStep.tsx | 6 +- .../GettingStartedModal/PhoneticsStep.tsx | 7 ++- .../GettingStartedModal/Preamble.tsx | 30 ++++++++++ .../GettingStartedModal/StepIndicator.tsx | 6 ++ src/components/GettingStartedModal/index.tsx | 17 +++++- src/components/Modal.tsx | 56 +++++++++++++------ 9 files changed, 111 insertions(+), 28 deletions(-) create mode 100644 src/components/GettingStartedModal/Preamble.tsx create mode 100644 src/components/GettingStartedModal/StepIndicator.tsx diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 9b67a457..fc721bfa 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -39,6 +39,13 @@ export const Button = styledWithDefault( &:hover { border: 1px solid ${theme.colors.TEXT_GRAY}; } + &:disabled{ + background: ${theme.colors.MED_GRAY}; + + &:hover { + border: 1px solid ${theme.colors.MED_GRAY}; + } + } `} `, { diff --git a/src/components/GettingStartedModal/ChooseSetStep.tsx b/src/components/GettingStartedModal/ChooseSetStep.tsx index 5f72980c..868052ac 100644 --- a/src/components/GettingStartedModal/ChooseSetStep.tsx +++ b/src/components/GettingStartedModal/ChooseSetStep.tsx @@ -1,4 +1,4 @@ -import { ChangeEvent, FormEvent, ReactElement, useEffect } from "react"; +import { ChangeEvent, FormEvent, ReactElement, useEffect , useState} from "react"; import { NavigationButtons, Step, StepProps } from "."; import { Button } from "../Button"; import { @@ -25,11 +25,13 @@ export const ChooseSetStep: Step = { }; function PhoneticsStepComponent({ + wizardState: { collectionId, groupId }, setWizardState, goToNextStep, goToPreviousStep, }: StepProps): ReactElement { + const [enabled, setEnbabled] = useState(collectionId != undefined); function setWizardStateCollectionId( newCollectionId: string ) { @@ -41,6 +43,7 @@ function PhoneticsStepComponent({ function onRadioChanged(e: ChangeEvent) { const collectionId = e.target.value; setWizardStateCollectionId(collectionId); + setEnbabled(true); } function onSubmit(e: FormEvent) { e.preventDefault(); @@ -68,6 +71,7 @@ function PhoneticsStepComponent({ : undefined; if (defaultCollectionId) { setWizardStateCollectionId(defaultCollectionId) + setEnbabled(true); } } }, [collectionId]); @@ -102,7 +106,7 @@ function PhoneticsStepComponent({ } + disabled = {!enabled} /> diff --git a/src/components/GettingStartedModal/FakeStep.tsx b/src/components/GettingStartedModal/FakeStep.tsx index eb6afa95..8e76e0dc 100644 --- a/src/components/GettingStartedModal/FakeStep.tsx +++ b/src/components/GettingStartedModal/FakeStep.tsx @@ -20,7 +20,7 @@ function FakeStepComponent({ goToNextStep()}>Finish!} + customNext = {} /> ); diff --git a/src/components/GettingStartedModal/GroupRegistrationStep.tsx b/src/components/GettingStartedModal/GroupRegistrationStep.tsx index 3481dd31..89f1ad89 100644 --- a/src/components/GettingStartedModal/GroupRegistrationStep.tsx +++ b/src/components/GettingStartedModal/GroupRegistrationStep.tsx @@ -1,4 +1,4 @@ -import { ChangeEvent, FormEvent } from "react"; +import { ChangeEvent, FormEvent, useState } from "react"; import { NavigationButtons, Step, StepProps } from "."; import { GROUPS } from "../../state/reducers/groupId"; import { Button } from "../Button"; @@ -17,9 +17,11 @@ export function GroupRegistrationForm({ goToNextStep, goToPreviousStep, }: StepProps) { + const [enabled, setEnbabled] = useState(groupId!=undefined); function onRadioChanged(e: ChangeEvent) { const groupId = e.target.value; setWizardState((s) => ({ ...s, groupId })); + setEnbabled(true); } function onSubmit(e: FormEvent) { e.preventDefault(); @@ -51,7 +53,7 @@ export function GroupRegistrationForm({ } + disabled = {!enabled} /> diff --git a/src/components/GettingStartedModal/PhoneticsStep.tsx b/src/components/GettingStartedModal/PhoneticsStep.tsx index 6eb5c6fc..f8b38c93 100644 --- a/src/components/GettingStartedModal/PhoneticsStep.tsx +++ b/src/components/GettingStartedModal/PhoneticsStep.tsx @@ -1,4 +1,4 @@ -import { ChangeEvent, FormEvent, ReactElement, useEffect } from "react"; +import { ChangeEvent, FormEvent, ReactElement, useEffect, useState } from "react"; import { NavigationButtons, Step, StepProps } from "."; import { Button } from "../Button"; import { @@ -22,6 +22,7 @@ function PhoneticsStepComponent({ goToNextStep, goToPreviousStep, }: StepProps): ReactElement { + const [enabled, setEnbabled] = useState(phoneticsPreference != undefined); function setWizardStatePhoneticsPreference( newPhoneticsPreference: PhoneticsPreference ) { @@ -34,6 +35,7 @@ function PhoneticsStepComponent({ const phoneticsPreference = e.target.value; if (isPhoneticsPreference(phoneticsPreference)) setWizardStatePhoneticsPreference(phoneticsPreference); + setEnbabled(true); } function onSubmit(e: FormEvent) { e.preventDefault(); @@ -48,6 +50,7 @@ function PhoneticsStepComponent({ : undefined; if (defaultPhoneticsPreference) { setWizardStatePhoneticsPreference(defaultPhoneticsPreference); + setEnbabled(true); } } }, [phoneticsPreference]); @@ -76,7 +79,7 @@ function PhoneticsStepComponent({ } + disabled ={!enabled} /> diff --git a/src/components/GettingStartedModal/Preamble.tsx b/src/components/GettingStartedModal/Preamble.tsx new file mode 100644 index 00000000..85723f8d --- /dev/null +++ b/src/components/GettingStartedModal/Preamble.tsx @@ -0,0 +1,30 @@ +import { ReactElement } from "react"; +import { NavigationButtons, Step, StepProps } from "."; +import { Button } from "../Button"; + +export const Preamble: Step = { + title: "Welcome to Cheroke Language Exercises!", + commitState: () => {}, + Component: FakeStepComponent, +}; + +function FakeStepComponent({ + wizardState, + goToNextStep, + goToPreviousStep, +}: StepProps): ReactElement { + return ( +
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sit amet luctus lacus. Ut arcu odio, placerat eu odio at, laoreet dictum urna. Cras a ipsum ante. Nulla consequat lorem nec consectetur commodo. Integer a elit ut velit ullamcorper elementum in eu nunc. Aliquam erat volutpat. Duis sed nisi ante. Aliquam convallis facilisis mauris et laoreet. Vestibulum nec tincidunt eros. Cras consectetur tortor quam, sed rutrum risus volutpat ac. Fusce pellentesque quis nulla et congue. Mauris vitae urna nec neque placerat imperdiet ut pellentesque tortor. Vestibulum at porta neque. Phasellus auctor, mi quis tincidunt sodales, dui risus pretium tellus, vehicula condimentum orci ex sed leo. Integer faucibus sodales euismod. Integer quis mauris elementum, varius felis non, porttitor libero. + + Aliquam eget auctor turpis. Nulla augue sem, tincidunt ac urna ac, aliquet efficitur est. Nunc laoreet sem ut dignissim hendrerit. Nulla egestas dignissim quam ut pulvinar. Phasellus condimentum, velit non volutpat ultrices, est sapien blandit ex, egestas vestibulum ligula est quis mi. Vestibulum pulvinar augue interdum arcu sodales luctus. Duis viverra condimentum eros, at malesuada urna faucibus non. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. + +Nunc malesuada lacus vel purus facilisis pharetra. Vivamus eu tortor pretium, venenatis sapien et, tempus ipsum. Phasellus quis libero varius, suscipit urna vitae, aliquam purus. Proin elementum, ipsum non consequat aliquet, turpis est posuere massa, vitae iaculis magna orci sit amet est. Vestibulum non nulla ac odio pulvinar tempus. Morbi ac risus in velit semper tincidunt. Curabitur imperdiet urna erat, ut tincidunt diam molestie eu. Nunc quis laoreet ante, ac consequat massa. Suspendisse eleifend vel ante vitae imperdiet. + +
+ ); +} diff --git a/src/components/GettingStartedModal/StepIndicator.tsx b/src/components/GettingStartedModal/StepIndicator.tsx new file mode 100644 index 00000000..33429a80 --- /dev/null +++ b/src/components/GettingStartedModal/StepIndicator.tsx @@ -0,0 +1,6 @@ +import styled from "styled-components"; +import { theme } from "../../theme"; + +export const StepIndicator = styled.h4` + text-align: center +`; diff --git a/src/components/GettingStartedModal/index.tsx b/src/components/GettingStartedModal/index.tsx index 6ce4b69e..1148a026 100644 --- a/src/components/GettingStartedModal/index.tsx +++ b/src/components/GettingStartedModal/index.tsx @@ -16,6 +16,8 @@ import { PhoneticsStep } from "./PhoneticsStep"; import { GroupRegistrationStep } from "./GroupRegistrationStep"; import { PhoneticsPreference } from "../../state/reducers/phoneticsPreference"; import { ChooseSetStep } from "./ChooseSetStep"; +import { StepIndicator } from "./StepIndicator"; +import { Preamble } from "./Preamble"; export interface WizardState { groupId: string; @@ -50,7 +52,7 @@ export interface Step { * * You will need to add more steps here. */ -const steps: Step[] = [GroupRegistrationStep, PhoneticsStep, ChooseSetStep, FakeStep]; +const steps: Step[] = [Preamble, GroupRegistrationStep, PhoneticsStep, ChooseSetStep, FakeStep]; export function GettingStartedModal() { const userStateContext = useUserStateContext(); @@ -78,7 +80,7 @@ export function GettingStartedModal() { // render current step of workflow const currentStep = steps[stepNumber]; return ( - {}}> + {/* Rendering a component from a variable! This is how we change the content from step to step */} + +
+ {stepNumber + 1} / {steps.length} + +
+ + ); } @@ -107,15 +116,17 @@ export function NavigationButtons({ goToPreviousStep, goToNextStep, customNext, + disabled }: Pick & { customNext?: ReactNode; + disabled?: boolean; }) { return (
{goToPreviousStep && ( )} - {customNext || } + {customNext || }
); } diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index c48f73cc..e71f3e58 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -44,25 +44,45 @@ export function Modal({ children, }: { title: string; - close: () => void; + close?: () => void; children?: ReactNode; }) { - return createPortal( - <> - close()}> - -
-
-

{title}

- + + if (close == undefined){ + return createPortal( + <> + + +
+
+

{title}

+
+
-
-
- {children} - - , - modalContainer! - ); + {children} + + , + modalContainer! + ); + }else{ + return createPortal( + <> + close()}> + +
+
+

{title}

+ +
+
+
+ {children} +
+ , + modalContainer! + ); + } + } From 617f18df5b58789b00d4d4ecdac4c93f9d0bf3dd Mon Sep 17 00:00:00 2001 From: Caleb L'Italien Date: Mon, 8 May 2023 09:04:29 -0400 Subject: [PATCH 08/14] changes to the starting modal --- .../GettingStartedModal/ChooseSetStep.tsx | 12 ++-- .../GettingStartedModal/FakeStep.tsx | 3 +- .../GroupRegistrationStep.tsx | 54 +++++++++++--- .../GettingStartedModal/PhoneticsStep.tsx | 4 +- .../GettingStartedModal/Preamble.tsx | 17 +++-- src/components/GettingStartedModal/index.tsx | 72 ++++++++++++++----- src/providers/UserStateProvider.tsx | 2 +- src/state/useUserState.test.tsx | 3 + src/state/useUserState.ts | 5 ++ 9 files changed, 128 insertions(+), 44 deletions(-) diff --git a/src/components/GettingStartedModal/ChooseSetStep.tsx b/src/components/GettingStartedModal/ChooseSetStep.tsx index 4d3cdd5a..1ca69318 100644 --- a/src/components/GettingStartedModal/ChooseSetStep.tsx +++ b/src/components/GettingStartedModal/ChooseSetStep.tsx @@ -15,9 +15,9 @@ import { useUserStateContext } from "../../providers/UserStateProvider"; import { TermsByProficiencyLevelChart } from "../TermsByProficiencyLevelChart"; export const ChooseSetStep: Step = { - + // creates an instance of the step interface - title: "Phonetics", + title: "Choose your first set", commitState: ({ collectionId }, {setUpstreamCollection}) => { setUpstreamCollection(collectionId); }, @@ -47,7 +47,7 @@ function PhoneticsStepComponent({ } function onSubmit(e: FormEvent) { e.preventDefault(); - if (collectionId) goToNextStep(); + if (collectionId && goToNextStep) goToNextStep(); } @@ -61,8 +61,6 @@ function PhoneticsStepComponent({ return t; } - - useEffect(() => { if (!collectionId) { const defaultCollectionId = @@ -79,11 +77,11 @@ function PhoneticsStepComponent({ return (
-

Choose your first set

+

- Choose your collection + Select a collection {Object.values(collections).map((collection, idx) => (
diff --git a/src/components/GettingStartedModal/FakeStep.tsx b/src/components/GettingStartedModal/FakeStep.tsx index 8e76e0dc..975256f1 100644 --- a/src/components/GettingStartedModal/FakeStep.tsx +++ b/src/components/GettingStartedModal/FakeStep.tsx @@ -16,11 +16,10 @@ function FakeStepComponent({ return (

Debug final step!

-

{JSON.stringify(wizardState)}

+

{JSON.stringify(wizardState)}

goToNextStep()}>Submit} />
); diff --git a/src/components/GettingStartedModal/GroupRegistrationStep.tsx b/src/components/GettingStartedModal/GroupRegistrationStep.tsx index 89f1ad89..c66c9adc 100644 --- a/src/components/GettingStartedModal/GroupRegistrationStep.tsx +++ b/src/components/GettingStartedModal/GroupRegistrationStep.tsx @@ -4,7 +4,7 @@ import { GROUPS } from "../../state/reducers/groupId"; import { Button } from "../Button"; export const GroupRegistrationStep: Step = { - title: "Select your group...", + title: "Personal Information", Component: GroupRegistrationForm, commitState: ({ groupId }, { registerGroup }) => { registerGroup(groupId); @@ -12,28 +12,62 @@ export const GroupRegistrationStep: Step = { }; export function GroupRegistrationForm({ - wizardState: { groupId }, + wizardState: { groupId , email, whereFound}, setWizardState, goToNextStep, goToPreviousStep, + exitWizard }: StepProps) { - const [enabled, setEnbabled] = useState(groupId!=undefined); + const [enabled, setEnabled] = useState(groupId!=undefined && email !== ''); function onRadioChanged(e: ChangeEvent) { const groupId = e.target.value; setWizardState((s) => ({ ...s, groupId })); - setEnbabled(true); + setEnabled(email !== ''); } + + function onEmailChanged(e: ChangeEvent) { + const email = e.target.value; + setWizardState((s) => ({ ...s, email })); + setEnabled(groupId !== undefined && email !== ''); + } + + function onWhereFoundChanged(e: ChangeEvent){ + const whereFound = e.target.value; + setWizardState((s) => ({ ...s, whereFound })); + setEnabled(whereFound !== undefined); + } + function onSubmit(e: FormEvent) { e.preventDefault(); - if (groupId) goToNextStep(); + if (groupId && email && goToNextStep) goToNextStep(); } return ( <> -

- Please select the group you are registered with to use the app. If you - are not part of any group, please choose the "Open beta" option. +

+ We're happy to have you here! Who are you, and how did you hear about us? +

+

+ After you've completed this, you're all set! Continue for advanced settings.

+
+ Email Address + +
+
+ Where did you find us? + +
Group {Object.entries(GROUPS).map(([id, group]) => ( @@ -53,8 +87,10 @@ export function GroupRegistrationForm({ + > + ); diff --git a/src/components/GettingStartedModal/PhoneticsStep.tsx b/src/components/GettingStartedModal/PhoneticsStep.tsx index f8b38c93..e40ddcc6 100644 --- a/src/components/GettingStartedModal/PhoneticsStep.tsx +++ b/src/components/GettingStartedModal/PhoneticsStep.tsx @@ -11,7 +11,7 @@ import { GROUPS, isGroupId } from "../../state/reducers/groupId"; export const PhoneticsStep: Step = { title: "Phonetics", commitState: ({ phoneticsPreference }, { setPhoneticsPreference }) => { - setPhoneticsPreference(phoneticsPreference); + if (phoneticsPreference) { setPhoneticsPreference(phoneticsPreference); } }, Component: PhoneticsStepComponent, }; @@ -39,7 +39,7 @@ function PhoneticsStepComponent({ } function onSubmit(e: FormEvent) { e.preventDefault(); - if (phoneticsPreference) goToNextStep(); + if (phoneticsPreference && goToNextStep) goToNextStep(); } useEffect(() => { diff --git a/src/components/GettingStartedModal/Preamble.tsx b/src/components/GettingStartedModal/Preamble.tsx index 85723f8d..7ba5d77e 100644 --- a/src/components/GettingStartedModal/Preamble.tsx +++ b/src/components/GettingStartedModal/Preamble.tsx @@ -12,18 +12,27 @@ function FakeStepComponent({ wizardState, goToNextStep, goToPreviousStep, + exitWizard }: StepProps): ReactElement { return (
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sit amet luctus lacus. Ut arcu odio, placerat eu odio at, laoreet dictum urna. Cras a ipsum ante. Nulla consequat lorem nec consectetur commodo. Integer a elit ut velit ullamcorper elementum in eu nunc. Aliquam erat volutpat. Duis sed nisi ante. Aliquam convallis facilisis mauris et laoreet. Vestibulum nec tincidunt eros. Cras consectetur tortor quam, sed rutrum risus volutpat ac. Fusce pellentesque quis nulla et congue. Mauris vitae urna nec neque placerat imperdiet ut pellentesque tortor. Vestibulum at porta neque. Phasellus auctor, mi quis tincidunt sodales, dui risus pretium tellus, vehicula condimentum orci ex sed leo. Integer faucibus sodales euismod. Integer quis mauris elementum, varius felis non, porttitor libero. + This website is dedicated to helping users develop knowledge of the Cherokee language. +

+

+ Code of Conduct: +

+ - Absolutely no being a poopyhead. +

+ - Zero poopyhead behavior allowed. +

+ - No poopyheads! +

- Aliquam eget auctor turpis. Nulla augue sem, tincidunt ac urna ac, aliquet efficitur est. Nunc laoreet sem ut dignissim hendrerit. Nulla egestas dignissim quam ut pulvinar. Phasellus condimentum, velit non volutpat ultrices, est sapien blandit ex, egestas vestibulum ligula est quis mi. Vestibulum pulvinar augue interdum arcu sodales luctus. Duis viverra condimentum eros, at malesuada urna faucibus non. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. + FAQ -Nunc malesuada lacus vel purus facilisis pharetra. Vivamus eu tortor pretium, venenatis sapien et, tempus ipsum. Phasellus quis libero varius, suscipit urna vitae, aliquam purus. Proin elementum, ipsum non consequat aliquet, turpis est posuere massa, vitae iaculis magna orci sit amet est. Vestibulum non nulla ac odio pulvinar tempus. Morbi ac risus in velit semper tincidunt. Curabitur imperdiet urna erat, ut tincidunt diam molestie eu. Nunc quis laoreet ante, ac consequat massa. Suspendisse eleifend vel ante vitae imperdiet.
); diff --git a/src/components/GettingStartedModal/index.tsx b/src/components/GettingStartedModal/index.tsx index 67648006..eb649bf4 100644 --- a/src/components/GettingStartedModal/index.tsx +++ b/src/components/GettingStartedModal/index.tsx @@ -18,10 +18,13 @@ import { useUserStateContext } from "../../providers/UserStateProvider"; import { UserInteractors } from "../../state/useUserState"; export interface WizardState { + // gives us information about the state of the entire wizard groupId: string; - phoneticsPreference: PhoneticsPreference; + email?: string; + whereFound?: string; + phoneticsPreference: PhoneticsPreference | null; collectionId: string; - + inWizard: boolean; } export interface StepProps { @@ -33,7 +36,8 @@ export interface StepProps { // these functions let us navigate through the steps // undefiend if on first step goToPreviousStep?: () => void; - goToNextStep: () => void; + goToNextStep?: () => void; + exitWizard?: () => void; } export interface Step { @@ -48,9 +52,8 @@ export interface Step { /** * This is the list of steps for the getting started modal. * - * You will need to add more steps here. */ -const steps: Step[] = [Preamble, GroupRegistrationStep, PhoneticsStep, ChooseSetStep, FakeStep]; +const steps: Step[] = [Preamble, GroupRegistrationStep, PhoneticsStep, ChooseSetStep]; export function GettingStartedModal() { const userStateContext = useUserStateContext(); @@ -58,18 +61,26 @@ export function GettingStartedModal() { // keep track of which step of workflow we are on (start on first step) const [stepNumber, setStepNumber] = useState(0); // keep track of what data the user has filled out - const [wizardState, setWizardState] = useState>({}); + const [wizardState, setWizardState] = useState>({ }); + + //const [fullState, setFullState] = useState(null); + const [fullState, setFullState] = useState({groupId: '', phoneticsPreference: null, collectionId: '', inWizard: true, email: '', whereFound: ''}); + + function exitWizard(){ + setWizardState((s) => ({ ...s, inWizard: false})); + + } /** * Take all the data the user input and run actions for each step using it */ function finish() { // any part of wizard state could be undefined, so unpack it all - const { groupId, phoneticsPreference, collectionId } = wizardState; + const { groupId, email, phoneticsPreference, collectionId, inWizard} = wizardState; // add checks here to make sure fields are defined - if (groupId !== undefined && phoneticsPreference !== undefined && collectionId != null) { + if (groupId !== undefined && email !== undefined && phoneticsPreference !== undefined && collectionId != null && inWizard !== undefined) { // reassemble state here (without Partial<>) - const fullState: WizardState = { groupId, phoneticsPreference, collectionId }; + const fullState: WizardState = { groupId, email, phoneticsPreference, collectionId, inWizard}; // dispatch the actions for each step steps.forEach((step) => step.commitState(fullState, userStateContext)); } @@ -82,9 +93,8 @@ export function GettingStartedModal() { {/* Rendering a component from a variable! This is how we change the content from step to step */} { + stepNumber === 0 ? undefined // if step one, is undefined + : () => { //otherwise, decrement setStepNumber(stepNumber - 1); } } @@ -96,6 +106,10 @@ export function GettingStartedModal() { finish(); } }} + exitWizard={() => { + finish(); + exitWizard(); + }} setWizardState={setWizardState} wizardState={wizardState} /> @@ -105,26 +119,46 @@ export function GettingStartedModal() {
- - ); } export function NavigationButtons({ goToPreviousStep, goToNextStep, - customNext, + exitWizard, disabled -}: Pick & { - customNext?: ReactNode; +}: Pick & { disabled?: boolean; + children?: React.ReactNode; }) { return ( -
+
{goToPreviousStep && ( )} - {customNext || } + {goToNextStep && ( + + )} + {exitWizard && ( + + )}
); } diff --git a/src/providers/UserStateProvider.tsx b/src/providers/UserStateProvider.tsx index b65280e1..ad602c51 100644 --- a/src/providers/UserStateProvider.tsx +++ b/src/providers/UserStateProvider.tsx @@ -157,7 +157,7 @@ function WrappedUserStateProvider({ return ( {children} - {(state.config.userEmail === null || state.config.groupId === null) && ( + {(state.config.userEmail === null || state.config.groupId === null && state.config.inWizard == true) && ( )} diff --git a/src/state/useUserState.test.tsx b/src/state/useUserState.test.tsx index 674470e3..666451d3 100644 --- a/src/state/useUserState.test.tsx +++ b/src/state/useUserState.test.tsx @@ -74,6 +74,7 @@ describe("useUserState", () => { lessonCreationError: null, }, config: { + inWizard: true, sets: { [setToAdd.id]: { setId: setToAdd.id, @@ -114,6 +115,7 @@ describe("useUserState", () => { lessonCreationError: null, }, config: { + inWizard: true, sets: {}, upstreamCollection: null, groupId: null, @@ -207,6 +209,7 @@ describe("useUserState", () => { lessonCreationError: null, }, config: { + inWizard: true, sets: {}, upstreamCollection: null, groupId: null, diff --git a/src/state/useUserState.ts b/src/state/useUserState.ts index 66737207..779e1d1d 100644 --- a/src/state/useUserState.ts +++ b/src/state/useUserState.ts @@ -37,6 +37,7 @@ export interface UserStateProps { } export interface LegacyUserState { + inWizard: boolean; /** Terms the user is learning and ther progress */ leitnerBoxes: LeitnerBoxState; /** Lessons that have been created for the user */ @@ -59,6 +60,7 @@ export interface LegacyUserState { * Like legacy state but no lessons */ export interface UserConfig { + inWizard: boolean; /** Sets the user is learning */ sets: UserSetsState; /** The collection from which new sets should be pulled when the user is ready for new terms */ @@ -147,6 +149,7 @@ function reduceUserState(state: UserState, action: UserStateAction): UserState { return { config: { + inWizard: state.config.inWizard, sets: reduceUserSetsState(state, action), upstreamCollection: reduceUpstreamCollection(state, action), groupId: reduceGroupId(state, action), @@ -163,6 +166,7 @@ function reduceUserState(state: UserState, action: UserStateAction): UserState { function blankUserState(initializationProps: UserStateProps): UserState { return { config: { + inWizard: true, sets: {}, upstreamCollection: null, groupId: null, @@ -182,6 +186,7 @@ function blankUserState(initializationProps: UserStateProps): UserState { export function convertLegacyState(state: LegacyUserState): UserState { return { config: { + inWizard: state.inWizard, sets: state.sets, groupId: state.groupId ?? null, phoneticsPreference: state.phoneticsPreference ?? null, From 3bb1237322c5bc283683340b8ba09190430be213 Mon Sep 17 00:00:00 2001 From: Caleb L'Italien Date: Fri, 12 May 2023 15:07:54 -0400 Subject: [PATCH 09/14] refactored getting started modal --- .../GettingStartedModal/ChooseSetStep.tsx | 8 ++-- .../GettingStartedModal/FakeStep.tsx | 26 ---------- .../GroupRegistrationStep.tsx | 5 +- .../GettingStartedModal/PhoneticsStep.tsx | 7 +++ .../GettingStartedModal/Preamble.tsx | 17 +++++-- .../GettingStartedModal/StepIndicator.tsx | 4 +- src/components/GettingStartedModal/index.tsx | 48 ++++++++++++------- src/providers/UserStateProvider.tsx | 2 +- src/state/actions.ts | 9 +++- src/state/useUserState.test.tsx | 3 -- src/state/useUserState.ts | 12 +++-- 11 files changed, 78 insertions(+), 63 deletions(-) delete mode 100644 src/components/GettingStartedModal/FakeStep.tsx diff --git a/src/components/GettingStartedModal/ChooseSetStep.tsx b/src/components/GettingStartedModal/ChooseSetStep.tsx index 1ca69318..e356c83b 100644 --- a/src/components/GettingStartedModal/ChooseSetStep.tsx +++ b/src/components/GettingStartedModal/ChooseSetStep.tsx @@ -14,12 +14,12 @@ import { collections, VocabSet } from "../../data/vocabSets"; import { useUserStateContext } from "../../providers/UserStateProvider"; import { TermsByProficiencyLevelChart } from "../TermsByProficiencyLevelChart"; -export const ChooseSetStep: Step = { - // creates an instance of the step interface - +export const ChooseSetStep: Step = { title: "Choose your first set", commitState: ({ collectionId }, {setUpstreamCollection}) => { - setUpstreamCollection(collectionId); + if (collectionId !== undefined){ + setUpstreamCollection(collectionId); + } }, Component: PhoneticsStepComponent, }; diff --git a/src/components/GettingStartedModal/FakeStep.tsx b/src/components/GettingStartedModal/FakeStep.tsx deleted file mode 100644 index 975256f1..00000000 --- a/src/components/GettingStartedModal/FakeStep.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { ReactElement } from "react"; -import { NavigationButtons, Step, StepProps } from "."; -import { Button } from "../Button"; - -export const FakeStep: Step = { - title: "Fake final step", - commitState: () => {}, - Component: FakeStepComponent, -}; - -function FakeStepComponent({ - wizardState, - goToNextStep, - goToPreviousStep, -}: StepProps): ReactElement { - return ( -
-

Debug final step!

-

{JSON.stringify(wizardState)}

- -
- ); -} diff --git a/src/components/GettingStartedModal/GroupRegistrationStep.tsx b/src/components/GettingStartedModal/GroupRegistrationStep.tsx index c66c9adc..45eb35da 100644 --- a/src/components/GettingStartedModal/GroupRegistrationStep.tsx +++ b/src/components/GettingStartedModal/GroupRegistrationStep.tsx @@ -6,8 +6,10 @@ import { Button } from "../Button"; export const GroupRegistrationStep: Step = { title: "Personal Information", Component: GroupRegistrationForm, - commitState: ({ groupId }, { registerGroup }) => { + commitState: ({ groupId, email, whereFound }, { registerGroup, setUserEmail, setWhereFound }) => { registerGroup(groupId); + setWhereFound(whereFound); + setUserEmail(email); }, }; @@ -19,6 +21,7 @@ export function GroupRegistrationForm({ exitWizard }: StepProps) { const [enabled, setEnabled] = useState(groupId!=undefined && email !== ''); + function onRadioChanged(e: ChangeEvent) { const groupId = e.target.value; setWizardState((s) => ({ ...s, groupId })); diff --git a/src/components/GettingStartedModal/PhoneticsStep.tsx b/src/components/GettingStartedModal/PhoneticsStep.tsx index e40ddcc6..95a70209 100644 --- a/src/components/GettingStartedModal/PhoneticsStep.tsx +++ b/src/components/GettingStartedModal/PhoneticsStep.tsx @@ -6,9 +6,13 @@ import { PhoneticsPreference, PREFERENCE_LITERATES, } from "../../state/reducers/phoneticsPreference"; + import { GROUPS, isGroupId } from "../../state/reducers/groupId"; export const PhoneticsStep: Step = { + /* + * Sets the phonetic preference to the wizard state. + */ title: "Phonetics", commitState: ({ phoneticsPreference }, { setPhoneticsPreference }) => { if (phoneticsPreference) { setPhoneticsPreference(phoneticsPreference); } @@ -17,6 +21,9 @@ export const PhoneticsStep: Step = { }; function PhoneticsStepComponent({ + /* + * Defines the phonetics step. Allows user to set phonetics preference, advance to the next step, or go to the previous step. Offers a default preference. + */ wizardState: { phoneticsPreference, groupId }, setWizardState, goToNextStep, diff --git a/src/components/GettingStartedModal/Preamble.tsx b/src/components/GettingStartedModal/Preamble.tsx index 7ba5d77e..b2b280ba 100644 --- a/src/components/GettingStartedModal/Preamble.tsx +++ b/src/components/GettingStartedModal/Preamble.tsx @@ -3,12 +3,18 @@ import { NavigationButtons, Step, StepProps } from "."; import { Button } from "../Button"; export const Preamble: Step = { + /* + * Defines the preamble step. Welcomes users to the site. + */ title: "Welcome to Cheroke Language Exercises!", commitState: () => {}, - Component: FakeStepComponent, + Component: FirstStepComponent, }; -function FakeStepComponent({ +function FirstStepComponent({ + /* + * The first component of the wizard. Advances to the next step or redirects to FAQ page. + */ wizardState, goToNextStep, goToPreviousStep, @@ -21,11 +27,12 @@ function FakeStepComponent({

Code of Conduct:

- - Absolutely no being a poopyhead. + - Filler text 1.

- - Zero poopyhead behavior allowed. + - Filler text 2.

- - No poopyheads! + - Filler text 3. +



FAQ diff --git a/src/components/GettingStartedModal/StepIndicator.tsx b/src/components/GettingStartedModal/StepIndicator.tsx index 33429a80..d26f9976 100644 --- a/src/components/GettingStartedModal/StepIndicator.tsx +++ b/src/components/GettingStartedModal/StepIndicator.tsx @@ -1,6 +1,8 @@ import styled from "styled-components"; import { theme } from "../../theme"; - +/* + * Defines css for steps. + */ export const StepIndicator = styled.h4` text-align: center `; diff --git a/src/components/GettingStartedModal/index.tsx b/src/components/GettingStartedModal/index.tsx index eb649bf4..5361e792 100644 --- a/src/components/GettingStartedModal/index.tsx +++ b/src/components/GettingStartedModal/index.tsx @@ -7,7 +7,6 @@ import { } from "react"; import { Button } from "../Button"; import { Modal } from "../Modal"; -import { FakeStep } from "./FakeStep"; import { PhoneticsStep } from "./PhoneticsStep"; import { GroupRegistrationStep } from "./GroupRegistrationStep"; import { PhoneticsPreference } from "../../state/reducers/phoneticsPreference"; @@ -20,11 +19,10 @@ import { UserInteractors } from "../../state/useUserState"; export interface WizardState { // gives us information about the state of the entire wizard groupId: string; - email?: string; - whereFound?: string; - phoneticsPreference: PhoneticsPreference | null; - collectionId: string; - inWizard: boolean; + email: string; + whereFound: string; + phoneticsPreference?: PhoneticsPreference; + collectionId?: string; } export interface StepProps { @@ -63,24 +61,40 @@ export function GettingStartedModal() { // keep track of what data the user has filled out const [wizardState, setWizardState] = useState>({ }); - //const [fullState, setFullState] = useState(null); - const [fullState, setFullState] = useState({groupId: '', phoneticsPreference: null, collectionId: '', inWizard: true, email: '', whereFound: ''}); + const [fullState, setFullState] = useState(null); + //const [fullState, setFullState] = useState({groupId: '', phoneticsPreference: null, collectionId: '', email: '', whereFound: ''}); function exitWizard(){ - setWizardState((s) => ({ ...s, inWizard: false})); + const { groupId, email, whereFound } = wizardState; + // check if all required fields have been filled out + if (groupId && email && whereFound) { + // if all fields are filled out, update user state and exit the modal + const fullState: WizardState = { + groupId, + email, + whereFound, + }; + steps.forEach((step) => step.commitState(fullState, userStateContext)); + setFullState(fullState); + // TODO: perform any necessary cleanup or finalization + } else { + // if any required fields are missing, show an error message or prevent the user from exiting + alert("Please fill out all required fields."); + // alternatively, you can disable the exit button until all required fields are filled out + } } /** * Take all the data the user input and run actions for each step using it */ - function finish() { + function exitWizardAtEnd() { // any part of wizard state could be undefined, so unpack it all - const { groupId, email, phoneticsPreference, collectionId, inWizard} = wizardState; + const { groupId, email, phoneticsPreference, collectionId, whereFound} = wizardState; // add checks here to make sure fields are defined - if (groupId !== undefined && email !== undefined && phoneticsPreference !== undefined && collectionId != null && inWizard !== undefined) { + if (groupId !== undefined && email !== undefined && phoneticsPreference !== undefined && collectionId != undefined && whereFound !== undefined) { // reassemble state here (without Partial<>) - const fullState: WizardState = { groupId, email, phoneticsPreference, collectionId, inWizard}; + const fullState: WizardState = { groupId, email, phoneticsPreference, collectionId, whereFound}; // dispatch the actions for each step steps.forEach((step) => step.commitState(fullState, userStateContext)); } @@ -103,12 +117,11 @@ export function GettingStartedModal() { if (nextStep < steps.length) { setStepNumber(nextStep); } else { - finish(); + exitWizardAtEnd(); } }} exitWizard={() => { - finish(); - exitWizard(); + exitWizard(); }} setWizardState={setWizardState} wizardState={wizardState} @@ -123,6 +136,9 @@ export function GettingStartedModal() { } export function NavigationButtons({ + /* + * Defines the buttons available to the user for steps of the Getting Started Modal. + */ goToPreviousStep, goToNextStep, exitWizard, diff --git a/src/providers/UserStateProvider.tsx b/src/providers/UserStateProvider.tsx index ad602c51..b65280e1 100644 --- a/src/providers/UserStateProvider.tsx +++ b/src/providers/UserStateProvider.tsx @@ -157,7 +157,7 @@ function WrappedUserStateProvider({ return ( {children} - {(state.config.userEmail === null || state.config.groupId === null && state.config.inWizard == true) && ( + {(state.config.userEmail === null || state.config.groupId === null) && ( )} diff --git a/src/state/actions.ts b/src/state/actions.ts index 4592cf12..23cb69c6 100644 --- a/src/state/actions.ts +++ b/src/state/actions.ts @@ -62,6 +62,12 @@ export type HandleSetChangesAction = { type: "HANDLE_SET_CHANGES"; }; +export type SetWhereFound = { + type: "WHERE_FOUND"; + whereFound: string; + +} + // FIXME: I think 'preferences' could get moved into a separate part of the codebase so this doesn't keep getting longer export type SetPhoneticsPreferenceAction = { type: "SET_PHONETICS_PREFERENCE"; @@ -81,4 +87,5 @@ export type UserStateAction = | LessonsAction | HandleSetChangesAction | SetPhoneticsPreferenceAction - | SetUserEmailAction; + | SetUserEmailAction + | SetWhereFound; diff --git a/src/state/useUserState.test.tsx b/src/state/useUserState.test.tsx index 666451d3..674470e3 100644 --- a/src/state/useUserState.test.tsx +++ b/src/state/useUserState.test.tsx @@ -74,7 +74,6 @@ describe("useUserState", () => { lessonCreationError: null, }, config: { - inWizard: true, sets: { [setToAdd.id]: { setId: setToAdd.id, @@ -115,7 +114,6 @@ describe("useUserState", () => { lessonCreationError: null, }, config: { - inWizard: true, sets: {}, upstreamCollection: null, groupId: null, @@ -209,7 +207,6 @@ describe("useUserState", () => { lessonCreationError: null, }, config: { - inWizard: true, sets: {}, upstreamCollection: null, groupId: null, diff --git a/src/state/useUserState.ts b/src/state/useUserState.ts index 779e1d1d..3dff64a0 100644 --- a/src/state/useUserState.ts +++ b/src/state/useUserState.ts @@ -37,7 +37,6 @@ export interface UserStateProps { } export interface LegacyUserState { - inWizard: boolean; /** Terms the user is learning and ther progress */ leitnerBoxes: LeitnerBoxState; /** Lessons that have been created for the user */ @@ -60,7 +59,6 @@ export interface LegacyUserState { * Like legacy state but no lessons */ export interface UserConfig { - inWizard: boolean; /** Sets the user is learning */ sets: UserSetsState; /** The collection from which new sets should be pulled when the user is ready for new terms */ @@ -93,6 +91,7 @@ interface MiscInteractors { registerGroup: (groupId: string) => void; setPhoneticsPreference: (newPreference: PhoneticsPreference) => void; setUserEmail: (newUserEmail: string) => void; + setWhereFound: (whereFound: string) => void; loadState: (state: LegacyUserState) => void; } @@ -149,7 +148,6 @@ function reduceUserState(state: UserState, action: UserStateAction): UserState { return { config: { - inWizard: state.config.inWizard, sets: reduceUserSetsState(state, action), upstreamCollection: reduceUpstreamCollection(state, action), groupId: reduceGroupId(state, action), @@ -166,7 +164,6 @@ function reduceUserState(state: UserState, action: UserStateAction): UserState { function blankUserState(initializationProps: UserStateProps): UserState { return { config: { - inWizard: true, sets: {}, upstreamCollection: null, groupId: null, @@ -186,7 +183,6 @@ function blankUserState(initializationProps: UserStateProps): UserState { export function convertLegacyState(state: LegacyUserState): UserState { return { config: { - inWizard: state.inWizard, sets: state.sets, groupId: state.groupId ?? null, phoneticsPreference: state.phoneticsPreference ?? null, @@ -276,6 +272,12 @@ export function useUserState(props: { }); } }, + setWhereFound(whereFound: string){ + dispatch({ + type: "WHERE_FOUND", + whereFound, + }) + }, loadState(state: LegacyUserState) { dispatch({ type: "LOAD_STATE", From b7d7bf73c92a8b321a8fd52468b16136b7b18055 Mon Sep 17 00:00:00 2001 From: Caleb L'Italien Date: Sat, 13 May 2023 20:29:54 -0400 Subject: [PATCH 10/14] pushing branch --- src/components/GettingStartedModal/ChooseSetStep.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/GettingStartedModal/ChooseSetStep.tsx b/src/components/GettingStartedModal/ChooseSetStep.tsx index e356c83b..22fb07dd 100644 --- a/src/components/GettingStartedModal/ChooseSetStep.tsx +++ b/src/components/GettingStartedModal/ChooseSetStep.tsx @@ -25,7 +25,6 @@ export const ChooseSetStep: Step = { }; function PhoneticsStepComponent({ - wizardState: { collectionId, groupId }, setWizardState, goToNextStep, From 1e2f3b7ed28e653febc42fd56ec24722b2578f5f Mon Sep 17 00:00:00 2001 From: Caleb L'Italien Date: Tue, 20 Jun 2023 20:15:52 -0400 Subject: [PATCH 11/14] changes to starting modal. not all issues resolved, some to work out --- .../GettingStartedModal/ChooseSetStep.tsx | 8 ++++---- .../GettingStartedModal/GroupRegistrationStep.tsx | 10 +++++----- .../GettingStartedModal/PhoneticsStep.tsx | 4 ++-- src/components/GettingStartedModal/Preamble.tsx | 13 +++---------- .../GettingStartedModal/StepIndicator.tsx | 8 -------- src/components/GettingStartedModal/index.tsx | 15 +++++++++++---- src/providers/UserStateProvider.tsx | 6 +++--- src/state/useUserState.test.tsx | 2 ++ src/state/useUserState.ts | 11 +++++++++++ 9 files changed, 41 insertions(+), 36 deletions(-) delete mode 100644 src/components/GettingStartedModal/StepIndicator.tsx diff --git a/src/components/GettingStartedModal/ChooseSetStep.tsx b/src/components/GettingStartedModal/ChooseSetStep.tsx index 22fb07dd..5891253b 100644 --- a/src/components/GettingStartedModal/ChooseSetStep.tsx +++ b/src/components/GettingStartedModal/ChooseSetStep.tsx @@ -30,7 +30,7 @@ function PhoneticsStepComponent({ goToNextStep, goToPreviousStep, }: StepProps): ReactElement { - const [enabled, setEnbabled] = useState(collectionId != undefined); + var canGoToNextStep = collectionId !== undefined; function setWizardStateCollectionId( newCollectionId: string ) { @@ -42,7 +42,7 @@ function PhoneticsStepComponent({ function onRadioChanged(e: ChangeEvent) { const collectionId = e.target.value; setWizardStateCollectionId(collectionId); - setEnbabled(true); + canGoToNextStep = true; } function onSubmit(e: FormEvent) { e.preventDefault(); @@ -68,7 +68,7 @@ function PhoneticsStepComponent({ : undefined; if (defaultCollectionId) { setWizardStateCollectionId(defaultCollectionId) - setEnbabled(true); + canGoToNextStep = true; } } }, [collectionId]); @@ -103,7 +103,7 @@ function PhoneticsStepComponent({
diff --git a/src/components/GettingStartedModal/GroupRegistrationStep.tsx b/src/components/GettingStartedModal/GroupRegistrationStep.tsx index 45eb35da..b8311241 100644 --- a/src/components/GettingStartedModal/GroupRegistrationStep.tsx +++ b/src/components/GettingStartedModal/GroupRegistrationStep.tsx @@ -22,7 +22,7 @@ export function GroupRegistrationForm({ }: StepProps) { const [enabled, setEnabled] = useState(groupId!=undefined && email !== ''); - function onRadioChanged(e: ChangeEvent) { + function onGroupIdChanged(e: ChangeEvent) { const groupId = e.target.value; setWizardState((s) => ({ ...s, groupId })); setEnabled(email !== ''); @@ -54,7 +54,7 @@ export function GroupRegistrationForm({

- Email Address +
- Where did you find us? +
- Group + Group {Object.entries(GROUPS).map(([id, group]) => (
diff --git a/src/components/GettingStartedModal/PhoneticsStep.tsx b/src/components/GettingStartedModal/PhoneticsStep.tsx index 95a70209..ce367da5 100644 --- a/src/components/GettingStartedModal/PhoneticsStep.tsx +++ b/src/components/GettingStartedModal/PhoneticsStep.tsx @@ -38,7 +38,7 @@ function PhoneticsStepComponent({ phoneticsPreference: newPhoneticsPreference, })); } - function onRadioChanged(e: ChangeEvent) { + function onPreferenceChanged(e: ChangeEvent) { const phoneticsPreference = e.target.value; if (isPhoneticsPreference(phoneticsPreference)) setWizardStatePhoneticsPreference(phoneticsPreference); @@ -77,7 +77,7 @@ function PhoneticsStepComponent({ value={value} id={value} checked={phoneticsPreference === value} - onChange={onRadioChanged} + onChange={onPreferenceChanged} />
diff --git a/src/components/GettingStartedModal/Preamble.tsx b/src/components/GettingStartedModal/Preamble.tsx index b2b280ba..f6341764 100644 --- a/src/components/GettingStartedModal/Preamble.tsx +++ b/src/components/GettingStartedModal/Preamble.tsx @@ -23,17 +23,10 @@ function FirstStepComponent({ return (
This website is dedicated to helping users develop knowledge of the Cherokee language. -

-

+
    Code of Conduct: -

    - - Filler text 1. -

    - - Filler text 2. -

    - - Filler text 3. -

    -

    +
  • Filler text
  • +
FAQ diff --git a/src/components/GettingStartedModal/StepIndicator.tsx b/src/components/GettingStartedModal/StepIndicator.tsx deleted file mode 100644 index d26f9976..00000000 --- a/src/components/GettingStartedModal/StepIndicator.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import styled from "styled-components"; -import { theme } from "../../theme"; -/* - * Defines css for steps. - */ -export const StepIndicator = styled.h4` - text-align: center -`; diff --git a/src/components/GettingStartedModal/index.tsx b/src/components/GettingStartedModal/index.tsx index 5361e792..d85ce7b2 100644 --- a/src/components/GettingStartedModal/index.tsx +++ b/src/components/GettingStartedModal/index.tsx @@ -11,10 +11,14 @@ import { PhoneticsStep } from "./PhoneticsStep"; import { GroupRegistrationStep } from "./GroupRegistrationStep"; import { PhoneticsPreference } from "../../state/reducers/phoneticsPreference"; import { ChooseSetStep } from "./ChooseSetStep"; -import { StepIndicator } from "./StepIndicator"; import { Preamble } from "./Preamble"; import { useUserStateContext } from "../../providers/UserStateProvider"; import { UserInteractors } from "../../state/useUserState"; +import styled from "styled-components"; + +export const StepIndicator = styled.h4` + text-align: center +`; export interface WizardState { // gives us information about the state of the entire wizard @@ -62,9 +66,8 @@ export function GettingStartedModal() { const [wizardState, setWizardState] = useState>({ }); const [fullState, setFullState] = useState(null); - //const [fullState, setFullState] = useState({groupId: '', phoneticsPreference: null, collectionId: '', email: '', whereFound: ''}); - function exitWizard(){ + function exitWizardNoAdvanced(){ const { groupId, email, whereFound } = wizardState; // check if all required fields have been filled out @@ -98,6 +101,10 @@ export function GettingStartedModal() { // dispatch the actions for each step steps.forEach((step) => step.commitState(fullState, userStateContext)); } + else { + // if any required fields are missing, show an error message or prevent the user from exiting + alert("Please fill out all required fields."); + } } // render current step of workflow @@ -121,7 +128,7 @@ export function GettingStartedModal() { } }} exitWizard={() => { - exitWizard(); + exitWizardNoAdvanced(); }} setWizardState={setWizardState} wizardState={wizardState} diff --git a/src/providers/UserStateProvider.tsx b/src/providers/UserStateProvider.tsx index b65280e1..e52a6834 100644 --- a/src/providers/UserStateProvider.tsx +++ b/src/providers/UserStateProvider.tsx @@ -140,8 +140,8 @@ function WrappedUserStateProvider({ const { state, interactors, dispatch } = useUserState({ storedUserState, initializationProps: { - leitnerBoxes: { - numBoxes: 6, + leitnerBoxes: { + numBoxes: 6, }, }, }); @@ -157,7 +157,7 @@ function WrappedUserStateProvider({ return ( {children} - {(state.config.userEmail === null || state.config.groupId === null) && ( + {(state.config.userEmail === null || state.config.groupId === null || state.config.whereFound === null) && ( )} diff --git a/src/state/useUserState.test.tsx b/src/state/useUserState.test.tsx index 674470e3..a55d15af 100644 --- a/src/state/useUserState.test.tsx +++ b/src/state/useUserState.test.tsx @@ -119,6 +119,7 @@ describe("useUserState", () => { groupId: null, phoneticsPreference: null, userEmail: null, + whereFound: null, }, }, initializationProps: { @@ -212,6 +213,7 @@ describe("useUserState", () => { groupId: null, phoneticsPreference: null, userEmail: null, + whereFound: null, }, }); }); diff --git a/src/state/useUserState.ts b/src/state/useUserState.ts index 3dff64a0..ba32afff 100644 --- a/src/state/useUserState.ts +++ b/src/state/useUserState.ts @@ -63,6 +63,8 @@ export interface UserConfig { sets: UserSetsState; /** The collection from which new sets should be pulled when the user is ready for new terms */ upstreamCollection: string | null; + /** Sets where the user fond the site*/ + whereFound: string | null; /** Group registration */ groupId: GroupId | null; /** Preference for how phonetics are shown */ @@ -126,6 +128,12 @@ function reducePhoneticsPreference( else return phoneticsPreference; } +function reduceWhereFound({config: {whereFound}}: UserState, action: UserStateAction + ): string | null{ + if (action.type === "WHERE_FOUND") return action.whereFound; + else return whereFound; +} + function reduceUserEmail( { config: { userEmail } }: UserState, action: UserStateAction @@ -153,6 +161,7 @@ function reduceUserState(state: UserState, action: UserStateAction): UserState { groupId: reduceGroupId(state, action), phoneticsPreference: reducePhoneticsPreference(state, action), userEmail: reduceUserEmail(state, action), + whereFound: reduceWhereFound(state, action), }, leitnerBoxes: reduceLeitnerBoxState(state, action), ephemeral: { @@ -169,6 +178,7 @@ function blankUserState(initializationProps: UserStateProps): UserState { groupId: null, phoneticsPreference: null, userEmail: null, + whereFound: null, }, ephemeral: { lessonCreationError: null, @@ -188,6 +198,7 @@ export function convertLegacyState(state: LegacyUserState): UserState { phoneticsPreference: state.phoneticsPreference ?? null, upstreamCollection: state.upstreamCollection ?? null, userEmail: null, + whereFound: null, }, leitnerBoxes: state.leitnerBoxes, ephemeral: { lessonCreationError: state.lessonCreationError ?? null }, From b5cf5b775711ee7b8fb38d9b1496e09ee93aa7c1 Mon Sep 17 00:00:00 2001 From: Caleb L'Italien Date: Wed, 21 Jun 2023 19:40:45 -0400 Subject: [PATCH 12/14] Changes to getting started modal --- src/components/GettingStartedModal/ChooseSetStep.tsx | 4 ++-- src/providers/UserStateProvider.tsx | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/GettingStartedModal/ChooseSetStep.tsx b/src/components/GettingStartedModal/ChooseSetStep.tsx index 5891253b..7b954c01 100644 --- a/src/components/GettingStartedModal/ChooseSetStep.tsx +++ b/src/components/GettingStartedModal/ChooseSetStep.tsx @@ -21,10 +21,10 @@ export const ChooseSetStep: Step = { setUpstreamCollection(collectionId); } }, - Component: PhoneticsStepComponent, + Component: ChooseStepComponent, }; -function PhoneticsStepComponent({ +function ChooseStepComponent({ wizardState: { collectionId, groupId }, setWizardState, goToNextStep, diff --git a/src/providers/UserStateProvider.tsx b/src/providers/UserStateProvider.tsx index e52a6834..4ad9af77 100644 --- a/src/providers/UserStateProvider.tsx +++ b/src/providers/UserStateProvider.tsx @@ -146,6 +146,7 @@ function WrappedUserStateProvider({ }, }); + var userNeedsSetup: boolean = (state.config.userEmail === null || state.config.groupId === null || state.config.whereFound === null) // sync segments of state independently useEffect(() => { setConfig(state.config); @@ -157,9 +158,7 @@ function WrappedUserStateProvider({ return ( {children} - {(state.config.userEmail === null || state.config.groupId === null || state.config.whereFound === null) && ( - - )} + {(userNeedsSetup) && ()} ); } From 02e9d30b8e743a5bbcb19d716a6a60e64172ee12 Mon Sep 17 00:00:00 2001 From: Caleb L'Italien Date: Fri, 23 Jun 2023 07:37:44 -0400 Subject: [PATCH 13/14] small change --- src/components/GettingStartedModal/ChooseSetStep.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/GettingStartedModal/ChooseSetStep.tsx b/src/components/GettingStartedModal/ChooseSetStep.tsx index 7b954c01..08999278 100644 --- a/src/components/GettingStartedModal/ChooseSetStep.tsx +++ b/src/components/GettingStartedModal/ChooseSetStep.tsx @@ -30,7 +30,7 @@ function ChooseStepComponent({ goToNextStep, goToPreviousStep, }: StepProps): ReactElement { - var canGoToNextStep = collectionId !== undefined; + let canGoToNextStep = collectionId !== undefined; function setWizardStateCollectionId( newCollectionId: string ) { From cd386e204dd4b7ca94e4f2cfca4ee392a3072b7f Mon Sep 17 00:00:00 2001 From: Caleb L'Italien Date: Fri, 7 Jul 2023 12:46:10 -0400 Subject: [PATCH 14/14] PWA --- public/index.html | 20 ++++++++++++++++---- public/manifest.json | 2 +- public/serviceWorker.js | 0 3 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 public/serviceWorker.js diff --git a/public/index.html b/public/index.html index a6f924aa..ad3dfac3 100644 --- a/public/index.html +++ b/public/index.html @@ -30,10 +30,10 @@ - + + To create a production bundle, use `npm run build` or `yarn build`. --> +
+ + diff --git a/public/manifest.json b/public/manifest.json index 2e13c649..e04b53f9 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -18,7 +18,7 @@ "sizes": "512x512" } ], - "start_url": ".", + "start_url": "index.html", "display": "standalone", "theme_color": "#000000", "background_color": "#ffffff" diff --git a/public/serviceWorker.js b/public/serviceWorker.js new file mode 100644 index 00000000..e69de29b