Skip to content

Commit 73ceadd

Browse files
feat: add link pending components
1 parent 79b1488 commit 73ceadd

File tree

6 files changed

+81
-58
lines changed

6 files changed

+81
-58
lines changed

src/pages/library/components/game-entry.tsx

+30-15
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,37 @@ export function GameEntry({ rom, width }) {
99
const { data: cover, isLoading } = useRomCover(rom)
1010

1111
return (
12-
<Link className='block transition-transform' style={{ width: width || 'auto' }} to={`/library/rom/${rom.id}`}>
13-
<div className='flex aspect-square size-full items-center justify-center'>
14-
{isLoading ? <div className='size-4/5 rounded bg-zinc-200' /> : null}
12+
<div className='relative'>
13+
<Link
14+
className='block'
15+
style={{ width: width || 'auto' }}
16+
to={`/library/rom/${rom.id}`}
17+
unstable_pending={
18+
<div className='z-1 absolute inset-0'>
19+
<div className='grid h-4/5 w-full place-items-center'>
20+
<div className='backdrop-blur-xs rounded-full bg-white/40 p-1.5'>
21+
<span className='icon-[svg-spinners--180-ring] block size-6 text-[var(--theme)]' />
22+
</div>
23+
</div>
24+
</div>
25+
}
26+
>
27+
<div className='flex aspect-square size-full items-center justify-center'>
28+
{isLoading ? <div className='size-4/5 rounded bg-zinc-200' /> : null}
1529

16-
{cover ? (
17-
<img
18-
alt={name}
19-
className={clsx('max-w-4/5 max-h-full rounded object-contain drop-shadow-lg', {
20-
rounded: cover.type === 'rom',
21-
})}
22-
src={cover.src}
23-
/>
24-
) : null}
25-
</div>
30+
{cover ? (
31+
<img
32+
alt={name}
33+
className={clsx('max-w-4/5 max-h-full rounded object-contain drop-shadow-lg', {
34+
rounded: cover.type === 'rom',
35+
})}
36+
src={cover.src}
37+
/>
38+
) : null}
39+
</div>
2640

27-
<div className='mt-2 line-clamp-2 text-center text-sm font-semibold'>{name}</div>
28-
</Link>
41+
<div className='mt-2 line-clamp-2 text-center text-sm font-semibold'>{name}</div>
42+
</Link>
43+
</div>
2944
)
3045
}

src/pages/library/components/sidebar-link.tsx

+12-9
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@ import { Link } from 'waku/router/client'
33

44
export function SidebarLink({ active = false, children, href }) {
55
return (
6-
<Link
7-
className={clsx(
8-
'mx-2 flex items-center gap-2 rounded px-4 py-2.5 font-semibold transition-colors hover:bg-rose-900 hover:text-white',
9-
active ? 'bg-rose-900 font-semibold text-white' : 'text-white/80 ',
10-
)}
11-
to={href}
12-
>
13-
{children}
14-
</Link>
6+
<div className='relative'>
7+
<Link
8+
className={clsx(
9+
'mx-2 flex items-center gap-2 rounded px-4 py-2.5 font-semibold transition-colors hover:text-white',
10+
active ? 'cursor-default bg-rose-900 font-semibold text-white' : 'text-white/80 ',
11+
)}
12+
to={href}
13+
unstable_pending={<div className='z-1 absolute top-0 size-full' />}
14+
>
15+
{children}
16+
</Link>
17+
</div>
1518
)
1619
}

src/pages/library/rom/components/game-overlay/game-overlay-buttons.tsx

+20-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { GameOverlayButton } from './game-overlay-button.tsx'
66

