Commit 990ac633 authored by timel's avatar timel

feat: initStore

parent bf06704b
Pipeline #22964 failed with stages
in 13 minutes and 2 seconds
...@@ -2,6 +2,7 @@ export * from "./accountStore"; ...@@ -2,6 +2,7 @@ export * from "./accountStore";
export * from "./cardStore"; export * from "./cardStore";
export * from "./chatStore"; export * from "./chatStore";
export * from "./deckStore"; export * from "./deckStore";
export * from "./initStore";
export * from "./matStore"; export * from "./matStore";
export * from "./placeStore"; export * from "./placeStore";
export * from "./replayStore"; export * from "./replayStore";
...@@ -15,6 +16,7 @@ import { accountStore } from "./accountStore"; ...@@ -15,6 +16,7 @@ import { accountStore } from "./accountStore";
import { cardStore } from "./cardStore"; import { cardStore } from "./cardStore";
import { chatStore } from "./chatStore"; import { chatStore } from "./chatStore";
import { deckStore } from "./deckStore"; import { deckStore } from "./deckStore";
import { initStore } from "./initStore";
import { matStore } from "./matStore"; import { matStore } from "./matStore";
import { placeStore } from "./placeStore"; import { placeStore } from "./placeStore";
import { replayStore } from "./replayStore"; import { replayStore } from "./replayStore";
...@@ -30,6 +32,7 @@ devtools(replayStore, { name: "replay", enabled: DEV }); ...@@ -30,6 +32,7 @@ devtools(replayStore, { name: "replay", enabled: DEV });
devtools(accountStore, { name: "account", enabled: DEV }); devtools(accountStore, { name: "account", enabled: DEV });
devtools(roomStore, { name: "room", enabled: DEV }); devtools(roomStore, { name: "room", enabled: DEV });
devtools(deckStore, { name: "deck", enabled: DEV }); devtools(deckStore, { name: "deck", enabled: DEV });
devtools(initStore, { name: "init", enabled: DEV });
// 重置所有`Store` // 重置所有`Store`
export const resetUniverse = () => { export const resetUniverse = () => {
......
import { proxy } from "valtio";
import { type NeosStore } from "./shared";
export const initStore = proxy({
sqlite: {
initialized: false,
progress: 0,
},
i18n: {
initialized: false,
},
// ...
reset() {},
} satisfies NeosStore);
...@@ -15,15 +15,22 @@ import { ...@@ -15,15 +15,22 @@ import {
Space, Space,
type ThemeConfig, type ThemeConfig,
} from "antd"; } from "antd";
import { useEffect, useState } from "react"; import { Suspense, useEffect, useState } from "react";
import {
Await,
defer,
type LoaderFunction,
useLoaderData,
} from "react-router-dom";
import { useSnapshot } from "valtio"; import { useSnapshot } from "valtio";
import { deckStore, type IDeck } from "@/stores"; import { deckStore, type IDeck, initStore } from "@/stores";
import { Background, ScrollableArea, YgoCard } from "@/ui/Shared"; import { Background, Loading, ScrollableArea, YgoCard } from "@/ui/Shared";
import { CardDetail } from "./CardDetail"; import { CardDetail } from "./CardDetail";
import { DeckSelect } from "./DeckSelect"; import { DeckSelect } from "./DeckSelect";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
import { initSqlite } from "./utils";
const theme: ThemeConfig = { const theme: ThemeConfig = {
components: { components: {
...@@ -33,7 +40,18 @@ const theme: ThemeConfig = { ...@@ -33,7 +40,18 @@ const theme: ThemeConfig = {
}, },
}; };
export const loader: LoaderFunction = async () => {
return defer({
initialized: initStore.sqlite.initialized
? true
: initSqlite().then(() => (initStore.sqlite.initialized = true)),
});
};
export const Component: React.FC = () => { export const Component: React.FC = () => {
const data = useLoaderData<{
initialized: boolean;
}>();
const snapDecks = useSnapshot(deckStore); const snapDecks = useSnapshot(deckStore);
const [selectedDeck, setSelectedDeck] = useState<IDeck>(deckStore.decks[0]); const [selectedDeck, setSelectedDeck] = useState<IDeck>(deckStore.decks[0]);
...@@ -57,8 +75,22 @@ export const Component: React.FC = () => { ...@@ -57,8 +75,22 @@ export const Component: React.FC = () => {
<CardDetail code={123} open={false} onClose={() => {}} /> <CardDetail code={123} open={false} onClose={() => {}} />
</div> </div>
<div className={styles.content}> <div className={styles.content}>
<Deck deck={selectedDeck} onSave={() => {}} /> <div className={styles.deck}>
<CardSelect /> <DeckEditor deck={selectedDeck} onSave={() => {}} />
</div>
<div className={styles.select}>
<Suspense
fallback={
<div className={styles.container}>
<Loading />
</div>
}
>
<Await resolve={data.initialized}>
<CardSelect />
</Await>
</Suspense>
</div>
</div> </div>
</div> </div>
</ConfigProvider> </ConfigProvider>
...@@ -67,7 +99,7 @@ export const Component: React.FC = () => { ...@@ -67,7 +99,7 @@ export const Component: React.FC = () => {
Component.displayName = "Build"; Component.displayName = "Build";
/** 正在编辑的卡组 */ /** 正在编辑的卡组 */
const Deck: React.FC<{ const DeckEditor: React.FC<{
deck: IDeck; deck: IDeck;
onSave: (deck: IDeck) => void; onSave: (deck: IDeck) => void;
}> = ({ deck, onSave }) => { }> = ({ deck, onSave }) => {
...@@ -76,50 +108,48 @@ const Deck: React.FC<{ ...@@ -76,50 +108,48 @@ const Deck: React.FC<{
setEditingDeck(deck); setEditingDeck(deck);
}, [deck]); }, [deck]);
return ( return (
<div className={styles.deck}> <div className={styles.container}>
<div className={styles.container}> <Space className={styles.title}>
<Space className={styles.title}> <Input
<Input placeholder="请输入卡组名字"
placeholder="请输入卡组名字" bordered={false}
bordered={false} prefix={<EditOutlined />}
prefix={<EditOutlined />} style={{ width: 400 }}
style={{ width: 400 }} onChange={(e) =>
onChange={(e) => setEditingDeck({ ...editingDeck, deckName: e.target.value })
setEditingDeck({ ...editingDeck, deckName: e.target.value }) }
} value={editingDeck.deckName}
value={editingDeck.deckName} />
/> <Space style={{ marginRight: 6 }}>
<Space style={{ marginRight: 6 }}> <Button type="text" size="small" icon={<DeleteOutlined />}>
<Button type="text" size="small" icon={<DeleteOutlined />}> 清空
清空 </Button>
</Button> <Button type="text" size="small" icon={<UndoOutlined />}>
<Button type="text" size="small" icon={<UndoOutlined />}> 重置
重置 </Button>
</Button> <Button
<Button type="text"
type="text" size="small"
size="small" icon={<CheckOutlined />}
icon={<CheckOutlined />} onClick={() => onSave(editingDeck)}
onClick={() => onSave(editingDeck)} >
> 保存
保存 </Button>
</Button>
</Space>
</Space> </Space>
<ScrollableArea className={styles["deck-zone"]}> </Space>
{(["main", "extra", "side"] as const).map((type) => ( <ScrollableArea className={styles["deck-zone"]}>
<div key={type} className={styles[type]}> {(["main", "extra", "side"] as const).map((type) => (
<div className={styles["card-continer"]}> <div key={type} className={styles[type]}>
{editingDeck[type].map((code, i) => ( <div className={styles["card-continer"]}>
<div className={styles.card} key={i}> {editingDeck[type].map((code, i) => (
<YgoCard code={code} /> <div className={styles.card} key={i}>
</div> <YgoCard code={code} />
))} </div>
</div> ))}
</div> </div>
))} </div>
</ScrollableArea> ))}
</div> </ScrollableArea>
</div> </div>
); );
}; };
...@@ -127,37 +157,35 @@ const Deck: React.FC<{ ...@@ -127,37 +157,35 @@ const Deck: React.FC<{
/** 卡片库,选择卡片加入正在编辑的卡组 */ /** 卡片库,选择卡片加入正在编辑的卡组 */
const CardSelect: React.FC = () => { const CardSelect: React.FC = () => {
return ( return (
<div className={styles.select}> <div className={styles.container}>
<div className={styles.container}> <div className={styles.title}>
<div className={styles.title}> <Input
<Input placeholder="搜索卡片"
placeholder="搜索卡片" bordered={false}
bordered={false} suffix={<Button type="text" icon={<SearchOutlined />} />}
suffix={<Button type="text" icon={<SearchOutlined />} />} />
/>
</div>
<div className={styles["select-btns"]}>
<Button block type="text" icon={<FilterOutlined />}>
筛选
{/* TODO: 下面这个Badge应根据有无筛选规则而显示 */}
{false && <Badge dot offset={[5, -5]} />}
</Button>
<Button block type="text" icon={<SortAscendingOutlined />}>
排列
{false && <Badge dot offset={[5, -5]} />}
</Button>
<Button block type="text" icon={<DeleteOutlined />}>
重置
</Button>
</div>
<ScrollableArea className={styles["search-cards-container"]}>
<div className={styles["search-cards"]}>
{Array.from({ length: 60 }).map((_, i) => (
<div className={styles.card} key={i} />
))}
</div>
</ScrollableArea>
</div> </div>
<div className={styles["select-btns"]}>
<Button block type="text" icon={<FilterOutlined />}>
筛选
{/* TODO: 下面这个Badge应根据有无筛选规则而显示 */}
{false && <Badge dot offset={[5, -5]} />}
</Button>
<Button block type="text" icon={<SortAscendingOutlined />}>
排列
{false && <Badge dot offset={[5, -5]} />}
</Button>
<Button block type="text" icon={<DeleteOutlined />}>
重置
</Button>
</div>
<ScrollableArea className={styles["search-cards-container"]}>
<div className={styles["search-cards"]}>
{Array.from({ length: 60 }).map((_, i) => (
<div className={styles.card} key={i} />
))}
</div>
</ScrollableArea>
</div> </div>
); );
}; };
import { useConfig } from "@/config";
import sqliteMiddleWare, { sqliteCmd } from "@/middleware/sqlite";
const { cardsDbUrl } = useConfig();
export const initSqlite = () =>
sqliteMiddleWare({
cmd: sqliteCmd.INIT,
initInfo: { dbUrl: cardsDbUrl },
});
.loading {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 10px;
.text {
font-size: 14px;
}
.icon {
font-size: 20px;
}
}
import { LoadingOutlined } from "@ant-design/icons";
import styles from "./index.module.scss";
export const Loading: React.FC<{ progress?: number; hiddenText?: boolean }> = ({
progress,
hiddenText,
}) => (
<div className={styles.loading}>
<span className={styles.icon}>
<LoadingOutlined />
</span>
{!hiddenText && (
<span className={styles.text}>
{progress ? `${progress}%` : "加载中"}
</span>
)}
</div>
);
export * from "./Background"; export * from "./Background";
export * from "./css"; export * from "./css";
export * from "./IconFont"; export * from "./IconFont";
export * from "./Loading";
export * from "./Scrollbar"; export * from "./Scrollbar";
export * from "./Select"; export * from "./Select";
export * from "./SpecialButton"; export * from "./SpecialButton";
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment