fix(plugin): screenshot limit, version/build fields, delete button, workspace scripts
- list_screenshots: bump limit 12 → 50 so users can scroll further back - Add versionString + buildId to DeckyVaultImportV1, SessionData, and buildImportPayload. Import endpoint now saves them on new game versions. - Screenshot delete button in preset detail modal: owner/admin sees a trash icon on hover that calls DELETE /api/performance/:id/screenshots/:sid - Root workspace scripts for plugin workflow: bun run plugin:build | plugin:zip | plugin:release | plugin:deploy - Add deploy.sh (SSH scripts to push plugin to Deck)
This commit is contained in:
@@ -169,6 +169,9 @@ export function PresetDetailModal({
|
||||
const [activeTab, setActiveTab] = useState<TabKey>("media")
|
||||
const [lightboxOpen, setLightboxOpen] = useState(false)
|
||||
const [lightboxIndex, setLightboxIndex] = useState(0)
|
||||
const [deletingScreenshotId, setDeletingScreenshotId] = useState<
|
||||
string | null
|
||||
>(null)
|
||||
|
||||
const isOwner = session?.user?.id === preset.userId
|
||||
const isAdmin = session?.user?.role === "admin"
|
||||
@@ -259,6 +262,23 @@ export function PresetDetailModal({
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDeleteScreenshot(screenshotId: string) {
|
||||
setDeletingScreenshotId(screenshotId)
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/performance/${preset.id}/screenshots/${screenshotId}`,
|
||||
{ method: "DELETE" },
|
||||
)
|
||||
if (res.ok) {
|
||||
router.refresh()
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to delete screenshot:", err)
|
||||
} finally {
|
||||
setDeletingScreenshotId(null)
|
||||
}
|
||||
}
|
||||
|
||||
const visibleTabs: TabKey[] = ["details", "media", "settings"]
|
||||
if (preset.userNotes) visibleTabs.push("notes")
|
||||
|
||||
@@ -854,31 +874,42 @@ export function PresetDetailModal({
|
||||
<div className='flex flex-col gap-3'>
|
||||
{preset.screenshots.map(
|
||||
(ss, i) => (
|
||||
<button
|
||||
key={
|
||||
ss.id
|
||||
}
|
||||
onClick={() => {
|
||||
setLightboxIndex(
|
||||
i,
|
||||
)
|
||||
setLightboxOpen(
|
||||
true,
|
||||
)
|
||||
}}
|
||||
className='block cursor-pointer focus:outline-none focus:ring-2 focus:ring-primary/50 rounded-lg overflow-hidden w-full'
|
||||
aria-label={`View screenshot ${i + 1}`}
|
||||
>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={
|
||||
ss.url
|
||||
}
|
||||
alt={`Screenshot ${i + 1}`}
|
||||
className='w-full h-auto object-cover border border-border hover:border-primary/50 transition-colors rounded-lg'
|
||||
loading='lazy'
|
||||
/>
|
||||
</button>
|
||||
<div key={ss.id} className='relative group'>
|
||||
<button
|
||||
onClick={() => {
|
||||
setLightboxIndex(
|
||||
i,
|
||||
)
|
||||
setLightboxOpen(
|
||||
true,
|
||||
)
|
||||
}}
|
||||
className='block w-full cursor-pointer focus:outline-none focus:ring-2 focus:ring-primary/50 rounded-lg overflow-hidden'
|
||||
aria-label={`View screenshot ${i + 1}`}
|
||||
>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={ss.url}
|
||||
alt={`Screenshot ${i + 1}`}
|
||||
className='w-full h-auto object-cover border border-border hover:border-primary/50 transition-colors rounded-lg'
|
||||
loading='lazy'
|
||||
/>
|
||||
</button>
|
||||
{(isOwner || isAdmin) && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
handleDeleteScreenshot(ss.id)
|
||||
}}
|
||||
disabled={deletingScreenshotId === ss.id}
|
||||
className='absolute top-2 right-2 p-1.5 rounded-md bg-black/50 text-white/60 hover:text-red-400 hover:bg-red-500/20 opacity-0 group-hover:opacity-100 transition-opacity cursor-pointer disabled:cursor-not-allowed'
|
||||
aria-label={`Delete screenshot ${i + 1}`}
|
||||
title='Delete screenshot'
|
||||
>
|
||||
<TrashIcon className='h-3.5 w-3.5' />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -88,6 +88,8 @@ export const performanceImportRoutes = new Elysia({
|
||||
.values({
|
||||
gameId: game.id,
|
||||
isLatest: true,
|
||||
versionString: body.versionString ?? null,
|
||||
buildId: body.buildId ?? null,
|
||||
})
|
||||
.returning({ id: gameVersions.id })
|
||||
version = newVersion
|
||||
@@ -260,6 +262,8 @@ export const performanceImportRoutes = new Elysia({
|
||||
fpsHigh: t.Optional(t.Nullable(t.Number())),
|
||||
protonVersion: t.Optional(t.Nullable(t.String())),
|
||||
osVersion: t.Optional(t.Nullable(t.String())),
|
||||
versionString: t.Optional(t.Nullable(t.String())),
|
||||
buildId: t.Optional(t.Nullable(t.String())),
|
||||
upscalerType: t.Optional(t.String()),
|
||||
upscalerVersion: t.Optional(t.Nullable(t.String())),
|
||||
frameGenMethod: t.Optional(t.String()),
|
||||
|
||||
Reference in New Issue
Block a user