Skip to content

Commit

Permalink
chore: improve handling of ESF encoded fits (#147)
Browse files Browse the repository at this point in the history
  • Loading branch information
TrueBrain authored Jun 8, 2024
1 parent cc97781 commit edd8e45
Show file tree
Hide file tree
Showing 21 changed files with 78 additions and 79 deletions.
14 changes: 7 additions & 7 deletions .storybook/fits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,17 @@ Nanite Repair Paste x150
`,
};

export const hashFits = {
export const esfEncodedFits = {
"Tornado v1":
"fit:v1:H4sIAAAAAAAACj3OsRVDIQwEwZwqXMAFnE7wUWw34gIM/Wd+ClA2m62LHZ9zfq/3d++z0UgMBthoF7rwiwBjGNisl1gyMJ6VUulBeDC1SnGlXmLJSir51YBxhYOSRdaSDJruLRyizMEWlpw9KYhz5d8f4N5qF/QAAAA=",
"v1:H4sIAAAAAAAACj3OsRVDIQwEwZwqXMAFnE7wUWw34gIM/Wd+ClA2m62LHZ9zfq/3d++z0UgMBthoF7rwiwBjGNisl1gyMJ6VUulBeDC1SnGlXmLJSir51YBxhYOSRdaSDJruLRyizMEWlpw9KYhz5d8f4N5qF/QAAAA=",
"Loki v2":
"fit:v2:H4sIAAAAAAAACmWQMZJDMQhD+38WChBgwx32Ijtb5P5d4ONkdpLOTwhbFjKT6efx90uXk+pmJqE+I9YKEueblC0WyXpDvABrZy2OTTR8UTIPwtp4UIQApNz3C/5DkiRYbwA3RA70TowLINl+8qHMJoYBIxPgTLwnHAOb4FtKOlkuxJeSn0p95lORLwVVQ+i6n9FKI77nTbXqbntPpqgrKoWVEDXN2pNtY01tWBM8rcAjTj9OtaJ6aBV5+qHdM345owlT0hPutE6T0AEAAA==",
"v2:H4sIAAAAAAAACmWQMZJDMQhD+38WChBgwx32Ijtb5P5d4ONkdpLOTwhbFjKT6efx90uXk+pmJqE+I9YKEueblC0WyXpDvABrZy2OTTR8UTIPwtp4UIQApNz3C/5DkiRYbwA3RA70TowLINl+8qHMJoYBIxPgTLwnHAOb4FtKOlkuxJeSn0p95lORLwVVQ+i6n9FKI77nTbXqbntPpqgrKoWVEDXN2pNtY01tWBM8rcAjTj9OtaJ6aBV5+qHdM345owlT0hPutE6T0AEAAA==",
"Loki v3":
"fit:v3:H4sIAAAAAAAACoWSwVLDMAxE7/0WHSzJsuUjNw5woV8ATSb1QAnTNjD8PTIQ0pJ4cpTf7s6srNM+vwGllBzc9c8ZNoe+GV5aGz4AgYgSws3unN/bK0TL6L5t8nAwIyZyXKFUqKYKZUvWWrAHjOJchQqgR08VGsAjUc0bi9fpf3qbu33Zg0SUkZH3qlcCWhPwmsCvCWRNEICdclgsYOVQ4nI5BdIofz52F8EPubPyjNHPvAVRQWG20YLYkOjMtR2etp+nc1tOxEvg2YlMAjKBJKkLuCTMf2wS+CKg6ZSaY/9aNmdlBXizezx2PTBHuyf8nUhDUEBxI7VtBMBwMeo0UojJAkYxslp0cm58+P6kn4cvMOSO9GcDAAA=",
"Killmail (structure)": "fit:killmail:117621358/efe9a3e74e6e0ef983846a82a234211090b94fd5",
"Killmail (ship)": "fit:killmail:117923593/4863ca35a23b480dc9feead5e87f2a9fbf1b1102",
"v3:H4sIAAAAAAAACoWSwVLDMAxE7/0WHSzJsuUjNw5woV8ATSb1QAnTNjD8PTIQ0pJ4cpTf7s6srNM+vwGllBzc9c8ZNoe+GV5aGz4AgYgSws3unN/bK0TL6L5t8nAwIyZyXKFUqKYKZUvWWrAHjOJchQqgR08VGsAjUc0bi9fpf3qbu33Zg0SUkZH3qlcCWhPwmsCvCWRNEICdclgsYOVQ4nI5BdIofz52F8EPubPyjNHPvAVRQWG20YLYkOjMtR2etp+nc1tOxEvg2YlMAjKBJKkLuCTMf2wS+CKg6ZSaY/9aNmdlBXizezx2PTBHuyf8nUhDUEBxI7VtBMBwMeo0UojJAkYxslp0cm58+P6kn4cvMOSO9GcDAAA=",
"Killmail (structure)": "killmail:117621358/efe9a3e74e6e0ef983846a82a234211090b94fd5",
"Killmail (ship)": "killmail:117923593/4863ca35a23b480dc9feead5e87f2a9fbf1b1102",
"Buzzard (eft)":
"fit:eft:H4sIAAAAAAAA/4VSy3IaMRC86yvmA5Iq8C42Pq7XdkIVYAI4l1QOg1asp7wrbQaJ19dnJCBUcclJU+rumZ7Hr6dwPCJXX6BokRlKZz27BpZuZ/i3mpBmB0XYU0PIB5jFbyGxgZGaonVrWsnHyHrDFhtYeA7ahwj/H1eDyRR+BKxgTK8wNxvPSNZUkKrukLuKaWvU3DSkoRD94RiTjdTy0JmvL/Biq8BkayiRawcLjdYaVvGFQv8JtCFPTmJmPERdQuZoa7MmW0XlP0ilpmbsVgbGGKz+OJUq3dawh7duA2Xj8DOKns2WtDmhbYfanxqsSKdy09A0tCZxohYtNg18Y9ySP4hNIZN3DO9dzVilFCfKd+mINx16kin9NI3TUfDWeWrp3LQquBXprEFvNrDvq6fAcVpjV8t4SmIdyMP+rq9kHCvxIZTyQ5Z6meiVk+fqGVusBSnYi1cdy05N4PT4neNPkWfqlUk4qbs1SsvXKpm6qZupifFxxZqxi/76A3VTNosnQV7SyNhcsFW0eD6pK+dyY7Lc5FUtzmMpvA/WtMZ6eLcR6qul7LIRh7eanloydd0VuaR/UMNer5dHrQT9ewlyiYYPmUSP6U/A/D5Gd4PYZ1L0H8XZMEVCzPKUJUrUXxFh1SJBAwAA",
"eft:H4sIAAAAAAAA/4VSy3IaMRC86yvmA5Iq8C42Pq7XdkIVYAI4l1QOg1asp7wrbQaJ19dnJCBUcclJU+rumZ7Hr6dwPCJXX6BokRlKZz27BpZuZ/i3mpBmB0XYU0PIB5jFbyGxgZGaonVrWsnHyHrDFhtYeA7ahwj/H1eDyRR+BKxgTK8wNxvPSNZUkKrukLuKaWvU3DSkoRD94RiTjdTy0JmvL/Biq8BkayiRawcLjdYaVvGFQv8JtCFPTmJmPERdQuZoa7MmW0XlP0ilpmbsVgbGGKz+OJUq3dawh7duA2Xj8DOKns2WtDmhbYfanxqsSKdy09A0tCZxohYtNg18Y9ySP4hNIZN3DO9dzVilFCfKd+mINx16kin9NI3TUfDWeWrp3LQquBXprEFvNrDvq6fAcVpjV8t4SmIdyMP+rq9kHCvxIZTyQ5Z6meiVk+fqGVusBSnYi1cdy05N4PT4neNPkWfqlUk4qbs1SsvXKpm6qZupifFxxZqxi/76A3VTNosnQV7SyNhcsFW0eD6pK+dyY7Lc5FUtzmMpvA/WtMZ6eLcR6qul7LIRh7eanloydd0VuaR/UMNer5dHrQT9ewlyiYYPmUSP6U/A/D5Gd4PYZ1L0H8XZMEVCzPKUJUrUXxFh1SJBAwAA",
};

export const esiFits = {
Expand Down
4 changes: 2 additions & 2 deletions src/components/FitButtonBar/ShareButton.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from "react";

import { useClipboard } from "@/hooks/Clipboard";
import { useExportEveShipFitHash } from "@/hooks/ExportEveShipFitHash";
import { useExportEveShipFit } from "@/hooks/ExportEveShipFit";

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

export const ShareButton = () => {
const link = useExportEveShipFitHash();
const link = useExportEveShipFit();
const { copy, copied } = useClipboard();

const onClick = React.useCallback(
Expand Down
4 changes: 2 additions & 2 deletions src/components/ShipFit/FitLink.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from "react";

import { useClipboard } from "@/hooks/Clipboard";
import { useExportEveShipFitHash } from "@/hooks/ExportEveShipFitHash";
import { useExportEveShipFit } from "@/hooks/ExportEveShipFit";

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

export const FitLink = (props: { isPreview?: boolean }) => {
const link = useExportEveShipFitHash();
const link = useExportEveShipFit();
const { copy, copied } = useClipboard();

const isRemote = typeof window !== "undefined";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import { useFitSelection, withDecoratorFull } from "../../../.storybook/helpers"

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

import { ExportEveShipFitHash } from "./ExportEveShipFitHash";
import { ExportEveShipFit } from "./ExportEveShipFit";

type StoryProps = React.ComponentProps<typeof ExportEveShipFitHash> & { fit: EsfFit | null };
type StoryProps = React.ComponentProps<typeof ExportEveShipFit> & { fit: EsfFit | null };

const meta: Meta<StoryProps> = {
component: ExportEveShipFitHash,
component: ExportEveShipFit,
tags: ["autodocs"],
};

Expand All @@ -29,6 +29,6 @@ export const Default: Story = {
render: ({ fit, ...args }) => {
useFitSelection(fit);

return <ExportEveShipFitHash {...args} />;
return <ExportEveShipFit {...args} />;
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -37,44 +37,44 @@ async function encodeFit(fit: EsfFit): Promise<string> {
/**
* Returns a link to https://eveship.fit that contains the current fit.
*
* `hashOnly` controls whether to only show the hash, or the full link.
* `fullLink` controls whether to only show the esfEncoded part, or the full link.
*/
export function useExportEveShipFitHash(hashOnly?: boolean) {
export function useExportEveShipFit(fullLink?: boolean) {
const currentFit = useCurrentFit();

const [fitHash, setFitHash] = React.useState<string | null>(null);
const [esfEncoded, setEsfEncoded] = React.useState<string | null>(null);

React.useEffect(() => {
async function createHash(fit: EsfFit | null) {
async function createEsfEncoded(fit: EsfFit | null) {
if (fit === null) {
setFitHash(null);
setEsfEncoded(null);
return;
}

const newFitHash = await encodeFit(fit);
setFitHash((hashOnly ? "" : "https://eveship.fit/") + `#fit:${newFitHash}`);
const newEsfEncoded = await encodeFit(fit);
setEsfEncoded((fullLink || fullLink === undefined ? "https://eveship.fit/?fit=" : "") + newEsfEncoded);
}

