This commit is contained in:
2026-03-25 14:59:06 +08:00
commit ae315100b4
92 changed files with 9285 additions and 0 deletions

4
src/store/Action.ts Normal file
View File

@@ -0,0 +1,4 @@
export interface Action<A, T> {
type: A;
payload?: Partial<T>;
}

View File

@@ -0,0 +1,13 @@
import { useReducer, type PropsWithChildren } from "react";
import { appContextReducer, initialState } from "./appcontext";
import { AppContext, AppContextDispatch } from "./AppContexts";
export function AppContextProvider({ children }: PropsWithChildren) {
const [state, dispatch] = useReducer(appContextReducer, initialState);
return (
<AppContext value={state}>
<AppContextDispatch value={dispatch}>{children}</AppContextDispatch>
</AppContext>
);
}

11
src/store/AppContexts.ts Normal file
View File

@@ -0,0 +1,11 @@
import { createContext, type ActionDispatch } from "react";
import {
type AppContextType,
type AppContextAction,
initialState,
} from "./appcontext";
export const AppContext = createContext<AppContextType>(initialState);
export const AppContextDispatch = createContext<
ActionDispatch<[AppContextAction]>
>(() => {});

7
src/store/Types.ts Normal file
View File

@@ -0,0 +1,7 @@
export const orderByTypes = {
ASC: "asc",
DESC: "desc",
} as const;
export type OrderByTypes =
| (typeof orderByTypes)[keyof typeof orderByTypes]
| null;

View File

@@ -0,0 +1,9 @@
import type { Action } from "../Action";
import type { AppContextType } from "./state";
export const SWITCH_THEME = "SWITCH_THEME";
export const RESTORE_APPCONTEXT = "RESTORE_APPCONTEXT";
type ActionType = typeof SWITCH_THEME | typeof RESTORE_APPCONTEXT;
export type AppContextAction = Action<ActionType, AppContextType>;

View File

@@ -0,0 +1,3 @@
export * from "./action";
export * from "./reducer";
export * from "./state";

View File

@@ -0,0 +1,28 @@
import type { AppContextAction } from "./action";
import { Themes, type AppContextType } from "./state";
export const initialState: AppContextType = {
theme: "light",
};
export const appContextReducer = (
state: AppContextType,
action: AppContextAction,
): AppContextType => {
switch (action.type) {
case "SWITCH_THEME": {
return {
...state,
theme: state.theme === Themes.Dark ? Themes.Light : Themes.Dark,
};
}
case "RESTORE_APPCONTEXT": {
return {
...state,
...action.payload,
};
}
default:
throw Error(`wrong action:${action.type}`);
}
};

View File

@@ -0,0 +1,13 @@
export const Themes = {
Dark: "dark",
Light: "light",
} as const;
export type ThemesType = (typeof Themes)[keyof typeof Themes];
interface AppContext {
theme: ThemesType;
nickName: string;
avatarUrl: string | null;
}
export type AppContextType = Partial<AppContext>;

182
src/store/home/action.ts Normal file
View File

