#3 TS エラー修正
This commit is contained in:
Generated
+352
-367
File diff suppressed because it is too large
Load Diff
+15
-9
@@ -22,6 +22,17 @@ const colours = ['bg-fuchsia-500 dark:bg-fuchsia-900',
|
|||||||
'bg-yellow-500 dark:bg-yellow-900'] as const
|
'bg-yellow-500 dark:bg-yellow-900'] as const
|
||||||
|
|
||||||
|
|
||||||
|
const ScrollToTop = () => {
|
||||||
|
const { pathname } = useLocation ()
|
||||||
|
|
||||||
|
useEffect (() => {
|
||||||
|
scrollTo (0, 0)
|
||||||
|
}, [pathname])
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const bgmRef = useRef<HTMLAudioElement | null> (null)
|
const bgmRef = useRef<HTMLAudioElement | null> (null)
|
||||||
|
|
||||||
@@ -35,7 +46,7 @@ export default () => {
|
|||||||
bgmRef.current.volume = 1
|
bgmRef.current.volume = 1
|
||||||
|
|
||||||
const playBGM = async () => {
|
const playBGM = async () => {
|
||||||
if (playing)
|
if (playing || !(bgmRef.current))
|
||||||
return
|
return
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -54,7 +65,7 @@ export default () => {
|
|||||||
document.addEventListener ('touchstart', playBGM)
|
document.addEventListener ('touchstart', playBGM)
|
||||||
|
|
||||||
const changeColour = () => {
|
const changeColour = () => {
|
||||||
setColourIndex (idx => Math.floor (Math.random () * colours.length))
|
setColourIndex (Math.floor (Math.random () * colours.length))
|
||||||
}
|
}
|
||||||
changeColour ()
|
changeColour ()
|
||||||
const colouringInterval = setInterval (changeColour, 3000)
|
const colouringInterval = setInterval (changeColour, 3000)
|
||||||
@@ -86,7 +97,7 @@ export default () => {
|
|||||||
{playing && (
|
{playing && (
|
||||||
<a href="#" onClick={ev => {
|
<a href="#" onClick={ev => {
|
||||||
ev.preventDefault ()
|
ev.preventDefault ()
|
||||||
setMute (bgmRef.current.muted = !(mute))
|
bgmRef.current && setMute (bgmRef.current.muted = !(mute))
|
||||||
}}>
|
}}>
|
||||||
{mute ? 'やっぱり BGM が恋しい人用' : 'BGM がうるさい人用'}
|
{mute ? 'やっぱり BGM が恋しい人用' : 'BGM がうるさい人用'}
|
||||||
</a>)}
|
</a>)}
|
||||||
@@ -94,12 +105,7 @@ export default () => {
|
|||||||
</header>
|
</header>
|
||||||
<main className="mb-8">
|
<main className="mb-8">
|
||||||
<Routes>
|
<Routes>
|
||||||
{() => {
|
<ScrollToTop />
|
||||||
const { pathname } = useLocation ()
|
|
||||||
useEffect (() => {
|
|
||||||
scrollTo (0, 0)
|
|
||||||
}, [pathname])
|
|
||||||
}}
|
|
||||||
<Route path="/" element={<Navigate to="/threads" replace />} />
|
<Route path="/" element={<Navigate to="/threads" replace />} />
|
||||||
<Route path="/threads" element={<ThreadListPage />} />
|
<Route path="/threads" element={<ThreadListPage />} />
|
||||||
<Route path="/threads/:id" element={<ThreadDetailPage />} />
|
<Route path="/threads/:id" element={<ThreadDetailPage />} />
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useEffect, useRef, useState } from 'react'
|
|||||||
import { FaRedo, FaUndo } from 'react-icons/fa'
|
import { FaRedo, FaUndo } from 'react-icons/fa'
|
||||||
import { Layer, Line, Rect, Stage, Image } from 'react-konva'
|
import { Layer, Line, Rect, Stage, Image } from 'react-konva'
|
||||||
|
|
||||||
import type { KonvaEventObject } from 'konva'
|
import type Konva from 'konva'
|
||||||
import type { ChangeEventHandler } from 'react'
|
import type { ChangeEventHandler } from 'react'
|
||||||
|
|
||||||
type ImageItem = { src: string }
|
type ImageItem = { src: string }
|
||||||
@@ -23,34 +23,35 @@ type Line = {
|
|||||||
strokeWidth: number }
|
strokeWidth: number }
|
||||||
|
|
||||||
const Mode = {
|
const Mode = {
|
||||||
Pen: Symbol (),
|
Pen: 'Pen',
|
||||||
Rubber: Symbol (),
|
Rubber: 'Rubber',
|
||||||
Image: Symbol () } as const
|
Image: 'Image' } as const
|
||||||
type Mode = keyof Mode
|
type Mode = (typeof Mode)[keyof typeof Mode]
|
||||||
|
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const drawingRef = useRef (false)
|
const drawingRef = useRef (false)
|
||||||
const fileInputRef = useRef<HTMLInputElement> (null)
|
const fileInputRef = useRef<HTMLInputElement> (null)
|
||||||
const stageRef = useRef<Stage | null> (null)
|
const stageRef = useRef<Konva.Stage | null> (null)
|
||||||
|
|
||||||
const [activeLayerId, setActiveLayerId] = useState<string | null> (null)
|
const [activeLayerId, setActiveLayerId] = useState<string | null> (null)
|
||||||
const [colour, setColour] = useState ('#000000')
|
const [colour, setColour] = useState ('#000000')
|
||||||
const [images, setImages] = useState<Record<string, window.Image[]>> ({ })
|
const [images, setImages] = useState<Record<string, HTMLImageElement>> ({ })
|
||||||
const [layers, setLayers] = useState<Layer[]> ([])
|
|
||||||
const [layerCnt, setLayerCnt] = useState (0)
|
const [layerCnt, setLayerCnt] = useState (0)
|
||||||
|
const [layers, setLayers] = useState<Layer[]> ([])
|
||||||
const [mode, setMode] = useState<Mode> (Mode.Pen)
|
const [mode, setMode] = useState<Mode> (Mode.Pen)
|
||||||
const [pointSize, setPointSize] = useState (3)
|
const [pointSize, setPointSize] = useState (3)
|
||||||
const [stageWidth, setStageWidth] = useState (480)
|
|
||||||
const [stageHeight, setStageHeight] = useState (480)
|
const [stageHeight, setStageHeight] = useState (480)
|
||||||
|
const [stageWidth, setStageWidth] = useState (480)
|
||||||
|
|
||||||
const activeLayer = layers.find (l => l.id === activeLayerId)
|
const activeLayer = layers.find (l => l.id === activeLayerId)
|
||||||
|
|
||||||
const handleMouseDown = (ev: KonvaEventObject<MouseEvent>) => {
|
const handleMouseDown = (ev: Konva.KonvaEventObject<MouseEvent | TouchEvent>) => {
|
||||||
drawingRef.current = true
|
|
||||||
const pos = ev.target.getStage ()?.getPointerPosition ()
|
const pos = ev.target.getStage ()?.getPointerPosition ()
|
||||||
if (!(pos))
|
if (!(pos) || !(activeLayer))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
drawingRef.current = true
|
||||||
const lines: Line[] = [
|
const lines: Line[] = [
|
||||||
...activeLayer.lines,
|
...activeLayer.lines,
|
||||||
{ mode,
|
{ mode,
|
||||||
@@ -60,9 +61,10 @@ export default () => {
|
|||||||
updateActiveLayerLines (lines)
|
updateActiveLayerLines (lines)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMouseMove = (ev: KonvaEventObject<MouseEvent>) => {
|
const handleMouseMove = (ev: Konva.KonvaEventObject<MouseEvent | TouchEvent>) => {
|
||||||
if (!(drawingRef.current))
|
if (!(drawingRef.current) || !(activeLayer))
|
||||||
return
|
return
|
||||||
|
|
||||||
const stage = ev.target.getStage ()
|
const stage = ev.target.getStage ()
|
||||||
const point = stage?.getPointerPosition ()
|
const point = stage?.getPointerPosition ()
|
||||||
if (!(point))
|
if (!(point))
|
||||||
@@ -80,17 +82,17 @@ export default () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleUndo = () => {
|
const handleUndo = () => {
|
||||||
if (activeLayer.lines.length === 0)
|
if (!(activeLayer) || activeLayer.lines.length === 0)
|
||||||
return
|
return
|
||||||
const newLines = [...activeLayer.lines]
|
const newLines = [...activeLayer.lines]
|
||||||
const last = newLines.pop()!
|
newLines.pop()
|
||||||
updateActiveLayerHistory ([...activeLayer.history, activeLayer.lines])
|
updateActiveLayerHistory ([...activeLayer.history, activeLayer.lines])
|
||||||
updateActiveLayerFuture ([])
|
updateActiveLayerFuture ([])
|
||||||
updateActiveLayerLines (newLines)
|
updateActiveLayerLines (newLines)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleRedo = () => {
|
const handleRedo = () => {
|
||||||
if (activeLayer.history.length === 0)
|
if (!(activeLayer) || activeLayer.history.length === 0)
|
||||||
return
|
return
|
||||||
const prev = activeLayer.history[activeLayer.history.length - 1]
|
const prev = activeLayer.history[activeLayer.history.length - 1]
|
||||||
updateActiveLayerLines (prev)
|
updateActiveLayerLines (prev)
|
||||||
@@ -181,9 +183,14 @@ export default () => {
|
|||||||
useEffect (() => {
|
useEffect (() => {
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const paint = JSON.parse (localStorage.getItem ('paint'))
|
const paintJSON = localStorage.getItem ('paint')
|
||||||
|
if (paintJSON == null)
|
||||||
|
throw new Error
|
||||||
|
|
||||||
|
const paint = JSON.parse (paintJSON)
|
||||||
if (!(paint instanceof Array))
|
if (!(paint instanceof Array))
|
||||||
throw new Error
|
throw new Error
|
||||||
|
|
||||||
setLayers (paint)
|
setLayers (paint)
|
||||||
setActiveLayerId (paint[0].id)
|
setActiveLayerId (paint[0].id)
|
||||||
}
|
}
|
||||||
@@ -254,7 +261,7 @@ export default () => {
|
|||||||
min={32}
|
min={32}
|
||||||
max={640}
|
max={640}
|
||||||
value={stageWidth}
|
value={stageWidth}
|
||||||
onChange={ev => setStageWidth (ev.target.value)} />
|
onChange={ev => setStageWidth (+ev.target.value)} />
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
高さ:
|
高さ:
|
||||||
@@ -262,7 +269,7 @@ export default () => {
|
|||||||
min={24}
|
min={24}
|
||||||
max={480}
|
max={480}
|
||||||
value={stageHeight}
|
value={stageHeight}
|
||||||
onChange={ev => setStageHeight (ev.target.value)} />
|
onChange={ev => setStageHeight (+ev.target.value)} />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -363,7 +370,6 @@ export default () => {
|
|||||||
<div className="border border-black dark:border-white">
|
<div className="border border-black dark:border-white">
|
||||||
<Stage ref={node => {
|
<Stage ref={node => {
|
||||||
stageRef.current = node
|
stageRef.current = node
|
||||||
return node
|
|
||||||
}}
|
}}
|
||||||
width={stageWidth}
|
width={stageWidth}
|
||||||
height={stageHeight}
|
height={stageHeight}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { StrictMode } from 'react'
|
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client'
|
||||||
import './index.css'
|
import './index.css'
|
||||||
import App from './App.tsx'
|
import App from './App.tsx'
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { useParams } from 'react-router-dom'
|
|||||||
import ThreadCanvas from '@/components/threads/ThreadCanvas'
|
import ThreadCanvas from '@/components/threads/ThreadCanvas'
|
||||||
import { API_BASE_URL } from '@/config'
|
import { API_BASE_URL } from '@/config'
|
||||||
|
|
||||||
|
import type { Post, Thread } from '@/types'
|
||||||
|
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const { id } = useParams ()
|
const { id } = useParams ()
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import toCamel from 'camelcase-keys'
|
import toCamel from 'camelcase-keys'
|
||||||
import cn from 'classnames'
|
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Accordion,
|
import { Accordion,
|
||||||
AccordionItem,
|
AccordionItem,
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
export type Post = {
|
||||||
|
id: number
|
||||||
|
threadId: number
|
||||||
|
postNo: number
|
||||||
|
name: string | null
|
||||||
|
message: string | null
|
||||||
|
imageUrl: string | null
|
||||||
|
held: boolean
|
||||||
|
sensitive: boolean
|
||||||
|
good: number
|
||||||
|
bad: number
|
||||||
|
createdAt: string
|
||||||
|
updatedAt: string
|
||||||
|
deletedAt: string | null }
|
||||||
|
|
||||||
|
export type Thread = {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
description: string | null
|
||||||
|
postCount: number
|
||||||
|
createdAt: string
|
||||||
|
updatedAt: string
|
||||||
|
deletedAt: string | null }
|
||||||
Reference in New Issue
Block a user