feat: show existing screenshots when editing entries with remove support
This commit is contained in:
@@ -9,7 +9,7 @@ import { type AntiCheatData } from "@/components/wizard/steps/anti-cheat-step"
|
|||||||
import { PerformanceStep, type PerformanceData } from "@/components/wizard/steps/performance-step"
|
import { PerformanceStep, type PerformanceData } from "@/components/wizard/steps/performance-step"
|
||||||
import { SettingsStep } from "@/components/wizard/steps/settings-step"
|
import { SettingsStep } from "@/components/wizard/steps/settings-step"
|
||||||
import { EnvironmentStep, type EnvironmentData } from "@/components/wizard/steps/environment-step"
|
import { EnvironmentStep, type EnvironmentData } from "@/components/wizard/steps/environment-step"
|
||||||
import { ReviewStep } from "@/components/wizard/steps/review-step"
|
import { ReviewStep, type ExistingScreenshot } from "@/components/wizard/steps/review-step"
|
||||||
import type { SettingCategory } from "@/components/wizard/settings-editor"
|
import type { SettingCategory } from "@/components/wizard/settings-editor"
|
||||||
import { performanceEntries } from "@/lib/db/schema"
|
import { performanceEntries } from "@/lib/db/schema"
|
||||||
|
|
||||||
@@ -46,6 +46,8 @@ export function GameEntryWizard({ gameId, gameVersions, defaultVersionId, editEn
|
|||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
const [screenshotFiles, setScreenshotFiles] = useState<File[]>([])
|
const [screenshotFiles, setScreenshotFiles] = useState<File[]>([])
|
||||||
const [submitPhase, setSubmitPhase] = useState<"idle" | "uploading" | "saving" | "success" | "error">("idle")
|
const [submitPhase, setSubmitPhase] = useState<"idle" | "uploading" | "saving" | "success" | "error">("idle")
|
||||||
|
const [existingScreenshots, setExistingScreenshots] = useState<ExistingScreenshot[]>([])
|
||||||
|
const [removedScreenshotIds, setRemovedScreenshotIds] = useState<string[]>([])
|
||||||
|
|
||||||
// Step 0: Setup — Hardware
|
// Step 0: Setup — Hardware
|
||||||
const [hardwareSlug, setHardwareSlug] = useState(editEntry?.hardwareSlug ?? "")
|
const [hardwareSlug, setHardwareSlug] = useState(editEntry?.hardwareSlug ?? "")
|
||||||
@@ -200,6 +202,11 @@ export function GameEntryWizard({ gameId, gameVersions, defaultVersionId, editEn
|
|||||||
return v.versionString || (v.buildId ? `Build ${v.buildId}` : "Unknown version")
|
return v.versionString || (v.buildId ? `Build ${v.buildId}` : "Unknown version")
|
||||||
}, [selectedVersionId, newVersionString, gameVersions, steamdbVersion])
|
}, [selectedVersionId, newVersionString, gameVersions, steamdbVersion])
|
||||||
|
|
||||||
|
const handleRemoveExistingScreenshot = useCallback((id: string) => {
|
||||||
|
setExistingScreenshots((prev) => prev.filter((ss) => ss.id !== id))
|
||||||
|
setRemovedScreenshotIds((prev) => [...prev, id])
|
||||||
|
}, [])
|
||||||
|
|
||||||
const fetchSteamDBVersion = useCallback(async () => {
|
const fetchSteamDBVersion = useCallback(async () => {
|
||||||
setSteamdbLoading(true)
|
setSteamdbLoading(true)
|
||||||
try {
|
try {
|
||||||
@@ -219,6 +226,22 @@ export function GameEntryWizard({ gameId, gameVersions, defaultVersionId, editEn
|
|||||||
}
|
}
|
||||||
}, [gameId])
|
}, [gameId])
|
||||||
|
|
||||||
|
// Initialize existing screenshots when editing
|
||||||
|
useEffect(() => {
|
||||||
|
if (editEntry && (editEntry as any).screenshots && Array.isArray((editEntry as any).screenshots)) {
|
||||||
|
setExistingScreenshots(
|
||||||
|
(editEntry as any).screenshots.map((ss: any) => ({
|
||||||
|
type: "existing" as const,
|
||||||
|
id: ss.id,
|
||||||
|
url: ss.url,
|
||||||
|
width: ss.width,
|
||||||
|
height: ss.height,
|
||||||
|
orderIndex: ss.orderIndex,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}, [editEntry])
|
||||||
|
|
||||||
// Fetch SteamDB version on mount
|
// Fetch SteamDB version on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchSteamDBVersion()
|
fetchSteamDBVersion()
|
||||||
@@ -339,6 +362,7 @@ export function GameEntryWizard({ gameId, gameVersions, defaultVersionId, editEn
|
|||||||
frameGenMethod: environment.frameGenMethod ?? "none",
|
frameGenMethod: environment.frameGenMethod ?? "none",
|
||||||
launchOptions: environment.launchOptions || null,
|
launchOptions: environment.launchOptions || null,
|
||||||
customSystem: environment.customSystem ?? false,
|
customSystem: environment.customSystem ?? false,
|
||||||
|
removedScreenshotIds: removedScreenshotIds.length > 0 ? removedScreenshotIds : undefined,
|
||||||
settingsJson: settingsJson.length > 0 ? settingsJson : null,
|
settingsJson: settingsJson.length > 0 ? settingsJson : null,
|
||||||
userNotes: userNotes || null,
|
userNotes: userNotes || null,
|
||||||
antiCheatRelevant: antiCheat.antiCheatRelevant,
|
antiCheatRelevant: antiCheat.antiCheatRelevant,
|
||||||
@@ -467,6 +491,8 @@ export function GameEntryWizard({ gameId, gameVersions, defaultVersionId, editEn
|
|||||||
screenshotFiles={screenshotFiles}
|
screenshotFiles={screenshotFiles}
|
||||||
onScreenshotFilesChange={setScreenshotFiles}
|
onScreenshotFilesChange={setScreenshotFiles}
|
||||||
submitPhase={submitPhase}
|
submitPhase={submitPhase}
|
||||||
|
existingScreenshots={existingScreenshots}
|
||||||
|
onRemoveExistingScreenshot={handleRemoveExistingScreenshot}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@@ -23,6 +23,15 @@ import type { PerformanceData } from "./performance-step"
|
|||||||
import type { EnvironmentData } from "./environment-step"
|
import type { EnvironmentData } from "./environment-step"
|
||||||
import { UPSCALER_TYPE_OPTIONS, FRAME_GEN_OPTIONS } from "./environment-step"
|
import { UPSCALER_TYPE_OPTIONS, FRAME_GEN_OPTIONS } from "./environment-step"
|
||||||
|
|
||||||
|
export interface ExistingScreenshot {
|
||||||
|
type: "existing"
|
||||||
|
id: string
|
||||||
|
url: string
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
orderIndex: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface ReviewData {
|
export interface ReviewData {
|
||||||
hardwareSlug: string
|
hardwareSlug: string
|
||||||
hardwareName: string
|
hardwareName: string
|
||||||
@@ -49,6 +58,8 @@ interface ReviewStepProps {
|
|||||||
screenshotFiles: File[]
|
screenshotFiles: File[]
|
||||||
onScreenshotFilesChange: (files: File[]) => void
|
onScreenshotFilesChange: (files: File[]) => void
|
||||||
submitPhase: "idle" | "uploading" | "saving" | "success" | "error"
|
submitPhase: "idle" | "uploading" | "saving" | "success" | "error"
|
||||||
|
existingScreenshots?: ExistingScreenshot[]
|
||||||
|
onRemoveExistingScreenshot?: (id: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function SectionHeader({
|
function SectionHeader({
|
||||||
@@ -185,6 +196,8 @@ export function ReviewStep({
|
|||||||
screenshotFiles,
|
screenshotFiles,
|
||||||
onScreenshotFilesChange,
|
onScreenshotFilesChange,
|
||||||
submitPhase,
|
submitPhase,
|
||||||
|
existingScreenshots,
|
||||||
|
onRemoveExistingScreenshot,
|
||||||
}: ReviewStepProps) {
|
}: ReviewStepProps) {
|
||||||
const {
|
const {
|
||||||
hardwareName,
|
hardwareName,
|
||||||
@@ -528,6 +541,41 @@ export function ReviewStep({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className='space-y-3'>
|
<div className='space-y-3'>
|
||||||
|
{/* Existing screenshots (from edit mode) */}
|
||||||
|
{existingScreenshots && existingScreenshots.length > 0 && (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<p className="text-[10px] text-text/30 uppercase tracking-wider">Existing screenshots</p>
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
{existingScreenshots.map((ss) => (
|
||||||
|
<div
|
||||||
|
key={ss.id}
|
||||||
|
className="group relative rounded-xl border border-border bg-text/3 overflow-hidden"
|
||||||
|
>
|
||||||
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
|
<img
|
||||||
|
src={ss.url}
|
||||||
|
alt={`Screenshot ${ss.orderIndex + 1}`}
|
||||||
|
className="w-full aspect-video object-cover"
|
||||||
|
draggable={false}
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-0 bg-linear-to-t from-black/60 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => onRemoveExistingScreenshot?.(ss.id)}
|
||||||
|
className="absolute top-2 right-2 p-1.5 rounded-lg bg-black/50 text-white/80 hover:text-white hover:bg-red-500/80 backdrop-blur-sm transition-colors opacity-0 group-hover:opacity-100 cursor-pointer"
|
||||||
|
title="Remove screenshot"
|
||||||
|
>
|
||||||
|
<X className="h-3.5 w-3.5" />
|
||||||
|
</button>
|
||||||
|
<div className="absolute bottom-2 left-2 px-2 py-0.5 rounded-md bg-black/50 backdrop-blur-sm text-[10px] font-medium text-white/90 opacity-0 group-hover:opacity-100">
|
||||||
|
Existing · #{ss.orderIndex + 1}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Screenshot grid with drag-to-reorder */}
|
{/* Screenshot grid with drag-to-reorder */}
|
||||||
{screenshotFiles && screenshotFiles.length > 0 && (
|
{screenshotFiles && screenshotFiles.length > 0 && (
|
||||||
<Reorder.Group
|
<Reorder.Group
|
||||||
|
|||||||
Reference in New Issue
Block a user