|
|
@ -19,20 +19,10 @@ import { BallPiece } from "../components/editor/BallPiece"
|
|
|
|
import { Rack } from "../components/Rack"
|
|
|
|
import { Rack } from "../components/Rack"
|
|
|
|
import { PlayerPiece } from "../components/editor/PlayerPiece"
|
|
|
|
import { PlayerPiece } from "../components/editor/PlayerPiece"
|
|
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
import { ComponentId, CourtType, StepContent, StepInfoNode, TacticComponent, TacticInfo } from "../model/tactic/Tactic"
|
|
|
|
ComponentId,
|
|
|
|
|
|
|
|
CourtType,
|
|
|
|
|
|
|
|
StepContent,
|
|
|
|
|
|
|
|
StepInfoNode,
|
|
|
|
|
|
|
|
TacticComponent,
|
|
|
|
|
|
|
|
TacticInfo,
|
|
|
|
|
|
|
|
} from "../model/tactic/Tactic"
|
|
|
|
|
|
|
|
import { fetchAPI, fetchAPIGet } from "../Fetcher"
|
|
|
|
import { fetchAPI, fetchAPIGet } from "../Fetcher"
|
|
|
|
|
|
|
|
|
|
|
|
import SavingState, {
|
|
|
|
import SavingState, { SaveState, SaveStates } from "../components/editor/SavingState"
|
|
|
|
SaveState,
|
|
|
|
|
|
|
|
SaveStates,
|
|
|
|
|
|
|
|
} from "../components/editor/SavingState"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import { BALL_TYPE } from "../model/tactic/CourtObjects"
|
|
|
|
import { BALL_TYPE } from "../model/tactic/CourtObjects"
|
|
|
|
import { CourtAction } from "../components/editor/CourtAction"
|
|
|
|
import { CourtAction } from "../components/editor/CourtAction"
|
|
|
@ -53,16 +43,10 @@ import {
|
|
|
|
updateComponent,
|
|
|
|
updateComponent,
|
|
|
|
} from "../editor/TacticContentDomains"
|
|
|
|
} from "../editor/TacticContentDomains"
|
|
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
import { BallState, Player, PlayerInfo, PlayerLike, PlayerTeam } from "../model/tactic/Player"
|
|
|
|
BallState,
|
|
|
|
|
|
|
|
Player,
|
|
|
|
|
|
|
|
PlayerInfo,
|
|
|
|
|
|
|
|
PlayerLike,
|
|
|
|
|
|
|
|
PlayerTeam,
|
|
|
|
|
|
|
|
} from "../model/tactic/Player"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import { RackedCourtObject, RackedPlayer } from "../editor/RackedItems"
|
|
|
|
import { RackedCourtObject, RackedPlayer } from "../editor/RackedItems"
|
|
|
|
import CourtPlayer from "../components/editor/CourtPlayer"
|
|
|
|
import { CourtPlayer, EditableCourtPlayer } from "../components/editor/CourtPlayer.tsx"
|
|
|
|
import {
|
|
|
|
import {
|
|
|
|
createAction,
|
|
|
|
createAction,
|
|
|
|
getActionKind,
|
|
|
|
getActionKind,
|
|
|
@ -74,21 +58,11 @@ import ArrowAction from "../components/actions/ArrowAction"
|
|
|
|
import { middlePos, Pos, ratioWithinBase } from "../geo/Pos"
|
|
|
|
import { middlePos, Pos, ratioWithinBase } from "../geo/Pos"
|
|
|
|
import { Action, ActionKind } from "../model/tactic/Action"
|
|
|
|
import { Action, ActionKind } from "../model/tactic/Action"
|
|
|
|
import BallAction from "../components/actions/BallAction"
|
|
|
|
import BallAction from "../components/actions/BallAction"
|
|
|
|
import {
|
|
|
|
import { computePhantomPositioning, getOrigin, removePlayer } from "../editor/PlayerDomains"
|
|
|
|
computePhantomPositioning,
|
|
|
|
|
|
|
|
getOrigin,
|
|
|
|
|
|
|
|
removePlayer,
|
|
|
|
|
|
|
|
} from "../editor/PlayerDomains"
|
|
|
|
|
|
|
|
import { CourtBall } from "../components/editor/CourtBall"
|
|
|
|
import { CourtBall } from "../components/editor/CourtBall"
|
|
|
|
import { useNavigate, useParams } from "react-router-dom"
|
|
|
|
import { useNavigate, useParams } from "react-router-dom"
|
|
|
|
import StepsTree from "../components/editor/StepsTree"
|
|
|
|
import StepsTree from "../components/editor/StepsTree"
|
|
|
|
import {
|
|
|
|
import { addStepNode, getAvailableId, getParent, getStepNode, removeStepNode } from "../editor/StepsDomain"
|
|
|
|
addStepNode,
|
|
|
|
|
|
|
|
getAvailableId,
|
|
|
|
|
|
|
|
getParent,
|
|
|
|
|
|
|
|
getStepNode,
|
|
|
|
|
|
|
|
removeStepNode,
|
|
|
|
|
|
|
|
} from "../editor/StepsDomain"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const ERROR_STYLE: CSSProperties = {
|
|
|
|
const ERROR_STYLE: CSSProperties = {
|
|
|
|
borderColor: "red",
|
|
|
|
borderColor: "red",
|
|
|
@ -282,7 +256,10 @@ function GuestModeEditor() {
|
|
|
|
|
|
|
|
|
|
|
|
function UserModeEditor() {
|
|
|
|
function UserModeEditor() {
|
|
|
|
const [tactic, setTactic] = useState<TacticDto | null>(null)
|
|
|
|
const [tactic, setTactic] = useState<TacticDto | null>(null)
|
|
|
|
const [stepsTree, setStepsTree] = useState<StepInfoNode>({ id: ROOT_STEP_ID, children: [] })
|
|
|
|
const [stepsTree, setStepsTree] = useState<StepInfoNode>({
|
|
|
|
|
|
|
|
id: ROOT_STEP_ID,
|
|
|
|
|
|
|
|
children: [],
|
|
|
|
|
|
|
|
})
|
|
|
|
const { tacticId: idStr } = useParams()
|
|
|
|
const { tacticId: idStr } = useParams()
|
|
|
|
const tacticId = parseInt(idStr!)
|
|
|
|
const tacticId = parseInt(idStr!)
|
|
|
|
const navigation = useNavigate()
|
|
|
|
const navigation = useNavigate()
|
|
|
@ -338,7 +315,6 @@ function UserModeEditor() {
|
|
|
|
[tacticId, stepId, stepsTree],
|
|
|
|
[tacticId, stepId, stepsTree],
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const [stepContent, setStepContent, saveState] =
|
|
|
|
const [stepContent, setStepContent, saveState] =
|
|
|
|
useContentState<ComputedStepContent>(
|
|
|
|
useContentState<ComputedStepContent>(
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -349,7 +325,6 @@ function UserModeEditor() {
|
|
|
|
useMemo(() => debounceAsync(saveContent, 250), [saveContent]),
|
|
|
|
useMemo(() => debounceAsync(saveContent, 250), [saveContent]),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
useEffect(() => {
|
|
|
|
async function initialize() {
|
|
|
|
async function initialize() {
|
|
|
|
const infoResponsePromise = fetchAPIGet(`tactics/${tacticId}`)
|
|
|
|
const infoResponsePromise = fetchAPIGet(`tactics/${tacticId}`)
|
|
|
@ -380,19 +355,22 @@ function UserModeEditor() {
|
|
|
|
setStepContent({ content, relativePositions: new Map() }, false)
|
|
|
|
setStepContent({ content, relativePositions: new Map() }, false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (tactic === null)
|
|
|
|
if (tactic === null) initialize()
|
|
|
|
initialize()
|
|
|
|
|
|
|
|
}, [tactic, tacticId, idStr, navigation, setStepContent])
|
|
|
|
}, [tactic, tacticId, idStr, navigation, setStepContent])
|
|
|
|
|
|
|
|
|
|
|
|
const onNameChange = useCallback(
|
|
|
|
const onNameChange = useCallback(
|
|
|
|
(name: string) =>
|
|
|
|
(name: string) =>
|
|
|
|
fetchAPI(`tactics/${tacticId}/name`, { name }, "PUT").then((r) => r.ok),
|
|
|
|
fetchAPI(`tactics/${tacticId}/name`, { name }, "PUT").then(
|
|
|
|
|
|
|
|
(r) => r.ok,
|
|
|
|
|
|
|
|
),
|
|
|
|
[tacticId],
|
|
|
|
[tacticId],
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
const selectStep = useCallback(
|
|
|
|
const selectStep = useCallback(
|
|
|
|
async (step: number) => {
|
|
|
|
async (step: number) => {
|
|
|
|
const response = await fetchAPIGet(`tactics/${tacticId}/steps/${step}`)
|
|
|
|
const response = await fetchAPIGet(
|
|
|
|
|
|
|
|
`tactics/${tacticId}/steps/${step}`,
|
|
|
|
|
|
|
|
)
|
|
|
|
if (!response.ok) return
|
|
|
|
if (!response.ok) return
|
|
|
|
setStepId(step)
|
|
|
|
setStepId(step)
|
|
|
|
setStepContent(
|
|
|
|
setStepContent(
|
|
|
@ -423,7 +401,11 @@ function UserModeEditor() {
|
|
|
|
|
|
|
|
|
|
|
|
const onRemoveStep = useCallback(
|
|
|
|
const onRemoveStep = useCallback(
|
|
|
|
async (step: StepInfoNode) => {
|
|
|
|
async (step: StepInfoNode) => {
|
|
|
|
const response = await fetchAPI(`tactics/${tacticId}/steps/${step.id}`, {}, "DELETE")
|
|
|
|
const response = await fetchAPI(
|
|
|
|
|
|
|
|
`tactics/${tacticId}/steps/${step.id}`,
|
|
|
|
|
|
|
|
{},
|
|
|
|
|
|
|
|
"DELETE",
|
|
|
|
|
|
|
|
)
|
|
|
|
setStepsTree(removeStepNode(stepsTree, step)!)
|
|
|
|
setStepsTree(removeStepNode(stepsTree, step)!)
|
|
|
|
return response.ok
|
|
|
|
return response.ok
|
|
|
|
},
|
|
|
|
},
|
|
|
@ -519,9 +501,9 @@ function EditorPage({
|
|
|
|
: newState
|
|
|
|
: newState
|
|
|
|
|
|
|
|
|
|
|
|
const courtBounds = courtRef.current?.getBoundingClientRect()
|
|
|
|
const courtBounds = courtRef.current?.getBoundingClientRect()
|
|
|
|
const relativePositions: ComputedRelativePositions = courtBounds ? computeRelativePositions(courtBounds, state) : new Map()
|
|
|
|
const relativePositions: ComputedRelativePositions = courtBounds
|
|
|
|
|
|
|
|
? computeRelativePositions(courtBounds, state)
|
|
|
|
console.log("in set: ", relativePositions)
|
|
|
|
: new Map()
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
content: state,
|
|
|
|
content: state,
|
|
|
@ -617,6 +599,7 @@ function EditorPage({
|
|
|
|
const renderAvailablePlayerActions = useCallback(
|
|
|
|
const renderAvailablePlayerActions = useCallback(
|
|
|
|
(info: PlayerInfo, player: PlayerLike) => {
|
|
|
|
(info: PlayerInfo, player: PlayerLike) => {
|
|
|
|
let canPlaceArrows: boolean
|
|
|
|
let canPlaceArrows: boolean
|
|
|
|
|
|
|
|
let isFrozen: boolean = false
|
|
|
|
|
|
|
|
|
|
|
|
if (player.type == "player") {
|
|
|
|
if (player.type == "player") {
|
|
|
|
canPlaceArrows =
|
|
|
|
canPlaceArrows =
|
|
|
@ -624,6 +607,7 @@ function EditorPage({
|
|
|
|
player.actions.findIndex(
|
|
|
|
player.actions.findIndex(
|
|
|
|
(p) => p.type != ActionKind.SHOOT,
|
|
|
|
(p) => p.type != ActionKind.SHOOT,
|
|
|
|
) == -1
|
|
|
|
) == -1
|
|
|
|
|
|
|
|
isFrozen = player.frozen
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
const origin = getOrigin(player, content.components)
|
|
|
|
const origin = getOrigin(player, content.components)
|
|
|
|
const path = origin.path!
|
|
|
|
const path = origin.path!
|
|
|
@ -654,7 +638,7 @@ function EditorPage({
|
|
|
|
setContent={setContent}
|
|
|
|
setContent={setContent}
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
),
|
|
|
|
),
|
|
|
|
(info.ballState === BallState.HOLDS_ORIGIN ||
|
|
|
|
!isFrozen && (info.ballState === BallState.HOLDS_ORIGIN ||
|
|
|
|
info.ballState === BallState.PASSED_ORIGIN) && (
|
|
|
|
info.ballState === BallState.PASSED_ORIGIN) && (
|
|
|
|
<BallAction
|
|
|
|
<BallAction
|
|
|
|
key={2}
|
|
|
|
key={2}
|
|
|
@ -688,10 +672,18 @@ function EditorPage({
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
info = component
|
|
|
|
info = component
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (component.frozen) {
|
|
|
|
|
|
|
|
return <CourtPlayer
|
|
|
|
|
|
|
|
playerInfo={info}
|
|
|
|
|
|
|
|
className={"player"}
|
|
|
|
|
|
|
|
availableActions={() => renderAvailablePlayerActions(info, component)}
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<CourtPlayer
|
|
|
|
<EditableCourtPlayer
|
|
|
|
key={component.id}
|
|
|
|
key={component.id}
|
|
|
|
className={isPhantom ? "phantom" : "player"}
|
|
|
|
className={isPhantom ? "phantom" : "player"}
|
|
|
|
playerInfo={info}
|
|
|
|
playerInfo={info}
|
|
|
@ -1206,7 +1198,6 @@ function useContentState<S>(
|
|
|
|
return [content, setContentSynced, savingState]
|
|
|
|
return [content, setContentSynced, savingState]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function computeRelativePositions(courtBounds: DOMRect, content: StepContent) {
|
|
|
|
function computeRelativePositions(courtBounds: DOMRect, content: StepContent) {
|
|
|
|
const relativePositionsCache: ComputedRelativePositions = new Map()
|
|
|
|
const relativePositionsCache: ComputedRelativePositions = new Map()
|
|
|
|
|
|
|
|
|
|
|
@ -1220,8 +1211,5 @@ function computeRelativePositions(courtBounds: DOMRect, content: StepContent) {
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log("computed bounds: ", relativePositionsCache)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return relativePositionsCache
|
|
|
|
return relativePositionsCache
|
|
|
|
}
|
|
|
|
}
|
|
|
|