@@ -0,0 +1,182 @@
import type { Action } from "../Action";
import type {
HomeState,
SearchResult,
ReplyToType,
ProductSearchType,
} from "./state";
import type { Category, Reply } from "../../api/models";
export const SET_LOADING = "set_loading";
export const SET_CATEGORIES = "set_categories";
export const SET_CATEGORY_ID = "set_category_id";
export const SET_KEYWORD = "set_keyword";
export const SET_PAGE_INDEX = "set_page_index";
export const SET_IS_SEARCHING = "set_is_searching";
export const SET_SEARCH_RESULT = "set_search_result";
export const SET_REPLIES = "set_replies";
export const TOGGLE_ORDER_BY_PUBLISH_TIME = "toggle_order_by_publish_time";
export const TOGGLE_ORDER_BY_CATEGORY_ID = "toggle_order_by_category_id";
export const TOGGLE_ORDER_BY_REPLY_AMOUNT = "toggle_order_by_reply_amount";
export const SET_IS_REPLING = "set_is_repling";
export const SET_REPLY_TO = "set_reply_to";
export const SET_WILL_SEARCHING = "set_will_searching";
export const SET_SEARCH_PRODUCTS = "set_search_products";
export const SET_SEARCH_FOCUSED = "set_search_focused";
export type ActionsType =
| typeof SET_LOADING
| typeof SET_CATEGORIES
| typeof SET_CATEGORY_ID
| typeof SET_KEYWORD
| typeof SET_PAGE_INDEX
| typeof SET_IS_SEARCHING
| typeof SET_SEARCH_RESULT
| typeof SET_REPLIES
| typeof TOGGLE_ORDER_BY_PUBLISH_TIME
| typeof TOGGLE_ORDER_BY_CATEGORY_ID
| typeof TOGGLE_ORDER_BY_REPLY_AMOUNT
| typeof SET_IS_REPLING
| typeof SET_REPLY_TO
| typeof SET_WILL_SEARCHING
| typeof SET_SEARCH_PRODUCTS
| typeof SET_SEARCH_FOCUSED;
export type HomeAction = Action<ActionsType, HomeState>;
export function setLoading(isLoading: boolean): HomeAction {
return {
type: "set_loading",
payload: {
isLoading,
},
};
}
export function setCategories(categories: Array<Category>): HomeAction {
return {
type: "set_categories",
payload: {
categories,
},
};
}
export function setCategoryId(categoryId: number): HomeAction {
return {
type: "set_category_id",
payload: {
categoryId,
},
};
}
export function setKeyword(keyword: string): HomeAction {
return {
type: "set_keyword",
payload: {
keyword,
},
};
}
export function setPageIndex(pageIndex: number): HomeAction {
return {
type: "set_page_index",
payload: {
pageIndex,
},
};
}
export function setIsSearch(isSearching: boolean): HomeAction {
return {
type: "set_is_searching",
payload: {
isSearching,
},
};
}
export function setSearchResult(result: SearchResult): HomeAction {
return {
type: "set_search_result",
payload: {
...result,
},
};
}
export function setReplies(replies: Array<Reply> | null): HomeAction {
return {
type: "set_replies",
payload: {
replies,
},
};
}
export function toggleOrderByPublishTime(): HomeAction {
return {
type: "toggle_order_by_publish_time",
};
}
export function toggleOrderByCategoryId(): HomeAction {
return {
type: "toggle_order_by_category_id",
};
}
export function toggleOrderByReplyAmount(): HomeAction {
return {
type: "toggle_order_by_reply_amount",
};
}
export function setIsRepling(isRepling: boolean): HomeAction {
return {
type: "set_is_repling",
payload: {
isRepling,
},
};
}
export function setReplyTo(reply: Partial<ReplyToType>): HomeAction {
return {
type: "set_reply_to",
payload: {
reply,
},
};
}
export function setWillSearching(willSearching: boolean): HomeAction {
return {
type: "set_will_searching",
payload: {
willSearching,
},
};
}
export function setSearchProducts(
products: Array<ProductSearchType>,
): HomeAction {
return {
type: "set_search_products",
payload: {
products,
},
};
}
export function setSearchFocused(searchFocused: boolean): HomeAction {
return {
type: "set_search_focused",
payload: {
searchFocused,
},
};
}

3
src/store/home/index.ts Normal file
View File

@@ -0,0 +1,3 @@
export * from "./action";
export * from "./reducer";
export * from "./state";

168
src/store/home/reducer.ts Normal file
View File

