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";
export * from "./cardStore";
export * from "./chatStore";
export * from "./deckStore";
export * from "./initStore";
export * from "./matStore";
export * from "./placeStore";
export * from "./replayStore";
......@@ -15,6 +16,7 @@ import { accountStore } from "./accountStore";
import { cardStore } from "./cardStore";
import { chatStore } from "./chatStore";
import { deckStore } from "./deckStore";
import { initStore } from "./initStore";
import { matStore } from "./matStore";
import { placeStore } from "./placeStore";
import { replayStore } from "./replayStore";
......@@ -30,6 +32,7 @@ devtools(replayStore, { name: "replay", enabled: DEV });
devtools(accountStore, { name: "account", enabled: DEV });
devtools(roomStore, { name: "room", enabled: DEV });
devtools(deckStore, { name: "deck", enabled: DEV });
devtools(initStore, { name: "init", enabled: DEV });
// 重置所有`Store`
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 {
Space,
type ThemeConfig,
} 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 { deckStore, type IDeck } from "@/stores";
import { Background, ScrollableArea, YgoCard } from "@/ui/Shared";
import { deckStore, type IDeck, initStore } from "@/stores";
import { Background, Loading, ScrollableArea, YgoCard } from "@/ui/Shared";
import { CardDetail } from "./CardDetail";
import { DeckSelect } from "./DeckSelect";
import styles from "./index.module.scss";
import { initSqlite } from "./utils";
const theme: ThemeConfig = {
components: {
......@@ -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 = () => {
const data = useLoaderData<{
initialized: boolean;
}>();
const snapDecks = useSnapshot(deckStore);
const [selectedDeck, setSelectedDeck] = useState<IDeck>(deckStore.decks[0]);
......@@ -57,8 +75,22 @@ export const Component: React.FC = () => {
<CardDetail code={123} open={false} onClose={() => {}} />
</div>
<div className={styles.content}>
<Deck deck={selectedDeck} onSave={() => {}} />
<CardSelect />
<div className={styles.deck}>
<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>
</ConfigProvider>
......@@ -67,7 +99,7 @@ export const Component: React.FC = () => {
Component.displayName = "Build";
/** 正在编辑的卡组 */
const Deck: React.FC<{
const DeckEditor: React.FC<{
deck: IDeck;
onSave: (deck: IDeck) => void;
}> = ({ deck, onSave }) => {
......@@ -76,50 +108,48 @@ const Deck: React.FC<{
setEditingDeck(deck);
}, [deck]);
return (
<div className={styles.deck}>
<div className={styles.container}>
<Space className={styles.title}>
<Input
placeholder="请输入卡组名字"
bordered={false}
prefix={<EditOutlined />}
style={{ width: 400 }}
onChange={(e) =>
setEditingDeck({ ...editingDeck, deckName: e.target.value })
}
value={editingDeck.deckName}
/>
<Space style={{ marginRight: 6 }}>
<Button type="text" size="small" icon={<DeleteOutlined />}>
清空
</Button>
<Button type="text" size="small" icon={<UndoOutlined />}>
重置
</Button>
<Button
type="text"
size="small"
icon={<CheckOutlined />}
onClick={() => onSave(editingDeck)}
>
保存
</Button>
</Space>
<div className={styles.container}>
<Space className={styles.title}>
<Input
placeholder="请输入卡组名字"
bordered={false}
prefix={<EditOutlined />}
style={{ width: 400 }}
onChange={(e) =>
setEditingDeck({ ...editingDeck, deckName: e.target.value })
}
value={editingDeck.deckName}
/>
<Space style={{ marginRight: 6 }}>
<Button type="text" size="small" icon={<DeleteOutlined />}>
清空
</Button>
<Button type="text" size="small" icon={<UndoOutlined />}>
重置
</Button>
<Button
type="text"
size="small"
icon={<CheckOutlined />}
onClick={() => onSave(editingDeck)}
>
保存
</Button>
</Space>
<ScrollableArea className={styles["deck-zone"]}>
{(["main", "extra", "side"] as const).map((type) => (
<div key={type} className={styles[type]}>
<div className={styles["card-continer"]}>
{editingDeck[type].map((code, i) => (
<div className={styles.card} key={i}>
<YgoCard code={code} />
</div>
))}
</div>
</Space>
<ScrollableArea className={styles["deck-zone"]}>
{(["main", "extra", "side"] as const).map((type) => (
<div key={type} className={styles[type]}>
<div className={styles["card-continer"]}>
{editingDeck[type].map((code, i) => (
<div className={styles.card} key={i}>
<YgoCard code={code} />
</div>
))}
</div>
))}
</ScrollableArea>
</div>
</div>
))}
</ScrollableArea>
</div>
);
};
......@@ -127,37 +157,35 @@ const Deck: React.FC<{
/** 卡片库,选择卡片加入正在编辑的卡组 */
const CardSelect: React.FC = () => {
return (
<div className={styles.select}>
<div className={styles.container}>
<div className={styles.title}>
<Input
placeholder="搜索卡片"
bordered={false}
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 className={styles.container}>
<div className={styles.title}>
<Input
placeholder="搜索卡片"
bordered={false}
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>
);
};
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 "./css";
export * from "./IconFont";
export * from "./Loading";
export * from "./Scrollbar";
export * from "./Select";
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