Commit 9fd24f08 authored by Chunchi Che's avatar Chunchi Che

Merge branch 'feat/ui/select_unselect_card' into 'main'

Feat/ui/select unselect card

See merge request mycard/Neos!86
parents f060973f d263da4f
Pipeline #19686 passed with stages
in 5 minutes and 15 seconds
import { AsyncThunk, createAsyncThunk } from "@reduxjs/toolkit";
import {
AsyncThunk,
CaseReducer,
createAsyncThunk,
PayloadAction,
} from "@reduxjs/toolkit";
import { CardMeta } from "../../api/cards";
import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import { fetchCard } from "../../api/cards";
import { DuelState } from "./mod";
export type DuelReducer<T> = CaseReducer<DuelState, PayloadAction<T>>;
export interface DuelFieldState {
inner: CardState[];
......
......@@ -46,6 +46,13 @@ import {
setOptionModalIsOpenImpl,
resetOptionModalImpl,
optionModalCase,
setCheckCardModalV2IsOpenImpl,
setCheckCardModalV2CancelAbleImpl,
setCheckCardModalV2MinMaxImpl,
setCheckCardModalV2FinishAbleImpl,
resetCheckCardModalV2Impl,
setCheckCardModalV2ResponseAbleImpl,
checkCardModalV2Case,
} from "./modal/mod";
import {
MonsterState,
......@@ -139,6 +146,14 @@ const initialState: DuelState = {
yesNoModal: { isOpen: false },
positionModal: { isOpen: false, positions: [] },
optionModal: { isOpen: false, options: [] },
checkCardModalV2: {
isOpen: false,
cancelAble: false,
finishAble: false,
responseable: false,
selectableOptions: [],
selectedOptions: [],
},
},
};
......@@ -219,6 +234,12 @@ const duelSlice = createSlice({
resetPositionModal: resetPositionModalImpl,
setOptionModalIsOpen: setOptionModalIsOpenImpl,
resetOptionModal: resetOptionModalImpl,
setCheckCardModalV2FinishAble: setCheckCardModalV2FinishAbleImpl,
setCheckCardModalV2MinMax: setCheckCardModalV2MinMaxImpl,
setCheckCardModalV2CancelAble: setCheckCardModalV2CancelAbleImpl,
setCheckCardModalV2IsOpen: setCheckCardModalV2IsOpenImpl,
resetCheckCardModalV2: resetCheckCardModalV2Impl,
setCheckCardModalV2ResponseAble: setCheckCardModalV2ResponseAbleImpl,
},
extraReducers(builder) {
handsCase(builder);
......@@ -230,6 +251,7 @@ const duelSlice = createSlice({
checkCardModalCase(builder);
YesNoModalCase(builder);
optionModalCase(builder);
checkCardModalV2Case(builder);
},
});
......@@ -287,6 +309,12 @@ export const {
addFieldPlaceInteractivities,
addFieldIdleInteractivities,
clearFieldIdleInteractivities,
setCheckCardModalV2IsOpen,
setCheckCardModalV2MinMax,
setCheckCardModalV2CancelAble,
setCheckCardModalV2FinishAble,
resetCheckCardModalV2,
setCheckCardModalV2ResponseAble,
} = duelSlice.actions;
export const selectDuelHsStart = (state: RootState) => {
return state.duel.meInitInfo != null;
......
import { DuelState } from "../mod";
import { DuelReducer } from "../generic";
import {
ActionReducerMapBuilder,
CaseReducer,
createAsyncThunk,
} from "@reduxjs/toolkit";
import { fetchCard } from "../../../api/cards";
import { RootState } from "../../../store";
// 更新打开状态
export const setCheckCardModalV2IsOpenImpl: DuelReducer<boolean> = (
state,
action
) => {
state.modalState.checkCardModalV2.isOpen = action.payload;
};
// 更新选择数目
export const setCheckCardModalV2MinMaxImpl: DuelReducer<{
min: number;
max: number;
}> = (state, action) => {
state.modalState.checkCardModalV2.selectMin = action.payload.min;
state.modalState.checkCardModalV2.selectMax = action.payload.max;
};
// 更新是否可以取消
export const setCheckCardModalV2CancelAbleImpl: DuelReducer<boolean> = (
state,
action
) => {
state.modalState.checkCardModalV2.cancelAble = action.payload;
};
// 更新是否可以结束
export const setCheckCardModalV2FinishAbleImpl: DuelReducer<boolean> = (
state,
action
) => {
state.modalState.checkCardModalV2.finishAble = action.payload;
};
// 更新是否可以回应
export const setCheckCardModalV2ResponseAbleImpl: DuelReducer<boolean> = (
state,
action
) => {
state.modalState.checkCardModalV2.responseable = action.payload;
};
// 增加卡牌选项
export const fetchCheckCardMetasV2 = createAsyncThunk(
"duel/fetchCheckCardMetaV2",
async (param: {
controler: number;
selected: boolean;
options: { code: number; response: number }[];
}) => {
const metas = await Promise.all(
param.options.map(async (option) => {
return await fetchCard(option.code);
})
);
const response = {
controler: param.controler,
selected: param.selected,
metas,
};
return response;
}
);
export const checkCardModalV2Case = (
builder: ActionReducerMapBuilder<DuelState>
) => {
builder.addCase(fetchCheckCardMetasV2.pending, (state, action) => {
const selected = action.meta.arg.selected;
const options = action.meta.arg.options;
if (selected) {
state.modalState.checkCardModalV2.selectedOptions = options;
} else {
state.modalState.checkCardModalV2.selectableOptions = options;
}
});
builder.addCase(fetchCheckCardMetasV2.fulfilled, (state, action) => {
const selected = action.payload.selected;
const metas = action.payload.metas;
const options = selected
? state.modalState.checkCardModalV2.selectedOptions
: state.modalState.checkCardModalV2.selectableOptions;
options.forEach((option) => {
metas.forEach((meta) => {
if (option.code == meta.id) {
option.name = meta.text.name;
option.desc = meta.text.desc;
}
});
});
});
};
export const resetCheckCardModalV2Impl: CaseReducer<DuelState> = (state) => {
const modalState = state.modalState.checkCardModalV2;
modalState.isOpen = false;
modalState.finishAble = false;
modalState.cancelAble = false;
modalState.responseable = false;
modalState.selectableOptions = [];
modalState.selectedOptions = [];
};
export const selectCheckCardModalV2IsOpen = (state: RootState) =>
state.duel.modalState.checkCardModalV2.isOpen;
export const selectCheckCardModalV2MinMax = (state: RootState) => {
return {
min: state.duel.modalState.checkCardModalV2.selectMin || 0,
max: state.duel.modalState.checkCardModalV2.selectMax || 0,
};
};
export const selectCheckCardModalV2CancelAble = (state: RootState) =>
state.duel.modalState.checkCardModalV2.cancelAble;
export const selectCheckCardModalV2FinishAble = (state: RootState) =>
state.duel.modalState.checkCardModalV2.finishAble;
export const selectCheckCardModalV2ResponseAble = (state: RootState) =>
state.duel.modalState.checkCardModalV2.responseable;
export const selectCheckCardModalV2SelectAbleOptions = (state: RootState) =>
state.duel.modalState.checkCardModalV2.selectableOptions;
export const selectCheckCardModalV2SelectedOptions = (state: RootState) =>
state.duel.modalState.checkCardModalV2.selectedOptions;
......@@ -52,6 +52,27 @@ export interface ModalState {
isOpen: boolean;
options: { msg: string; response: number }[];
};
// 卡牌选择弹窗V2
checkCardModalV2: {
isOpen: boolean;
cancelAble: boolean;
finishAble: boolean;
selectMin?: number;
selectMax?: number;
responseable: boolean;
selectableOptions: {
code: number;
name?: string;
desc?: string;
response: number;
}[];
selectedOptions: {
code: number;
name?: string;
desc?: string;
response: number;
}[];
};
}
export * from "./cardModalSlice";
......@@ -60,3 +81,4 @@ export * from "./checkCardModalSlice";
export * from "./yesNoModalSlice";
export * from "./positionModalSlice";
export * from "./optionModalSlice";
export * from "./checkCardModalV2Slice";
......@@ -16,6 +16,7 @@ import onMsgSelectOption from "./selectOption";
import onMsgShuffleHand from "./shuffleHand";
import onMsgSelectBattleCmd from "./selectBattleCmd";
import onMsgPosChange from "./posChange";
import onMsgSelectUnselectCard from "./selectUnselectCard";
export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
const dispatch = store.dispatch;
......@@ -102,6 +103,11 @@ export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
break;
}
case "select_unselect_card": {
onMsgSelectUnselectCard(msg.select_unselect_card, dispatch);
break;
}
default: {
break;
}
......
import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import {
setCheckCardModalV2CancelAble,
setCheckCardModalV2FinishAble,
setCheckCardModalV2IsOpen,
setCheckCardModalV2MinMax,
setCheckCardModalV2ResponseAble,
} from "../../reducers/duel/mod";
import { fetchCheckCardMetasV2 } from "../../reducers/duel/modal/checkCardModalV2Slice";
import { AppDispatch } from "../../store";
import MsgSelectUnselectCard = ygopro.StocGameMessage.MsgSelectUnselectCard;
......@@ -6,5 +14,38 @@ export default (
selectUnselectCard: MsgSelectUnselectCard,
dispatch: AppDispatch
) => {
console.log(selectUnselectCard);
const controler = selectUnselectCard.player;
const finishable = selectUnselectCard.finishable;
const cancelable = selectUnselectCard.cancelable;
const min = selectUnselectCard.min;
const max = selectUnselectCard.max;
const selectableCards = selectUnselectCard.selectable_cards;
const selectedCards = selectUnselectCard.selected_cards;
dispatch(setCheckCardModalV2IsOpen(true));
dispatch(setCheckCardModalV2FinishAble(finishable));
dispatch(setCheckCardModalV2CancelAble(cancelable));
dispatch(setCheckCardModalV2MinMax({ min, max }));
dispatch(
fetchCheckCardMetasV2({
controler,
selected: false,
options: selectableCards.map((card) => {
return { code: card.code, response: card.response };
}),
})
);
dispatch(
fetchCheckCardMetasV2({
controler,
selected: true,
options: selectedCards.map((card) => {
return { code: card.code, response: card.response };
}),
})
);
dispatch(setCheckCardModalV2ResponseAble(true));
};
import React from "react";
import { useAppSelector } from "../../hook";
import { store } from "../../store";
import { Modal, Button, Card, Row, Col } from "antd";
import { CheckCard } from "@ant-design/pro-components";
import {
selectCheckCardModalV2CancelAble,
selectCheckCardModalV2FinishAble,
selectCheckCardModalV2IsOpen,
selectCheckCardModalV2MinMax,
selectCheckCardModalV2ResponseAble,
selectCheckCardModalV2SelectAbleOptions,
selectCheckCardModalV2SelectedOptions,
} from "../../reducers/duel/modal/checkCardModalV2Slice";
import { sendSelectUnselectCardResponse } from "../../api/ocgcore/ocgHelper";
import {
resetCheckCardModalV2,
setCheckCardModalV2IsOpen,
setCheckCardModalV2ResponseAble,
} from "../../reducers/duel/mod";
const CheckCardModalV2 = () => {
const dispatch = store.dispatch;
const isOpen = useAppSelector(selectCheckCardModalV2IsOpen);
const { min, max } = useAppSelector(selectCheckCardModalV2MinMax);
const cancelable = useAppSelector(selectCheckCardModalV2CancelAble);
const finishable = useAppSelector(selectCheckCardModalV2FinishAble);
const selectableOptions = useAppSelector(
selectCheckCardModalV2SelectAbleOptions
);
const selectedOptions = useAppSelector(selectCheckCardModalV2SelectedOptions);
const responseable = useAppSelector(selectCheckCardModalV2ResponseAble);
const onFinish = () => {
sendSelectUnselectCardResponse({ cancel_or_finish: true });
dispatch(setCheckCardModalV2IsOpen(false));
dispatch(resetCheckCardModalV2());
dispatch(setCheckCardModalV2ResponseAble(false));
};
const onCancel = () => {
sendSelectUnselectCardResponse({ cancel_or_finish: true });
dispatch(setCheckCardModalV2ResponseAble(false));
};
return (
<Modal
title={`请选择未选择的卡片,最少${min}张,最多${max}张`}
open={isOpen}
closable={false}
footer={
<>
<Button disabled={!finishable || !responseable} onClick={onFinish}>
finish
</Button>
<Button disabled={!cancelable || !responseable} onClick={onCancel}>
cancel
</Button>
</>
}
width={800}
>
<CheckCard.Group
bordered
size="small"
onChange={(value) => {
if (responseable) {
dispatch(setCheckCardModalV2IsOpen(false));
// @ts-ignore
sendSelectUnselectCardResponse({ selected_ptr: value });
dispatch(setCheckCardModalV2ResponseAble(false));
}
}}
>
<Row>
{selectableOptions.map((option, idx) => {
return (
<Col span={4} key={idx}>
<CheckCard
title={option.name}
description={option.desc}
style={{ width: 120 }}
cover={
<img
alt={option.code.toString()}
src={`https://cdn02.moecube.com:444/images/ygopro-images-zh-CN/${option.code}.jpg`}
style={{ width: 100 }}
/>
}
value={option.response}
/>
</Col>
);
})}
</Row>
</CheckCard.Group>
<p>已经选择的卡片</p>
<Row>
{selectedOptions.map((option, idx) => {
return (
<Col span={4} key={idx}>
<Card
hoverable
style={{ width: 120 }}
cover={
<img
alt={option.code.toString()}
src={`https://cdn02.moecube.com:444/images/ygopro-images-zh-CN/${option.code}.jpg`}
/>
}
/>
</Col>
);
})}
</Row>
</Modal>
);
};
export default CheckCardModalV2;
......@@ -18,6 +18,7 @@ import YesNoModal from "./yesNoModal";
import PositionModal from "./positionModal";
import OptionModal from "./optionModal";
import Phase from "./phase";
import CheckCardModalV2 from "./checkCardModalV2";
// Ref: https://github.com/brianzinn/react-babylonjs/issues/126
const NeosDuel = () => (
......@@ -51,6 +52,7 @@ const NeosDuel = () => (
<YesNoModal />
<PositionModal />
<OptionModal />
<CheckCardModalV2 />
</>
);
......
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