@@ -0,0 +1,168 @@
import type { HomeAction } from "./action";
import type { HomeState } from "./state";
export const initialState: HomeState = {
categories: [],
isLoading: true,
pageIndex: 1,
categoryId: 0,
keyword: "",
isSearching: false,
pageCount: 0,
pageSize: 10,
requests: [],
orderBys: {
categoryId: null,
publishTime: "desc",
replyAmount: null,
},
replies: null,
isRepling: false,
reply: {
amount: 0,
categoryId: 0,
memo: "",
price: 0,
productId: 0,
productName: "",
requestId: 0,
},
searchFocused: false,
willSearching: false,
products: [],
};
export const homeReducer = (
state: HomeState,
action: HomeAction,
): HomeState => {
switch (action.type) {
case "set_loading": {
return {
...state,
isLoading: action.payload?.isLoading || false,
};
}
case "set_categories": {
return {
...state,
categories: action.payload?.categories || [],
};
}
case "set_category_id": {
return {
...state,
categoryId: action.payload?.categoryId || 0,
};
}
case "set_is_searching": {
return {
...state,
isSearching: action.payload?.isSearching || false,
};
}
case "set_keyword": {
return {
...state,
keyword: action.payload?.keyword || "",
};
}
case "set_page_index": {
return {
...state,
pageIndex: action.payload?.pageIndex || 1,
};
}
case "set_search_result": {
return {
...state,
requests:
state.pageIndex == 1
? action.payload?.requests || []
: [...state.requests, ...(action.payload?.requests || [])],
pageCount: action.payload?.pageCount || 0,
};
}
case "set_replies": {
return {
...state,
isRepling: false,
replies: action.payload?.replies || null,
};
}
case "toggle_order_by_publish_time": {
return {
...state,
orderBys: {
...state.orderBys,
publishTime:
!state.orderBys.publishTime || state.orderBys.publishTime == "desc"
? "asc"
: "desc",
},
};
}
case "toggle_order_by_category_id": {
return {
...state,
orderBys: {
...state.orderBys,
categoryId:
!state.orderBys.categoryId || state.orderBys.categoryId == "desc"
? "asc"
: "desc",
},
};
}
case "toggle_order_by_reply_amount": {
return {
...state,
orderBys: {
...state.orderBys,
replyAmount:
!state.orderBys.replyAmount || state.orderBys.replyAmount == "desc"
? "asc"
: "desc",
},
};
}
case "set_is_repling": {
return {
...state,
isRepling: action.payload?.isRepling ?? false,
};
}
case "set_reply_to": {
return {
...state,
isRepling: true,
willSearching: false,
reply: {
...state.reply,
...action.payload?.reply,
},
};
}
case "set_will_searching": {
return {
...state,
willSearching: action.payload?.willSearching ?? false,
};
}
case "set_search_products": {
return {
...state,
willSearching: false,
products: action.payload?.products ?? [],
};
}
case "set_search_focused": {
return {
...state,
searchFocused: action.payload?.searchFocused ?? false,
};
}
default:
throw Error(`未知操作(${action.type})`);
}
};

65
src/store/home/state.ts Normal file
View File

@@ -0,0 +1,65 @@
import type { Category } from "../../api/models";
import type { Request } from "../../api/models";
import type { Reply } from "../../api/models";
import type { OrderByTypes } from "../Types";
interface OrderByState {
publishTime: OrderByTypes;
categoryId: OrderByTypes;
replyAmount: OrderByTypes;
}
interface SearchState {
pageIndex: number;
pageSize: number;
categoryId: number;
keyword: string;
orderBys: OrderByState;
isSearching: boolean;
}
export interface SearchResult {
requests: Array<Request>;
pageCount: number;
}
export interface RequestReplyState {
replies: Array<Reply> | null;
}
export interface ReplyToType {
categoryId: number;
requestId: number;
productId: number;
productName: string;
amount: number;
price: number;
memo: string;
}
export interface ReplyToState {
isRepling: boolean;
reply: Partial<ReplyToType>;
}
export interface ProductSearchType {
productId: number;
productName: string;
productDescription: string | null;
price: number;
}
export interface ProductSearchState {
searchFocused: boolean;
willSearching: boolean;
products: Array<ProductSearchType>;
}
export type HomeState = {
categories: Array<Category>;
isLoading: boolean;
} & SearchState &
SearchResult &
RequestReplyState &
ReplyToState &
ProductSearchState;

135
src/store/product/action.ts Normal file
View File

