feat(plugin): add main panel component (record/stop + recent sessions)
This commit is contained in:
@@ -0,0 +1,152 @@
|
|||||||
|
import { useEffect, useState } from "react"
|
||||||
|
import {
|
||||||
|
ButtonItem,
|
||||||
|
PanelSection,
|
||||||
|
PanelSectionRow,
|
||||||
|
staticClasses,
|
||||||
|
} from "@decky/ui"
|
||||||
|
import {
|
||||||
|
FaPlay,
|
||||||
|
FaStop,
|
||||||
|
FaClock,
|
||||||
|
} from "react-icons/fa"
|
||||||
|
import type { RecordingState, SessionData, RecentSession, PluginSettings } from "../lib/store"
|
||||||
|
import SessionForm from "./session-form"
|
||||||
|
|
||||||
|
interface MainPanelProps {
|
||||||
|
recordingState: RecordingState
|
||||||
|
session: SessionData
|
||||||
|
recentSessions: RecentSession[]
|
||||||
|
error: string
|
||||||
|
settings: PluginSettings
|
||||||
|
onStart: () => void
|
||||||
|
onStop: () => void
|
||||||
|
onUpdateSession: (updates: Partial<SessionData>) => void
|
||||||
|
onAddToRecent: (sess: SessionData) => void
|
||||||
|
onReset: () => void
|
||||||
|
setError: (msg: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MainPanel({
|
||||||
|
recordingState,
|
||||||
|
session,
|
||||||
|
recentSessions,
|
||||||
|
error,
|
||||||
|
settings,
|
||||||
|
onStart,
|
||||||
|
onStop,
|
||||||
|
onUpdateSession,
|
||||||
|
onAddToRecent,
|
||||||
|
onReset,
|
||||||
|
setError,
|
||||||
|
}: MainPanelProps) {
|
||||||
|
const [elapsed, setElapsed] = useState(0)
|
||||||
|
|
||||||
|
// Timer for recording state
|
||||||
|
useEffect(() => {
|
||||||
|
if (recordingState !== "recording") {
|
||||||
|
setElapsed(0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setElapsed(Math.floor((Date.now() - session.startedAt) / 1000))
|
||||||
|
}, 1000)
|
||||||
|
return () => clearInterval(interval)
|
||||||
|
}, [recordingState, session.startedAt])
|
||||||
|
|
||||||
|
function formatTime(seconds: number): string {
|
||||||
|
const m = Math.floor(seconds / 60)
|
||||||
|
const s = seconds % 60
|
||||||
|
return `${m}:${s.toString().padStart(2, "0")}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Stopped state: show the session form ──────────────────────
|
||||||
|
if (recordingState === "stopped") {
|
||||||
|
return (
|
||||||
|
<SessionForm
|
||||||
|
session={session}
|
||||||
|
error={error}
|
||||||
|
settings={settings}
|
||||||
|
onUpdateSession={onUpdateSession}
|
||||||
|
onAddToRecent={onAddToRecent}
|
||||||
|
onReset={onReset}
|
||||||
|
setError={setError}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Idle or Recording state ───────────────────────────────────
|
||||||
|
return (
|
||||||
|
<PanelSection title="Recording">
|
||||||
|
{error && (
|
||||||
|
<PanelSectionRow>
|
||||||
|
<div className={staticClasses.Text} style={{ color: "#e74c3c", padding: "8px" }}>
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
</PanelSectionRow>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<PanelSectionRow>
|
||||||
|
{recordingState === "idle" ? (
|
||||||
|
<ButtonItem layout="below" onClick={onStart}>
|
||||||
|
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||||
|
<FaPlay />
|
||||||
|
Start Recording
|
||||||
|
</div>
|
||||||
|
</ButtonItem>
|
||||||
|
) : (
|
||||||
|
<ButtonItem layout="below" onClick={onStop} disabled={false}>
|
||||||
|
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||||
|
<FaStop />
|
||||||
|
Stop Recording
|
||||||
|
</div>
|
||||||
|
</ButtonItem>
|
||||||
|
)}
|
||||||
|
</PanelSectionRow>
|
||||||
|
|
||||||
|
{recordingState === "recording" && (
|
||||||
|
<>
|
||||||
|
<PanelSectionRow>
|
||||||
|
<div className={staticClasses.Text} style={{ padding: "8px 0" }}>
|
||||||
|
<div style={{ display: "flex", alignItems: "center", gap: "6px", marginBottom: "4px" }}>
|
||||||
|
<FaClock />
|
||||||
|
<strong>{formatTime(elapsed)}</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{session.gameName
|
||||||
|
? `Recording: ${session.gameName}`
|
||||||
|
: "No game detected — recording anyway"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PanelSectionRow>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{recordingState === "idle" && (
|
||||||
|
<PanelSectionRow>
|
||||||
|
<div className={staticClasses.Text} style={{ padding: "8px 0", fontSize: "12px", opacity: 0.7 }}>
|
||||||
|
Enable MangoHud for your game, then press Start Recording before launching.
|
||||||
|
Configure MangoHud in the Settings tab.
|
||||||
|
</div>
|
||||||
|
</PanelSectionRow>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{recentSessions.length > 0 && recordingState === "idle" && (
|
||||||
|
<PanelSection title="Recent Recordings">
|
||||||
|
{recentSessions.map((rs, i) => (
|
||||||
|
<PanelSectionRow key={i}>
|
||||||
|
<div className={staticClasses.Text} style={{ padding: "4px 0", fontSize: "13px" }}>
|
||||||
|
<strong>{rs.gameName || "Unknown game"}</strong>
|
||||||
|
<br />
|
||||||
|
<span style={{ opacity: 0.6 }}>
|
||||||
|
{rs.fpsAvg ? `${rs.fpsAvg} FPS avg` : "No data"} ·{" "}
|
||||||
|
{new Date(rs.date).toLocaleDateString()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</PanelSectionRow>
|
||||||
|
))}
|
||||||
|
</PanelSection>
|
||||||
|
)}
|
||||||
|
</PanelSection>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user