77
export function GameOverlayButtons() {
88
const { emulator, exit } = useEmulator()
9-
const { isSavingState, saveState } = useGameStates()
9+
const { saveState } = useGameStates()
1010
const { setIsPending, toggle } = useGameOverlay()
1111

1212
function handleClickResume() {
@@ -21,23 +21,38 @@ export function GameOverlayButtons() {
2121

2222
async function handleClickSaveState() {
2323
setIsPending(true)
24-
await saveState()
25-
setIsPending(false)
24+
try {
25+
await saveState()
26+
toggle()
27+
} finally {
28+
setIsPending(false)
29+
}
2630
}
2731

2832
function handleClickExit() {
2933
exit()
3034
toggle()
3135
}
3236

37+
async function handleClickSaveExit() {
38+
setIsPending(true)
39+
try {
40+
await saveState()
41+
toggle()
42+
exit()
43+
} finally {
44+
setIsPending(false)
45+
}
46+
}
47+
3348
return (
3449
<>
3550
<GameOverlayButton onClick={handleClickResume}>
3651
<span className='icon-[material-symbols--resume] size-7' />
3752
Resume
3853
</GameOverlayButton>
3954

40-
<GameOverlayButton isLoading={isSavingState} onClick={handleClickSaveState}>
55+
<GameOverlayButton onClick={handleClickSaveState}>
4156
<span className='icon-[mdi--content-save] size-7' />
4257
Save State
4358
</GameOverlayButton>
@@ -53,7 +68,7 @@ export function GameOverlayButtons() {
5368
Exit
5469
</GameOverlayButton>
5570

56-
<GameOverlayButton>
71+
<GameOverlayButton onClick={handleClickSaveExit}>
5772
<span className='icon-[mdi--location-exit] size-7' />
5873
Save & Exit
5974
</GameOverlayButton>

src/pages/library/rom/hooks/use-emulator.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ export function useEmulator() {
2020
rom: romUrl,
2121
shader,
2222
style: {
23-
height: '100px',
24-
width: '100px',
23+
// height: '100px',
24+
// width: '100px',
2525
},
2626
})
2727
})
+16-26
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,32 @@
11
import ky from 'ky'
22
import useSWRMutation from 'swr/mutation'
33
import { useEmulator } from './use-emulator.ts'
4-
import { useGameOverlay } from './use-game-overlay.ts'
54

65
export function useGameStates() {
76
const { core, emulator, rom } = useEmulator()
8-
const { toggle } = useGameOverlay()
97

108
const {
119
data: states,
1210
isMutating: isStatesLoading,
1311
trigger: reloadStates,
1412
} = useSWRMutation('/api/v1/states', (url) => ky(url, { searchParams: { rom_id: rom.id } }).json())
1513

16-
const { isMutating: isSavingState, trigger: saveState } = useSWRMutation(
17-
'/api/v1/state/new',
18-
async (url) => {
19-
if (!emulator || !core) {
20-
throw new Error('invalid emulator or core')
21-
}
22-
const { state, thumbnail } = await emulator.saveState()
23-
const formData = new FormData()
24-
formData.append('state', state)
25-
if (thumbnail) {
26-
formData.append('thumbnail', thumbnail)
27-
}
28-
formData.append('rom_id', rom.id)
29-
formData.append('core', core)
30-
formData.append('type', 'manual')
31-
await ky.post(url, { body: formData })
32-
await reloadStates()
33-
},
34-
{
35-
onSuccess() {
36-
toggle()
37-
},
38-
},
39-
)
14+
const { isMutating: isSavingState, trigger: saveState } = useSWRMutation('/api/v1/state/new', async (url) => {
15+
if (!emulator || !core) {
16+
throw new Error('invalid emulator or core')
17+
}
18+
const { state, thumbnail } = await emulator.saveState()
19+
const formData = new FormData()
20+
formData.append('state', state)
21+
if (thumbnail) {
22+
formData.append('thumbnail', thumbnail)
23+
}
24+
formData.append('rom_id', rom.id)
25+
formData.append('core', core)
26+
formData.append('type', 'manual')
27+
await ky.post(url, { body: formData })
28+
await reloadStates()
29+
})
4030

4131
return { isSavingState, isStatesLoading, reloadStates, saveState, states }
4232
}

src/styles/globals.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ body {
99
}
1010

1111
body[data-theme="rose"] {
12-
/* --theme: var(--color-rose-700); */
12+
--theme: var(--color-rose-700);
1313
}
1414

1515
button {

0 commit comments

Comments
 (0)