@@ -0,0 +1,135 @@
import type { Action } from "../Action";
import type { ProductState, SearchResult, EditProductType } from "./state";
import type { Category } from "../../api/models";
export const SET_LOADING = "set_loading";
export const SET_CATEGORIES = "set_categories";
export const SET_CATEGORY = "set_category";
export const SET_KEYWORD = "set_keyword";
export const SET_PAGE_INDEX = "set_page_index";
export const SET_IS_SEARCHING = "set_is_searching";
export const SET_SEARCH_RESULT = "set_search_result";
export const SET_EDIT_PRODUCT = "set_edit_product";
export const REMOVE_PRODUCT = "remove_product";
export const TOGGLE_ORDER_BY_CREATE_TIME = "toggle_order_by_create_time";
export const TOGGLE_ORDER_BY_CATEGORY_ID = "toggle_order_by_category_id";
export const TOGGLE_ORDER_BY_SOLD_AMOUNT = "toggle_order_by_sold_amount";
export type ActionsType =
| typeof SET_LOADING
| typeof SET_CATEGORIES
| typeof SET_CATEGORY
| typeof SET_KEYWORD
| typeof SET_PAGE_INDEX
| typeof SET_IS_SEARCHING
| typeof SET_SEARCH_RESULT
| typeof SET_EDIT_PRODUCT
| typeof REMOVE_PRODUCT
| typeof TOGGLE_ORDER_BY_CREATE_TIME
| typeof TOGGLE_ORDER_BY_CATEGORY_ID
| typeof TOGGLE_ORDER_BY_SOLD_AMOUNT;
export type ProductAction = Action<ActionsType, ProductState>;
export function setLoading(isLoading: boolean): ProductAction {
return {
type: "set_loading",
payload: {
isLoading,
},
};
}
export function setCategories(categories: Array<Category>): ProductAction {
return {
type: "set_categories",
payload: {
categories,
},
};
}
export function setCategory(category: Category): ProductAction {
return {
type: "set_category",
payload: {
categoryId: category.id,
editProduct: {
categoryId: category.id,
categoryName: category.name,
},
},
};
}
export function setKeyword(keyword: string): ProductAction {
return {
type: "set_keyword",
payload: {
keyword,
},
};
}
export function setPageIndex(pageIndex: number): ProductAction {
return {
type: "set_page_index",
payload: {
pageIndex,
},
};
}
export function setIsSearch(isSearching: boolean): ProductAction {
return {
type: "set_is_searching",
payload: {
isSearching,
},
};
}
export function setSearchResult(result: SearchResult): ProductAction {
return {
type: "set_search_result",
payload: {
...result,
},
};
}
export function setEditProduct(product: EditProductType): ProductAction {
return {
type: "set_edit_product",
payload: {
editProduct: product,
},
};
}
export function removeProduct(productId: number): ProductAction {
return {
type: "remove_product",
payload: {
removedProductId: productId,
},
};
}
export function toggleOrderByCreateTime(): ProductAction {
return {
type: "toggle_order_by_create_time",
};
}
export function toggleOrderByCategoryId(): ProductAction {
return {
type: "toggle_order_by_category_id",
};
}
export function toggleOrderBySoldAmount(): ProductAction {
return {
type: "toggle_order_by_sold_amount",
};
}

View File

@@ -0,0 +1,3 @@
export * from "./action";
export * from "./reducer";
export * from "./state";

View File

