Skip to content

Commit

Permalink
refactor: reworked all providers, so they are more well defined in th…
Browse files Browse the repository at this point in the history
…eir actions (#128)
  • Loading branch information
TrueBrain authored May 20, 2024
1 parent 20630b5 commit 955e884
Show file tree
Hide file tree
Showing 113 changed files with 3,092 additions and 2,604 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ module.exports = {
{
// The files listed below are part of the build process, so they will be using packages that are listed
// under devDependences and/or peerDependencies, so we need to be lenient with the import/no-extraneous-dependencies
files: [".storybook/**/*.ts", ".eslintrc.js", "rollup.config.mjs"],
files: [".storybook/**/*.ts", ".storybook/**/*.tsx", ".eslintrc.js", "rollup.config.mjs", "**/*.stories.tsx"],
rules: {
"import/no-extraneous-dependencies": ["error", { peerDependencies: true, devDependencies: true }],
},
Expand Down
26 changes: 21 additions & 5 deletions .storybook/fits.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
export const eftFit = `[Loki,Loki basic PVE]
import { EsfFit } from "@/providers";
import { InputType } from "@storybook/types";

export const eftFits = {
Loki: `[Loki,Loki basic PVE]
Caldari Navy Ballistic Control System
Caldari Navy Ballistic Control System
Caldari Navy Ballistic Control System
Expand Down Expand Up @@ -28,12 +32,15 @@ Loki Offensive - Launcher Efficiency Configuration
Loki Propulsion - Wake Limiter
Hammerhead II x1
`;
`,
};

export const hashFit =
"fit:v2:H4sIAAAAAAAACmWQMZJDMQhD+38WChBgwx32Ijtb5P5d4ONkdpLOTwhbFjKT6efx90uXk+pmJqE+I9YKEueblC0WyXpDvABrZy2OTTR8UTIPwtp4UIQApNz3C/5DkiRYbwA3RA70TowLINl+8qHMJoYBIxPgTLwnHAOb4FtKOlkuxJeSn0p95lORLwVVQ+i6n9FKI77nTbXqbntPpqgrKoWVEDXN2pNtY01tWBM8rcAjTj9OtaJ6aBV5+qHdM345owlT0hPutE6T0AEAAA==";
export const hashFits = {
Loki: "fit:v2:H4sIAAAAAAAACmWQMZJDMQhD+38WChBgwx32Ijtb5P5d4ONkdpLOTwhbFjKT6efx90uXk+pmJqE+I9YKEueblC0WyXpDvABrZy2OTTR8UTIPwtp4UIQApNz3C/5DkiRYbwA3RA70TowLINl+8qHMJoYBIxPgTLwnHAOb4FtKOlkuxJeSn0p95lORLwVVQ+i6n9FKI77nTbXqbntPpqgrKoWVEDXN2pNtY01tWBM8rcAjTj9OtaJ6aBV5+qHdM345owlT0hPutE6T0AEAAA==",
};

export const fullFits = [
null,
{
name: "Tengu",
ship_type_id: 29984,
Expand Down Expand Up @@ -148,8 +155,8 @@ export const fullFits = [
],
},
{
ship_type_id: 35833,
name: "Killmail 117621358",
ship_type_id: 35833,
description: "",
items: [
{ flag: 5, type_id: 37821, quantity: 6 },
Expand Down Expand Up @@ -195,3 +202,12 @@ export const fullFits = [
];

export const fullFit = fullFits[2];

export const fitArgType: InputType = {
control: "select",
options: fullFits.map((fit: EsfFit | null) => fit?.name ?? "(empty)"),
mapping: fullFits.reduce((acc: Record<string, EsfFit | null>, fit: EsfFit | null) => {
acc[fit?.name ?? "(empty)"] = fit;
return acc;
}, {}),
};
44 changes: 44 additions & 0 deletions .storybook/helpers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from "react";
import { StoryFn } from "@storybook/react";

import { ModalDialogAnchor } from "@/components/ModalDialog";
import { EveDataProvider } from "@/providers/EveDataProvider";
import { DogmaEngineProvider } from "@/providers/DogmaEngineProvider";
import { CurrentFitProvider, EsfFit, useCurrentFit } from "@/providers/CurrentFitProvider";
import { LocalFitsProvider } from "@/providers/LocalFitsProvider";
import { DefaultCharactersProvider, EsiCharactersProvider } from "@/providers/Characters";
import { CurrentCharacterProvider } from "@/providers/CurrentCharacterProvider";
import { StatisticsProvider } from "@/providers/StatisticsProvider";
import { FitManagerProvider } from "@/providers/FitManagerProvider";

export const withDecoratorFull = (Story: StoryFn) => (
<EveDataProvider>
<DogmaEngineProvider>
<CurrentFitProvider>
<LocalFitsProvider>
<DefaultCharactersProvider>
<EsiCharactersProvider>
<CurrentCharacterProvider>
<StatisticsProvider>
<FitManagerProvider>
<ModalDialogAnchor />
<Story />
</FitManagerProvider>
</StatisticsProvider>
</CurrentCharacterProvider>
</EsiCharactersProvider>
</DefaultCharactersProvider>
</LocalFitsProvider>
</CurrentFitProvider>
</DogmaEngineProvider>
</EveDataProvider>
);

export const useFitSelection = (fit: EsfFit | null) => {
const currentFit = useCurrentFit();
const setFit = currentFit.setFit;

React.useEffect(() => {
setFit(fit);
}, [setFit, fit]);
};
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@
"@storybook/addon-links": "^8",
"@storybook/addon-webpack5-compiler-babel": "^3",
"@storybook/blocks": "^8",
"@storybook/preview-api": "^8",
"@storybook/react": "^8",
"@storybook/react-webpack5": "^8",
"@storybook/test": "^8",
"@storybook/types": "^8",
"@types/react": "^18",
"@types/react-dom": "^18",
"@typescript-eslint/eslint-plugin": "^7",
Expand Down
48 changes: 18 additions & 30 deletions src/components/CalculationDetail/CalculationDetail.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,35 @@
import type { Decorator, Meta, StoryObj } from "@storybook/react";
import type { Meta, StoryObj } from "@storybook/react";
import React from "react";

import { fullFit } from "../../../.storybook/fits";
import { fitArgType } from "../../../.storybook/fits";
import { useFitSelection, withDecoratorFull } from "../../../.storybook/helpers";

import { EsfFit } from "@/providers/CurrentFitProvider";

import { DogmaEngineProvider } from "@/providers/DogmaEngineProvider";
import { EsiProvider } from "@/providers/EsiProvider";
import { EveDataProvider } from "@/providers/EveDataProvider";
import { ShipSnapshotProvider } from "@/providers/ShipSnapshotProvider";
import { CalculationDetail } from "./";

const meta: Meta<typeof CalculationDetail> = {
type StoryProps = React.ComponentProps<typeof CalculationDetail> & { fit: EsfFit | null };

const meta: Meta<StoryProps> = {
component: CalculationDetail,
tags: ["autodocs"],
title: "Component/CalculationDetail",
};

export default meta;
type Story = StoryObj<typeof CalculationDetail>;

const useShipSnapshotProvider: Decorator<{
source: "Ship" | "Char" | "Structure" | "Target" | { Item?: number; Cargo?: number };
}> = (Story, context) => {
return (
<EveDataProvider>
<DogmaEngineProvider>
<ShipSnapshotProvider {...context.parameters.snapshot}>
<EsiProvider>
<Story {...context.args} />
</EsiProvider>
</ShipSnapshotProvider>
</DogmaEngineProvider>
</EveDataProvider>
);
};
type Story = StoryObj<StoryProps>;

export const Default: Story = {
argTypes: {
fit: fitArgType,
},
args: {
fit: null,
source: "Ship",
},
decorators: [useShipSnapshotProvider],
parameters: {
snapshot: {
initialFit: fullFit,
},
decorators: [withDecoratorFull],
render: ({ fit, ...args }) => {
useFitSelection(fit);

return <CalculationDetail {...args} />;
},
};
57 changes: 29 additions & 28 deletions src/components/CalculationDetail/CalculationDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import clsx from "clsx";
import React from "react";

import { EveDataContext } from "@/providers/EveDataProvider";
import {
ShipSnapshotContext,
ShipSnapshotItemAttribute,
ShipSnapshotItemAttributeEffect,
} from "@/providers/ShipSnapshotProvider";
import { Icon } from "@/components/Icon";
import { useEveData } from "@/providers/EveDataProvider";
import { StatisticsItemAttribute, StatisticsItemAttributeEffect, useStatistics } from "@/providers/StatisticsProvider";

import styles from "./CalculationDetail.module.css";

Expand Down Expand Up @@ -46,11 +42,13 @@ function stateToInteger(state: string): number {
}
}

const Effect = (props: { effect: ShipSnapshotItemAttributeEffect }) => {
const eveData = React.useContext(EveDataContext);
const shipSnapshot = React.useContext(ShipSnapshotContext);
const Effect = (props: { effect: StatisticsItemAttributeEffect }) => {
const eveData = useEveData();
const statistics = useStatistics();

const eveAttribute = eveData.dogmaAttributes?.[props.effect.source_attribute_id];
if (eveData === null || statistics === null) return <></>;

const eveAttribute = eveData.dogmaAttributes[props.effect.source_attribute_id];

let sourceName = "Unknown";
let attribute = undefined;
Expand All @@ -59,22 +57,22 @@ const Effect = (props: { effect: ShipSnapshotItemAttributeEffect }) => {
switch (props.effect.source) {
case "Ship":
sourceName = "Ship";
attribute = shipSnapshot.hull?.attributes.get(props.effect.source_attribute_id);
attribute = statistics.hull.attributes.get(props.effect.source_attribute_id);
break;

case "Char":
sourceName = "Character";
attribute = shipSnapshot.char?.attributes.get(props.effect.source_attribute_id);
attribute = statistics.char.attributes.get(props.effect.source_attribute_id);
break;

case "Structure":
sourceName = "Structure";
attribute = shipSnapshot.structure?.attributes.get(props.effect.source_attribute_id);
attribute = statistics.structure.attributes.get(props.effect.source_attribute_id);
break;

case "Target":
sourceName = "Target";
attribute = shipSnapshot.target?.attributes.get(props.effect.source_attribute_id);
attribute = statistics.target.attributes.get(props.effect.source_attribute_id);
break;

default:
Expand All @@ -83,13 +81,13 @@ const Effect = (props: { effect: ShipSnapshotItemAttributeEffect }) => {

/* Lookup the source of the effect. */
if (props.effect.source.Item !== undefined) {
item = shipSnapshot.items?.[props.effect.source.Item];
item = statistics.items[props.effect.source.Item];
sourceType = "Item";
} else if (props.effect.source.Skill !== undefined) {
item = shipSnapshot.skills?.[props.effect.source.Skill];
item = statistics.skills[props.effect.source.Skill];
sourceType = "Skill";
} else if (props.effect.source.Charge !== undefined) {
item = shipSnapshot.items?.[props.effect.source.Charge].charge;
item = statistics.items[props.effect.source.Charge].charge;
sourceType = "Charge";
}

Expand Down Expand Up @@ -125,11 +123,13 @@ const Effect = (props: { effect: ShipSnapshotItemAttributeEffect }) => {
);
};

const CalculationDetailMeta = (props: { attributeId: number; attribute: ShipSnapshotItemAttribute }) => {
const CalculationDetailMeta = (props: { attributeId: number; attribute: StatisticsItemAttribute }) => {
const [expanded, setExpanded] = React.useState(false);
const eveData = React.useContext(EveDataContext);
const eveData = useEveData();

if (eveData === null) return <></>;

const eveAttribute = eveData.dogmaAttributes?.[props.attributeId];
const eveAttribute = eveData.dogmaAttributes[props.attributeId];
const sortedEffects = Object.values(props.attribute.effects).sort((a, b) => {
const aIndex = Object.keys(EffectOperatorOrder).indexOf(a.operator);
const bIndex = Object.keys(EffectOperatorOrder).indexOf(b.operator);
Expand Down Expand Up @@ -172,25 +172,26 @@ const CalculationDetailMeta = (props: { attributeId: number; attribute: ShipSnap
export const CalculationDetail = (props: {
source: "Ship" | "Char" | "Structure" | "Target" | { Item?: number; Charge?: number };
}) => {
const shipSnapshot = React.useContext(ShipSnapshotContext);
const statistics = useStatistics();
if (statistics === null) return <></>;

let attributes: [number, ShipSnapshotItemAttribute][] = [];
let attributes: [number, StatisticsItemAttribute][] = [];

if (props.source === "Ship") {
attributes = [...(shipSnapshot.hull?.attributes.entries() || [])];
attributes = [...(statistics.hull.attributes.entries() ?? [])];
} else if (props.source === "Char") {
attributes = [...(shipSnapshot.char?.attributes.entries() || [])];
attributes = [...(statistics.char.attributes.entries() ?? [])];
} else if (props.source === "Structure") {
attributes = [...(shipSnapshot.structure?.attributes.entries() || [])];
attributes = [...(statistics.structure.attributes.entries() ?? [])];
} else if (props.source === "Target") {
attributes = [...(shipSnapshot.target?.attributes.entries() || [])];
attributes = [...(statistics.target.attributes.entries() ?? [])];
} else if (props.source.Item !== undefined) {
const item = shipSnapshot.items?.[props.source.Item];
const item = statistics.items[props.source.Item];
if (item !== undefined) {
attributes = [...item.attributes.entries()];
}
} else if (props.source.Charge !== undefined) {
const item = shipSnapshot.items?.[props.source.Charge].charge;
const item = statistics.items[props.source.Charge].charge;
if (item !== undefined) {
attributes = [...item.attributes.entries()];
}
Expand Down
30 changes: 30 additions & 0 deletions src/components/CharacterSelection/CharacterSelection.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { Meta, StoryObj } from "@storybook/react";
import React from "react";

import { EveDataProvider } from "@/providers/EveDataProvider";
import { DefaultCharactersProvider, EsiCharactersProvider } from "@/providers/Characters";
import { CurrentCharacterProvider } from "@/providers/CurrentCharacterProvider";

import { CharacterSelection } from "./";

const meta: Meta<typeof CharacterSelection> = {
component: CharacterSelection,
tags: ["autodocs"],
};

export default meta;
type Story = StoryObj<typeof CharacterSelection>;

export const Default: Story = {
render: () => (
<EveDataProvider>
<DefaultCharactersProvider>
<EsiCharactersProvider>
<CurrentCharacterProvider>
<CharacterSelection />
</CurrentCharacterProvider>
</EsiCharactersProvider>
</DefaultCharactersProvider>
</EveDataProvider>
),
};
46 changes: 46 additions & 0 deletions src/components/CharacterSelection/CharacterSelection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from "react";

import { useCharacters, useEsiCharacters } from "@/providers/Characters";
import { useCurrentCharacter } from "@/providers/CurrentCharacterProvider";

import styles from "./CharacterSelection.module.css";

/**
* Character selection for EsiProvider.
*
* It shows both a dropdown for all the characters that the EsiProvider knows,
* and a button to add another character.
*/
export const CharacterSelection = () => {
const characters = useCharacters();
const currentCharacter = useCurrentCharacter();
const esiCharactersProvider = useEsiCharacters();

const isExpired = currentCharacter.character?.expired ?? false;

return (
<div className={styles.character}>
<select onChange={(e) => currentCharacter.setCharacterId(e.target.value)} value={currentCharacter.characterId}>
{Object.entries(characters)
.sort()
.map(([id, name]) => {
return (
<option key={id} value={id}>
{name.name} {name.expired ? "(access expired)" : ""}
</option>
);
})}
</select>
{isExpired && (
<button onClick={esiCharactersProvider.refresh} title="Refresh access">
R
</button>
)}
{!isExpired && (
<button onClick={esiCharactersProvider.login} title="Add another character">
+
</button>
)}
</div>
);
};
1 change: 1 addition & 0 deletions src/components/CharacterSelection/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { CharacterSelection } from "./CharacterSelection";
Loading

0 comments on commit 955e884

Please sign in to comment.