I have the following svelte store:
export type SessionStore = {
session?: Session,
sessionType?: SessionType,
inputs: []
}
The generation of the inputs field will change depending on the type of session:
I have a custom store that generates the session state based on the user's action:
function makeSessionStore() {
const {subscribe, set, update} = writable(initialValue());
return {
subscribe,
createLocalSession: () => update(() => ({
session: {state: SessionState.RUNNING},
sessionType: SessionType.LOCAL,
inputs: [] // <--- how to generate this based on the session type?
})),
createNetworkSession: () => createSession().then(session => update(() => ({
session,
sessionType: SessionType.NETWORK,
inputs: []
})))
reset: () => set(initialValue())
}
}
My question: How can I attach a reactive store property to the inputs property?
This is e.g. how I get the inputs from the user's keyboard:
const keysPressed: Readable<string[]> = readable([], function(set) {
let keys = [];
const onKeydown = ({key}) => {
if (keys.includes(key)) {
return;
}
keys = [...keys, key];
set(keys);
}
const onKeyup = ({key}) => {
if (!keys.includes(key)) {
return;
}
keys = keys.filter(k => k !== key);
set(keys);
}
document.addEventListener('keydown', onKeydown);
document.addEventListener('keyup', onKeyup);
return () => {
document.removeEventListener('keydown', onKeydown);
document.removeEventListener('keyup', onKeyup);
}
})
I would like the components using the inputs to be unaware of the strategy switching behind the scenes, they should be able to use the inputs like this:
import {sessionStore} from './session';
console.log($sessionStore.inputs) // ['CONTROL', 'w']
I ended up using slotted components instead of stores for this problem.
Basic idea: Wrap the input depending component(s) in wrappers, that define the input type.
{#if !session}
<h1>no session</h1>
{:else if session.type === SessionType.LOCAL}
<LocalSessionWrapper let:inputs={inputs}>
<slot inputs={inputs}></slot>
</LocalSessionWrapper>
{:else}
{#if session.state === SessionState.PENDING}
<h3>waiting for other player...</h3>
{:else if session.state === SessionState.CLOSED}
<h3>game over!</h3>
{:else if session.state === SessionState.RUNNING}
<NetworkSessionWrapper let:inputs={inputs}>
<slot inputs={inputs}></slot>
</NetworkSessionWrapper>
{:else }
<h3>unknown game state</h3>
{/if}
{/if}
The wrapper components use stores to define the source of input:
LocalSessionWrapper:
<script lang="ts">
import {localSessionInputs, Session} from "./game/session";
export let session: Session;
</script>
<slot inputs={$localSessionInputs}></slot>