@@ -0,0 +1,144 @@
import type { ProductAction } from "./action";
import type { ProductState } from "./state";
export const initialState: ProductState = {
categories: [],
isLoading: true,
pageIndex: 1,
pageSize: 10,
categoryId: 0,
keyword: "",
isSearching: false,
pageCount: 0,
products: [],
removedProductId: 0,
editProduct: {
categoryId: undefined,
description: null,
detail: null,
id: 0,
logoName: null,
logoUrl: null,
minimumUnit: null,
name: null,
unitPrice: undefined,
},
orderBys: {
createTime: "desc",
categoryId: null,
soldAmount: null,
},
};
export const productReducer = (
state: ProductState,
action: ProductAction,
): ProductState => {
switch (action.type) {
case "set_loading": {
return {
...state,
isLoading: action.payload?.isLoading || false,
};
}
case "set_categories": {
return {
...state,
categories: action.payload?.categories || [],
};
}
case "set_category": {
return {
...state,
categoryId: action.payload?.categoryId || 0,
editProduct: {
...state.editProduct,
...action.payload?.editProduct,
},
};
}
case "set_is_searching": {
return {
...state,
isSearching: action.payload?.isSearching || false,
};
}
case "set_keyword": {
return {
...state,
keyword: action.payload?.keyword || "",
};
}
case "set_page_index": {
return {
...state,
pageIndex: action.payload?.pageIndex || 1,
};
}
case "set_search_result": {
return {
...state,
products:
state.pageIndex == 1
? action.payload?.products || []
: [...state.products, ...(action.payload?.products || [])],
pageCount: action.payload?.pageCount || 0,
};
}
case "set_edit_product": {
return {
...state,
editProduct: {
...state.editProduct,
...(action.payload?.editProduct || {}),
},
};
}
case "remove_product": {
return {
...state,
products: state.products.filter(
(p) => p.id != action.payload?.removedProductId,
),
};
}
case "toggle_order_by_create_time": {
return {
...state,
orderBys: {
...state.orderBys,
createTime:
!state.orderBys.createTime || state.orderBys.createTime == "desc"
? "asc"
: "desc",
},
};
}
case "toggle_order_by_category_id": {
return {
...state,
orderBys: {
...state.orderBys,
categoryId:
!state.orderBys.categoryId || state.orderBys.categoryId == "desc"
? "asc"
: "desc",
},
};
}
case "toggle_order_by_sold_amount": {
return {
...state,
orderBys: {
...state.orderBys,
soldAmount:
!state.orderBys.soldAmount || state.orderBys.soldAmount == "desc"
? "asc"
: "desc",
},
};
}
default:
throw Error(`未知操作(${action.type})`);
}
};

View File

@@ -0,0 +1,34 @@
import type { Category, Product, EditProductParams } from "../../api/models";
import type { OrderByTypes } from "../Types";
interface OrderByState {
createTime: OrderByTypes;
categoryId: OrderByTypes;
soldAmount: OrderByTypes;
}
interface SearchState {
pageIndex: number;
pageSize: number;
categoryId: number;
keyword: string;
orderBys: OrderByState;
isSearching: boolean;
}
export interface SearchResult {
products: Array<Product>;
pageCount: number;
}
export type EditProductType = Partial<
EditProductParams & { logoUrl: string | null; categoryName: string | null }
>;
export type ProductState = {
categories: Array<Category>;
isLoading: boolean;
editProduct: EditProductType;
removedProductId: number;
} & SearchState &
SearchResult;

144
src/store/request/action.ts Normal file
View File

