diff --git a/src/api-calls/tracks/_getRef.ts b/src/api-calls/tracks/_getRef.ts index cb7f1e43..2bd52db3 100644 --- a/src/api-calls/tracks/_getRef.ts +++ b/src/api-calls/tracks/_getRef.ts @@ -61,7 +61,7 @@ export function convertToDatabase(track: Track): TrackDocument { return { ...rest, createdTimestamp: Timestamp.fromDate(createdDate), - }; + } as TrackDocument; } export function convertFromDatabase(track: TrackDocument): Track { diff --git a/src/api-calls/tracks/_track.type.ts b/src/api-calls/tracks/_track.type.ts index 59ef1750..7aab4be4 100644 --- a/src/api-calls/tracks/_track.type.ts +++ b/src/api-calls/tracks/_track.type.ts @@ -1,5 +1,5 @@ import { Timestamp } from "firebase/firestore"; -import { Clock, ProgressTrack } from "types/Track.type"; +import { Clock, ProgressTrack, SceneChallenge } from "types/Track.type"; export interface ProgressTrackDocument extends Omit { @@ -9,4 +9,9 @@ export interface ClockDocument extends Omit { createdTimestamp: Timestamp; } +export interface SceneChallengeDocument + extends Omit { + createdTimestamp: Timestamp; +} + export type TrackDocument = ProgressTrackDocument | ClockDocument; diff --git a/src/api-calls/tracks/listenToProgressTracks.ts b/src/api-calls/tracks/listenToProgressTracks.ts index fe06d0c3..b40c2d14 100644 --- a/src/api-calls/tracks/listenToProgressTracks.ts +++ b/src/api-calls/tracks/listenToProgressTracks.ts @@ -4,17 +4,14 @@ import { getCampaignTracksCollection, getCharacterTracksCollection, } from "./_getRef"; -import { Track, TrackTypes } from "types/Track.type"; +import { Track, TrackSectionTracks } from "types/Track.type"; export function listenToProgressTracks( campaignId: string | undefined, characterId: string | undefined, status: string, addOrUpdateTracks: (tracks: { [trackId: string]: Track }) => void, - removeTrack: ( - trackId: string, - trackType: TrackTypes.Fray | TrackTypes.Journey | TrackTypes.Vow - ) => void, + removeTrack: (trackId: string, trackType: TrackSectionTracks) => void, // eslint-disable-next-line @typescript-eslint/no-explicit-any onError: (error: any) => void ): Unsubscribe | undefined { @@ -36,13 +33,7 @@ export function listenToProgressTracks( snapshot.docChanges().forEach((change) => { if (change.type === "removed") { - removeTrack( - change.doc.id, - change.doc.data().type as - | TrackTypes.Fray - | TrackTypes.Journey - | TrackTypes.Vow - ); + removeTrack(change.doc.id, change.doc.data().type); } else { addOrUpdateChanges[change.doc.id] = convertFromDatabase( change.doc.data() diff --git a/src/components/features/ProgressTrack/EditOrCreateTrackDialog.tsx b/src/components/features/ProgressTrack/EditOrCreateTrackDialog.tsx index c6afe09c..efd81f24 100644 --- a/src/components/features/ProgressTrack/EditOrCreateTrackDialog.tsx +++ b/src/components/features/ProgressTrack/EditOrCreateTrackDialog.tsx @@ -18,15 +18,19 @@ import { Difficulty, TrackStatus, TrackSectionProgressTracks, + TrackTypes, + SceneChallenge, } from "types/Track.type"; export interface EditOrCreateTrackDialogProps { open: boolean; handleClose: () => void; - initialTrack?: ProgressTrack; - trackType: TrackSectionProgressTracks; + initialTrack?: ProgressTrack | SceneChallenge; + trackType: TrackSectionProgressTracks | TrackTypes.SceneChallenge; trackTypeName: string; - handleTrack: (track: ProgressTrack) => Promise; + handleTrack: ( + track: ProgressTrack | SceneChallenge + ) => Promise; } export function EditOrCreateTrackDialog(props: EditOrCreateTrackDialogProps) { @@ -69,16 +73,29 @@ export function EditOrCreateTrackDialog(props: EditOrCreateTrackDialogProps) { return; } - const track: ProgressTrack = { - createdDate: new Date(), - status: TrackStatus.Active, - type: trackType, - ...(initialTrack ?? {}), - label: title, - description, - difficulty: difficulty, - value: initialTrack && !resetProgress ? initialTrack.value : 0, - }; + const track: ProgressTrack | SceneChallenge = + trackType === TrackTypes.SceneChallenge + ? { + createdDate: new Date(), + status: TrackStatus.Active, + type: trackType, + ...((initialTrack as SceneChallenge | undefined) ?? {}), + label: title, + description, + difficulty: difficulty, + value: initialTrack && !resetProgress ? initialTrack.value : 0, + segmentsFilled: 0, + } + : { + createdDate: new Date(), + status: TrackStatus.Active, + type: trackType, + ...((initialTrack as ProgressTrack | undefined) ?? {}), + label: title, + description, + difficulty: difficulty, + value: initialTrack && !resetProgress ? initialTrack.value : 0, + }; setLoading(true); handleTrack(track) diff --git a/src/components/features/ProgressTrack/ProgressTrack.tsx b/src/components/features/ProgressTrack/ProgressTrack.tsx index 4b1af4f7..9e92ea64 100644 --- a/src/components/features/ProgressTrack/ProgressTrack.tsx +++ b/src/components/features/ProgressTrack/ProgressTrack.tsx @@ -16,14 +16,16 @@ import { useRoller } from "stores/appState/useRoller"; import { GAME_SYSTEMS, GameSystemChooser } from "types/GameSystems.type"; import { useGameSystemValue } from "hooks/useGameSystemValue"; import { useStore } from "stores/store"; +import { DebouncedClockCircle } from "../charactersAndCampaigns/Clocks/DebouncedClockCircle"; const trackMoveIdSystemValues: GameSystemChooser<{ - [key in ProgressTracks]: string; + [key in ProgressTracks | TrackTypes.SceneChallenge]: string; }> = { [GAME_SYSTEMS.IRONSWORN]: { [TrackTypes.Vow]: "classic/moves/quest/fulfill_your_vow", [TrackTypes.Journey]: "classic/moves/adventure/reach_your_destination", [TrackTypes.Fray]: "classic/moves/combat/end_the_fight", + [TrackTypes.SceneChallenge]: "", [TrackTypes.BondProgress]: "", }, [GAME_SYSTEMS.STARFORGED]: { @@ -31,11 +33,13 @@ const trackMoveIdSystemValues: GameSystemChooser<{ [TrackTypes.Journey]: "starforged/moves/exploration/finish_an_expedition", [TrackTypes.Fray]: "starforged/moves/combat/take_decisive_action", [TrackTypes.BondProgress]: "starforged/moves/connection/forge_a_bond", + [TrackTypes.SceneChallenge]: + "starforged/moves/scene_challenge/finish_the_scene", }, }; -export interface ProgressTracksProps { - trackType?: ProgressTracks; +export interface BaseProgressTrackProps { + trackType?: ProgressTracks | TrackTypes.SceneChallenge; status?: TrackStatus; label?: string; difficulty?: Difficulty; @@ -49,6 +53,22 @@ export interface ProgressTracksProps { hideRollButton?: boolean; } +interface ProgressTrackProgressProps extends BaseProgressTrackProps { + trackType?: ProgressTracks; +} + +interface SceneChallengeProgressProps extends BaseProgressTrackProps { + trackType: TrackTypes.SceneChallenge; + sceneChallenge: { + filledSegments: number; + onChange?: (newValue: number) => void; + }; +} + +export type ProgressTracksProps = + | ProgressTrackProgressProps + | SceneChallengeProgressProps; + const getDifficultyLabel = (difficulty: Difficulty): string => { switch (difficulty) { case Difficulty.Dangerous: @@ -227,114 +247,142 @@ export function ProgressTrack(props: ProgressTracksProps) { )} - - {onValueChange && ( - { - if (onValueChange) { - const newValue = Math.max( - value - getDifficultyStep(difficulty), - 0 - ); - onValueChange(newValue); - if (newValue === value) { - announce(`${label} is already at zero ticks`); - } else { - announce(`Updated ${label} to ${getValueText(newValue)}`); - } - } - }} - focusRipple - sx={(theme) => ({ - backgroundColor: - theme.palette.darkGrey[ - theme.palette.mode === "light" ? "main" : "light" - ], - color: theme.palette.darkGrey.contrastText, - px: 0.5, - "&:hover": { - backgroundColor: theme.palette.darkGrey.dark, - }, - borderTopLeftRadius: `${theme.shape.borderRadius}px`, - borderBottomLeftRadius: `${theme.shape.borderRadius}px`, - })} - > - - - )} + theme.palette.background.paper} - color={(theme) => - theme.palette.mode === "light" - ? theme.palette.grey[600] - : theme.palette.grey[300] - } - borderTop={1} - borderBottom={1} - borderLeft={onValueChange ? 0 : 1} - borderRight={onValueChange ? 0 : 1} - borderColor={(theme) => theme.palette.divider} - role={"meter"} - aria-labelledby={labelId} - aria-valuemin={0} - aria-valuemax={max} - aria-valuenow={value} - aria-valuetext={getValueText(value)} + mt={label ? 1 : 0} + mr={trackType === TrackTypes.SceneChallenge ? 2 : 0} > - {checks.map((value, index) => ( - { + if (onValueChange) { + const newValue = Math.max( + value - getDifficultyStep(difficulty), + 0 + ); + onValueChange(newValue); + if (newValue === value) { + announce(`${label} is already at zero ticks`); + } else { + announce(`Updated ${label} to ${getValueText(newValue)}`); + } + } + }} + focusRipple sx={(theme) => ({ - borderWidth: 1, - borderStyle: "solid", - borderColor: "transparent", - borderLeftColor: - index !== 0 ? theme.palette.divider : undefined, + backgroundColor: + theme.palette.darkGrey[ + theme.palette.mode === "light" ? "main" : "light" + ], + color: theme.palette.darkGrey.contrastText, + px: 0.5, + "&:hover": { + backgroundColor: theme.palette.darkGrey.dark, + }, + borderTopLeftRadius: `${theme.shape.borderRadius}px`, + borderBottomLeftRadius: `${theme.shape.borderRadius}px`, })} > - - - ))} - - {onValueChange && ( - { - if (onValueChange) { - const newValue = Math.min( - value + getDifficultyStep(difficulty), - max - ); - onValueChange(newValue); - if (newValue === value) { - announce( - `${label} is already at its maximum value of ${max} ticks` + + + )} + theme.palette.background.paper} + color={(theme) => + theme.palette.mode === "light" + ? theme.palette.grey[600] + : theme.palette.grey[300] + } + borderTop={1} + borderBottom={1} + borderLeft={onValueChange ? 0 : 1} + borderRight={onValueChange ? 0 : 1} + borderColor={(theme) => theme.palette.divider} + role={"meter"} + aria-labelledby={labelId} + aria-valuemin={0} + aria-valuemax={max} + aria-valuenow={value} + aria-valuetext={getValueText(value)} + > + {checks.map((value, index) => ( + ({ + borderWidth: 1, + borderStyle: "solid", + borderColor: "transparent", + borderLeftColor: + index !== 0 ? theme.palette.divider : undefined, + })} + > + + + ))} + + {onValueChange && ( + { + if (onValueChange) { + const newValue = Math.min( + value + getDifficultyStep(difficulty), + max ); - } else { - announce(`Updated ${label} to ${getValueText(newValue)}`); + onValueChange(newValue); + if (newValue === value) { + announce( + `${label} is already at its maximum value of ${max} ticks` + ); + } else { + announce(`Updated ${label} to ${getValueText(newValue)}`); + } } - } - }} - focusRipple - sx={(theme) => ({ - backgroundColor: - theme.palette.darkGrey[ - theme.palette.mode === "light" ? "main" : "light" - ], - color: theme.palette.darkGrey.contrastText, - px: 0.5, - "&:hover": { - backgroundColor: theme.palette.darkGrey.dark, - }, + }} + focusRipple + sx={(theme) => ({ + backgroundColor: + theme.palette.darkGrey[ + theme.palette.mode === "light" ? "main" : "light" + ], + color: theme.palette.darkGrey.contrastText, + px: 0.5, + "&:hover": { + backgroundColor: theme.palette.darkGrey.dark, + }, - borderTopRightRadius: `${theme.shape.borderRadius}px`, - borderBottomRightRadius: `${theme.shape.borderRadius}px`, - })} - > - - + borderTopRightRadius: `${theme.shape.borderRadius}px`, + borderBottomRightRadius: `${theme.shape.borderRadius}px`, + })} + > + + + )} + + + {trackType === TrackTypes.SceneChallenge && ( + { + let newValue = props.sceneChallenge.filledSegments + 1; + if (newValue > 4) { + newValue = 0; + } + if (props.sceneChallenge.onChange) { + props.sceneChallenge.onChange(newValue); + } + } + : undefined + } + /> )} {onDelete && ( diff --git a/src/components/features/ProgressTrack/ProgressTrackList.tsx b/src/components/features/ProgressTrack/ProgressTrackList.tsx index 3624d055..dc76a366 100644 --- a/src/components/features/ProgressTrack/ProgressTrackList.tsx +++ b/src/components/features/ProgressTrack/ProgressTrackList.tsx @@ -1,5 +1,5 @@ import { Button, Checkbox, FormControlLabel } from "@mui/material"; -import { TrackSectionProgressTracks } from "types/Track.type"; +import { TrackSectionProgressTracks, TrackTypes } from "types/Track.type"; import { EditOrCreateTrackDialog } from "./EditOrCreateTrackDialog"; import { SectionHeading } from "components/shared/SectionHeading"; import { useState } from "react"; @@ -7,7 +7,7 @@ import { useStore } from "stores/store"; import { ProgressTracks } from "./ProgressTracks"; export interface ProgressTrackListProps { - trackType: TrackSectionProgressTracks; + trackType: TrackSectionProgressTracks | TrackTypes.SceneChallenge; typeLabel: string; headingBreakContainer?: boolean; readOnly?: boolean; diff --git a/src/components/features/ProgressTrack/ProgressTracks.tsx b/src/components/features/ProgressTrack/ProgressTracks.tsx index cc141a65..ccde3c77 100644 --- a/src/components/features/ProgressTrack/ProgressTracks.tsx +++ b/src/components/features/ProgressTrack/ProgressTracks.tsx @@ -1,7 +1,12 @@ import { Divider, Stack } from "@mui/material"; import { useState } from "react"; import { useStore } from "stores/store"; -import { TrackSectionProgressTracks, TrackStatus } from "types/Track.type"; +import { + SceneChallenge, + TrackSectionProgressTracks, + TrackStatus, + TrackTypes, +} from "types/Track.type"; import { ProgressTrack } from "./ProgressTrack"; import { EmptyState } from "components/shared/EmptyState"; import { EditOrCreateTrackDialog } from "./EditOrCreateTrackDialog"; @@ -9,7 +14,7 @@ import { EditOrCreateTrackDialog } from "./EditOrCreateTrackDialog"; export interface ProgressTracksProps { isCampaign?: boolean; isCompleted?: boolean; - trackType: TrackSectionProgressTracks; + trackType: TrackSectionProgressTracks | TrackTypes.SceneChallenge; typeLabel: string; headingBreakContainer?: boolean; readOnly?: boolean; @@ -71,6 +76,13 @@ export function ProgressTracks(props: ProgressTracksProps) { updateProgressTrack(trackId, { value }).catch(() => {}); }; + const updateSceneChallengeValue = ( + trackId: string, + segmentsFilled: number + ) => { + updateProgressTrack(trackId, { segmentsFilled }).catch(() => {}); + }; + return ( <> setCurrentlyEditingTrackId(trackId) } hideRollButton={readOnly || isCompleted} + {...(trackType === TrackTypes.SceneChallenge + ? { + sceneChallenge: { + filledSegments: (tracks[trackId] as SceneChallenge) + .segmentsFilled, + onChange: + readOnly || isCompleted + ? undefined + : (newFilledSegments: number) => + updateSceneChallengeValue( + trackId, + newFilledSegments + ), + }, + trackType: TrackTypes.SceneChallenge, + } + : { trackType })} /> )) ) : ( diff --git a/src/components/features/assets/AssetCard/AssetControlClock.tsx b/src/components/features/assets/AssetCard/AssetControlClock.tsx index 1b5971d6..406a1d20 100644 --- a/src/components/features/assets/AssetCard/AssetControlClock.tsx +++ b/src/components/features/assets/AssetCard/AssetControlClock.tsx @@ -1,8 +1,6 @@ import { Datasworn } from "@datasworn/core"; import { Box, Typography } from "@mui/material"; -import { useDebouncedState } from "hooks/useDebouncedState"; -import { useStore } from "stores/store"; -import { ClockCircle } from "components/features/charactersAndCampaigns/Clocks/ClockCircle"; +import { DebouncedClockCircle } from "components/features/charactersAndCampaigns/Clocks/DebouncedClockCircle"; export interface AssetControlClockProps { value?: number; @@ -13,28 +11,6 @@ export interface AssetControlClockProps { export function AssetControlClock(props: AssetControlClockProps) { const { value, field, onChange } = props; - const [localValue, setLocalValue] = useDebouncedState( - (value) => onChange && onChange(value), - value ?? field.value, - 500 - ); - - const announce = useStore((store) => store.appState.announce); - - const handleIncrement = () => { - setLocalValue((prev) => { - const newValue = prev + 1; - if (typeof field.max === "number" && field.max < newValue) { - announce( - `Cannot increase ${field.label} beyond ${field.max}. Resetting field to 0` - ); - return 0; - } - announce(`Increased ${field.label} by 1 for a total of ${newValue}`); - return newValue; - }); - }; - return ( - ); diff --git a/src/components/features/charactersAndCampaigns/Clocks/ClockCircle.tsx b/src/components/features/charactersAndCampaigns/Clocks/ClockCircle.tsx index dcddcc9e..54a66454 100644 --- a/src/components/features/charactersAndCampaigns/Clocks/ClockCircle.tsx +++ b/src/components/features/charactersAndCampaigns/Clocks/ClockCircle.tsx @@ -2,14 +2,22 @@ import { Box, ButtonBase, SxProps, useTheme } from "@mui/material"; import { ClockSegment } from "./ClockSegment"; import { PropsWithChildren } from "react"; +export type ClockSize = "small" | "medium"; + +const sizes: Record = { + small: 60, + medium: 100, +}; + export interface ClockCircleProps { segments: number; value: number; onClick?: () => void; + size?: ClockSize; } export function ClockCircle(props: ClockCircleProps) { - const { segments, value, onClick } = props; + const { segments, value, onClick, size = "medium" } = props; const theme = useTheme(); const Wrapper = ( @@ -38,8 +46,8 @@ export function ClockCircle(props: ClockCircleProps) { return ( diff --git a/src/components/features/charactersAndCampaigns/Clocks/DebouncedClockCircle.tsx b/src/components/features/charactersAndCampaigns/Clocks/DebouncedClockCircle.tsx new file mode 100644 index 00000000..caa41245 --- /dev/null +++ b/src/components/features/charactersAndCampaigns/Clocks/DebouncedClockCircle.tsx @@ -0,0 +1,47 @@ +import { useDebouncedState } from "hooks/useDebouncedState"; +import { ClockCircle, ClockSize } from "./ClockCircle"; +import { useStore } from "stores/store"; + +export interface DebouncedClockCircleProps { + segments: number; + value: number; + onFilledSegmentsChange?: (value: number) => void; + size?: ClockSize; + voiceLabel: string; +} + +export function DebouncedClockCircle(props: DebouncedClockCircleProps) { + const { segments, value, onFilledSegmentsChange, size, voiceLabel } = props; + + const [localFilledSegments, setLocalFilledSegments] = useDebouncedState( + onFilledSegmentsChange ? onFilledSegmentsChange : () => {}, + value + ); + + const announce = useStore((store) => store.appState.announce); + + const handleIncrement = () => { + setLocalFilledSegments((prev) => { + const newValue = prev + 1; + if (segments < newValue) { + announce( + `Cannot increase clock ${voiceLabel} beyond ${segments}. Resetting field to 0` + ); + return 0; + } + announce( + `Increased clock ${voiceLabel} by 1 for a total of ${newValue} of ${segments} segments.` + ); + return newValue; + }); + }; + + return ( + + ); +} diff --git a/src/pages/Campaign/CampaignGMScreenPage/components/TracksSection.tsx b/src/pages/Campaign/CampaignGMScreenPage/components/TracksSection.tsx index 94c34f5d..e1b2632a 100644 --- a/src/pages/Campaign/CampaignGMScreenPage/components/TracksSection.tsx +++ b/src/pages/Campaign/CampaignGMScreenPage/components/TracksSection.tsx @@ -72,6 +72,13 @@ export function TracksSection() { typeLabel={isStarforged ? "Shared Expedition" : "Shared Journey"} isCampaign /> + {isStarforged && ( + + )} {Object.keys(characterTracks).map((characterId) => (
{characters[characterId] && @@ -116,7 +123,7 @@ export function TracksSection() {
))} - {isStarforged && } +
); } diff --git a/src/pages/Campaign/CampaignSheetPage/CampaignSheetPage.tsx b/src/pages/Campaign/CampaignSheetPage/CampaignSheetPage.tsx index 68e2742d..f3432781 100644 --- a/src/pages/Campaign/CampaignSheetPage/CampaignSheetPage.tsx +++ b/src/pages/Campaign/CampaignSheetPage/CampaignSheetPage.tsx @@ -13,8 +13,6 @@ import { Head } from "providers/HeadProvider/Head"; import { useStore } from "stores/store"; import { useSyncStore } from "./hooks/useSyncStore"; import { ClockSection } from "components/features/charactersAndCampaigns/Clocks/ClockSection"; -import { useGameSystem } from "hooks/useGameSystem"; -import { GAME_SYSTEMS } from "types/GameSystems.type"; import { EmptyState } from "components/shared/EmptyState"; import { LinkComponent } from "components/shared/LinkComponent"; import { CAMPAIGN_ROUTES, constructCampaignPath } from "../routes"; @@ -29,8 +27,6 @@ enum TABS { export function CampaignSheetPage() { useSyncStore(); - const showClocks = useGameSystem().gameSystem === GAME_SYSTEMS.STARFORGED; - const uid = useStore((store) => store.auth.uid); const [searchParams] = useSearchParams(); @@ -134,7 +130,7 @@ export function CampaignSheetPage() { {selectedTab === TABS.TRACKS && ( <> - {showClocks && } + )} diff --git a/src/pages/Campaign/CampaignSheetPage/components/CampaignProgressTracks.tsx b/src/pages/Campaign/CampaignSheetPage/components/CampaignProgressTracks.tsx index 0274f574..25609fc9 100644 --- a/src/pages/Campaign/CampaignSheetPage/components/CampaignProgressTracks.tsx +++ b/src/pages/Campaign/CampaignSheetPage/components/CampaignProgressTracks.tsx @@ -32,6 +32,14 @@ export function CampaignProgressTracks(props: CampaignProgressTracksProps) { isCampaign headingBreakContainer={!addPadding} /> + {isStarforged && ( + + )} ); } diff --git a/src/pages/Character/CharacterSheetPage/Tabs/ProgressTrackSection.tsx b/src/pages/Character/CharacterSheetPage/Tabs/ProgressTrackSection.tsx index 358a2d7f..7ff9eb9f 100644 --- a/src/pages/Character/CharacterSheetPage/Tabs/ProgressTrackSection.tsx +++ b/src/pages/Character/CharacterSheetPage/Tabs/ProgressTrackSection.tsx @@ -1,10 +1,10 @@ import { Box } from "@mui/material"; import { ProgressTrackList } from "components/features/ProgressTrack"; -import { TrackSectionProgressTracks } from "types/Track.type"; +import { TrackSectionProgressTracks, TrackTypes } from "types/Track.type"; import { useStore } from "stores/store"; export interface ProgressTrackSectionProps { - type: TrackSectionProgressTracks; + type: TrackSectionProgressTracks | TrackTypes.SceneChallenge; typeLabel: string; showPersonalIfInCampaign?: boolean; } diff --git a/src/pages/Character/CharacterSheetPage/Tabs/TracksSection/TracksSection.tsx b/src/pages/Character/CharacterSheetPage/Tabs/TracksSection/TracksSection.tsx index 596ddf80..763f4c4e 100644 --- a/src/pages/Character/CharacterSheetPage/Tabs/TracksSection/TracksSection.tsx +++ b/src/pages/Character/CharacterSheetPage/Tabs/TracksSection/TracksSection.tsx @@ -22,7 +22,11 @@ export function TracksSection() { type={TrackTypes.Journey} typeLabel={isStarforged ? "Expedition" : "Journey"} /> - {isStarforged && } + + ); } diff --git a/src/stores/campaign/currentCampaign/characters/campaignCharacters.slice.ts b/src/stores/campaign/currentCampaign/characters/campaignCharacters.slice.ts index c50adbe1..46e4aa68 100644 --- a/src/stores/campaign/currentCampaign/characters/campaignCharacters.slice.ts +++ b/src/stores/campaign/currentCampaign/characters/campaignCharacters.slice.ts @@ -5,7 +5,7 @@ import { listenToCampaignCharacters } from "api-calls/campaign/listenToCampaignC import { listenToAssets } from "api-calls/assets/listenToAssets"; import { updateCharacter } from "api-calls/character/updateCharacter"; import { listenToProgressTracks } from "api-calls/tracks/listenToProgressTracks"; -import { ProgressTrack, TrackStatus, TrackTypes } from "types/Track.type"; +import { TrackStatus, TrackTypes } from "types/Track.type"; export const createCampaignCharactersSlice: CreateSliceType< CampaignCharactersSlice @@ -83,29 +83,15 @@ export const createCampaignCharactersSlice: CreateSliceType< [TrackTypes.Fray]: {}, [TrackTypes.Journey]: {}, [TrackTypes.Vow]: {}, + [TrackTypes.SceneChallenge]: {}, + [TrackTypes.Clock]: {}, }; } Object.keys(tracks).forEach((trackId) => { const track = tracks[trackId]; - switch (track.type) { - case TrackTypes.Fray: - store.campaigns.currentCampaign.characters.characterTracks[ - characterId - ][TrackTypes.Fray][trackId] = track as ProgressTrack; - break; - case TrackTypes.Journey: - store.campaigns.currentCampaign.characters.characterTracks[ - characterId - ][TrackTypes.Journey][trackId] = track as ProgressTrack; - break; - case TrackTypes.Vow: - store.campaigns.currentCampaign.characters.characterTracks[ - characterId - ][TrackTypes.Vow][trackId] = track as ProgressTrack; - break; - default: - break; - } + store.campaigns.currentCampaign.characters.characterTracks[ + characterId + ][track.type][trackId] = track; }); }); }, diff --git a/src/stores/campaign/currentCampaign/characters/campaignCharacters.slice.type.ts b/src/stores/campaign/currentCampaign/characters/campaignCharacters.slice.type.ts index 573c901c..d822aa8d 100644 --- a/src/stores/campaign/currentCampaign/characters/campaignCharacters.slice.type.ts +++ b/src/stores/campaign/currentCampaign/characters/campaignCharacters.slice.type.ts @@ -1,7 +1,12 @@ import { CharacterDocument } from "api-calls/character/_character.type"; import { Unsubscribe } from "firebase/firestore"; import { AssetDocument } from "api-calls/assets/_asset.type"; -import { ProgressTrack, TrackTypes } from "types/Track.type"; +import { + Clock, + ProgressTrack, + SceneChallenge, + TrackTypes, +} from "types/Track.type"; export interface CampaignCharactersSliceData { characterMap: Record; @@ -18,6 +23,12 @@ export interface CampaignCharactersSliceData { [TrackTypes.Vow]: { [key: string]: ProgressTrack; }; + [TrackTypes.SceneChallenge]: { + [key: string]: SceneChallenge; + }; + [TrackTypes.Clock]: { + [key: string]: Clock; + }; }; }; } diff --git a/src/stores/campaign/currentCampaign/tracks/campaignTracks.slice.default.ts b/src/stores/campaign/currentCampaign/tracks/campaignTracks.slice.default.ts index f60303e1..aaae96e8 100644 --- a/src/stores/campaign/currentCampaign/tracks/campaignTracks.slice.default.ts +++ b/src/stores/campaign/currentCampaign/tracks/campaignTracks.slice.default.ts @@ -8,12 +8,14 @@ export const defaultCampaignTracksSlice: CampaignTracksSliceData = { [TrackTypes.Fray]: {}, [TrackTypes.Journey]: {}, [TrackTypes.Vow]: {}, + [TrackTypes.SceneChallenge]: {}, [TrackTypes.Clock]: {}, }, [TrackStatus.Completed]: { [TrackTypes.Fray]: {}, [TrackTypes.Journey]: {}, [TrackTypes.Vow]: {}, + [TrackTypes.SceneChallenge]: {}, [TrackTypes.Clock]: {}, }, }, diff --git a/src/stores/campaign/currentCampaign/tracks/campaignTracks.slice.ts b/src/stores/campaign/currentCampaign/tracks/campaignTracks.slice.ts index af6c95ab..fc4775be 100644 --- a/src/stores/campaign/currentCampaign/tracks/campaignTracks.slice.ts +++ b/src/stores/campaign/currentCampaign/tracks/campaignTracks.slice.ts @@ -2,12 +2,7 @@ import { CreateSliceType } from "stores/store.type"; import { CampaignTracksSlice } from "./campaignTracks.slice.type"; import { defaultCampaignTracksSlice } from "./campaignTracks.slice.default"; import { listenToProgressTracks } from "api-calls/tracks/listenToProgressTracks"; -import { - Clock, - ProgressTrack, - TrackStatus, - TrackTypes, -} from "types/Track.type"; +import { TrackStatus } from "types/Track.type"; import { addProgressTrack } from "api-calls/tracks/addProgressTrack"; import { updateProgressTrack } from "api-calls/tracks/updateProgressTrack"; @@ -26,30 +21,9 @@ export const createCampaignTracksSlice: CreateSliceType = ( set((store) => { Object.keys(tracks).forEach((trackId) => { const track = tracks[trackId]; - switch (track.type) { - case TrackTypes.Fray: - store.campaigns.currentCampaign.tracks.trackMap[status][ - TrackTypes.Fray - ][trackId] = track as ProgressTrack; - break; - case TrackTypes.Journey: - store.campaigns.currentCampaign.tracks.trackMap[status][ - TrackTypes.Journey - ][trackId] = track as ProgressTrack; - break; - case TrackTypes.Vow: - store.campaigns.currentCampaign.tracks.trackMap[status][ - TrackTypes.Vow - ][trackId] = track as ProgressTrack; - break; - case TrackTypes.Clock: - store.campaigns.currentCampaign.tracks.trackMap[status][ - TrackTypes.Clock - ][trackId] = track as Clock; - break; - default: - break; - } + store.campaigns.currentCampaign.tracks.trackMap[status][track.type][ + trackId + ] = track; }); }); }, diff --git a/src/stores/campaign/currentCampaign/tracks/campaignTracks.slice.type.ts b/src/stores/campaign/currentCampaign/tracks/campaignTracks.slice.type.ts index 2c6ed90e..a9d0c619 100644 --- a/src/stores/campaign/currentCampaign/tracks/campaignTracks.slice.type.ts +++ b/src/stores/campaign/currentCampaign/tracks/campaignTracks.slice.type.ts @@ -5,6 +5,7 @@ import { TrackStatus, TrackTypes, Track, + SceneChallenge, } from "types/Track.type"; export interface CampaignTracksSliceData { @@ -12,10 +13,11 @@ export interface CampaignTracksSliceData { trackMap: Record< TrackStatus, { - [TrackTypes.Fray]: { [trackId: string]: ProgressTrack }; - [TrackTypes.Journey]: { [trackId: string]: ProgressTrack }; - [TrackTypes.Vow]: { [trackId: string]: ProgressTrack }; - [TrackTypes.Clock]: { [clockId: string]: Clock }; + [TrackTypes.Fray]: Record; + [TrackTypes.Journey]: Record; + [TrackTypes.Vow]: Record; + [TrackTypes.SceneChallenge]: Record; + [TrackTypes.Clock]: Record; } >; error?: string; diff --git a/src/stores/character/currentCharacter/tracks/characterTracks.slice.default.ts b/src/stores/character/currentCharacter/tracks/characterTracks.slice.default.ts index 5d1f4ea8..1b585827 100644 --- a/src/stores/character/currentCharacter/tracks/characterTracks.slice.default.ts +++ b/src/stores/character/currentCharacter/tracks/characterTracks.slice.default.ts @@ -8,12 +8,14 @@ export const defaultCharacterTracksSlice: CharacterTracksSliceData = { [TrackTypes.Fray]: {}, [TrackTypes.Journey]: {}, [TrackTypes.Vow]: {}, + [TrackTypes.SceneChallenge]: {}, [TrackTypes.Clock]: {}, }, [TrackStatus.Completed]: { [TrackTypes.Fray]: {}, [TrackTypes.Journey]: {}, [TrackTypes.Vow]: {}, + [TrackTypes.SceneChallenge]: {}, [TrackTypes.Clock]: {}, }, }, diff --git a/src/stores/character/currentCharacter/tracks/characterTracks.slice.ts b/src/stores/character/currentCharacter/tracks/characterTracks.slice.ts index 57a41740..b98c9176 100644 --- a/src/stores/character/currentCharacter/tracks/characterTracks.slice.ts +++ b/src/stores/character/currentCharacter/tracks/characterTracks.slice.ts @@ -2,12 +2,7 @@ import { CreateSliceType } from "stores/store.type"; import { CharacterTracksSlice } from "./characterTracks.slice.type"; import { defaultCharacterTracksSlice } from "./characterTracks.slice.default"; import { listenToProgressTracks } from "api-calls/tracks/listenToProgressTracks"; -import { - Clock, - ProgressTrack, - TrackStatus, - TrackTypes, -} from "types/Track.type"; +import { TrackStatus } from "types/Track.type"; import { addProgressTrack } from "api-calls/tracks/addProgressTrack"; import { updateProgressTrack } from "api-calls/tracks/updateProgressTrack"; @@ -25,30 +20,9 @@ export const createCharacterTracksSlice: CreateSliceType< set((store) => { Object.keys(tracks).forEach((trackId) => { const track = tracks[trackId]; - switch (track.type) { - case TrackTypes.Fray: - store.characters.currentCharacter.tracks.trackMap[status][ - TrackTypes.Fray - ][trackId] = track as ProgressTrack; - break; - case TrackTypes.Journey: - store.characters.currentCharacter.tracks.trackMap[status][ - TrackTypes.Journey - ][trackId] = track as ProgressTrack; - break; - case TrackTypes.Vow: - store.characters.currentCharacter.tracks.trackMap[status][ - TrackTypes.Vow - ][trackId] = track as ProgressTrack; - break; - case TrackTypes.Clock: - store.characters.currentCharacter.tracks.trackMap[status][ - TrackTypes.Clock - ][trackId] = track as Clock; - break; - default: - break; - } + store.characters.currentCharacter.tracks.trackMap[status][ + track.type + ][trackId] = track; }); }); }, diff --git a/src/stores/character/currentCharacter/tracks/characterTracks.slice.type.ts b/src/stores/character/currentCharacter/tracks/characterTracks.slice.type.ts index e4b5d5af..3f4f0783 100644 --- a/src/stores/character/currentCharacter/tracks/characterTracks.slice.type.ts +++ b/src/stores/character/currentCharacter/tracks/characterTracks.slice.type.ts @@ -5,6 +5,7 @@ import { TrackStatus, TrackTypes, Track, + SceneChallenge, } from "types/Track.type"; export interface CharacterTracksSliceData { @@ -12,10 +13,11 @@ export interface CharacterTracksSliceData { trackMap: Record< TrackStatus, { - [TrackTypes.Fray]: { [trackId: string]: ProgressTrack }; - [TrackTypes.Journey]: { [trackId: string]: ProgressTrack }; - [TrackTypes.Vow]: { [trackId: string]: ProgressTrack }; - [TrackTypes.Clock]: { [trackId: string]: Clock }; + [TrackTypes.Fray]: Record; + [TrackTypes.Journey]: Record; + [TrackTypes.Vow]: Record; + [TrackTypes.SceneChallenge]: Record; + [TrackTypes.Clock]: Record; } >; error?: string; diff --git a/src/types/Track.type.ts b/src/types/Track.type.ts index f3336171..87b246c8 100644 --- a/src/types/Track.type.ts +++ b/src/types/Track.type.ts @@ -7,6 +7,7 @@ export enum TrackTypes { Fray = "fray", BondProgress = "bondProgress", Clock = "clock", + SceneChallenge = "sceneChallenge", } export type ProgressTracks = @@ -18,7 +19,10 @@ export type TrackSectionProgressTracks = | TrackTypes.Fray | TrackTypes.Journey | TrackTypes.Vow; -export type TrackSectionTracks = TrackSectionProgressTracks | TrackTypes.Clock; +export type TrackSectionTracks = + | TrackSectionProgressTracks + | TrackTypes.Clock + | TrackTypes.SceneChallenge; export enum TrackStatus { Active = "active", @@ -47,12 +51,20 @@ export interface BaseTrackDocument extends Omit { } export interface ProgressTrack extends BaseTrack { + type: TrackSectionProgressTracks; + difficulty: Difficulty; +} + +export interface SceneChallenge extends BaseTrack { + type: TrackTypes.SceneChallenge; + segmentsFilled: number; difficulty: Difficulty; } export interface Clock extends BaseTrack { + type: TrackTypes.Clock; segments: number; oracleKey?: AskTheOracle; } -export type Track = ProgressTrack | Clock; +export type Track = ProgressTrack | Clock | SceneChallenge;