mirror of
https://github.com/Pandipipas/scoreko-dev.git
synced 2026-06-06 03:32:06 +00:00
Add Pinia persistence and scoreboard support (#15)
* Add scoreboard replicant and Pinia persistence * Fix scoreboard replicant sync (#16)
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import type { Ref } from 'vue';
|
||||
import { playersReplicant } from '../../../browser_shared/replicants';
|
||||
import type { Schemas } from '../../../types';
|
||||
|
||||
type PlayersMap = Schemas.Players;
|
||||
type Player = PlayersMap[string];
|
||||
|
||||
const STORAGE_KEY = 'scoreko-dev.players';
|
||||
|
||||
const normalizePlayer = (input: unknown): Player => {
|
||||
const candidate = typeof input === 'object' && input !== null ? (input as Record<string, unknown>) : {};
|
||||
return {
|
||||
gamertag: typeof candidate.gamertag === 'string' ? candidate.gamertag : '',
|
||||
team: typeof candidate.team === 'string' ? candidate.team : '',
|
||||
country: typeof candidate.country === 'string' ? candidate.country : '',
|
||||
twitter: typeof candidate.twitter === 'string' ? candidate.twitter : '',
|
||||
realName: typeof candidate.realName === 'string' ? candidate.realName : '',
|
||||
pronouns: typeof candidate.pronouns === 'string' ? candidate.pronouns : '',
|
||||
twitch: typeof candidate.twitch === 'string' ? candidate.twitch : '',
|
||||
notes: typeof candidate.notes === 'string' ? candidate.notes : '',
|
||||
};
|
||||
};
|
||||
|
||||
const normalizePlayers = (input: unknown): PlayersMap => {
|
||||
if (typeof input !== 'object' || input === null) {
|
||||
return {};
|
||||
}
|
||||
const result: PlayersMap = {};
|
||||
Object.entries(input as Record<string, unknown>).forEach(([id, value]) => {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
result[id] = normalizePlayer(value);
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
const readStorage = (): PlayersMap | null => {
|
||||
if (typeof window === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const raw = window.localStorage.getItem(STORAGE_KEY);
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
const parsed = JSON.parse(raw) as unknown;
|
||||
return normalizePlayers(parsed);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const writeStorage = (value: PlayersMap) => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(value));
|
||||
} catch {
|
||||
// Ignore storage errors (quota, disabled, etc.)
|
||||
}
|
||||
};
|
||||
|
||||
export const usePlayersStore = defineStore('players', () => {
|
||||
const players = ref<PlayersMap>({});
|
||||
const replicantRef = playersReplicant?.data as unknown as Ref<PlayersMap | undefined> | undefined;
|
||||
const storageSnapshot = readStorage();
|
||||
if (storageSnapshot) {
|
||||
players.value = storageSnapshot;
|
||||
}
|
||||
|
||||
const isApplyingReplicant = ref(false);
|
||||
|
||||
watch(
|
||||
() => replicantRef?.value,
|
||||
(value) => {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
isApplyingReplicant.value = true;
|
||||
players.value = normalizePlayers(value);
|
||||
isApplyingReplicant.value = false;
|
||||
writeStorage(players.value);
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
players,
|
||||
(value) => {
|
||||
writeStorage(value);
|
||||
if (isApplyingReplicant.value || !replicantRef) {
|
||||
return;
|
||||
}
|
||||
replicantRef.value = normalizePlayers(value);
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const setPlayers = (value: PlayersMap) => {
|
||||
players.value = normalizePlayers(value);
|
||||
};
|
||||
|
||||
const upsertPlayer = (id: string, player: Player) => {
|
||||
players.value = {
|
||||
...players.value,
|
||||
[id]: normalizePlayer(player),
|
||||
};
|
||||
};
|
||||
|
||||
const removePlayer = (id: string) => {
|
||||
const next = { ...players.value };
|
||||
delete next[id];
|
||||
players.value = next;
|
||||
};
|
||||
|
||||
const rows = computed(() => Object.entries(players.value).map(([id, player]) => ({
|
||||
id,
|
||||
...player,
|
||||
})));
|
||||
|
||||
return {
|
||||
players,
|
||||
rows,
|
||||
setPlayers,
|
||||
upsertPlayer,
|
||||
removePlayer,
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user