@@ -0,0 +1,144 @@
import type { Action } from "../Action";
import type { RequestState, SearchResult } from "./state";
import type { Category, Reply, CreateRequestParams } from "../../api/models";
export const SET_LOADING = "set_loading";
export const SET_CATEGORIES = "set_categories";
export const SET_CATEGORY_ID = "set_category_id";
export const SET_KEYWORD = "set_keyword";
export const SET_PAGE_INDEX = "set_page_index";
export const SET_IS_SEARCHING = "set_is_searching";
export const SET_SEARCH_RESULT = "set_search_result";
export const SET_REPLIES = "set_replies";
export const TOGGLE_ORDER_BY_PUBLISH_TIME = "toggle_order_by_publish_time";
export const TOGGLE_ORDER_BY_CATEGORY_ID = "toggle_order_by_category_id";
export const TOGGLE_ORDER_BY_REPLY_AMOUNT = "toggle_order_by_reply_amount";
export const SET_IS_PUBLISH = "set_is_publish";
export const SET_REQUEST_PUBLISH = "set_request_publish";
export type ActionsType =
| typeof SET_LOADING
| typeof SET_CATEGORIES
| typeof SET_CATEGORY_ID
| typeof SET_KEYWORD
| typeof SET_PAGE_INDEX
| typeof SET_IS_SEARCHING
| typeof SET_SEARCH_RESULT
| typeof SET_REPLIES
| typeof TOGGLE_ORDER_BY_PUBLISH_TIME
| typeof TOGGLE_ORDER_BY_CATEGORY_ID
| typeof TOGGLE_ORDER_BY_REPLY_AMOUNT
| typeof SET_IS_PUBLISH
| typeof SET_REQUEST_PUBLISH;
export type RequestAction = Action<ActionsType, RequestState>;
export function setLoading(isLoading: boolean): RequestAction {
return {
type: "set_loading",
payload: {
isLoading,
},
};
}
export function setCategories(categories: Array<Category>): RequestAction {
return {
type: "set_categories",
payload: {
categories,
},
};
}
export function setCategoryId(categoryId: number): RequestAction {
return {
type: "set_category_id",
payload: {
categoryId,
},
};
}
export function setKeyword(keyword: string): RequestAction {
return {
type: "set_keyword",
payload: {
keyword,
},
};
}
export function setPageIndex(pageIndex: number): RequestAction {
return {
type: "set_page_index",
payload: {
pageIndex,
},
};
}
export function setIsSearch(isSearching: boolean): RequestAction {
return {
type: "set_is_searching",
payload: {
isSearching,
},
};
}
export function setSearchResult(result: SearchResult): RequestAction {
return {
type: "set_search_result",
payload: {
...result,
},
};
}
export function setReplies(replies: Array<Reply> | null): RequestAction {
return {
type: "set_replies",
payload: {
replies,
},
};
}
export function toggleOrderByPublishTime(): RequestAction {
return {
type: "toggle_order_by_publish_time",
};
}
export function toggleOrderByCategoryId(): RequestAction {
return {
type: "toggle_order_by_category_id",
};
}
export function toggleOrderByReplyAmount(): RequestAction {
return {
type: "toggle_order_by_reply_amount",
};
}
export function setIsPublish(isPublish: boolean): RequestAction {
return {
type: "set_is_publish",
payload: {
isPublish,
},
};
}
export function setRequestPublish(
requestPublish: Partial<CreateRequestParams>,
): RequestAction {
return {
type: "set_request_publish",
payload: {
requestPublish,
},
};
}

View File

@@ -0,0 +1,3 @@
export * from "./action";
export * from "./reducer";
export * from "./state";

View File

@@ -0,0 +1,140 @@
import type { RequestAction } from "./action";
import type { RequestState } from "./state";
export const initialState: RequestState = {
categories: [],
isLoading: true,
pageIndex: 1,
categoryId: 0,
keyword: "",
isSearching: false,
pageCount: 0,
pageSize: 10,
requests: [],
orderBys: {
categoryId: null,
publishTime: "desc",
replyAmount: null,
},
replies: null,
isPublish: false,
requestPublish: {
categoryId: 0,
deadline: "",
description: null,
name: "",
},
};
export const requestReducer = (
state: RequestState,
action: RequestAction,
): RequestState => {
switch (action.type) {
case "set_loading": {
return {
...state,
isLoading: action.payload?.isLoading || false,
};
}
case "set_categories": {
return {
...state,
categories: action.payload?.categories || [],
};
}
case "set_category_id": {
return {
...state,
categoryId: action.payload?.categoryId || 0,
};
}
case "set_is_searching": {
return {
...state,
isSearching: action.payload?.isSearching || false,
};
}
case "set_keyword": {
return {
...state,
keyword: action.payload?.keyword || "",
};
}
case "set_page_index": {
return {
...state,
pageIndex: action.payload?.pageIndex || 1,
};
}
case "set_search_result": {
return {
...state,
requests:
state.pageIndex == 1
? action.payload?.requests || []
: [...state.requests, ...(action.payload?.requests || [])],
pageCount: action.payload?.pageCount || 0,
};
}
case "set_replies": {
return {
...state,
replies: action.payload?.replies || null,
};
}
case "toggle_order_by_publish_time": {
return {
...state,
orderBys: {
...state.orderBys,
publishTime:
!state.orderBys.publishTime || state.orderBys.publishTime == "desc"
? "asc"
: "desc",
},
};
}
case "toggle_order_by_category_id": {
return {
...state,
orderBys: {
...state.orderBys,
categoryId:
!state.orderBys.categoryId || state.orderBys.categoryId == "desc"
? "asc"
: "desc",
},
};
}
case "toggle_order_by_reply_amount": {
return {
...state,
orderBys: {
...state.orderBys,
replyAmount:
!state.orderBys.replyAmount || state.orderBys.replyAmount == "desc"
? "asc"
: "desc",
},
};
}
case "set_is_publish": {
return {
...state,
isPublish: action.payload?.isPublish ?? false,
};
}
case "set_request_publish": {
return {
...state,
requestPublish: {
...state.requestPublish,
...action.payload?.requestPublish,
},
};
}
default:
throw Error(`未知操作(${action.type})`);
}
};

