You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

191 lines
7.9 KiB
TypeScript

import { route } from 'preact-router'
import { useContext, useEffect, useState } from 'preact/hooks'
import { isAdministrator, Problem as ProblemModel, Solution as SolutionModel } from '../../shared/model'
import { prependBaseUrl } from '../../shared/utils'
import { server } from '../api'
import { Header } from '../components/Header'
import { MarkdownEditor } from '../components/MarkdownEditor'
import { Problem } from '../components/Problem'
import { Solution } from '../components/Solution'
import { MetadataContext, useListResource, useResource, ServerContext, DatabaseContext, useServerAsyncCallback } from '../hooks'
import { useLoggedInUser } from '../hooks/useCurrentUser'
type RouteProps = {
id: string
}
export const ProblemPage = ({ id }: RouteProps) => {
const metadata = useContext(MetadataContext)
const db = useContext(DatabaseContext)
if (db) {
useServerAsyncCallback(async () => {
const problem = await db.getProblem(id)
if (problem) {
metadata.title = `PHC Problemi | ${problem.title}`
metadata.description = problem.content
}
})
}
const [user] = useLoggedInUser()
const [source, setSource] = useState('')
const [problem, refreshProblem] = useResource<ProblemModel | null>(`/api/problem/${id}`, null, problem => {
if (problem === null) {
route(prependBaseUrl(`/error?message=${encodeURIComponent(`Il problema "${id}" non esiste`)}`))
}
})
const [solutions, refreshSolutions, setSolutionHeuristic] = useListResource<SolutionModel>(`/api/solutions?problem=${id}`)
const userSolutions: [number, SolutionModel][] = solutions.flatMap((s, index) => (user && s.sentBy === user.id ? [[index, s]] : []))
const otherSolutions: [number, SolutionModel][] = solutions.flatMap((s, index) => (user && s.sentBy !== user.id ? [[index, s]] : []))
const notLoggedSolutions: [number, SolutionModel][] = user ? [] : solutions.map((s, index) => [index, s])
const sendSolution = async () => {
if (source.trim().length === 0) {
alert('La soluzione che hai inserito è vuota!')
return
}
await server.post('/api/solution', {
forProblem: id,
content: source,
})
setSource('')
refreshSolutions()
}
const [editing, setEditing] = useState<boolean>(false)
const [modifiedProblemSource, setModifiedProblemSource] = useState<string>('')
const [modifiedProblemTitle, setModifiedProblemTitle] = useState<string>(problem?.title ?? '')
useEffect(() => {
if (problem) {
setModifiedProblemTitle(problem.title)
}
}, [problem?.title])
useEffect(() => {
if (problem) {
setModifiedProblemSource(problem.content)
}
}, [problem?.content])
const updateProblem = async () => {
await server.patch(`/api/problem/${id}`, {
content: modifiedProblemSource,
title: modifiedProblemTitle,
})
refreshProblem()
setEditing(false)
}
const deleteProblem = async () => {
if (confirm('Sei veramente sicuro di voler eliminare questo problema?')) {
await server.delete(`/api/problem/${id}`)
route(prependBaseUrl('/'))
}
}
return (
<>
<Header />
<main class="page-problem">
<div class="subtitle">Testo del problema</div>
{!editing ? (
<>
{problem && <Problem {...problem} />}
{user && isAdministrator(user.role) && (
<>
<button onClick={() => setEditing(true)}>Modifica problema</button>
<button onClick={() => deleteProblem()}>Elimina problema</button>
</>
)}
</>
) : (
<>
<div class="problem-title">
<label>Titolo</label>
<input
type="text"
value={modifiedProblemTitle}
onInput={e => setModifiedProblemTitle(e.target instanceof HTMLInputElement ? e.target.value : '')}
placeholder="Problema..."
/>
</div>
<MarkdownEditor
placeholder="Modifica testo del problema..."
source={modifiedProblemSource}
setSource={setModifiedProblemSource}
/>
<div class="col">
<button onClick={() => updateProblem()}>Aggiorna problema</button>
<button onClick={() => setEditing(false)}>Annulla</button>
</div>
</>
)}
{userSolutions.length > 0 && (
<details open>
<summary>Le tue soluzioni</summary>
<div class="solution-list">
{userSolutions.map(([index, s]) => (
<Solution
{...s}
adminControls={user !== null && isAdministrator(user.role)}
refreshSolution={refreshSolutions}
setSolution={solFn => setSolutionHeuristic(index, solFn)}
/>
))}
</div>
</details>
)}
{otherSolutions.length > 0 && (
<details>
<summary>Altre soluzioni</summary>
<div class="solution-list">
{otherSolutions.map(([index, s]) => (
<Solution
{...s}
adminControls={user !== null && isAdministrator(user.role)}
refreshSolution={refreshSolutions}
setSolution={solFn => setSolutionHeuristic(index, solFn)}
/>
))}
</div>
</details>
)}
{notLoggedSolutions.length > 0 && (
<details>
<summary>Soluzioni</summary>
<div class="solution-list">
{notLoggedSolutions.map(([index, s]) => (
<Solution
{...s}
adminControls={user !== null && isAdministrator(user.role)}
refreshSolution={refreshSolutions}
setSolution={solFn => setSolutionHeuristic(index, solFn)}
/>
))}
</div>
</details>
)}
{user && (
<>
<hr />
<div class="subtitle">Invia una soluzione al problema</div>
<MarkdownEditor placeholder="Scrivi una soluzione al problema..." {...{ source, setSource }} />
<button onClick={sendSolution}>Invia Soluzione</button>
<p class="icon-text">
<span class="material-symbols-outlined">warning</span>
Attenzione, una soluzione inviata non può essere modificata!
</p>
</>
)}
</main>
</>
)
}