convert to typescript

This commit is contained in:
hzrd149 2023-02-07 17:04:18 -06:00
parent 27260a9f9b
commit 40352f77c7
41 changed files with 470 additions and 191 deletions

View File

@ -8,6 +8,6 @@
</head>
<body>
<div id="root"></div>
<script type="module" src="src/index.jsx"></script>
<script type="module" src="src/index.tsx"></script>
</body>
</html>

View File

@ -5,14 +5,9 @@
"license": "MIT",
"scripts": {
"start": "vite serve",
"build": "vite build",
"build": "tsc && vite build",
"format": "prettier --ignore-path .gitignore -w ."
},
"devDependencies": {
"prettier": "^2.8.1",
"vite": "^4.0.2",
"vite-plugin-pwa": "^0.14.0"
},
"dependencies": {
"@chakra-ui/icons": "^2.0.14",
"@chakra-ui/react": "^2.4.4",
@ -29,5 +24,14 @@
"react-router-dom": "^6.5.0",
"react-use": "^17.4.0",
"rxjs": "^7.8.0"
},
"devDependencies": {
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"@vitejs/plugin-react": "^3.0.0",
"prettier": "^2.8.1",
"typescript": "^4.9.4",
"vite": "^4.0.2",
"vite-plugin-pwa": "^0.14.0"
}
}

View File