createHash(currentFit.currentFit);
}, [currentFit.currentFit, hashOnly]);
createEsfEncoded(currentFit.currentFit);
}, [currentFit.currentFit, fullLink]);

return fitHash;
return esfEncoded;
}

export interface ExportEveShipFitHashProps {
/** Whether to only show the hash, not the full link. */
hashOnly?: boolean;
export interface ExportEveShipFitProps {
/** Whether to only show the esfEncoded part, or the full link. */
fullLink?: boolean;
}

/**
* `useExportEveShipFitHash` converts the current fit into a link to https://eveship.fit.
* `useExportEveShipFit` converts the current fit into a link to https://eveship.fit.
*
* Note: do not use this React component itself, but the `useExportEveShipFitHash` React hook instead.
* Note: do not use this React component itself, but the `useExportEveShipFit` React hook instead.
*/
export const ExportEveShipFitHash = (props: ExportEveShipFitHashProps) => {
const exportEveShipFitHash = useExportEveShipFitHash(props.hashOnly);
export const ExportEveShipFit = (props: ExportEveShipFitProps) => {
const exportEveShipFit = useExportEveShipFit(props.fullLink);

if (exportEveShipFitHash === null) return <></>;
if (exportEveShipFit === null) return <></>;

return <pre>{exportEveShipFitHash}</pre>;
return <pre>{exportEveShipFit}</pre>;
};
1 change: 1 addition & 0 deletions src/hooks/ExportEveShipFit/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useExportEveShipFit } from "./ExportEveShipFit";
1 change: 0 additions & 1 deletion src/hooks/ExportEveShipFitHash/index.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/hooks/ImportEsiFitting/ImportEsiFitting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";

import { EsfCargo, EsfDrone, EsfFit, EsfModule } from "@/providers/CurrentFitProvider";
import { useEveData } from "@/providers/EveDataProvider";
import { esiFlagToEsfSlot } from "@/hooks/ImportEveShipFitHash";
import { esiFlagToEsfSlot } from "@/hooks/ImportEveShipFit";
import { useCleanImportFit } from "@/hooks/CleanImportFit";

export interface EsiFit {
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { esiFlagToEsfSlot } from "./EsiFlags";
export function useFetchKillMail() {
const eveData = useEveData();

return async (killMailHash: string): Promise<EsfFit | null> => {
return async (killMailIdHash: string): Promise<EsfFit | null> => {
if (eveData === null) return null;

/* The hash is in the format "id/hash". */
const [killmailId, killmailHash] = killMailHash.split("/", 2);
/* The string is in the format "id/hash". */
const [killmailId, killmailHash] = killMailIdHash.split("/", 2);

/* Fetch the killmail from ESI. */
const response = await fetch(`https://esi.evetech.net/v1/killmails/${killmailId}/${killmailHash}/`);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export async function decompress(base64compressedBytes: string): Promise<string> {
const stream = new Blob([Uint8Array.from(atob(base64compressedBytes), (c) => c.charCodeAt(0))]).stream();
const stream = new Blob([
Uint8Array.from(atob(base64compressedBytes.replace(/ /g, "+")), (c) => c.charCodeAt(0)),
]).stream();
const decompressedStream = stream.pipeThrough(new DecompressionStream("gzip"));
const reader = decompressedStream.getReader();

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import type { Meta, StoryObj } from "@storybook/react";
import React from "react";

import { hashFits } from "../../../.storybook/fits";
import { esfEncodedFits } from "../../../.storybook/fits";

import { EveDataProvider } from "@/providers/EveDataProvider";

import { ImportEveShipFitHash } from "./ImportEveShipFitHash";
import { ImportEveShipFit } from "./ImportEveShipFit";

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

export default meta;
type Story = StoryObj<typeof ImportEveShipFitHash>;
type Story = StoryObj<typeof ImportEveShipFit>;

export const Default: Story = {
argTypes: {
fitHash: {
esfEncoded: {
control: "select",
options: Object.keys(hashFits),
mapping: hashFits,
options: Object.keys(esfEncodedFits),
mapping: esfEncodedFits,
},
},
decorators: [
Expand All @@ -30,5 +30,5 @@ export const Default: Story = {
</EveDataProvider>
),
],
render: (args) => <ImportEveShipFitHash {...args} />,
render: (args) => <ImportEveShipFit {...args} />,
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,19 @@ import { useDecodeEft } from "./DecodeEft";
import { useFetchKillMail } from "./DecodeKillMail";

/**
* Convert a hash from window.location.hash to an ESI fit.
* Convert a encoded ESF fit to an ESF fit.
*/
export function useImportEveShipFitHash() {
export function useImportEveShipFit() {
const fetchKillMail = useFetchKillMail();
const decodeEft = useDecodeEft();
const cleanImportFit = useCleanImportFit();

return async (fitHash: string): Promise<EsfFit | undefined | null> => {
const fitPrefix = fitHash.split(":")[0];
const fitVersion = fitHash.split(":")[1];
const fitEncoded = fitHash.split(":")[2];

if (fitPrefix !== "fit") return null;
return async (esfEncoded: string): Promise<EsfFit | undefined | null> => {
const esfType = esfEncoded.split(":")[0];
const fitEncoded = esfEncoded.split(":")[1];

let fit = undefined;
switch (fitVersion) {
switch (esfType) {
case "v1":
fit = await decodeEsfFitV1(fitEncoded);
break;
Expand All @@ -51,38 +48,38 @@ export function useImportEveShipFitHash() {
};
}

export interface ImportEveShipFitHashProps {
/** The hash of the fit string. */
fitHash: string;
export interface ImportEveShipFitProps {
/** The encoded ESF value. */
esfEncoded: string;
}

/**
* `importEveShipFitHash` converts a hash from window.location.hash to an ESF fit.
* `importEveShipFit` converts a encoded ESF to an ESF fit.
*
* Note: do not use this React component itself, but the importEveShipFitHash() function instead.
* Note: do not use this React component itself, but the importEveShipFit() function instead.
*/
export const ImportEveShipFitHash = (props: ImportEveShipFitHashProps) => {
const importEveShipFitHash = useImportEveShipFitHash();
export const ImportEveShipFit = (props: ImportEveShipFitProps) => {
const importEveShipFit = useImportEveShipFit();
const [fit, setFit] = React.useState<EsfFit | null | undefined>(undefined);

const importEveShipFitHashRef = React.useRef(importEveShipFitHash);
importEveShipFitHashRef.current = importEveShipFitHash;
const importEveShipFitRef = React.useRef(importEveShipFit);
importEveShipFitRef.current = importEveShipFit;

React.useEffect(() => {
async function getFit(fitHash: string) {
setFit(await importEveShipFitHashRef.current(fitHash));
async function getFit(esfEncoded: string) {
setFit(await importEveShipFitRef.current(esfEncoded));
}

getFit(props.fitHash);
}, [props.fitHash]);
getFit(props.esfEncoded);
}, [props.esfEncoded]);

if (props.fitHash === undefined) {
return <div>Select a fit hash.</div>;
if (props.esfEncoded === undefined) {
return <div>Select a encoded ESF fit.</div>;
}

return (
<div>
Hash: <pre>{props.fitHash}</pre>
Encoded: <pre>{props.esfEncoded}</pre>
Fit: <pre>{JSON.stringify(fit, null, 2)}</pre>
</div>
);
Expand Down
2 changes: 2 additions & 0 deletions src/hooks/ImportEveShipFit/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { useImportEveShipFit } from "./ImportEveShipFit";
export { esiFlagToEsfSlot } from "./EsiFlags";
2 changes: 0 additions & 2 deletions src/hooks/ImportEveShipFitHash/index.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ export * from "./components/TreeListing";
export * from "./hooks/CleanImportFit";
export * from "./hooks/Clipboard";
export * from "./hooks/ExportEft";
export * from "./hooks/ExportEveShipFitHash";
export * from "./hooks/ExportEveShipFit";
export * from "./hooks/ImportEft";
export * from "./hooks/ImportEsiFitting";
export * from "./hooks/ImportEveShipFitHash";
export * from "./hooks/ImportEveShipFit";
export * from "./hooks/LocalStorage";
export * from "./providers/Characters";
export * from "./providers/CurrentCharacterProvider";
Expand Down
2 changes: 1 addition & 1 deletion src/providers/LocalFitsProvider/ConvertV2.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { esiFlagToEsfSlot } from "@/hooks/ImportEveShipFitHash";
import { esiFlagToEsfSlot } from "@/hooks/ImportEveShipFit";
import { EsfCargo, EsfDrone, EsfFit, EsfModule } from "../CurrentFitProvider/CurrentFitProvider";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down

0 comments on commit edd8e45

Please sign in to comment.