Migration away from maps since they cannot be easily de-/serialized

This commit is contained in:
Neshura 2023-08-24 05:04:27 +02:00
parent ef7bb99f41
commit 40dee1e8f8
Signed by: Neshura
GPG key ID: B6983AAA6B9A7A6C
14 changed files with 321 additions and 372 deletions

View file

@ -2,7 +2,7 @@
"useTabs": true, "useTabs": true,
"singleQuote": true, "singleQuote": true,
"trailingComma": "none", "trailingComma": "none",
"printWidth": 100, "printWidth": 160,
"plugins": ["prettier-plugin-svelte"], "plugins": ["prettier-plugin-svelte"],
"pluginSearchDirs": ["."], "pluginSearchDirs": ["."],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]

3
src/app.d.ts vendored
View file

@ -6,6 +6,9 @@ declare global {
// interface Locals {} // interface Locals {}
// interface PageData {} // interface PageData {}
// interface Platform {} // interface Platform {}
interface Locals {
authenticated: boolean | null
}
} }
} }

13
src/hooks.server.ts Normal file
View file

@ -0,0 +1,13 @@
import { redirect } from "@sveltejs/kit";
export async function handle({ event, resolve }) {
event.locals.authenticated = true;
if (event.url.pathname.startsWith('/admin')) {
if (!event.locals.authenticated) {
throw redirect(303, '/401');
}
}
return await resolve(event);
}

View file

@ -1,5 +1,10 @@
import { writable, type Writable } from "svelte/store"; import { writable, type Writable } from "svelte/store";
const SelectedGameGroupsStore: Writable<Map<number, Array<number>>> = writable(new Map()); export type SelectedChellarisGroups = {
gameId: number,
selectedGroups: Array<number>,
}
const SelectedGameGroupsStore: Writable<Array<SelectedChellarisGroups>> = writable([]); // Key should always be the same as gameId
export default SelectedGameGroupsStore; export default SelectedGameGroupsStore;

View file