View File

@@ -0,0 +1,44 @@
import type {
Category,
Request,
CreateRequestParams,
Reply,
} from "../../api/models";
import type { OrderByTypes } from "../Types";
interface OrderByState {
publishTime: OrderByTypes;
categoryId: OrderByTypes;
replyAmount: OrderByTypes;
}
interface SearchState {
pageIndex: number;
pageSize: number;
categoryId: number;
keyword: string;
orderBys: OrderByState;
isSearching: boolean;
}
export interface SearchResult {
requests: Array<Request>;
pageCount: number;
}
export interface RequestReplyState {
replies: Array<Reply> | null;
}
export interface PublishState {
isPublish: boolean;
requestPublish: Partial<CreateRequestParams>;
}
export type RequestState = {
categories: Array<Category>;
isLoading: boolean;
} & SearchState &
SearchResult &
RequestReplyState &
PublishState;

View File

@@ -0,0 +1,40 @@
import { type Action } from "../Action";
import type { SignInState, SignInType, SignUpType } from "./state";
export const SET_SIGN_IN = "set_sign_in";
export const SET_SIGN_UP = "set_sign_up";
export const SET_IS_SUBMITING = "set_is_submiting";
export type ActionType =
| typeof SET_SIGN_IN
| typeof SET_SIGN_UP
| typeof SET_IS_SUBMITING;
export type SignInAction = Action<ActionType, SignInState>;
export function setSignIn(signIn: SignInType): SignInAction {
return {
type: "set_sign_in",
payload: {
signInUser: signIn,
},
};
}
export function setSignUp(signUp: SignUpType): SignInAction {
return {
type: "set_sign_up",
payload: {
signUpUser: signUp,
},
};
}
export function setIsSubmiting(isSubmiting: boolean): SignInAction {
return {
type: "set_is_submiting",
payload: {
isSubmiting,
},
};
}

View File

@@ -0,0 +1,3 @@
export * from "./action";
export * from "./reducer";
export * from "./state";

View File

@@ -0,0 +1,47 @@
import { type SignInState } from "./state";
import { type SignInAction } from "./action";
export const initialState: SignInState = {
isSubmiting: false,
signInUser: {
account: "",
password: "",
},
signUpUser: {
account: "",
defaultRole: "Buyer",
nickName: "",
password: "",
},
};
export function signInReducer(
state: SignInState,
action: SignInAction,
): SignInState {
switch (action.type) {
case "set_sign_in":
return {
...state,
signInUser: {
...state.signInUser,
...action.payload?.signInUser,
},
};
case "set_sign_up":
return {
...state,
signUpUser: {
...state.signUpUser,
...action.payload?.signUpUser,
},
};
case "set_is_submiting":
return {
...state,
isSubmiting: action.payload?.isSubmiting ?? false,
};
default:
throw Error(`wrong action type:${action.type}`);
}
}

10
src/store/signin/state.ts Normal file
View File

@@ -0,0 +1,10 @@
import type { SignInParams, SignUpParams } from "../../api/models";
export type SignInType = Partial<SignInParams>;
export type SignUpType = Partial<SignUpParams & { confirmPassword: string }>;
export interface SignInState {
isSubmiting: boolean;
signInUser: SignInType;
signUpUser: SignUpType;
}