@ -1,3 +1,4 @@
import React, { useState } from "react";
import {
Button,
Modal,
@ -10,7 +11,6 @@ import {
Table,
Thead,
Tbody,
Tfoot,
Tr,
Th,
Td,
@ -19,11 +19,11 @@ import {
useDisclosure,
Badge,
} from "@chakra-ui/react";
import React, { useState } from "react";
import { useInterval } from "react-use";
import { Relay } from "../services/relays";
import relayPool from "../services/relays/relay-pool";
const getRelayStatusText = (relay) => {
const getRelayStatusText = (relay: Relay) => {
if (relay.connecting) return "Connecting...";
if (relay.connected) return "Connected";
if (relay.closing) return "Disconnecting...";
@ -32,7 +32,7 @@ const getRelayStatusText = (relay) => {
export const ConnectedRelays = () => {
const { isOpen, onOpen, onClose } = useDisclosure();
const [relays, setRelays] = useState([]);
const [relays, setRelays] = useState<Relay[]>([]);
useInterval(() => {
setRelays(relayPool.getRelays());

View File

@ -1,5 +1,8 @@
import React from "react";
import { ErrorBoundary as ErrorBoundaryHelper } from "react-error-boundary";
import {
ErrorBoundary as ErrorBoundaryHelper,
FallbackProps,
} from "react-error-boundary";
import {
Alert,
AlertIcon,
@ -7,7 +10,7 @@ import {
AlertDescription,
} from "@chakra-ui/react";
export function ErrorFallback({ error, resetErrorBoundary }) {
export function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
return (
<Alert status="error">
<AlertIcon />
@ -17,7 +20,12 @@ export function ErrorFallback({ error, resetErrorBoundary }) {
);
}
export const ErrorBoundary = ({ children, ...props }) => (
export const ErrorBoundary = ({
children,
...props
}: {
children: React.ReactNode;
}) => (
<ErrorBoundaryHelper FallbackComponent={ErrorFallback} {...props}>
{children}
</ErrorBoundaryHelper>

View File

@ -14,7 +14,7 @@ import {
import { useAsync } from "react-use";
import { getRelaysEventWasSeen } from "../services/events-seen";
export const EventSeenOn = ({ id }) => {
export const EventSeenOn = ({ id }: { id: string }) => {
const { value } = useAsync(() => getRelaysEventWasSeen(id), [id]);
if (!value) return null;

View File

@ -4,7 +4,7 @@ import { useNavigate } from "react-router-dom";
import { ErrorBoundary } from "./error-boundary";
import { ConnectedRelays } from "./connected-relays";
export const Page = ({ children }) => {
export const Page = ({ children }: {children: React.ReactNode}) => {
const navigate = useNavigate();
return (

View File

@ -10,8 +10,15 @@ import {
ModalOverlay,
} from "@chakra-ui/react";
import ReactMarkdown from "react-markdown";
import { NostrEvent } from "../types/nostr-event";
export const PostModal = ({ event, onClose, isOpen }) => (
export type PostModalProps = {
event: NostrEvent;
isOpen: boolean;
onClose: () => void;
};
export const PostModal = ({ event, onClose, isOpen }: PostModalProps) => (
<Modal isOpen={isOpen} onClose={onClose} size="6xl">
<ModalOverlay />
<ModalContent>

View File

@ -8,6 +8,7 @@ import {
CardHeader,
Flex,
Heading,
HStack,
Text,
useDisclosure,
VStack,
@ -16,9 +17,12 @@ import ReactMarkdown from "react-markdown";
import { Link } from "react-router-dom";
import moment from "moment";
import { PostModal } from "./post-modal";
import { EventSeenOn } from "./event-seen-on";
import { NostrEvent } from "../types/nostr-event";
export const Post = ({ event }) => {
export type PostProps = {
event: NostrEvent;
};
export const Post = ({ event }: PostProps) => {
const { isOpen, onClose, onOpen } = useDisclosure();
const isLong = event.content.length > 800;
@ -26,7 +30,7 @@ export const Post = ({ event }) => {
return (
<Card>
<CardHeader>
<Flex spacing="4">
<HStack spacing="4">
<Flex flex="1" gap="4" alignItems="center" flexWrap="wrap">
<Avatar name="Segun Adebayo" src="https://bit.ly/sage-adebayo" />
@ -37,7 +41,7 @@ export const Post = ({ event }) => {
<Text>{moment(event.created_at * 1000).fromNow()}</Text>
</Box>
</Flex>
</Flex>
</HStack>
</CardHeader>
<CardBody pt={0}>
<VStack alignItems="flex-start" justifyContent="stretch">

View File

@ -1,9 +1,14 @@
import { useRef } from "react";
import { useDeepCompareEffect, useMount, useUnmount } from "react-use";
import { Subscription } from "../services/subscriptions";
import { NostrQuery } from "../types/nostr-query";
export function useSubscription(urls, query, name) {
const sub = useRef(null);
export function useSubscription(
urls: string[],
query: NostrQuery,
name?: string
) {
const sub = useRef<Subscription | null>(null);
sub.current = sub.current || new Subscription(urls, query, name);
useMount(() => {
@ -16,5 +21,5 @@ export function useSubscription(urls, query, name) {
if (sub.current) sub.current.close();
});
return sub.current;
return sub.current as Subscription;
}

View File

@ -4,7 +4,9 @@ import { App } from "./app";
import { Providers } from "./providers";
import "./services/events-seen";
const root = createRoot(document.getElementById("root"));
const element = document.getElementById("root");
if (!element) throw new Error("missing mount point");
const root = createRoot(element);
root.render(
<Providers>
<App />

View File

@ -4,7 +4,7 @@ import { RelaysProvider } from "./relay-provider";
import { HashRouter } from "react-router-dom";
import theme from "../theme";
export const Providers = ({ children }) => (
export const Providers = ({ children }: { children: React.ReactNode }) => (
<ChakraProvider theme={theme}>
<HashRouter>
<RelaysProvider>{children}</RelaysProvider>

View File

@ -8,17 +8,17 @@ import React, {
import settingsService from "../services/settings";
const relaysContext = createContext({
relays: [],
relays: [] as string[],
loading: true,
overwriteRelays: () => {},
overwriteRelays: (urls: string[]) => {},
});
export function useRelays() {
return useContext(relaysContext);
}
export const RelaysProvider = ({ children }) => {
const [relays, setRelays] = useState([]);
export const RelaysProvider = ({ children }: { children: React.ReactNode }) => {
const [relays, setRelays] = useState<string[]>([]);
const [loading, setLoading] = useState(true);
const update = useCallback(async () => {
@ -27,7 +27,7 @@ export const RelaysProvider = ({ children }) => {
}, [setRelays]);
const overwriteRelays = useCallback(
async (urls) => {
async (urls: string[]) => {
if (urls) await settingsService.setRelays(urls);
await update();
},

View File

@ -1,9 +0,0 @@
import { openDB } from "idb";
import { upgrade } from "./migrations";
const version = 1;
const db = await openDB("storage", version, {
upgrade,
});
export default db;

50
src/services/db/index.ts Normal file
View File

@ -0,0 +1,50 @@
import { openDB } from "idb";
import { IDBPDatabase, IDBPTransaction, StoreNames } from "idb";
import { CustomSchema } from "./schema";
type MigrationFunction = (
database: IDBPDatabase<CustomSchema>,
transaction: IDBPTransaction<
CustomSchema,
StoreNames<CustomSchema>[],
"versionchange"
>,
event: IDBVersionChangeEvent
) => void;
const MIGRATIONS: MigrationFunction[] = [
// 0 -> 1
function (db, transaction, event) {
db.createObjectStore("user-metadata", {
keyPath: "pubkey",
});
const eventsSeen = db.createObjectStore("events-seen", { keyPath: "id" });
eventsSeen.createIndex("lastSeen", "lastSeen");
// db.createObjectStore("contacts", {
// keyPath: "pubkey",
// });
// setup data
const settings = db.createObjectStore("settings");
settings.put(["wss://nostr.rdfriedl.com"], "relays");
},
];
const version = 1;
const db = await openDB<CustomSchema>("storage", version, {
upgrade(db, oldVersion, newVersion, transaction, event) {
// TODO: why is newVersion sometimes null?
// @ts-ignore
for (let i = oldVersion; i <= newVersion; i++) {
if (MIGRATIONS[i]) {
console.log(`Running database migration ${i}`);
MIGRATIONS[i](db, transaction, event);
}
}
},
});
export default db;

View File

@ -1,29 +0,0 @@
const MIGRATIONS = [
// 0 -> 1
function (db, transaction, event) {
const userMetadata = db.createObjectStore("user-metadata", {
keyPath: "pubkey",
});
userMetadata.createIndex("id", "id", { unique: true });
const eventsSeen = db.createObjectStore("events-seen", { keyPath: "id" });
eventsSeen.createIndex("lastSeen", "lastSeen");
db.createObjectStore("contacts", {
keyPath: "pubkey",
});
// setup data
const settings = db.createObjectStore("settings");
settings.put(["wss://nostr.rdfriedl.com"], "relays");
},
];
export function upgrade(db, oldVersion, newVersion, transaction, event) {
for (let i = oldVersion; i <= newVersion; i++) {
if (MIGRATIONS[i]) {
console.log(`Running database migration ${i}`);
MIGRATIONS[i](db, transaction, event);
}
}
}

21
src/services/db/schema.ts Normal file
View File

@ -0,0 +1,21 @@
import { DBSchema } from "idb";
export interface CustomSchema extends DBSchema {
"user-metadata": {
key: string;
value: any;
};
"events-seen": {
key: string;
value: {
id: string;
relays: string[];
lastSeen: Date;
};
indexes: { lastSeen: Date };
};
settings: {
key: string;
value: any;
};
}

View File

@ -22,6 +22,6 @@ relayPool.onRelayCreated.subscribe((relay) => {
});
});
export async function getRelaysEventWasSeen(id) {
export async function getRelaysEventWasSeen(id: string) {
return await db.get("events-seen", id);
}

View File

@ -3,28 +3,28 @@ import { Relay } from "./relay";
import settingsService from "../settings";
export class RelayPool {
relays = new Map();
relayClaims = new Map();
onRelayCreated = new Subject();
relays = new Map<string, Relay>();
relayClaims = new Map<string, Set<any>>();
onRelayCreated = new Subject<Relay>();
getRelays() {
return Array.from(this.relays.values());
}
getRelayClaims(url) {
getRelayClaims(url: string) {
if (!this.relayClaims.has(url)) {
this.relayClaims.set(url, new Set());
}
return this.relayClaims.get(url);
return this.relayClaims.get(url) as Set<any>;
}
requestRelay(url, connect = true) {
requestRelay(url: string, connect = true) {
if (!this.relays.has(url)) {
const newRelay = new Relay(url);
this.relays.set(url, newRelay);
this.onRelayCreated.next(newRelay);
}
const relay = this.relays.get(url);
const relay = this.relays.get(url) as Relay;
if (connect && !relay.okay) {
relay.open();
}
@ -49,10 +49,10 @@ export class RelayPool {
}
// id can be anything
addClaim(url, id) {
addClaim(url: string, id: any) {
this.getRelayClaims(url).add(id);
}
removeClaim(url, id) {
removeClaim(url: string, id: any) {
this.getRelayClaims(url).delete(id);
}
@ -68,6 +68,7 @@ export class RelayPool {
const relayPool = new RelayPool();
if (import.meta.env.DEV) {
// @ts-ignore
window.relayPool = relayPool;
}

View File

@ -1,7 +1,26 @@
import { Subject } from "rxjs";
import { IncomingNostrEvent, NostrEvent } from "../../types/nostr-event";
import { NostrOutgoingMessage } from "../../types/nostr-query";
export type IncomingEvent = {
type: "EVENT";
subId: string;
body: NostrEvent;
};
export type IncomingNotice = {
type: "NOTICE";
message: string;
};
export class Relay {
constructor(url) {
url: string;
onOpen: Subject<Relay>;
onClose: Subject<Relay>;
onEvent: Subject<IncomingEvent>;
onNotice: Subject<IncomingNotice>;
ws?: WebSocket;
constructor(url: string) {
this.url = url;
this.onOpen = new Subject();
@ -30,9 +49,9 @@ export class Relay {
};
this.ws.onmessage = this.handleMessage.bind(this);
}
send(json) {
send(json: NostrOutgoingMessage) {
if (this.connected) {
this.ws.send(JSON.stringify(json));
this.ws?.send(JSON.stringify(json));
}
}
close() {
@ -58,20 +77,20 @@ export class Relay {
return this.ws?.readyState;
}
handleMessage(event) {
handleMessage(event: MessageEvent<string>) {
// skip empty events
if (!event.data) return;
try {
const data = JSON.parse(event.data);
const data: IncomingNostrEvent = JSON.parse(event.data);
const type = data[0];
switch (type) {
case "EVENT":
this.onEvent.next({ type, subId: data[1], body: data[2] }, this);
this.onEvent.next({ type, subId: data[1], body: data[2] });
break;
case "NOTICE":
this.onNotice.next({ type, message: data[1] }, this);
this.onNotice.next({ type, message: data[1] });
break;
}
} catch (e) {

View File

@ -1,10 +1,10 @@
import db from "./db";
export async function getRelays() {
export async function getRelays(): Promise<string[]> {
return await db.get("settings", "relays");
}
export async function setRelays(relays = []) {
return await db.put("settings", relays, "relays");
export async function setRelays(relays: string[] = []) {
await db.put("settings", relays, "relays");
}
const settingsService = {
@ -13,6 +13,7 @@ const settingsService = {
};
if (import.meta.env.DEV) {
// @ts-ignore
window.settingsService = settingsService;
}

View File

@ -1,16 +1,24 @@
import { Subject } from "rxjs";
import { Subject, SubscriptionLike } from "rxjs";
import { NostrEvent } from "../types/nostr-event";
import { NostrOutgoingMessage, NostrQuery } from "../types/nostr-query";
import { Relay } from "./relays";
import { IncomingEvent } from "./relays/relay";
import relayPool from "./relays/relay-pool";
import settingsService from "./settings";
export class Subscription {
static OPEN = "open";
static CLOSED = "closed";
id: string;
name?: string;
query?: NostrQuery;
relayUrls: string[];
relays: Relay[];
state = Subscription.CLOSED;
onEvent = new Subject();
cleanup = [];
onEvent = new Subject<NostrEvent>();
cleanup: SubscriptionLike[] = [];
constructor(relayUrls, query, name) {
constructor(relayUrls: string[], query?: NostrQuery, name?: string) {
this.id = String(Math.floor(Math.random() * 1000000));
this.query = query;
this.name = name;
@ -18,22 +26,23 @@ export class Subscription {
this.relays = relayUrls.map((url) => relayPool.requestRelay(url));
}
handleOpen(relay) {
handleOpen(relay: Relay) {
if (!this.query) return;
// when the relay connects send the req event
relay.send(["REQ", this.id, this.query]);
}
handleEvent(event) {
handleEvent(event: IncomingEvent) {
if (event.subId === this.id) {
this.onEvent.next(event.body);
}
}
send(message) {
send(message: NostrOutgoingMessage) {
for (const relay of this.relays) {
relay.send(message);
}
}
setQuery(query) {
setQuery(query: NostrQuery) {
this.query = query;
// if open, than update remote subscription
@ -75,8 +84,3 @@ export class Subscription {
}
}
}
export async function createSubscription(query) {
const urls = await settingsService.getRelays();
return new Subscription(urls, query);
}

View File

@ -1,22 +1,14 @@
import { BehaviorSubject, Subject } from "rxjs";
import { BehaviorSubject } from "rxjs";
import { NostrEvent } from "../types/nostr-event";
import settingsService from "./settings";
import { Subscription } from "./subscriptions";
function waitOnSignal(signal) {
return new Promise((res) => {
const listener = (event) => {
signal.removeListener(listener);
res(event);
};
signal.addListener(listener);
});
}
class UserMetadata {
requests = new Map();
requests = new Map<string, BehaviorSubject<NostrEvent | null>>();
subscription: Subscription;
constructor(relayUrls = []) {
this.subscription = new Subscription(relayUrls, null, "user-metadata");
constructor(relayUrls: string[] = []) {
this.subscription = new Subscription(relayUrls, undefined, "user-metadata");
this.subscription.onEvent.subscribe((event) => {
try {
@ -30,9 +22,9 @@ class UserMetadata {
}, 1000 * 10);
}
requestUserMetadata(pubkey) {
requestUserMetadata(pubkey: string) {
if (!this.requests.has(pubkey)) {
this.requests.set(pubkey, new BehaviorSubject(null));
this.requests.set(pubkey, new BehaviorSubject<NostrEvent | null>(null));
this.updateSubscription();
}
return this.requests.get(pubkey);
@ -69,6 +61,7 @@ class UserMetadata {
const userMetadata = new UserMetadata(await settingsService.getRelays());
if (import.meta.env.DEV) {
// @ts-ignore
window.userMetadata = userMetadata;
}

13
src/types/nostr-event.ts Normal file
View File

@ -0,0 +1,13 @@
export type NostrEvent = {
id: string;
pubkey: string;
created_at: number;
kind: number;
tags: any[];
content: string;
sig: string;
};
export type IncomingNostrEvent =
| ["EVENT", string, NostrEvent]
| ["NOTICE", string];

21
src/types/nostr-query.ts Normal file
View File

@ -0,0 +1,21 @@
import { NostrEvent } from "./nostr-event";
export type NostrOutgoingEvent = ["EVENT", NostrEvent];
export type NostrOutgoingRequest = ["REQ", string, NostrQuery];
export type NostrOutgoingClose = ["CLOSE", string];
export type NostrOutgoingMessage =
| NostrOutgoingEvent
| NostrOutgoingRequest
| NostrOutgoingClose;
export type NostrQuery = {
ids?: string[];
authors?: string[];
kinds?: number[];
// "#e": <a list of event ids that are referenced in an "e" tag>,
// "#p": <a list of pubkeys that are referenced in a "p" tag>,
since?: number;
until?: number;
limit?: number;
};

View File

@ -4,10 +4,11 @@ import { useSubscription } from "../../hooks/use-subscription";
import { useRelays } from "../../providers/relay-provider";
import { Post } from "../../components/post";
import moment from "moment/moment";
import { NostrEvent } from "../../types/nostr-event";
export const GlobalView = () => {
const { relays } = useRelays();
const [events, setEvents] = useState({});
const [events, setEvents] = useState<Record<string, NostrEvent>>({});
const sub = useSubscription(
relays,

View File

@ -6,14 +6,14 @@ import {
Stack,
Textarea,
} from "@chakra-ui/react";
import React, { useState } from "react";
import React, { SyntheticEvent, useState } from "react";
import { useRelays } from "../providers/relay-provider";
export const SettingsView = () => {
const { relays, overwriteRelays } = useRelays();
const [relayUrls, setRelayUrls] = useState(relays.join("\n"));
const handleSubmit = async (event) => {
const handleSubmit = async (event: SyntheticEvent<HTMLFormElement>) => {
event.preventDefault();
const newRelays = relayUrls
.split("\n")

View File

@ -13,22 +13,23 @@ import { useObservable } from "react-use";
import userMetadata from "../../services/user-metadata";
export const UserView = () => {
const params = useParams();
if (!params.pubkey) {
const { pubkey } = useParams();
if (!pubkey) {
// TODO: better 404
throw new Error("No pubkey");
}
const observable = useMemo(
() => userMetadata.requestUserMetadata(params.pubkey),
[params.pubkey]
() => userMetadata.requestUserMetadata(pubkey),
[pubkey]
);
// @ts-ignore
const metadata = useObservable(observable);
return (
<>
<Heading>{metadata?.name ?? params.pubkey}</Heading>
{/* @ts-ignore */}
<Heading>{metadata?.name ?? pubkey}</Heading>
<Tabs>
<TabList>
<Tab>Posts</Tab>
@ -38,7 +39,7 @@ export const UserView = () => {
<TabPanels>
<TabPanel>
<UserPostsTab pubkey={params.pubkey} />
<UserPostsTab pubkey={pubkey} />
</TabPanel>
<TabPanel>
<p>two!</p>

View File

@ -3,11 +3,12 @@ import { SkeletonText } from "@chakra-ui/react";
import settingsService from "../../services/settings";
import { useSubscription } from "../../hooks/use-subscription";
import { Post } from "../../components/post";
import { NostrEvent } from "../../types/nostr-event";
const relayUrls = await settingsService.getRelays();
export const UserPostsTab = ({ pubkey }) => {
const [events, setEvents] = useState({});
export const UserPostsTab = ({ pubkey }: { pubkey: string }) => {
const [events, setEvents] = useState<Record<string, NostrEvent>>({});
const sub = useSubscription(
relayUrls,
@ -36,5 +37,11 @@ export const UserPostsTab = ({ pubkey }) => {
return <SkeletonText />;
}
return timeline.map((event) => <Post key={event.id} event={event} />);
return (
<>
{timeline.map((event) => (
<Post key={event.id} event={event} />
))}
</>
);
};

View File

@ -1,41 +0,0 @@
// import React, { useEffect, useState } from "react";
// import { Card, CardBody, SkeletonText } from "@chakra-ui/react";
// import ReactMarkdown from "react-markdown";
// import { onEvent, subscribeToAuthor } from "../../services/relays";
// import { useSignal } from "../../hooks/use-signal";
// export const UserRelaysTab = ({ pubkey }) => {
// const [events, setEvents] = useState({});
// useEffect(() => {
// if (pubkey) {
// subscribeToAuthor(pubkey);
// }
// }, [pubkey]);
// useSignal(
// onEvent,
// (event) => {
// if (event.body.kind === 1) {
// setEvents((dir) => ({ [event.body.id]: event.body, ...dir }));
// }
// },
// [setEvents]
// );
// const timeline = Object.values(events).sort(
// (a, b) => a.created_at - b.created_at
// );
// if (timeline.length === 0) {
// return <SkeletonText />;
// }
// return timeline.map((event) => (
// <Card key={event.id}>
// <CardBody>
// <ReactMarkdown>{event.content}</ReactMarkdown>
// </CardBody>
// </Card>
// ));
// };

1
src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

21
tsconfig.json Normal file
View File

@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

9
tsconfig.node.json Normal file
View File

@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

View File

@ -1,16 +0,0 @@
import { VitePWA } from "vite-plugin-pwa";
import { defineConfig } from "vite";
export default defineConfig({
build: {
target: ["chrome89", "edge89", "firefox89", "safari15"],
},
// plugins: [
// VitePWA({
// registerType: "autoUpdate",
// devOptions: {
// enabled: true,
// },
// }),
// ],
});

10
vite.config.ts Normal file
View File

@ -0,0 +1,10 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
// https://vitejs.dev/config/
export default defineConfig({
build: {
target: ["chrome89", "edge89", "firefox89", "safari15"],
},
plugins: [react()],
});

175
yarn.lock
View File

@ -26,7 +26,7 @@
dependencies:
"@babel/highlight" "^7.18.6"
"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.0", "@babel/compat-data@^7.20.1":
"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.0", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5":
version "7.20.5"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.5.tgz#86f172690b093373a933223b4745deeb6049e733"
integrity sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==
@ -52,6 +52,27 @@
json5 "^2.2.1"
semver "^6.3.0"
"@babel/core@^7.20.5":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.7.tgz#37072f951bd4d28315445f66e0ec9f6ae0c8c35f"
integrity sha512-t1ZjCluspe5DW24bn2Rr1CDb2v9rn/hROtg9a2tmd0+QYf4bsloYfLQzjG4qHPNMhWtKdGC33R5AxGR2Af2cBw==
dependencies:
"@ampproject/remapping" "^2.1.0"
"@babel/code-frame" "^7.18.6"
"@babel/generator" "^7.20.7"
"@babel/helper-compilation-targets" "^7.20.7"
"@babel/helper-module-transforms" "^7.20.7"
"@babel/helpers" "^7.20.7"
"@babel/parser" "^7.20.7"
"@babel/template" "^7.20.7"
"@babel/traverse" "^7.20.7"
"@babel/types" "^7.20.7"
convert-source-map "^1.7.0"
debug "^4.1.0"
gensync "^1.0.0-beta.2"
json5 "^2.2.1"
semver "^6.3.0"
"@babel/generator@^7.20.5":
version "7.20.5"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.5.tgz#cb25abee3178adf58d6814b68517c62bdbfdda95"
@ -61,6 +82,15 @@
"@jridgewell/gen-mapping" "^0.3.2"
jsesc "^2.5.1"
"@babel/generator@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.7.tgz#f8ef57c8242665c5929fe2e8d82ba75460187b4a"
integrity sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==
dependencies:
"@babel/types" "^7.20.7"
"@jridgewell/gen-mapping" "^0.3.2"
jsesc "^2.5.1"
"@babel/helper-annotate-as-pure@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb"
@ -86,6 +116,17 @@
browserslist "^4.21.3"
semver "^6.3.0"
"@babel/helper-compilation-targets@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb"
integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==
dependencies:
"@babel/compat-data" "^7.20.5"
"@babel/helper-validator-option" "^7.18.6"
browserslist "^4.21.3"
lru-cache "^5.1.1"
semver "^6.3.0"
"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.5":
version "7.20.5"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.5.tgz#327154eedfb12e977baa4ecc72e5806720a85a06"
@ -174,6 +215,20 @@
"@babel/traverse" "^7.20.1"
"@babel/types" "^7.20.2"
"@babel/helper-module-transforms@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.7.tgz#7a6c9a1155bef55e914af574153069c9d9470c43"
integrity sha512-FNdu7r67fqMUSVuQpFQGE6BPdhJIhitoxhGzDbAXNcA07uoVG37fOiMk3OSV8rEICuyG6t8LGkd9EE64qIEoIA==
dependencies:
"@babel/helper-environment-visitor" "^7.18.9"
"@babel/helper-module-imports" "^7.18.6"
"@babel/helper-simple-access" "^7.20.2"
"@babel/helper-split-export-declaration" "^7.18.6"
"@babel/helper-validator-identifier" "^7.19.1"
"@babel/template" "^7.20.7"
"@babel/traverse" "^7.20.7"
"@babel/types" "^7.20.7"
"@babel/helper-optimise-call-expression@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe"
@ -262,6 +317,15 @@
"@babel/traverse" "^7.20.5"
"@babel/types" "^7.20.5"
"@babel/helpers@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.7.tgz#04502ff0feecc9f20ecfaad120a18f011a8e6dce"
integrity sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==
dependencies:
"@babel/template" "^7.20.7"
"@babel/traverse" "^7.20.7"
"@babel/types" "^7.20.7"
"@babel/highlight@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
@ -276,6 +340,11 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8"
integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==
"@babel/parser@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.7.tgz#66fe23b3c8569220817d5feb8b9dcdc95bb4f71b"
integrity sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2"
@ -717,6 +786,20 @@
dependencies:
"@babel/helper-plugin-utils" "^7.18.6"
"@babel/plugin-transform-react-jsx-self@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.18.6.tgz#3849401bab7ae8ffa1e3e5687c94a753fc75bda7"
integrity sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==
dependencies:
"@babel/helper-plugin-utils" "^7.18.6"
"@babel/plugin-transform-react-jsx-source@^7.19.6":
version "7.19.6"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz#88578ae8331e5887e8ce28e4c9dc83fb29da0b86"
integrity sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==
dependencies:
"@babel/helper-plugin-utils" "^7.19.0"
"@babel/plugin-transform-regenerator@^7.18.6":
version "7.20.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz#57cda588c7ffb7f4f8483cc83bdcea02a907f04d"
@ -891,6 +974,15 @@
"@babel/parser" "^7.18.10"
"@babel/types" "^7.18.10"
"@babel/template@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8"
integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==
dependencies:
"@babel/code-frame" "^7.18.6"
"@babel/parser" "^7.20.7"
"@babel/types" "^7.20.7"
"@babel/traverse@^7.19.1", "@babel/traverse@^7.20.1", "@babel/traverse@^7.20.5":
version "7.20.5"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.5.tgz#78eb244bea8270fdda1ef9af22a5d5e5b7e57133"
@ -907,6 +999,22 @@
debug "^4.1.0"
globals "^11.1.0"
"@babel/traverse@^7.20.7":
version "7.20.8"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.8.tgz#e3a23eb04af24f8bbe8a8ba3eef6155b77df0b08"
integrity sha512-/RNkaYDeCy4MjyV70+QkSHhxbvj2JO/5Ft2Pa880qJOG8tWrqcT/wXUuCCv43yogfqPzHL77Xu101KQPf4clnQ==
dependencies:
"@babel/code-frame" "^7.18.6"
"@babel/generator" "^7.20.7"
"@babel/helper-environment-visitor" "^7.18.9"
"@babel/helper-function-name" "^7.19.0"
"@babel/helper-hoist-variables" "^7.18.6"
"@babel/helper-split-export-declaration" "^7.18.6"
"@babel/parser" "^7.20.7"
"@babel/types" "^7.20.7"
debug "^4.1.0"
globals "^11.1.0"
"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.4.4":
version "7.20.5"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.5.tgz#e206ae370b5393d94dfd1d04cd687cace53efa84"
@ -916,6 +1024,15 @@
"@babel/helper-validator-identifier" "^7.19.1"
to-fast-properties "^2.0.0"
"@babel/types@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f"
integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==
dependencies:
"@babel/helper-string-parser" "^7.19.4"
"@babel/helper-validator-identifier" "^7.19.1"
to-fast-properties "^2.0.0"
"@chakra-ui/accordion@2.1.4":
version "2.1.4"
resolved "https://registry.yarnpkg.com/@chakra-ui/accordion/-/accordion-2.1.4.tgz#a3eca38f8e52d5a5f4b9528fb9d269dcdcb035ac"
@ -2158,11 +2275,27 @@
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
"@types/prop-types@^15.0.0":
"@types/prop-types@*", "@types/prop-types@^15.0.0":
version "15.7.5"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
"@types/react-dom@^18.0.9":
version "18.0.9"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.9.tgz#ffee5e4bfc2a2f8774b15496474f8e7fe8d0b504"
integrity sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg==
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@^18.0.26":
version "18.0.26"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.26.tgz#8ad59fc01fef8eaf5c74f4ea392621749f0b7917"
integrity sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/resolve@1.17.1":
version "1.17.1"
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
@ -2170,6 +2303,11 @@
dependencies:
"@types/node" "*"
"@types/scheduler@*":
version "0.16.2"
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
"@types/trusted-types@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756"
@ -2180,6 +2318,17 @@
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==
"@vitejs/plugin-react@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-3.0.0.tgz#f36ee1b2ce958dd11ac63fdf746a3b27b0d258ed"
integrity sha512-1mvyPc0xYW5G8CHQvJIJXLoMjl5Ct3q2g5Y2s6Ccfgwm45y48LBvsla7az+GkkAtYikWQ4Lxqcsq5RHLcZgtNQ==
dependencies:
"@babel/core" "^7.20.5"
"@babel/plugin-transform-react-jsx-self" "^7.18.6"
"@babel/plugin-transform-react-jsx-source" "^7.19.6"
magic-string "^0.27.0"
react-refresh "^0.14.0"
"@xobotyi/scrollbar-width@^1.9.5":
version "1.9.5"
resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d"
@ -3222,6 +3371,13 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
lru-cache@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
dependencies:
yallist "^3.0.2"
magic-string@^0.25.0, magic-string@^0.25.7:
version "0.25.9"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
@ -3758,6 +3914,11 @@ react-markdown@^8.0.4:
unist-util-visit "^4.0.0"
vfile "^5.0.0"
react-refresh@^0.14.0:
version "0.14.0"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==
react-remove-scroll-bar@^2.3.3:
version "2.3.4"
resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz#53e272d7a5cb8242990c7f144c44d8bd8ab5afd9"
@ -4282,6 +4443,11 @@ type-fest@^0.16.0:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.16.0.tgz#3240b891a78b0deae910dbeb86553e552a148860"
integrity sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==
typescript@^4.9.4:
version "4.9.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78"
integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==
unbox-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
@ -4664,6 +4830,11 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
yallist@^3.0.2:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
yaml@^1.10.0:
version "1.10.2"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"