feat: integrate "default characters" into character selection (#30)

This commit is contained in:
Patric Stout
2023-11-30 21:49:57 +01:00
committed by GitHub
parent eb8041a50a
commit 0406a4a464
5 changed files with 75 additions and 19 deletions

View File

@@ -3,6 +3,7 @@ import React from "react";
import { EsiProvider } from '../EsiProvider';
import { EsiCharacterSelection } from './';
import { EveDataProvider } from '../EveDataProvider';
const meta: Meta<typeof EsiCharacterSelection> = {
component: EsiCharacterSelection,
@@ -15,9 +16,11 @@ type Story = StoryObj<typeof EsiCharacterSelection>;
const withEsiProvider: Decorator<Record<string, never>> = (Story) => {
return (
<EsiProvider>
<Story />
</EsiProvider>
<EveDataProvider>
<EsiProvider setSkills={console.log}>
<Story />
</EsiProvider>
</EveDataProvider>
);
}

View File

@@ -13,17 +13,9 @@ import styles from "./EsiCharacterSelection.module.css";
export const EsiCharacterSelection = () => {
const esi = React.useContext(EsiContext);
if (Object.keys(esi.characters ?? {}).length === 0) {
return <div className={styles.character}>
<button className={styles.noCharacter} onClick={esi.login}>
Login to load characters skills and fits
</button>
</div>
}
return <div className={styles.character}>
<select onChange={e => esi.changeCharacter(e.target.value)} value={esi.currentCharacter}>
{Object.entries(esi.characters).map(([id, name]) => {
{Object.entries(esi.characters).sort().map(([id, name]) => {
return <option key={id} value={id}>{name.name}</option>
})}
</select>

View File

@@ -2,6 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import React from "react";
import { EsiContext, EsiProvider } from './';
import { EveDataProvider } from '../EveDataProvider';
const meta: Meta<typeof EsiProvider> = {
component: EsiProvider,
@@ -33,10 +34,13 @@ const TestEsi = () => {
export const Default: Story = {
args: {
setSkills: (skills: Record<string, number>) => { console.log(skills); }
},
render: (args) => (
<EsiProvider {...args}>
<TestEsi />
</EsiProvider>
<EveDataProvider>
<EsiProvider {...args}>
<TestEsi />
</EsiProvider>
</EveDataProvider>
),
};

View File

@@ -6,6 +6,7 @@ import { EsiFit } from "../ShipSnapshotProvider";
import { getAccessToken } from "./EsiAccessToken";
import { getSkills } from "./EsiSkills";
import { getCharFittings } from "./EsiFittings";
import { EveDataContext } from "../EveDataProvider";
export interface EsiCharacter {
name: string;
@@ -41,6 +42,8 @@ export const EsiContext = React.createContext<Esi>({
});
export interface EsiProps {
/** Callback to call when skills are changed. */
setSkills: (skills: Record<string, number>) => void;
/** Children that can use this provider. */
children: React.ReactNode;
}
@@ -75,6 +78,8 @@ const useLocalStorage = function <T>(key: string, initialValue: T) {
* Keeps track (in local storage) of ESI characters and their refresh token.
*/
export const EsiProvider = (props: EsiProps) => {
const eveData = React.useContext(EveDataContext);
const [esi, setEsi] = React.useState<Esi>({
loaded: undefined,
characters: {},
@@ -137,10 +142,43 @@ export const EsiProvider = (props: EsiProps) => {
}, [esiPrivate.accessTokens, esiPrivate.refreshTokens]);
React.useEffect(() => {
if (!eveData.loaded) return;
const characterId = esi.currentCharacter;
if (characterId === undefined) return;
/* Skills already fetched? We won't do it again till the user reloads. */
if (esi.characters[characterId]?.skills !== undefined) return;
const currentSkills = esi.characters[characterId]?.skills;
if (currentSkills !== undefined) {
props.setSkills(currentSkills);
return;
}
if (characterId === '.all-0' || characterId === '.all-5') {
const level = characterId === '.all-0' ? 0 : 5;
const skills: Record<string, number> = {};
for (const typeId in eveData.typeIDs) {
if (eveData?.typeIDs?.[typeId].categoryID !== 16) continue;
skills[typeId] = level;
}
setEsi((oldEsi: Esi) => {
return {
...oldEsi,
characters: {
...oldEsi.characters,
[characterId]: {
...oldEsi.characters[characterId],
skills,
charFittings: [],
},
},
};
});
props.setSkills(skills);
return;
}
ensureAccessToken(characterId).then((accessToken) => {
if (accessToken === undefined) return;
@@ -148,6 +186,13 @@ export const EsiProvider = (props: EsiProps) => {
getSkills(characterId, accessToken).then((skills) => {
if (skills === undefined) return;
/* Ensure all skills are set; also those not learnt. */
for (const typeId in eveData.typeIDs) {
if (eveData?.typeIDs?.[typeId].categoryID !== 16) continue;
if (skills[typeId] !== undefined) continue;
skills[typeId] = 0;
}
setEsi((oldEsi: Esi) => {
return {
...oldEsi,
@@ -160,6 +205,8 @@ export const EsiProvider = (props: EsiProps) => {
},
};
});
props.setSkills(skills);
});
getCharFittings(characterId, accessToken).then((charFittings) => {
@@ -182,7 +229,7 @@ export const EsiProvider = (props: EsiProps) => {
/* We only update when currentCharacter changes, and ignore all others. */
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [esi.currentCharacter]);
}, [esi.currentCharacter, eveData.loaded]);
React.useEffect(() => {
if (typeof window === 'undefined') return;
@@ -265,9 +312,19 @@ export const EsiProvider = (props: EsiProps) => {
}
async function startup() {
const charactersDefault = {
'.all-0': {
name: 'Default character - All Skills L0',
},
'.all-5': {
name: 'Default character - All Skills L5',
},
...characters,
};
setEsi({
loaded: true,
characters,
characters: charactersDefault,
currentCharacter,
changeCharacter,
login,

View File

@@ -21,7 +21,7 @@ type Story = StoryObj<typeof HullListing>;
const withEsiProvider: Decorator<{ changeHull: (typeId: number) => void, changeFit: (fit: EsiFit) => void }> = (Story, context) => {
return (
<EveDataProvider>
<EsiProvider>
<EsiProvider setSkills={console.log}>
<DogmaEngineProvider>
<ShipSnapshotProvider {...context.parameters.snapshot}>
<div style={{height: "400px"}}>