Skip to content

Commit

Permalink
feat: ability to load characters and their skills from ESI
Browse files Browse the repository at this point in the history
This can track multiple characters, and tries to make as few
as possible ESI calls.

In the localstorage, we keep track of all the characters and
their refresh-tokens. Access-tokens and skills are not stored
in the localstorage, which means that every reload, this
information is fetched from ESI again.
  • Loading branch information
TrueBrain committed Nov 26, 2023
1 parent 012a259 commit 17b8bab
Show file tree
Hide file tree
Showing 11 changed files with 458 additions and 1 deletion.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"license": "MIT",
"dependencies": {
"@eveshipfit/dogma-engine": "^2.2.1",
"clsx": "^2.0.0"
"clsx": "^2.0.0",
"jwt-decode": "^4.0.0"
},
"devDependencies": {
"@babel/preset-env": "^7.23.3",
Expand Down
25 changes: 25 additions & 0 deletions src/EsiCharacterSelection/EsiCharacterSelection.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.character {
width: 100%;
}

.character > select {
background-color: #1d1d1d;
color: #c5c5c5;
height: 24px;
width: calc(100% - 20px);
}

.character > button {
background-color: #1d1d1d;
color: #c5c5c5;
cursor: pointer;
height: 24px;
text-align: center;
width: 20px;
}

.character > button.noCharacter {
text-align: left;
padding-left: 5px;
width: 100%;
}
28 changes: 28 additions & 0 deletions src/EsiCharacterSelection/EsiCharacterSelection.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Decorator, Meta, StoryObj } from '@storybook/react';
import React from "react";

import { EsiProvider } from '../EsiProvider';
import { EsiCharacterSelection } from './';

const meta: Meta<typeof EsiCharacterSelection> = {
component: EsiCharacterSelection,
tags: ['autodocs'],
title: 'Component/EsiCharacterSelection',
};

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

const withEsiProvider: Decorator<Record<string, never>> = (Story) => {
return (
<EsiProvider>
<Story />
</EsiProvider>
);
}

export const Default: Story = {
args: {
},
decorators: [withEsiProvider],
};
34 changes: 34 additions & 0 deletions src/EsiCharacterSelection/EsiCharacterSelection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from "react";

import { EsiContext } from "../EsiProvider";

import styles from "./EsiCharacterSelection.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 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]) => {
return <option key={id} value={id}>{name.name}</option>
})}
</select>
<button onClick={esi.login} title="Add another character">
+
</button>
</div>
};
1 change: 1 addition & 0 deletions src/EsiCharacterSelection/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { EsiCharacterSelection } from "./EsiCharacterSelection";
20 changes: 20 additions & 0 deletions src/EsiProvider/EsiAccessToken.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export async function getAccessToken(refreshToken: string): Promise<string | undefined> {
let response;
try {
response = await fetch('https://esi.eveship.fit/', {
method: 'POST',
body: JSON.stringify({
refresh_token: refreshToken,
}),
});
} catch (e) {
return undefined;
}

if (response.status !== 201) {
return undefined;
}

const data = await response.json();
return data.access_token;
};
42 changes: 42 additions & 0 deletions src/EsiProvider/EsiProvider.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { Meta, StoryObj } from '@storybook/react';
import React from "react";

import { EsiContext, EsiProvider } from './';

const meta: Meta<typeof EsiProvider> = {
component: EsiProvider,
tags: ['autodocs'],
title: 'Provider/EsiProvider',
};

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

const TestEsi = () => {
const esi = React.useContext(EsiContext);

if (!esi.loaded) {
return (
<div>
Esi: loading<br/>
</div>
);
}

return (
<div>
Esi: loaded<br/>
<pre>{JSON.stringify(esi, null, 2)}</pre>
</div>
);
}

export const Default: Story = {
args: {
},
render: (args) => (
<EsiProvider {...args}>
<TestEsi />
</EsiProvider>
),
};
Loading

0 comments on commit 17b8bab

Please sign in to comment.