Apply suggestion
continuous-integration/drone/push Build is passing Details

pull/119/head
maxime 1 year ago
parent cab6fc43ca
commit f9e436ea12

@ -147,8 +147,9 @@ export default function App() {
path={"/tactic/:tacticId/view"}
element={suspense(
<LoggedInPage>
<VisualizerPage guestMode={false} />,
</LoggedInPage>
<VisualizerPage guestMode={false} />
,
</LoggedInPage>,
)}
/>
<Route

@ -68,9 +68,14 @@ export class Fetcher {
}
const nextToken = response.headers.get("Next-Authorization")!
const expirationDate = response.headers.get("Next-Authorization-Expiration-Date")!
const expirationDate = response.headers.get(
"Next-Authorization-Expiration-Date",
)!
if (nextToken && expirationDate) {
this.auth = { token: nextToken, expirationDate: new Date(expirationDate) }
this.auth = {
token: nextToken,
expirationDate: new Date(expirationDate),
}
}
return response

@ -1,8 +1,23 @@
import { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from "react"
import {
ReactNode,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react"
import { APITacticService } from "../service/APITacticService.ts"
import { CourtType, StepContent, StepInfoNode, TacticComponent } from "../model/tactic/Tactic.ts"
import {
CourtType,
StepContent,
StepInfoNode,
TacticComponent,
} from "../model/tactic/Tactic.ts"
import { getParent } from "../domains/StepsDomain.ts"
import { computeRelativePositions, getPhantomInfo } from "../domains/PlayerDomains.ts"
import {
computeRelativePositions,
getPhantomInfo,
} from "../domains/PlayerDomains.ts"
import { PlayerInfo, PlayerLike } from "../model/tactic/Player.ts"
import { CourtPlayer } from "./editor/CourtPlayer.tsx"
import { BALL_TYPE } from "../model/tactic/CourtObjects.ts"
@ -12,7 +27,6 @@ import { BasketCourt, Court } from "./editor/BasketCourt.tsx"
import { TacticService } from "../service/MutableTacticService.ts"
import { useAppFetcher } from "../App.tsx"
export interface VisualizerProps {
tacticId: number
stepId?: number
@ -23,7 +37,10 @@ export function Visualizer({ tacticId, stepId }: VisualizerProps) {
const [courtType, setCourtType] = useState<CourtType | null>()
const [stepsTree, setStepsTree] = useState<StepInfoNode | null>()
const fetcher = useAppFetcher()
const service = useMemo(() => new APITacticService(fetcher, tacticId), [tacticId])
const service = useMemo(
() => new APITacticService(fetcher, tacticId),
[tacticId],
)
const isNotInit = !stepsTree || !courtType
@ -35,13 +52,11 @@ export function Visualizer({ tacticId, stepId }: VisualizerProps) {
return
}
const rootStep = contextResult.stepsTree
setStepsTree(rootStep)
setCourtType(contextResult.courtType)
}
if (isNotInit)
init()
if (isNotInit) init()
}, [isNotInit, service])
if (panicMessage) {
@ -51,12 +66,14 @@ export function Visualizer({ tacticId, stepId }: VisualizerProps) {
return <p>Loading...</p>
}
return <StepVisualizer
return (
<StepVisualizer
courtType={courtType}
stepsTree={stepsTree}
stepId={stepId}
service={service}
/>
)
}
export interface StepVisualizerProps {
@ -66,7 +83,12 @@ export interface StepVisualizerProps {
service: TacticService
}
export function StepVisualizer({stepId, stepsTree, courtType, service}: StepVisualizerProps) {
export function StepVisualizer({
stepId,
stepsTree,
courtType,
service,
}: StepVisualizerProps) {
const [panicMessage, setPanicMessage] = useState<string | null>(null)
const [content, setContent] = useState<StepContent | null>(null)
const [parentContent, setParentContent] = useState<StepContent | null>()
@ -75,7 +97,6 @@ export function StepVisualizer({stepId, stepsTree, courtType, service}: StepVisu
useEffect(() => {
async function init() {
const contentStepId = stepId ?? stepsTree.id
const contentResult = await service.getContent(contentStepId)
@ -130,7 +151,6 @@ export function VisualizerFrame({
parentContent,
courtType,
}: VisualizerFrameProps) {
const courtRef = useRef<HTMLDivElement>(null)
const courtBounds = useCallback(

@ -2,7 +2,11 @@ import { ReactElement, ReactNode, RefObject, useEffect, useState } from "react"
import { Action } from "../../model/tactic/Action"
import { CourtAction } from "./CourtAction"
import { ComponentId, TacticComponent } from "../../model/tactic/Tactic"
import {
ComponentId,
CourtType,
TacticComponent,
} from "../../model/tactic/Tactic"
import PlainCourt from "../../assets/court/full_court.svg?react"
import HalfCourt from "../../assets/court/half_court.svg?react"
@ -34,7 +38,6 @@ export function BasketCourt({
courtImage,
courtRef,
}: BasketCourtProps) {
const [court, setCourt] = useState(courtRef.current)
//force update once the court reference is set
@ -49,15 +52,11 @@ export function BasketCourt({
style={{ position: "relative" }}>
{courtImage}
{court &&
parentComponents?.map((i) => renderComponent(i, true))}
{court &&
parentComponents?.flatMap((i) => renderActions(i, true))}
{court && parentComponents?.map((i) => renderComponent(i, true))}
{court && parentComponents?.flatMap((i) => renderActions(i, true))}
{court &&
components.map((i) => renderComponent(i, false))}
{court &&
components.flatMap((i) => renderActions(i, false))}
{court && components.map((i) => renderComponent(i, false))}
{court && components.flatMap((i) => renderActions(i, false))}
{previewAction && (
<CourtAction
@ -75,14 +74,11 @@ export function BasketCourt({
)
}
export function Court({ courtType }: { courtType: string }) {
export function Court({ courtType }: { courtType: CourtType }) {
const CourtSvg = courtType === "PLAIN" ? PlainCourt : HalfCourt
return (
<div className="court-image-div">
{courtType == "PLAIN" ? (
<PlainCourt className="court-image" />
) : (
<HalfCourt className="court-image" />
)}
<CourtSvg className="court-image" />
</div>
)
}

@ -67,8 +67,7 @@ function StepsTreeNode({
next: "step-piece-" + child.id,
},
]}
onSegmentsChanges={() => {
}}
onSegmentsChanges={() => {}}
forceStraight={true}
wavy={false}
readOnly={true}
@ -80,9 +79,8 @@ function StepsTreeNode({
<StepPiece
id={node.id}
isSelected={selectedStepId === node.id}
onAddButtonClicked={onAddChildren
? () => onAddChildren(node)
: undefined
onAddButtonClicked={
onAddChildren ? () => onAddChildren(node) : undefined
}
onRemoveButtonClicked={
rootNode.id === node.id || !onRemoveNode

@ -408,7 +408,6 @@ export function drainTerminalStateOnChildContent(
childContent.components,
)
if (!childComponent) {
//if the child does not contain the parent's component, add it to the children's content.
childContent = {
@ -432,7 +431,6 @@ export function drainTerminalStateOnChildContent(
},
childContent,
)
}
// ensure that the component is a player

@ -52,7 +52,9 @@ export default function ProfileSettings() {
useEffect(() => {
passwordConfirmRef.current!.setCustomValidity(
password === confirmPassword ? "" : "Les mots de passe ne correspondent pas !"
password === confirmPassword
? ""
: "Les mots de passe ne correspondent pas !",
)
}, [confirmPassword, password])
@ -110,9 +112,8 @@ export default function ProfileSettings() {
autoComplete="username"
required
placeholder="Nom d'utilisateur"
value={name}
onChange={e => setName(e.target.value)}
onChange={(e) => setName(e.target.value)}
/>
<label htmlFor="email">Adresse email</label>
@ -124,9 +125,8 @@ export default function ProfileSettings() {
placeholder={"Adresse email"}
autoComplete="email"
required
value={email}
onChange={e => setEmail(e.target.value)}
onChange={(e) => setEmail(e.target.value)}
/>
<label htmlFor="password">Mot de passe</label>
@ -137,12 +137,13 @@ export default function ProfileSettings() {
type="password"
placeholder={"Mot de passe"}
autoComplete="new-password"
value={password}
onChange={e => setPassword(e.target.value)}
onChange={(e) => setPassword(e.target.value)}
/>
<label htmlFor="confirmPassword">Confirmez le mot de passe</label>
<label htmlFor="confirmPassword">
Confirmez le mot de passe
</label>
<input
ref={passwordConfirmRef}
className="settings-input"
@ -151,14 +152,13 @@ export default function ProfileSettings() {
type="password"
autoComplete="new-password"
placeholder={"Confirmation du mot de passe"}
value={confirmPassword}
onChange={e => setConfirmPassword(e.target.value)}
onChange={(e) =>
setConfirmPassword(e.target.value)
}
/>
<button
className="settings-button"
type="submit">
<button className="settings-button" type="submit">
Mettre à jour
</button>
</form>
@ -207,11 +207,12 @@ function ProfileImageInputPopup({ show, onHide }: ProfileImageInputPopupProps) {
if (e.key === "Escape") onHide()
}
window.addEventListener('keyup', onKeyUp)
return () => window.removeEventListener('keyup', onKeyUp)
window.addEventListener("keyup", onKeyUp)
return () => window.removeEventListener("keyup", onKeyUp)
}, [onHide])
const handleForm = useCallback(async (e: FormEvent) => {
const handleForm = useCallback(
async (e: FormEvent) => {
e.preventDefault()
const url = urlRef.current!.value
@ -225,7 +226,9 @@ function ProfileImageInputPopup({ show, onHide }: ProfileImageInputPopupProps) {
setUser({ ...user!, profilePicture: url })
setErrorMessages([])
onHide()
}, [fetcher, onHide, setUser, user])
},
[fetcher, onHide, setUser, user],
)
if (!show) return <></>
@ -243,7 +246,9 @@ function ProfileImageInputPopup({ show, onHide }: ProfileImageInputPopupProps) {
{msg}
</div>
))}
<label id="profile-picture-popup-subtitle" htmlFor="profile-picture">
<label
id="profile-picture-popup-subtitle"
htmlFor="profile-picture">
Saisissez le lien vers votre nouvelle photo de profil
</label>
<input
@ -260,7 +265,7 @@ function ProfileImageInputPopup({ show, onHide }: ProfileImageInputPopupProps) {
required
placeholder={"lien vers une image"}
value={link}
onChange={e => setLink(e.target.value)}
onChange={(e) => setLink(e.target.value)}
/>
<div id="profile-picture-popup-footer">
<button className={"settings-button"} onClick={onHide}>

@ -1,7 +1,11 @@
import { ServiceError, TacticService } from "../service/MutableTacticService.ts"
import { useNavigate, useParams } from "react-router-dom"
import { useCallback, useEffect, useMemo, useReducer, useState } from "react"
import { VisualizerState, VisualizerStateActionKind, visualizerStateReducer } from "../visualizer/VisualizerState.ts"
import { useCallback, useEffect, useMemo, useState } from "react"
import {
useVisualizer,
VisualizerState,
VisualizerStateActionKind,
} from "../visualizer/VisualizerState.ts"
import { getParent } from "../domains/StepsDomain.ts"
import { mapToParentContent } from "../domains/TacticContentDomains.ts"
import StepsTree from "../components/editor/StepsTree.tsx"
@ -25,14 +29,18 @@ export function VisualizerPage({ guestMode }: VisualizerPageProps) {
const fetcher = useAppFetcher()
if (guestMode || !idStr) {
return (
<ServedVisualizerPage service={LocalStorageTacticService.init()}
openEditor={() => navigate("/tactic/edit-guest")} />
<ServedVisualizerPage
service={LocalStorageTacticService.init()}
openEditor={() => navigate("/tactic/edit-guest")}
/>
)
}
return (
<ServedVisualizerPage service={new APITacticService(fetcher, parseInt(idStr))}
openEditor={() => navigate(`/tactic/${idStr}/edit`)} />
<ServedVisualizerPage
service={new APITacticService(fetcher, parseInt(idStr))}
openEditor={() => navigate(`/tactic/${idStr}/edit`)}
/>
)
}
@ -47,9 +55,12 @@ interface ServedVisualizerPageProps {
openEditor: () => void
}
function ServedVisualizerPage({ service, openEditor }: ServedVisualizerPageProps) {
function ServedVisualizerPage({
service,
openEditor,
}: ServedVisualizerPageProps) {
const [panicMessage, setPanicMessage] = useState<string>()
const [state, dispatch] = useReducer(visualizerStateReducer, null)
const [state, dispatch] = useVisualizer(null)
const [canEdit, setCanEdit] = useState(false)
useEffect(() => {
@ -118,7 +129,6 @@ function ServedVisualizerPage({ service, openEditor }: ServedVisualizerPageProps
[openEditor, service, state],
)
if (panicMessage) {
return <p>{panicMessage}</p>
}
@ -127,7 +137,13 @@ function ServedVisualizerPage({ service, openEditor }: ServedVisualizerPageProps
return <p>Retrieving tactic context. Please wait...</p>
}
return <VisualizerPageContent state={state} service={visualizerService} showEditButton={canEdit} />
return (
<VisualizerPageContent
state={state}
service={visualizerService}
showEditButton={canEdit}
/>
)
}
interface VisualizerPageContentProps {
@ -179,12 +195,12 @@ function VisualizerPageContent({
onClick={() => setStepsTreeVisible((b) => !b)}>
ETAPES
</button>
{showEditButton && <button
onClick={() => service.openEditor()}>
{showEditButton && (
<button onClick={() => service.openEditor()}>
EDITER
</button>}
</button>
)}
</div>
</div>
<div id="editor-div">

@ -16,7 +16,9 @@ export class APITacticService implements MutableTacticService {
}
async canBeEdited(): Promise<boolean> {
const response = await this.fetcher.fetchAPIGet(`tactics/${this.tacticId}/can-edit`)
const response = await this.fetcher.fetchAPIGet(
`tactics/${this.tacticId}/can-edit`,
)
const { canEdit } = await response.json()
return canEdit
}

@ -33,7 +33,7 @@ export class LocalStorageTacticService implements MutableTacticService {
)
localStorage.setItem(
GUEST_MODE_STEP_CONTENT_STORAGE_KEY + 1,
JSON.stringify(<StepContent>{components: []})
JSON.stringify(<StepContent>{ components: [] }),
)
}

@ -1,4 +1,5 @@
import { CourtType, StepContent, StepInfoNode } from "../model/tactic/Tactic.ts"
import { useReducer } from "react"
export interface VisualizerState {
stepId: number
@ -9,7 +10,7 @@ export interface VisualizerState {
parentContent: StepContent | null
}
export enum VisualizerStateActionKind {
export const enum VisualizerStateActionKind {
INIT,
SET_CONTENTS,
}
@ -26,7 +27,11 @@ export type VisualizerStateAction =
stepId: number
}
export function visualizerStateReducer(
export function useVisualizer(initialState: VisualizerState | null) {
return useReducer(visualizerStateReducer, initialState)
}
function visualizerStateReducer(
state: VisualizerState | null,
action: VisualizerStateAction,
): VisualizerState | null {

Loading…
Cancel
Save