feat(game): 优化界面组件
- 在国际化文件中添加钱包流水相关翻译项 - 在用户个人资料页面添加复制邀请链接功能 - 优化桌面端动物组件的视觉效果和动画参数 - 添加虚拟滚动功能到财务记录标签页提升性能 - 为桌面端控制面板添加投注数量调节按钮 - 更新消息模态框为通知列表和详情展示 - 在头部余额显示旁添加充值图标入口
This commit is contained in:
@@ -24,10 +24,16 @@ export interface GameAutoHostingStoreState {
|
||||
balanceAfterBet: number | null
|
||||
completedRounds: number
|
||||
isHosting: boolean
|
||||
lastIsJackpot: boolean | null
|
||||
lastSingleWinAmount: number | null
|
||||
lastSubmittedRoundId: string | null
|
||||
rules: AutoHostingStopRules
|
||||
selections: BetSelection[]
|
||||
markRoundSubmitted: (roundId: string, balanceAfterBet: number | null) => void
|
||||
recordBetWin: (input: {
|
||||
isJackpot: boolean
|
||||
singleWinAmount: number | null
|
||||
}) => void
|
||||
startHosting: (input: StartAutoHostingInput) => void
|
||||
stopHosting: () => void
|
||||
}
|
||||
@@ -49,6 +55,8 @@ export const useGameAutoHostingStore = create<GameAutoHostingStoreState>()(
|
||||
balanceAfterBet: null,
|
||||
completedRounds: 0,
|
||||
isHosting: false,
|
||||
lastIsJackpot: null,
|
||||
lastSingleWinAmount: null,
|
||||
lastSubmittedRoundId: null,
|
||||
rules: DEFAULT_AUTO_HOSTING_RULES,
|
||||
selections: [],
|
||||
@@ -65,11 +73,25 @@ export const useGameAutoHostingStore = create<GameAutoHostingStoreState>()(
|
||||
}
|
||||
})
|
||||
},
|
||||
recordBetWin: ({ isJackpot, singleWinAmount }) => {
|
||||
set((state) => {
|
||||
if (!state.isHosting) {
|
||||
return state
|
||||
}
|
||||
|
||||
return {
|
||||
lastIsJackpot: isJackpot,
|
||||
lastSingleWinAmount: singleWinAmount,
|
||||
}
|
||||
})
|
||||
},
|
||||
startHosting: ({ balanceAfterBet, rules, selections }) => {
|
||||
set({
|
||||
balanceAfterBet,
|
||||
completedRounds: 0,
|
||||
isHosting: true,
|
||||
lastIsJackpot: null,
|
||||
lastSingleWinAmount: null,
|
||||
lastSubmittedRoundId: null,
|
||||
rules,
|
||||
selections: selections.map((selection) => ({
|
||||
@@ -82,6 +104,8 @@ export const useGameAutoHostingStore = create<GameAutoHostingStoreState>()(
|
||||
balanceAfterBet: null,
|
||||
completedRounds: 0,
|
||||
isHosting: false,
|
||||
lastIsJackpot: null,
|
||||
lastSingleWinAmount: null,
|
||||
lastSubmittedRoundId: null,
|
||||
selections: [],
|
||||
})
|
||||
|
||||
@@ -31,6 +31,8 @@ type GameRoundSlice = Pick<
|
||||
| 'trends'
|
||||
>
|
||||
|
||||
const MIN_BET_QUANTITY = 1
|
||||
|
||||
export type RevealAnimationPhase = 'idle' | 'spinning' | 'stopping' | 'result'
|
||||
export type RewardAnimationType = 'none' | 'small' | 'big'
|
||||
|
||||
@@ -102,8 +104,49 @@ function resolveRecentActiveChipId(
|
||||
DEFAULT_ACTIVE_CHIP_ID)
|
||||
}
|
||||
|
||||
function normalizeBetQuantity(quantity: number) {
|
||||
if (!Number.isFinite(quantity)) {
|
||||
return MIN_BET_QUANTITY
|
||||
}
|
||||
|
||||
return Math.max(MIN_BET_QUANTITY, Math.floor(quantity))
|
||||
}
|
||||
|
||||
function getSelectionBetAmount(chip: Chip, quantity: number) {
|
||||
return chip.amount * normalizeBetQuantity(quantity)
|
||||
}
|
||||
|
||||
function syncSelectionsBetAmount(
|
||||
selections: BetSelection[],
|
||||
chipId: string,
|
||||
amount: number,
|
||||
) {
|
||||
return selections.map((selection) => ({
|
||||
...selection,
|
||||
amount,
|
||||
chipId,
|
||||
}))
|
||||
}
|
||||
|
||||
function resolveSelectionQuantity(
|
||||
selections: BetSelection[],
|
||||
chips: Chip[],
|
||||
activeChipId: string,
|
||||
) {
|
||||
const chip = getChipById(chips, activeChipId)
|
||||
const firstSelection = selections[0]
|
||||
|
||||
if (!chip || !firstSelection || chip.amount <= 0) {
|
||||
return MIN_BET_QUANTITY
|
||||
}
|
||||
|
||||
return normalizeBetQuantity(firstSelection.amount / chip.amount)
|
||||
}
|
||||
|
||||
export interface GameRoundStoreState extends GameRoundSlice {
|
||||
activeChipId: string
|
||||
activeBetQuantity: number
|
||||
adjustBetQuantity: (delta: number) => void
|
||||
clearSelections: () => void
|
||||
clearRewardAnimation: () => void
|
||||
finishRevealAnimation: () => void
|
||||
@@ -135,6 +178,7 @@ export interface GameRoundStoreState extends GameRoundSlice {
|
||||
|
||||
function createInitialRoundState(): GameRoundSlice & {
|
||||
activeChipId: string
|
||||
activeBetQuantity: number
|
||||
recentSuccessfulSelections: BetSelection[]
|
||||
revealAnimation: RevealAnimationState
|
||||
} {
|
||||
@@ -142,6 +186,7 @@ function createInitialRoundState(): GameRoundSlice & {
|
||||
|
||||
return {
|
||||
activeChipId: DEFAULT_ACTIVE_CHIP_ID,
|
||||
activeBetQuantity: MIN_BET_QUANTITY,
|
||||
cells: snapshot.cells,
|
||||
chips: snapshot.chips,
|
||||
history: snapshot.history,
|
||||
@@ -156,6 +201,30 @@ function createInitialRoundState(): GameRoundSlice & {
|
||||
|
||||
export const useGameRoundStore = create<GameRoundStoreState>()((set) => ({
|
||||
...createInitialRoundState(),
|
||||
adjustBetQuantity: (delta) => {
|
||||
set((state) => {
|
||||
const activeChip =
|
||||
getChipById(state.chips, state.activeChipId) ??
|
||||
state.chips.find((chip) => chip.isDefault) ??
|
||||
state.chips[0]
|
||||
const nextQuantity = normalizeBetQuantity(state.activeBetQuantity + delta)
|
||||
|
||||
if (!activeChip) {
|
||||
return {
|
||||
activeBetQuantity: nextQuantity,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
activeBetQuantity: nextQuantity,
|
||||
selections: syncSelectionsBetAmount(
|
||||
state.selections,
|
||||
activeChip.id,
|
||||
getSelectionBetAmount(activeChip, nextQuantity),
|
||||
),
|
||||
}
|
||||
})
|
||||
},
|
||||
clearSelections: () => {
|
||||
set({ selections: [] })
|
||||
},
|
||||
@@ -211,24 +280,29 @@ export const useGameRoundStore = create<GameRoundStoreState>()((set) => ({
|
||||
})
|
||||
},
|
||||
hydrateRound: (snapshot) => {
|
||||
set((state) => ({
|
||||
activeChipId: getChipById(snapshot.chips, state.activeChipId)
|
||||
set((state) => {
|
||||
const nextActiveChipId = getChipById(snapshot.chips, state.activeChipId)
|
||||
? state.activeChipId
|
||||
: (snapshot.chips.find((chip) => chip.isDefault)?.id ??
|
||||
snapshot.chips[0]?.id ??
|
||||
DEFAULT_ACTIVE_CHIP_ID),
|
||||
cells: snapshot.cells,
|
||||
chips: snapshot.chips,
|
||||
history: snapshot.history,
|
||||
maxSelectionCount: snapshot.maxSelectionCount,
|
||||
revealAnimation:
|
||||
snapshot.round.phase === 'betting' || snapshot.round.phase === 'waiting'
|
||||
? createIdleRevealAnimationPreservingReward(state.revealAnimation)
|
||||
: state.revealAnimation,
|
||||
round: snapshot.round,
|
||||
selections: snapshot.selections,
|
||||
trends: snapshot.trends,
|
||||
}))
|
||||
DEFAULT_ACTIVE_CHIP_ID)
|
||||
|
||||
return {
|
||||
activeChipId: nextActiveChipId,
|
||||
cells: snapshot.cells,
|
||||
chips: snapshot.chips,
|
||||
history: snapshot.history,
|
||||
maxSelectionCount: snapshot.maxSelectionCount,
|
||||
revealAnimation:
|
||||
snapshot.round.phase === 'betting' ||
|
||||
snapshot.round.phase === 'waiting'
|
||||
? createIdleRevealAnimationPreservingReward(state.revealAnimation)
|
||||
: state.revealAnimation,
|
||||
round: snapshot.round,
|
||||
selections: snapshot.selections,
|
||||
trends: snapshot.trends,
|
||||
}
|
||||
})
|
||||
},
|
||||
placeBet: (cellId) => {
|
||||
set((state) => {
|
||||
@@ -256,7 +330,7 @@ export const useGameRoundStore = create<GameRoundStoreState>()((set) => ({
|
||||
selections: [
|
||||
...state.selections,
|
||||
{
|
||||
amount: activeChip.amount,
|
||||
amount: getSelectionBetAmount(activeChip, state.activeBetQuantity),
|
||||
cellId,
|
||||
chipId: activeChip.id,
|
||||
id: `bet-${cellId}-${state.selections.length + 1}-${Date.now()}`,
|
||||
@@ -337,13 +411,28 @@ export const useGameRoundStore = create<GameRoundStoreState>()((set) => ({
|
||||
return false
|
||||
}
|
||||
|
||||
const nextActiveChipId = resolveRecentActiveChipId(
|
||||
state.chips,
|
||||
nextSelections,
|
||||
state.activeChipId,
|
||||
)
|
||||
const nextActiveChip = getChipById(state.chips, nextActiveChipId)
|
||||
const nextBetQuantity = resolveSelectionQuantity(
|
||||
nextSelections,
|
||||
state.chips,
|
||||
nextActiveChipId,
|
||||
)
|
||||
|
||||
set({
|
||||
activeChipId: resolveRecentActiveChipId(
|
||||
state.chips,
|
||||
nextSelections,
|
||||
state.activeChipId,
|
||||
),
|
||||
selections: nextSelections,
|
||||
activeBetQuantity: nextBetQuantity,
|
||||
activeChipId: nextActiveChipId,
|
||||
selections: nextActiveChip
|
||||
? syncSelectionsBetAmount(
|
||||
nextSelections,
|
||||
nextActiveChipId,
|
||||
getSelectionBetAmount(nextActiveChip, nextBetQuantity),
|
||||
)
|
||||
: nextSelections,
|
||||
})
|
||||
|
||||
return true
|
||||
@@ -357,11 +446,21 @@ export const useGameRoundStore = create<GameRoundStoreState>()((set) => ({
|
||||
},
|
||||
selectChip: (chipId) => {
|
||||
set((state) => {
|
||||
if (!getChipById(state.chips, chipId)) {
|
||||
const nextChip = getChipById(state.chips, chipId)
|
||||
|
||||
if (!nextChip) {
|
||||
return state
|
||||
}
|
||||
|
||||
return { activeChipId: chipId }
|
||||
return {
|
||||
activeBetQuantity: MIN_BET_QUANTITY,
|
||||
activeChipId: chipId,
|
||||
selections: syncSelectionsBetAmount(
|
||||
state.selections,
|
||||
chipId,
|
||||
getSelectionBetAmount(nextChip, MIN_BET_QUANTITY),
|
||||
),
|
||||
}
|
||||
})
|
||||
},
|
||||
setPhase: (phase) => {
|
||||
|
||||
Reference in New Issue
Block a user