@ -1,35 +1,38 @@
import type { Ethic, Species } from './stellaris'; import type { Ethic, Species } from './stellaris';
export interface ChellarisInfo { export interface ChellarisInfo {
games: Map<number, ChellarisGame>, // Key is Game Id games: Array<ChellarisGame>, // Key is Game Id
ethics: Map<number, Ethic>, // Key is Ethic Id ethics: Array<Ethic>, // Key is Ethic Id
species: Map<number, Species>, // Key is Species Group Id species: Array<Species>, // Key is Species Group Id
} }
export type ChellarisGame = { export type ChellarisGame = {
id: number,
name: string, name: string,
groups: Map<number, ChellarisGameGroup>, // Key is Group Id groups: Array<ChellarisGameGroup>, // Key is Group Id
empires: Map<number, ChellarisEmpire>, // Key is Empire Id empires: Array<ChellarisEmpire>, // Key is Empire Id
} }
export type ChellarisGameGroup = { export type ChellarisGameGroup = {
id: number,
name: string, name: string,
} }
export type ChellarisEmpire = { export type ChellarisEmpire = {
id: number,
gestalt: boolean, gestalt: boolean,
machine: boolean, machine: boolean,
group: number, group: number,
empire_portrait: number, // TODO replace with Enum? empire_portrait: number, // TODO replace with Enum?
empire_portrait_group: number, // TODO replace with Enum? empire_portrait_group: number, // TODO replace with Enum?
discord_user?: string, discord_user?: string,
ethics: Map<number, Ethic>, ethics: Array<Ethic>,
} }
export const createChellarisInfo = (): ChellarisInfo => { export const createChellarisInfo = (): ChellarisInfo => {
const newChellarisInfo = { const newChellarisInfo = {
games: new Map(), games: [],
ethics: new Map(), ethics: [],
species: new Map(), species: [],
}; };
return newChellarisInfo; return newChellarisInfo;
@ -37,9 +40,10 @@ export const createChellarisInfo = (): ChellarisInfo => {
export const createChellarisGame = (): ChellarisGame => { export const createChellarisGame = (): ChellarisGame => {
const newChellarisGame = { const newChellarisGame = {
id: 0,
name: "", name: "",
groups: new Map(), groups: [],
empires: new Map(), empires: [],
}; };
return newChellarisGame; return newChellarisGame;
@ -47,6 +51,7 @@ export const createChellarisGame = (): ChellarisGame => {
export const createChellarisGameGroup = (): ChellarisGameGroup => { export const createChellarisGameGroup = (): ChellarisGameGroup => {
const newChellarisGameGroup = { const newChellarisGameGroup = {
id: 0,
name: "", name: "",
}; };
@ -55,8 +60,9 @@ export const createChellarisGameGroup = (): ChellarisGameGroup => {
export const createChellarisEmpire = ( export const createChellarisEmpire = (
{ {
discord_user, group_id, gestalt, empire_portrait_id, empire_portrait_group_id id, discord_user, group_id, gestalt, empire_portrait_id, empire_portrait_group_id
}: { }: {
id: number,
discord_user?: string, discord_user?: string,
group_id: number, group_id: number,
gestalt: boolean, gestalt: boolean,
@ -64,12 +70,13 @@ export const createChellarisEmpire = (
empire_portrait_group_id: number, empire_portrait_group_id: number,
}): ChellarisEmpire => { }): ChellarisEmpire => {
const newChellarisEmpire = { const newChellarisEmpire = {
id: id,
gestalt: gestalt, gestalt: gestalt,
machine: false, machine: false,
group: group_id, group: group_id,
empire_portrait: empire_portrait_id, empire_portrait: empire_portrait_id,
empire_portrait_group: empire_portrait_group_id, empire_portrait_group: empire_portrait_group_id,
ethics: new Map(), ethics: [],
discord_user: discord_user, discord_user: discord_user,
}; };

View file

@ -65,17 +65,20 @@ export class EthicsDataLegacy {
} }
export type Ethic = { export type Ethic = {
id: number,
displayName: string, displayName: string,
machine: boolean, machine: boolean,
fanatic?: boolean, fanatic?: boolean,
} }
export type Species = { export type Species = {
id: number,
displayName: string, displayName: string,
portraits: Map<number, Portrait>, // Key is Portrait Id portraits: Array<Portrait>, // Key is Portrait Id
} }
export type Portrait = { export type Portrait = {
id: number,
hires: string, hires: string,
lores: string, lores: string,
} }

View file

@ -0,0 +1,12 @@
<svelte:head>
<title>About</title>
<meta name="description" content="401: Unauthorized" />
</svelte:head>
<div class="text-column">
<h1>Unauthorized</h1>
<p>
Your request was denied, the authentication token in your settings seems to be invalid.
</p>
</div>

View file

@ -21,9 +21,9 @@
<li aria-current={$page.url.pathname === '/legacy-graphs' ? 'page' : undefined}> <li aria-current={$page.url.pathname === '/legacy-graphs' ? 'page' : undefined}>
<a href="/legacy-graphs">Game 15 Graphs</a> <a href="/legacy-graphs">Game 15 Graphs</a>
</li> </li>
<li aria-current={$page.url.pathname.startsWith('/sign-up') ? 'page' : undefined}> <!--<li aria-current={$page.url.pathname.startsWith('/sign-up') ? 'page' : undefined}>
<a href="/sign-up">Empire Sign-Up</a> <a href="/sign-up">Empire Sign-Up</a>
</li> </li>-->
<li aria-current={$page.url.pathname.startsWith('/admin') ? 'page' : undefined}> <li aria-current={$page.url.pathname.startsWith('/admin') ? 'page' : undefined}>
<a href="/admin">Admin Menu</a> <a href="/admin">Admin Menu</a>
</li> </li>

View file

@ -1,6 +1,6 @@
import ChellarisDataStore from '$lib/stores/ChellarisData'; import ChellarisDataStore from '$lib/stores/ChellarisData';
import SelectedGameStore from '$lib/stores/GameFilter'; import SelectedGameStore from '$lib/stores/GameFilter';
import SelectedGameGroupsStore from "$lib/stores/GameGroupFilter"; import SelectedGameGroupsStore, { type SelectedChellarisGroups } from "$lib/stores/GameGroupFilter";
import GraphsTabStore from '$lib/stores/GraphsTab'; import GraphsTabStore from '$lib/stores/GraphsTab';
import { createChellarisInfo, type ChellarisGame, createChellarisGame, createChellarisGameGroup, createChellarisEmpire } from "$lib/types/chellaris"; import { createChellarisInfo, type ChellarisGame, createChellarisGame, createChellarisGameGroup, createChellarisEmpire } from "$lib/types/chellaris";
import type { LayoutLoad } from "./$types"; import type { LayoutLoad } from "./$types";
@ -13,26 +13,27 @@ export const load: LayoutLoad = async ({ fetch }) => {
// Chellaris Data Code // Chellaris Data Code
const games: {id: number, name: string}[] = await (await fetch(apiBaseUrl + '/games')).json(); const games: { id: number, name: string }[] = await (await fetch(apiBaseUrl + '/games')).json();
games.sort((a, b) => (a.name < b.name ? -1 : 1)); games.sort((a, b) => (a.name < b.name ? -1 : 1));
games.forEach(game => { games.forEach(game => {
const newGame: ChellarisGame = createChellarisGame(); const newGame: ChellarisGame = createChellarisGame();
newGame.id = game.id;
newGame.name = game.name; newGame.name = game.name;
chellarisData.games.set(game.id, newGame); chellarisData.games[game.id] = newGame;
}); });
const groups: {id: number, name: string, game_id: number}[] = await (await fetch(apiBaseUrl + '/game_groups')).json(); const groups: { id: number, name: string, game_id: number }[] = await (await fetch(apiBaseUrl + '/game_groups')).json();
groups.sort((a, b) => (a.name < b.name ? -1 : 1)); groups.sort((a, b) => (a.name < b.name ? -1 : 1));
groups.forEach(group => { groups.forEach(group => {
const gameData = chellarisData.games.get(group.game_id); const gameData = chellarisData.games[group.game_id];
if (typeof gameData !== "undefined") { if (typeof gameData !== "undefined") {
const newGroup = createChellarisGameGroup(); const newGroup = createChellarisGameGroup();
newGroup.id = group.id;
newGroup.name = group.name; newGroup.name = group.name;
gameData.groups.set(group.id, newGroup) chellarisData.games[group.game_id].groups[group.id] = newGroup;
chellarisData.games.set(group.game_id, gameData);
} }
}) })
@ -43,25 +44,26 @@ export const load: LayoutLoad = async ({ fetch }) => {
gestalt: boolean, gestalt: boolean,
empire_portrait_id: number, empire_portrait_id: number,
empire_portrait_group_id: number, empire_portrait_group_id: number,
group_game_id: number}[] = await (await fetch(apiBaseUrl + '/empires')).json(); group_game_id: number
}[] = await (await fetch(apiBaseUrl + '/empires')).json();
empires.sort((a, b) => (a.id < b.id ? -1 : 1)); empires.sort((a, b) => (a.id < b.id ? -1 : 1));
empires.forEach(empire => { empires.forEach(empire => {
const gameData = chellarisData.games.get(empire.group_game_id); const gameData = chellarisData.games[empire.group_game_id];
if (typeof gameData !== "undefined") { if (typeof gameData !== "undefined") {
const newEmpire = createChellarisEmpire(empire); const newEmpire = createChellarisEmpire(empire);
gameData.empires.set(empire.id, newEmpire); chellarisData.games[empire.group_game_id].empires[empire.id] = newEmpire;
} }
}); });
const ethics: {id: number, name: string, machine_ethic: boolean}[] = await (await fetch(apiBaseUrl + '/ethics')).json(); const ethics: { id: number, name: string, machine_ethic: boolean }[] = await (await fetch(apiBaseUrl + '/ethics')).json();
ethics.sort((a, b) => (a.id < b.id ? -1 : 1)); ethics.sort((a, b) => (a.id < b.id ? -1 : 1));
ethics.forEach(ethic => { ethics.forEach(ethic => {
const newEthic: Ethic = {displayName: ethic.name, machine: ethic.machine_ethic}; const newEthic: Ethic = { id: ethic.id, displayName: ethic.name, machine: ethic.machine_ethic };
chellarisData.ethics.set(ethic.id, newEthic); chellarisData.ethics[ethic.id] = newEthic;
}); });
const empireEthics: { const empireEthics: {
@ -69,20 +71,21 @@ export const load: LayoutLoad = async ({ fetch }) => {
empires_group_id: number, empires_group_id: number,
empires_group_game_id: number, empires_group_game_id: number,
ethics_id: number, ethics_id: number,
ethics_fanatic: boolean}[] = await (await fetch(apiBaseUrl + '/empire_ethics')).json(); ethics_fanatic: boolean
}[] = await (await fetch(apiBaseUrl + '/empire_ethics')).json();
empireEthics.forEach(empireEthic => { empireEthics.forEach(empireEthic => {
const gameData = chellarisData.games.get(empireEthic.empires_group_game_id); const gameData = chellarisData.games[empireEthic.empires_group_game_id];
const ethic = chellarisData.ethics.get(empireEthic.ethics_id); const ethic = chellarisData.ethics[empireEthic.ethics_id];
if (typeof gameData !== "undefined" && typeof ethic !== "undefined") { if (typeof gameData !== "undefined" && typeof ethic !== "undefined") {
const empireData = gameData.empires.get(empireEthic.empires_id); const empireData = gameData.empires[empireEthic.empires_id];
if (typeof empireData !== "undefined") { if (typeof empireData !== "undefined") {
const tmpEthic: Ethic = {machine: ethic.machine, displayName: ethic.displayName, fanatic: empireEthic.ethics_fanatic}; const tmpEthic: Ethic = { id: ethic.id, machine: ethic.machine, displayName: ethic.displayName, fanatic: empireEthic.ethics_fanatic };
if (tmpEthic.machine) { if (tmpEthic.machine) {
empireData.machine = true; empireData.machine = true;
} }
empireData.ethics.set(empireEthic.ethics_id, tmpEthic); empireData.ethics[empireEthic.ethics_id] = tmpEthic;
} }
} }
}); });
@ -95,8 +98,8 @@ export const load: LayoutLoad = async ({ fetch }) => {
portraitGroups.sort((a, b) => (a.id < b.id ? -1 : 1)); portraitGroups.sort((a, b) => (a.id < b.id ? -1 : 1));
portraitGroups.forEach(portraitGroup => { portraitGroups.forEach(portraitGroup => {
const newPortraitGroup = { displayName: portraitGroup.name, portraits: new Map() }; const newPortraitGroup = { id: portraitGroup.id, displayName: portraitGroup.name, portraits: [] };
chellarisData.species.set(portraitGroup.id, newPortraitGroup); chellarisData.species[portraitGroup.id] = newPortraitGroup;
}); });
const portraits: { const portraits: {
@ -109,56 +112,81 @@ export const load: LayoutLoad = async ({ fetch }) => {
portraits.sort((a, b) => (a.id < b.id ? -1 : 1)); portraits.sort((a, b) => (a.id < b.id ? -1 : 1));
portraits.forEach(portrait => { portraits.forEach(portrait => {
const portraitGroupData = chellarisData.species.get(portrait.group_id); const portraitGroupData = chellarisData.species[portrait.group_id];
if (typeof portraitGroupData !== "undefined") { if (typeof portraitGroupData !== "undefined") {
const newPortraitData = { hires: portrait.hires, lores: portrait.lores }; const newPortraitData = { id: portrait.id, hires: portrait.hires, lores: portrait.lores };
portraitGroupData.portraits.set(portrait.id, newPortraitData); portraitGroupData.portraits[portrait.id] = newPortraitData;
} }
}) })
ChellarisDataStore.set(chellarisData); ChellarisDataStore.set(chellarisData);
// Local Storage Code // Local Storage Code
let gameGroupSelections: Array<SelectedChellarisGroups> = [];
let gameSelection: number | undefined;
if (typeof localStorage !== 'undefined') { if (typeof localStorage !== 'undefined') {
// Tab Selection // Tab Selection
store = localStorage.getItem('graphsTab'); store = localStorage.getItem('graphsTab');
if (typeof store == 'string') { if (typeof store === 'string') {
GraphsTabStore.set(store); GraphsTabStore.set(store);
} }
// Game Selection // Game Selection
store = localStorage.getItem('gameSelection'); store = localStorage.getItem('gameSelection');
let selectedGame = 1; if (typeof store === 'string' && store != "\"\"") {
if (typeof store == 'string' && store != "\"\"") { gameSelection = JSON.parse(store);
selectedGame = JSON.parse(store); }
if (typeof gameSelection === 'undefined') {
gameSelection = chellarisData.games[0].id;
} }
SelectedGameStore.set(selectedGame);
// Game Groups Selection // Game Groups Selection
store = localStorage.getItem('gameGroupSelection'); store = localStorage.getItem('gameGroupSelection');
if (typeof store == 'string') { if (typeof store === 'string') {
let selectedGameGroups: Array<number> = []; gameGroupSelections = JSON.parse(store);
const gameGroupSelectionMap = new Map<number, Array<number>>(JSON.parse(store));
const tmp = gameGroupSelectionMap.get(selectedGame);
if (typeof tmp !== 'undefined') { if (typeof gameGroupSelections[gameSelection] === 'undefined') {
selectedGameGroups = tmp;
} else {
const tmpGameData = chellarisData.games.get(selectedGame);
// If this fails an empty array is precisely what we want
if (typeof tmpGameData !== "undefined") {
// Default to all available groups // Default to all available groups
selectedGameGroups = [...tmpGameData.groups.keys()]; gameGroupSelections[gameSelection] = { gameId: gameSelection, selectedGroups: chellarisData.games[gameSelection].groups.map(group => group.id) };
// Set Local Storage to default Values if not previously defined // Set Local Storage to default Values if not previously defined
localStorage.setItem('gameGroupSelection', JSON.stringify(Array.from(selectedGameGroups.entries()))); localStorage.setItem('gameGroupSelection', JSON.stringify(gameGroupSelections));
}
else {
Object.keys(gameGroupSelections).forEach(
gKey => {
if (gameGroupSelections[+gKey] == null) {
delete gameGroupSelections[+gKey];
}
else {
Object.keys(gameGroupSelections[+gKey].selectedGroups).forEach(
key => gameGroupSelections[+gKey].selectedGroups[+key] != null || delete gameGroupSelections[+gKey].selectedGroups[+key]
)
}
});
} }
} }
gameGroupSelectionMap.set(selectedGame, selectedGameGroups);
SelectedGameGroupsStore.set(gameGroupSelectionMap);
} }
if (typeof gameSelection === 'undefined') {
gameSelection = chellarisData.games[0].id;
} }
SelectedGameStore.set(gameSelection);
if (typeof gameGroupSelections[gameSelection] === 'undefined') {
// Default to all available groups
gameGroupSelections[gameSelection] = { gameId: gameSelection, selectedGroups: chellarisData.games[gameSelection].groups.map(group => group.id) };
}
SelectedGameGroupsStore.set(gameGroupSelections);
gameGroupSelections = []; // TODO: actually assing a value
return { chellarisData } return { chellarisData }
} }

View file

@ -4,88 +4,54 @@
import SelectedGameGroupsStore from '$lib/stores/GameGroupFilter'; import SelectedGameGroupsStore from '$lib/stores/GameGroupFilter';
import SelectedGameStore from '$lib/stores/GameFilter'; import SelectedGameStore from '$lib/stores/GameFilter';
import ChellarisDataStore from '$lib/stores/ChellarisData'; import ChellarisDataStore from '$lib/stores/ChellarisData';
import { createChellarisInfo, type ChellarisGame, type ChellarisGameGroup, type ChellarisInfo } from '$lib/types/chellaris';
let selectedGame: number;
let selectedGameGroups: Array<number> = [];
let selectedGameGroupsMap: Map<number, Array<number>> = new Map();
let gameGroups: Map<number, ChellarisGameGroup> = new Map();
let chellarisData: ChellarisInfo = createChellarisInfo();
// Chellaris Data Code
const updateGameGroups = () => {
let tmpData;
if (!selectedGame) {
tmpData = chellarisData.games.get(chellarisData.games.keys().next().value);
}
else {
tmpData = chellarisData.games.get(selectedGame);
}
if (typeof tmpData !== 'undefined') {
gameGroups = tmpData.groups;
}
}
ChellarisDataStore.subscribe((data) => {
chellarisData = data;
updateGameGroups();
// TODO Update selection if Groups Differ? Does this value ever even update without a full page reload?
});
// Group Selection Code
const updateSelectedGroups = () => {
const tmp = selectedGameGroupsMap.get(selectedGame);
if (typeof tmp !== 'undefined') {
selectedGameGroups = [...tmp.values()];
} else {
selectedGameGroups = [...gameGroups.keys()];
selectedGameGroupsMap.set(selectedGame, selectedGameGroups);
SelectedGameGroupsStore.set(selectedGameGroupsMap);
}
};
// Game Selection Code
SelectedGameStore.subscribe((selection) => { SelectedGameStore.subscribe((selection) => {
selectedGame = selection; if (typeof $SelectedGameGroupsStore[selection] === 'undefined') {
// Default to all available groups
updateGameGroups(); $SelectedGameGroupsStore[selection] = { gameId: $SelectedGameStore, selectedGroups: $ChellarisDataStore.games[$SelectedGameStore].groups.map((group) => group.id) };
SelectedGameGroupsStore.update(() => $SelectedGameGroupsStore);
if (selectedGameGroupsMap.size != 0) {
updateSelectedGroups();
} }
}); })
SelectedGameGroupsStore.subscribe((selection) => {
selectedGameGroupsMap = selection;
updateSelectedGroups();
// Game Group Selection Code
$: {
if (typeof localStorage !== 'undefined') { if (typeof localStorage !== 'undefined') {
localStorage.setItem('gameGroupSelection', JSON.stringify(Array.from(selection.entries()))); localStorage.setItem('gameGroupSelection', JSON.stringify($SelectedGameGroupsStore));
}
} }
});
const updateGameGroupSelection = () => { $: selectedGameData = $SelectedGameGroupsStore[$SelectedGameStore];
SelectedGameGroupsStore.update((selection) => selection.set(selectedGame, selectedGameGroups)); $: groupJoiner = selectedGameData.selectedGroups.length > 2 ? ', ' : ' & ';
};
const updateGroupStore = () => {
SelectedGameGroupsStore.update(() => $SelectedGameGroupsStore);
}
const dropdownId = crypto.randomUUID(); const dropdownId = crypto.randomUUID();
</script> </script>
<DropDown {dropdownId} dropdownTitle={(selectedGameGroups.length > 1 ? "Groups " : "Group ") + selectedGameGroups.map((selection) => gameGroups.get(selection)?.name).join(", ")}> <DropDown
{#each gameGroups as group} {dropdownId}
dropdownTitle={(selectedGameData.selectedGroups.length > 1 ? 'Groups ' : 'Group ') +
selectedGameData.selectedGroups
.map((selection) => $ChellarisDataStore.games[$SelectedGameStore].groups[selection]?.name)
.join(groupJoiner)}
>
{#each $ChellarisDataStore.games[$SelectedGameStore].groups as group}
{#if group}
<DropDownElement {dropdownId}> <DropDownElement {dropdownId}>
<input <input
id={'checkbox' + group[0]} id={'checkbox' + group.id}
data-dropdown={dropdownId} data-dropdown={dropdownId}
type="checkbox" type="checkbox"
bind:group={selectedGameGroups} bind:group={selectedGameData.selectedGroups}
on:change={updateGameGroupSelection} on:change={updateGroupStore}
value={group[0]} value={group.id}
/> />
<label for={'checkbox' + group[0]} data-dropdown={dropdownId}>{group[1].name}</label> <label for={'checkbox' + group.id} data-dropdown={dropdownId}>{group.name}</label>
</DropDownElement> </DropDownElement>
{/if}
{/each} {/each}
</DropDown> </DropDown>

View file

@ -3,45 +3,29 @@
import DropDownElement from '$lib/components/DropDownElement.svelte'; import DropDownElement from '$lib/components/DropDownElement.svelte';
import SelectedGameStore from '$lib/stores/GameFilter'; import SelectedGameStore from '$lib/stores/GameFilter';
import ChellarisDataStore from '$lib/stores/ChellarisData'; import ChellarisDataStore from '$lib/stores/ChellarisData';
import type { ChellarisGame } from '$lib/types/chellaris';
let selectedGame: number;
let gameList: Map<number, ChellarisGame> = new Map();
// Chellaris Data Code
ChellarisDataStore.subscribe((data) => {
gameList = data.games;
});
// Game Selection Code // Game Selection Code
SelectedGameStore.subscribe((selection) => { $: {
selectedGame = selection;
if (typeof localStorage !== 'undefined') { if (typeof localStorage !== 'undefined') {
localStorage.setItem('gameSelection', JSON.stringify(selectedGame)); localStorage.setItem('gameSelection', JSON.stringify($SelectedGameStore));
} }
});
const updateGameSelection = () => {
SelectedGameStore.update(() => selectedGame);
}; };
// Dropdown UUID to make the Click Event work properly // Dropdown UUID to make the Click Event work properly
const dropdownId = crypto.randomUUID(); const dropdownId = crypto.randomUUID();
</script> </script>
<DropDown {dropdownId} dropdownTitle={gameList.get(selectedGame)?.name}> <DropDown {dropdownId} dropdownTitle={$ChellarisDataStore.games[$SelectedGameStore]?.name}>
{#each gameList as game} {#each $ChellarisDataStore.games as game}
<DropDownElement {dropdownId}> <DropDownElement {dropdownId}>
<input <input
id={'checkbox' + game[0]} id={'checkbox' + game.id}
data-dropdown={dropdownId} data-dropdown={dropdownId}
type="radio" type="radio"
bind:group={selectedGame} bind:group={$SelectedGameStore}
on:change={updateGameSelection} value={game.id}
value={game[0]}
/> />
<label for={'checkbox' + game[0]} data-dropdown={dropdownId}>{game[1].name}</label> <label for={'checkbox' + game.id} data-dropdown={dropdownId}>{game.name}</label>
</DropDownElement> </DropDownElement>
{/each} {/each}
</DropDown> </DropDown>

View file

@ -12,12 +12,12 @@
<path d="M0,0 L1,2 C1.5,3 1.5,3 2,3 L2,0 Z" /> <path d="M0,0 L1,2 C1.5,3 1.5,3 2,3 L2,0 Z" />
</svg> </svg>
<ul> <ul>
<li aria-current={$page.url.pathname === '/graphs/tab' ? 'page' : undefined}> <!--<li aria-current={$page.url.pathname === '/graphs/tab' ? 'page' : undefined}>
<a href="/graphs/tab">Tab 1</a> <a href="/graphs/tab">Tab 1</a>
</li> </li>
<li aria-current={$page.url.pathname === '/graphs/tab2' ? 'page' : undefined}> <li aria-current={$page.url.pathname === '/graphs/tab2' ? 'page' : undefined}>
<a href="/graphs/tab2">Tab 2</a> <a href="/graphs/tab2">Tab 2</a>
</li> </li>-->
<li aria-current={$page.url.pathname === '/graphs/excel-style' ? 'page' : undefined}> <li aria-current={$page.url.pathname === '/graphs/excel-style' ? 'page' : undefined}>
<a href="/graphs/excel-style">Excel Style</a> <a href="/graphs/excel-style">Excel Style</a>
</li> </li>

View file

@ -1,96 +1,88 @@
<script lang="ts"> <script lang="ts">
import SelectedGameGroupsStore from '$lib/stores/GameGroupFilter'; import SelectedGameGroupsStore from '$lib/stores/GameGroupFilter';
import SelectedGameStore from '$lib/stores/GameFilter'; import SelectedGameStore from '$lib/stores/GameFilter';
import { import type { ChellarisGameGroup, ChellarisInfo, ChellarisEmpire, ChellarisGame } from '$lib/types/chellaris';
createChellarisInfo,
type ChellarisGameGroup,
type ChellarisInfo,
type ChellarisGame,
createChellarisGame,
type ChellarisEmpire
} from '$lib/types/chellaris';
import ChellarisDataStore from '$lib/stores/ChellarisData'; import ChellarisDataStore from '$lib/stores/ChellarisData';
import GraphsTabStore from '$lib/stores/GraphsTab'; import GraphsTabStore from '$lib/stores/GraphsTab';
import type { LayoutData } from '../$types';
import type { Ethic } from '$lib/types/stellaris';
import { page } from '$app/stores';
import LoadingSpinner from '$lib/components/LoadingSpinner.svelte'; import LoadingSpinner from '$lib/components/LoadingSpinner.svelte';
export let data: LayoutData; $: selectedGame = $ChellarisDataStore.games[$SelectedGameStore];
let selectedGameGroups: Array<number> = []; $: selectedGameGroupData = $SelectedGameGroupsStore[$SelectedGameStore];
let selectedGameGroupsMap: Map<number, Array<number>> = new Map(); $: groupNoun = selectedGameGroupData.selectedGroups.length > 1 ? 'Groups' : 'Group';
let gameGroups: Map<number, ChellarisGameGroup> = new Map(); $: groupJoiner = selectedGameGroupData.selectedGroups.length > 2 ? ', ' : ' & ';
let chellarisData: ChellarisInfo = data.chellarisData;
let selectedGameIdx: number; let groupPortraitLimit: number;
let selectedGame: ChellarisGame = createChellarisGame(); $: {
let containsNA = false;
selectedGameGroupData.selectedGroups.forEach((selection) => {
if ($ChellarisDataStore.games[$SelectedGameStore].groups[selection]?.name == 'N/A') {
containsNA = true;
}
});
groupPortraitLimit = containsNA ? $ChellarisDataStore.games[$SelectedGameStore].groups.length - 1 : selectedGameGroupData.selectedGroups.length;
}
let pageData: { let pageData: {
init: boolean; init: boolean;
empireCount: number; empireCount: number;
gestaltCount: { total: number; machines: number }; gestaltCount: { total: number; machines: number };
ethicsData: Map< ethicsData: Array<{
number, id: number;
{
machine: boolean; machine: boolean;
displayName: string; displayName: string;
regular: number; regular: number;
fanatic: number; fanatic: number;
} }>;
>; takenPortraits: Array<Array<[number, Array<number>]>>; // <SpeciesGroup, <Portrait, <SumTaken, <GroupsTaken>>>>
takenPortraits: Map<number, Map<number, number>>; };
$: {
console.log('Reran'); // DEBUG
let newPageData: {
init: boolean;
empireCount: number;
gestaltCount: { total: number; machines: number };
ethicsData: Array<{
id: number;
machine: boolean;
displayName: string;
regular: number;
fanatic: number;
}>;
takenPortraits: Array<Array<[number, Array<number>]>>; // <SpeciesGroup, <Portrait, <SumTaken, <GroupsTaken>>>>
} = { } = {
init: false, init: false,
empireCount: 0, empireCount: 0,
gestaltCount: { total: 0, machines: 0 }, gestaltCount: { total: 0, machines: 0 },
ethicsData: new Map(), ethicsData: [],
takenPortraits: new Map() takenPortraits: []
}; };
// Save Tab to Store if (selectedGame) {
GraphsTabStore.update(() => 'excel-style'); // Empty init of Ethics Data
$ChellarisDataStore.ethics.forEach((ethic) => {
const updateGameGroups = () => {
let tmpData;
if (!selectedGame) {
tmpData = chellarisData.games.get(chellarisData.games.keys().next().value);
} else {
tmpData = chellarisData.games.get(selectedGameIdx);
}
if (typeof tmpData !== 'undefined') {
gameGroups = tmpData.groups;
}
};
const updatePageData = () => {
const tmpGameData = chellarisData.games.get(selectedGameIdx);
if (typeof tmpGameData !== 'undefined') {
const groupEmpires: Map<number, ChellarisEmpire> = new Map();
pageData.ethicsData = new Map();
pageData.takenPortraits = new Map();
pageData.gestaltCount = { total: 0, machines: 0 };
chellarisData.ethics.forEach((ethic, id) => {
const newEthicsData: { const newEthicsData: {
id: number;
machine: boolean; machine: boolean;
displayName: string; displayName: string;
regular: number; regular: number;
fanatic: number; fanatic: number;
} = { } = {
id: ethic.id,
machine: ethic.machine, machine: ethic.machine,
displayName: ethic.displayName, displayName: ethic.displayName,
regular: 0, regular: 0,
fanatic: 0 fanatic: 0
}; };
pageData.ethicsData.set(id, newEthicsData); newPageData.ethicsData[ethic.id] = newEthicsData;
}); });
tmpGameData.empires.forEach((empire, index) => { selectedGame.empires.forEach((empire, index) => {
if (selectedGameGroups.includes(empire.group)) { if (selectedGameGroupData.selectedGroups.includes(empire.group)) {
groupEmpires.set(index, empire); newPageData.empireCount = newPageData.empireCount + 1;
if (empire.gestalt) { if (empire.gestalt) {
pageData.gestaltCount.total = pageData.gestaltCount.total + 1; newPageData.gestaltCount.total = newPageData.gestaltCount.total + 1;
let machine = false; let machine = false;
empire.ethics.forEach((ethic) => { empire.ethics.forEach((ethic) => {
@ -100,12 +92,12 @@
}); });
if (machine) { if (machine) {
pageData.gestaltCount.machines = pageData.gestaltCount.machines + 1; newPageData.gestaltCount.machines = newPageData.gestaltCount.machines + 1;
} }
} }
empire.ethics.forEach((ethic, id) => { empire.ethics.forEach((ethic, id) => {
const tmpEthicPageData = pageData.ethicsData.get(id); const tmpEthicPageData = newPageData.ethicsData[id];
if (typeof tmpEthicPageData !== 'undefined') { if (typeof tmpEthicPageData !== 'undefined') {
tmpEthicPageData.displayName = ethic.displayName; tmpEthicPageData.displayName = ethic.displayName;
@ -115,14 +107,16 @@
tmpEthicPageData.fanatic = tmpEthicPageData.fanatic + 1; tmpEthicPageData.fanatic = tmpEthicPageData.fanatic + 1;
} }
pageData.ethicsData.set(id, tmpEthicPageData); newPageData.ethicsData[id] = tmpEthicPageData;
} else { } else {
let newEthicsData: { let newEthicsData: {
id: number;
machine: boolean; machine: boolean;
displayName: string; displayName: string;
regular: number; regular: number;
fanatic: number; fanatic: number;
} = { } = {
id: ethic.id,
machine: ethic.machine, machine: ethic.machine,
displayName: ethic.displayName, displayName: ethic.displayName,
regular: 0, regular: 0,
@ -135,72 +129,53 @@
newEthicsData.fanatic = 1; newEthicsData.fanatic = 1;
} }
pageData.ethicsData.set(id, newEthicsData); newPageData.ethicsData[id] = newEthicsData;
} }
}); });
if (!pageData.takenPortraits.has(empire.empire_portrait_group)) { if (typeof newPageData.takenPortraits[empire.empire_portrait_group] === 'undefined') {
pageData.takenPortraits.set(empire.empire_portrait_group, new Map()); newPageData.takenPortraits[empire.empire_portrait_group] = [];
} }
const portraitGroupData = pageData.takenPortraits.get(empire.empire_portrait_group); const portraitGroupData = newPageData.takenPortraits[empire.empire_portrait_group];
if (typeof portraitGroupData !== 'undefined') { if (typeof portraitGroupData !== 'undefined') {
if (!portraitGroupData.has(empire.empire_portrait)) { if (typeof portraitGroupData[empire.empire_portrait] === 'undefined') {
portraitGroupData.set(empire.empire_portrait, 0); portraitGroupData[empire.empire_portrait] = [0, []];
} }
let portraitData = portraitGroupData.get(empire.empire_portrait); let portraitData = portraitGroupData[empire.empire_portrait];
if (typeof portraitData !== 'undefined') { if (typeof portraitData !== 'undefined') {
portraitData = portraitData + 1; portraitData[0] = portraitData[0] + 1;
portraitGroupData.set(empire.empire_portrait, portraitData); portraitData[1].push(empire.group);
pageData.takenPortraits.set(empire.empire_portrait_group, portraitGroupData); portraitGroupData[empire.empire_portrait] = portraitData;
newPageData.takenPortraits[empire.empire_portrait_group] = portraitGroupData;
} }
} }
} }
}); });
pageData.empireCount = groupEmpires.size;
pageData.init = true;
}
};
ChellarisDataStore.subscribe((data) => { // Fill undefined Portrait Info with default information.
chellarisData = data; for (let i = 0; i < $ChellarisDataStore.species.length; i++) {
if (typeof newPageData.takenPortraits[i] === 'undefined') {
updateGameGroups(); newPageData.takenPortraits[i] = [];
// TODO Update selection if Groups Differ? Does this value ever even update without a full page reload?
});
SelectedGameStore.subscribe((selection) => {
selectedGameIdx = selection;
const tmpGameData = chellarisData.games.get(selectedGameIdx);
if (typeof tmpGameData !== 'undefined') {
selectedGame = tmpGameData;
} }
updateGameGroups(); for (let ii = 0; ii < $ChellarisDataStore.species[i].portraits.length; ii++) {
if (typeof newPageData.takenPortraits[i][ii] === 'undefined') {
if (selectedGameGroupsMap.size != 0) { newPageData.takenPortraits[i][ii] = [0, []];
const tmp = selectedGameGroupsMap.get(selectedGameIdx); }
if (typeof tmp !== 'undefined') {
selectedGameGroups = [...tmp.values()];
} }
} }
updatePageData(); newPageData.init = true;
});
SelectedGameGroupsStore.subscribe((selection) => {
selectedGameGroupsMap = selection;
const tmp = selection.get(selectedGameIdx);
if (typeof tmp !== 'undefined') {
selectedGameGroups = [...tmp.values()];
} }
updatePageData();
});
const groupNoun = selectedGameGroups.length > 1 ? 'Groups' : 'Group'; pageData = newPageData;
const groupJoiner = selectedGameGroups.length > 2 ? ', ' : ' & '; }
// Save Tab to Store
GraphsTabStore.update(() => 'excel-style');
</script> </script>
<svelte:head> <svelte:head>
@ -211,7 +186,7 @@
{#if pageData.init} {#if pageData.init}
<h1> <h1>
{selectedGame.name} Sign-Up Info for {groupNoun} {selectedGame.name} Sign-Up Info for {groupNoun}
{selectedGameGroups.map((selection) => gameGroups.get(selection)?.name).join(groupJoiner)} {selectedGameGroupData.selectedGroups.map((selection) => $ChellarisDataStore.games[$SelectedGameStore].groups[selection]?.name).join(groupJoiner)}
</h1> </h1>
<div class="text-column"> <div class="text-column">
@ -227,11 +202,11 @@
<th># Fanatic</th> <th># Fanatic</th>
</tr> </tr>
{#each pageData.ethicsData as ethicData} {#each pageData.ethicsData as ethicData}
{#if !ethicData[1].machine} {#if ethicData && !ethicData.machine}
<tr> <tr>
<td class="table-label">{ethicData[1].displayName}</td> <td class="table-label">{ethicData.displayName}</td>
<td>{ethicData[1].regular}</td> <td>{ethicData.regular}</td>
<td>{ethicData[1].fanatic}</td> <td>{ethicData.fanatic}</td>
</tr> </tr>
{/if} {/if}
{/each} {/each}
@ -247,10 +222,10 @@
<th>#</th> <th>#</th>
</tr> </tr>
{#each pageData.ethicsData as ethicData} {#each pageData.ethicsData as ethicData}
{#if ethicData[1].machine} {#if ethicData && ethicData.machine}
<tr> <tr>
<td class="table-label">{ethicData[1].displayName}</td> <td class="table-label">{ethicData.displayName}</td>
<td>{ethicData[1].regular}</td> <td>{ethicData.regular}</td>
</tr> </tr>
{/if} {/if}
{/each} {/each}
@ -266,37 +241,32 @@
<th>{index + 1}</th> <th>{index + 1}</th>
{/each} {/each}
</tr> </tr>
{#each chellarisData.species as portraitGroup} {#each $ChellarisDataStore.species ?? false as portraitGroup}
{#if portraitGroup}
<tr> <tr>
<td>{portraitGroup[1].displayName}</td> <td>{portraitGroup.displayName}</td>
{#each portraitGroup[1].portraits as portrait} {#each portraitGroup.portraits ?? false as portrait}
{#if portrait}
<td <td
class="image-box" class="image-box"
style="--color-hovered-portrait: {gameGroups.size - style="--color-hovered-portrait: {groupPortraitLimit - pageData.takenPortraits[portraitGroup.id][portrait.id][0] == 0
1 -
(pageData.takenPortraits.get(portraitGroup[0])?.get(portrait[0]) || 0) ==
0
? 'var(--color-warn-2)' ? 'var(--color-warn-2)'
: gameGroups.size - : groupPortraitLimit - pageData.takenPortraits[portraitGroup.id][portrait.id][0] != groupPortraitLimit
1 -
(pageData.takenPortraits.get(portraitGroup[0])?.get(portrait[0]) || 0) !=
gameGroups.size - 1
? 'var(--color-warn-1)' ? 'var(--color-warn-1)'
: 'var(--color-bg)'}" : 'var(--color-bg)'}"
> >
<input <input
id={portraitGroup[0] + '-' + portrait[0]} id={portraitGroup.id + '-' + portrait.id}
type="image" type="image"
src={"../"+ portrait[1].lores} src={'../' + portrait.lores}
alt={portraitGroup[1].displayName + '-' + portrait[0]} alt={portraitGroup.displayName + '-' + portrait.id}
/> />
<label for={portraitGroup[0] + '-' + portrait[0]} <label for={portraitGroup.id + '-' + portrait.id}>{pageData.takenPortraits[portraitGroup.id][portrait.id][0]}/{groupPortraitLimit}</label>
>{pageData.takenPortraits.get(portraitGroup[0])?.get(portrait[0]) ||
0}/{gameGroups.size - 1}</label
>
</td> </td>
{/if}
{/each} {/each}
</tr> </tr>
{/if}
{/each} {/each}
</table> </table>
</div> </div>

View file

@ -1,56 +1,14 @@
<script lang="ts"> <script lang="ts">
import SelectedGameGroupsStore from '$lib/stores/GameGroupFilter'; import SelectedGameGroupsStore from '$lib/stores/GameGroupFilter';
import SelectedGameStore from '$lib/stores/GameFilter'; import SelectedGameStore from '$lib/stores/GameFilter';
import { createChellarisInfo, type ChellarisGameGroup, type ChellarisInfo } from '$lib/types/chellaris';
import ChellarisDataStore from '$lib/stores/ChellarisData'; import ChellarisDataStore from '$lib/stores/ChellarisData';
import GraphsTabStore from '$lib/stores/GraphsTab'; import GraphsTabStore from '$lib/stores/GraphsTab';
let selectedGameGroups: Array<string> = [];
let selectedGameGroupsMap: Map<string, Array<string>> = new Map();
let gameGroups: Map<string, ChellarisGameGroup> = new Map();
let chellarisData = createChellarisInfo();
let selectedGame = '';
// Save Tab to Store // Save Tab to Store
GraphsTabStore.update(() => 'tab'); GraphsTabStore.update(() => 'tab');
const updateGameGroups = () => { $: selectedGroups = $SelectedGameGroupsStore[$SelectedGameStore].selectedGroups;
let tmpData; $: groupJoiner = selectedGroups.length > 2 ? ', ' : ' & ';
if (selectedGame == '') {
tmpData = chellarisData.games.get(chellarisData.games.keys().next().value);
} else {
tmpData = chellarisData.games.get(selectedGame);
}
if (typeof tmpData !== 'undefined') {
gameGroups = tmpData.groups;
}
};
ChellarisDataStore.subscribe((data) => {
chellarisData = data;
updateGameGroups();
// TODO Update selection if Groups Differ? Does this value ever even update without a full page reload?
});
SelectedGameStore.subscribe((selection) => {
selectedGame = selection;
if (selectedGameGroupsMap.size != 0) {
const tmp = selectedGameGroupsMap.get(selectedGame);
if (typeof tmp !== 'undefined') {
selectedGameGroups = [...tmp.values()];
}
}
});
SelectedGameGroupsStore.subscribe((selection) => {
selectedGameGroupsMap = selection;
const tmp = selection.get(selectedGame);
if (typeof tmp !== 'undefined') {
selectedGameGroups = [...tmp.values()];
}
});
</script> </script>
<svelte:head> <svelte:head>
@ -61,7 +19,7 @@
<h1>Example Tab</h1> <h1>Example Tab</h1>
<p> <p>
{selectedGameGroups.length > 1 ? 'Groups' : 'Group'} {selectedGroups.length > 1 ? 'Groups' : 'Group'}
{selectedGameGroups.map((selection) => gameGroups.get(selection)?.name).join(', ')} {selectedGroups.map((selection) => $ChellarisDataStore.games[$SelectedGameStore].groups[selection]?.name).join(groupJoiner)}
{selectedGameGroups.length > 1 ? 'are' : 'is'} selected {selectedGroups.length > 1 ? 'are' : 'is'} selected
</p> </p>