Fix desync in dragged elements with client side state object
This commit is contained in:
parent
a4e4f1157b
commit
4d24b90afa
4 changed files with 66 additions and 12 deletions
44
src/lib/client/AlbumClientState.svelte.ts
Normal file
44
src/lib/client/AlbumClientState.svelte.ts
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import { getContext, onDestroy, setContext } from 'svelte';
|
||||||
|
import type { AlbumData, AlbumDataField } from '$lib/types';
|
||||||
|
|
||||||
|
export class AlbumClientState {
|
||||||
|
albums = $state<AlbumData>({
|
||||||
|
names: [],
|
||||||
|
artists: [],
|
||||||
|
images: []
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
onDestroy(() => {
|
||||||
|
this.albums = {
|
||||||
|
names: [],
|
||||||
|
artists: [],
|
||||||
|
images: []
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
update(items: AlbumDataField[], type: string) {
|
||||||
|
if (type === 'names') {
|
||||||
|
this.albums.names = items;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'artists') {
|
||||||
|
this.albums.artists = items;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'images') {
|
||||||
|
this.albums.images = items;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ALBUM_KEY = Symbol('ALBUM');
|
||||||
|
|
||||||
|
export function setAlbumClientState() {
|
||||||
|
return setContext(ALBUM_KEY, new AlbumClientState());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAlbumClientState() {
|
||||||
|
return getContext<ReturnType<typeof setAlbumClientState>>(ALBUM_KEY);
|
||||||
|
}
|
|
@ -11,16 +11,20 @@
|
||||||
type Item
|
type Item
|
||||||
} from 'svelte-dnd-action';
|
} from 'svelte-dnd-action';
|
||||||
import * as Card from '$lib/components/ui/card/index.js';
|
import * as Card from '$lib/components/ui/card/index.js';
|
||||||
|
import { getAlbumClientState } from '$lib/client/AlbumClientState.svelte';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
let { items = $bindable(), image = false, type = 'default' } = $props();
|
let { items, image = false, type = 'default' } = $props();
|
||||||
|
|
||||||
const flipDurationMs = 300;
|
const flipDurationMs = 300;
|
||||||
|
const clientState = getAlbumClientState();
|
||||||
|
|
||||||
function handleDndConsider(e: CustomEvent<DndEvent<Item>>) {
|
function handleDndConsider(e: CustomEvent<DndEvent<Item>>) {
|
||||||
items = e.detail.items;
|
items = e.detail.items;
|
||||||
}
|
}
|
||||||
function handleDndFinalize(e: CustomEvent<DndEvent<Item>>) {
|
function handleDndFinalize(e: CustomEvent<DndEvent<Item>>) {
|
||||||
items = e.detail.items;
|
items = e.detail.items;
|
||||||
|
clientState.update(e.detail.items as AlbumDataField[], type);
|
||||||
}
|
}
|
||||||
function transformDraggedElement(draggedEl: HTMLElement | undefined) {
|
function transformDraggedElement(draggedEl: HTMLElement | undefined) {
|
||||||
if (!draggedEl) {
|
if (!draggedEl) {
|
||||||
|
@ -44,6 +48,10 @@
|
||||||
: 'border-blue-400'
|
: 'border-blue-400'
|
||||||
}`
|
}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
clientState.update(items as AlbumDataField[], type);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet card(item: AlbumDataField, i: number)}
|
{#snippet card(item: AlbumDataField, i: number)}
|
||||||
|
|
9
src/routes/(games)/pakubiiti/+layout.svelte
Normal file
9
src/routes/(games)/pakubiiti/+layout.svelte
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { setAlbumClientState } from '$lib/client/AlbumClientState.svelte';
|
||||||
|
|
||||||
|
let { children } = $props();
|
||||||
|
|
||||||
|
setAlbumClientState();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{@render children()}
|
|
@ -10,20 +10,13 @@
|
||||||
import type { AlbumData } from '$lib/types';
|
import type { AlbumData } from '$lib/types';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
import { expoIn, expoOut } from 'svelte/easing';
|
import { expoIn, expoOut } from 'svelte/easing';
|
||||||
|
import { getAlbumClientState } from '$lib/client/AlbumClientState.svelte';
|
||||||
|
|
||||||
let { data }: { data: PageData; form: FormData } = $props();
|
let { data }: { data: PageData; form: FormData } = $props();
|
||||||
|
|
||||||
let loading = $state(false);
|
let loading = $state(false);
|
||||||
let oldAlbums: AlbumData | undefined = $state();
|
|
||||||
|
|
||||||
// Used when user answers wrong and no new data comes in
|
const clientState = getAlbumClientState();
|
||||||
$effect(() => {
|
|
||||||
if (data.streamed?.albums) {
|
|
||||||
data.streamed.albums.then((data) => {
|
|
||||||
oldAlbums = data as AlbumData;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet footer(loading: boolean)}
|
{#snippet footer(loading: boolean)}
|
||||||
|
@ -93,7 +86,7 @@
|
||||||
</AlertDialog.Content>
|
</AlertDialog.Content>
|
||||||
</AlertDialog.Root>
|
</AlertDialog.Root>
|
||||||
|
|
||||||
<header class="font-title mb-12 flex flex-col items-center">
|
<header class="mb-12 flex flex-col items-center font-title">
|
||||||
<h1 class="mb-1 scroll-m-20 text-5xl font-extrabold tracking-tight lg:text-6xl">Paku biiti</h1>
|
<h1 class="mb-1 scroll-m-20 text-5xl font-extrabold tracking-tight lg:text-6xl">Paku biiti</h1>
|
||||||
<p class="text-xl font-semibold text-muted-foreground">
|
<p class="text-xl font-semibold text-muted-foreground">
|
||||||
Lohista kokku õiged albumi <span class="text-red-600 dark:text-red-400">nimed</span>,
|
Lohista kokku õiged albumi <span class="text-red-600 dark:text-red-400">nimed</span>,
|
||||||
|
@ -126,7 +119,7 @@
|
||||||
{/await}
|
{/await}
|
||||||
{:else}
|
{:else}
|
||||||
<div class="grid w-full gap-4" out:fade={{ duration: 150, easing: expoOut }}>
|
<div class="grid w-full gap-4" out:fade={{ duration: 150, easing: expoOut }}>
|
||||||
{@render playArea(oldAlbums)}
|
{@render playArea(clientState.albums)}
|
||||||
</div>
|
</div>
|
||||||
{@render footer(false)}
|
{@render footer(false)}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
Loading…
Add table
Reference in a new issue