feat(state): 迁移 lobbyLoader/purchaseDialogState/promoCode/supportUrl/rulesUpdated/notificationByCountry/availabilityByRank
This commit is contained in:
1 parent
df179dea51
commit
49e8c1efc4
14 files changed
+716
-48
No files matched your search
@@ -143,7 +143,7 @@
|
||||
`names/root_wiring.json` + `TankiOffline/.../App/State/RootState.kt`、`RootWiring.kt`。
|
||||
- 根状态 `z6`(`TOState`)的 **89 个槽位**全部入表;字段顺序取 `toString`,槽位取构造函数 `<名>_1`。
|
||||
- 根 reducer `k6` 的逐切片委派记全:reducer 引用、委派表达式、顶层 `instanceof` 动作名。
|
||||
- 已迁移切片 **21/89**:
|
||||
- 已迁移切片 **28/89**:
|
||||
- `inviteState` → `aD` / `pD.er5`(`InviteReducer.kt`)
|
||||
- `loginByPassword` → `gD` / `MD.cr6`(`LoginReducer.kt`,含 `SetLogin`/`SetPassword` 把 status 打回 `NONE` 的原语义)
|
||||
- `twitchState` → `g9` / `y9.gta`(`TwitchReducer.kt`;`SetAccount` 是 Twitch 账号,不是 UID)
|
||||
@@ -224,12 +224,28 @@
|
||||
`Hrt` 类体为空、reducer 读的 `t.iu5_1` 是 `undefined`(`cd` 也是 `undefined`),三个 copy 参数全走默认值,
|
||||
这一支实际是 no-op;Kotlin 侧保留 `SetNotCompletedQuests` 动作但归约时原样返回。
|
||||
`tX` / `nX` 是屏幕标识类,已从动作表剔除并记入 `Markers`(`screen-id-class`)。
|
||||
- `lobbyLoader` → `KW` / `Bs.es1`(`LobbyLoaderReducer.kt`):2 字段,3 个分支。
|
||||
缺省 `isShowLoader=false`、`activeLoadersCount=0`;`FW`→`ShowLoader` 计数 +1 并显示,
|
||||
`GW`→`HideLoader` 计数 `max(count-1,0)`,显示取 `count != 0`(计数夹到 0 后自动隐藏)。
|
||||
- `purchaseDialogState` → `CO` / `je.ur2`(`PurchaseDialogReducer.kt`):4 字段,4 个分支。
|
||||
`NO`→`ResetPurchaseDialog` 复位四字段;`ChangeEmailStatus` 只写 `emailStatus`(`PurchaseEmailStatus`:
|
||||
`BUSY/FORBIDDEN/FREE`);`SuccessfullyPurchase` 写 withEmail/time,`goods` 是**追加**(`Im` = List.plus)。
|
||||
- `promoCode` → `cZ` / `Q_.ysg`(`PromoCodeReducer.kt`):1 字段,2 个实例臂。
|
||||
`ActivatePromoCode` 置 `WAIT`,`SetCodeValidateStatus` 写入服务端状态;
|
||||
`PromoCodeStatus` = `NONE/INVALID/WAIT/NOT_ACTIVATED/ACTIVATED`,缺省 `NONE`。
|
||||
- `supportUrl` → `I4` / `Yl.ct7`(`SupportUrlReducer.kt`):1 字段,2 个分支。
|
||||
`SetUrl` 写链接,`R4`→`ClearSupportUrl` 清成空串。
|
||||
- `rulesUpdated` → `i1` / `Ch.dsm`(`RulesUpdatedReducer.kt`):1 字段,`UpdateRules` 写正文,缺省 null。
|
||||
- `notificationByCountry` → `gJ` / `R_.ssd`(`NotificationByCountryReducer.kt`):4 字段,2 个分支。
|
||||
`Init` 写三条国家提示消息,`qJ`→`MarkHomeScreenDialogShown` 只把 `homeScreenDialogShown` 置 true。
|
||||
- `availabilityByRank` → `tet` / `$p.xth`(`AvailabilityByRankReducer.kt`):1 字段,
|
||||
`SetLoadedData` 整体替换等级可购买表,缺省空 Map。
|
||||
- `launchParams` → `(pass-through)`:原根 reducer 对 `hpx_1` 没有调用,缺省是 `ag()` 空 Map;
|
||||
`RootState.LaunchParams` 收口为 `LaunchParamsStateSlice = Map<String, String>`,不新增 reducer/动作。
|
||||
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 20 个有 reducer 的切片,
|
||||
`launchParams` 与其余 **68 个字段原样透传**;
|
||||
迁移进度用 `RootReducer.Coverage`(`已迁移 21/89`)与 `RootWiring.Unmigrated` 暴露。
|
||||
- 自检:`TankiOffline/tools/RunStateTest.ps1` **376 项全绿**,含 89 槽位完整性、20 个归约切片一次归约互不串片、未迁移字段透传,
|
||||
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 27 个有 reducer 的切片,
|
||||
`launchParams` 与其余 **61 个字段原样透传**;
|
||||
迁移进度用 `RootReducer.Coverage`(`已迁移 28/89`)与 `RootWiring.Unmigrated` 暴露。
|
||||
- 自检:`TankiOffline/tools/RunStateTest.ps1` **419 项全绿**,含 89 槽位完整性、27 个归约切片一次归约互不串片、未迁移字段透传,
|
||||
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
|
||||
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
|
||||
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
|
||||
@@ -241,7 +257,8 @@
|
||||
screen 的 9 个、lootBoxes 的 6 个、garage 的 25 个、garagePreview 的 15 个、
|
||||
entrance 的 6 个、user 的 3 个、settings 的 2 个、accountSettings 的 11 个、
|
||||
language 的 1 个、announcement 的 3 个、news 的 3 个(含两个单例)、notifications 的 2 个、
|
||||
quests 的 1 个匿名类 / 单例同样按效果定名并登记。
|
||||
quests 的 1 个、lobbyLoader 的 2 个、purchaseDialogState 的 1 个、supportUrl 的 1 个、
|
||||
notificationByCountry 的 1 个匿名类 / 单例同样按效果定名并登记。
|
||||
|
||||
### S4 引擎核心
|
||||
- 模块:32088(3D 数学)、92980(渲染)、61378(ECS)、55088(物理)、36659 + 71105(资源)、36734(音频)、3569(输入)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"generatedAt": "2026-10-24T05:13:42.080Z",
|
||||
"generatedAt": "2026-10-24T05:17:45.346Z",
|
||||
"module": "module_10032.js",
|
||||
"RootState": {
|
||||
"Class": "z6",
|
||||
@@ -27,7 +27,14 @@
|
||||
"launchParams",
|
||||
"news",
|
||||
"notifications",
|
||||
"quests"
|
||||
"quests",
|
||||
"lobbyLoader",
|
||||
"purchaseDialogState",
|
||||
"promoCode",
|
||||
"supportUrl",
|
||||
"rulesUpdated",
|
||||
"notificationByCountry",
|
||||
"availabilityByRank"
|
||||
],
|
||||
"AnonymousActions": [
|
||||
{
|
||||
@@ -660,6 +667,36 @@
|
||||
"Name": "SetNotCompletedQuests",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "状态不变(`n.au5(cd, cd, t.iu5_1)`;动作类无字段,`t.iu5_1` 与 `cd` 都是 undefined,三个参数全走默认值 → no-op)"
|
||||
},
|
||||
{
|
||||
"Local": "FW",
|
||||
"Name": "ShowLoader",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "isShowLoader=true、activeLoadersCount = count + 1(`n.fs1(!0, (n.ss0_1 + 1) | 0)`)"
|
||||
},
|
||||
{
|
||||
"Local": "GW",
|
||||
"Name": "HideLoader",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "activeLoadersCount = max(count - 1, 0)、isShowLoader = count != 0(`n.fs1(!(0 === e), e)`)"
|
||||
},
|
||||
{
|
||||
"Local": "NO",
|
||||
"Name": "ResetPurchaseDialog",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "withEmail=false、goods=emptyList()、emailStatus=null、time=null(`n.jr2(!1, md(), null, null)`)"
|
||||
},
|
||||
{
|
||||
"Local": "R4",
|
||||
"Name": "ClearSupportUrl",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "url=\"\"(`n.at7()`)"
|
||||
},
|
||||
{
|
||||
"Local": "qJ",
|
||||
"Name": "MarkHomeScreenDialogShown",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "homeScreenDialogShown=true(`n.isd(cd, cd, cd, !0)`)"
|
||||
}
|
||||
],
|
||||
"Markers": [
|
||||
@@ -1495,11 +1532,11 @@
|
||||
"Reducer": "Bs.es1",
|
||||
"Delegate": "Bs.es1(t, n.fpw_1)",
|
||||
"Actions": [
|
||||
"?FW",
|
||||
"?GW"
|
||||
"HideLoader",
|
||||
"ShowLoader"
|
||||
],
|
||||
"Default": "new KW()",
|
||||
"Migrated": false
|
||||
"Migrated": true
|
||||
},
|
||||
{
|
||||
"Field": "battleResult",
|
||||
@@ -1901,12 +1938,12 @@
|
||||
"Reducer": "je.ur2",
|
||||
"Delegate": "je.ur2(t, n.bpx_1)",
|
||||
"Actions": [
|
||||
"?NO",
|
||||
"ChangeEmailStatus",
|
||||
"ResetPurchaseDialog",
|
||||
"SuccessfullyPurchase"
|
||||
],
|
||||
"Default": "new CO()",
|
||||
"Migrated": false
|
||||
"Migrated": true
|
||||
},
|
||||
{
|
||||
"Field": "battleInviteState",
|
||||
@@ -2102,7 +2139,7 @@
|
||||
"UpdateRules"
|
||||
],
|
||||
"Default": "new i1()",
|
||||
"Migrated": false
|
||||
"Migrated": true
|
||||
},
|
||||
{
|
||||
"Field": "announcement",
|
||||
@@ -2126,11 +2163,11 @@
|
||||
"Reducer": "R_.ssd",
|
||||
"Delegate": "R_.ssd(t, n.ppx_1)",
|
||||
"Actions": [
|
||||
"?qJ",
|
||||
"Init"
|
||||
"Init",
|
||||
"MarkHomeScreenDialogShown"
|
||||
],
|
||||
"Default": "new gJ()",
|
||||
"Migrated": false
|
||||
"Migrated": true
|
||||
},
|
||||
{
|
||||
"Field": "userRenameState",
|
||||
@@ -2319,7 +2356,7 @@
|
||||
"SetLoadedData"
|
||||
],
|
||||
"Default": "new tet()",
|
||||
"Migrated": false
|
||||
"Migrated": true
|
||||
},
|
||||
{
|
||||
"Field": "userReportsState",
|
||||
@@ -2382,7 +2419,7 @@
|
||||
"SetCodeValidateStatus"
|
||||
],
|
||||
"Default": "new cZ()",
|
||||
"Migrated": false
|
||||
"Migrated": true
|
||||
},
|
||||
{
|
||||
"Field": "referralEvent",
|
||||
@@ -2406,11 +2443,11 @@
|
||||
"Reducer": "Yl.ct7",
|
||||
"Delegate": "Yl.ct7(t, n.ipy_1)",
|
||||
"Actions": [
|
||||
"?R4",
|
||||
"ClearSupportUrl",
|
||||
"SetUrl"
|
||||
],
|
||||
"Default": "new I4()",
|
||||
"Migrated": false
|
||||
"Migrated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -170,6 +170,15 @@ const AnonymousActionNames = new Map([
|
||||
['hJ', 'SetNewsFlag'],
|
||||
['x_', 'OpenNews'],
|
||||
['j_', 'CloseNews'],
|
||||
// lobbyLoader 切片(KW / VW.es1):FW/GW 没有 toString,按 reducer 效果定名。
|
||||
['FW', 'ShowLoader'],
|
||||
['GW', 'HideLoader'],
|
||||
// purchaseDialogState 切片(CO / DO.ur2):NO 没有 toString,按 reducer 效果定名。
|
||||
['NO', 'ResetPurchaseDialog'],
|
||||
// supportUrl 切片(I4 / P4.ct7):R4 没有 toString,按 reducer 效果定名。
|
||||
['R4', 'ClearSupportUrl'],
|
||||
// notificationByCountry 切片(gJ / $J.ssd):qJ 没有 toString,按 reducer 效果定名。
|
||||
['qJ', 'MarkHomeScreenDialogShown'],
|
||||
]);
|
||||
|
||||
// 非动作的 `instanceof`:屏幕标识类,用于 GoTo 分支判断(不是 dispatch 的动作)。
|
||||
@@ -573,6 +582,16 @@ const Migrated = {
|
||||
Reduce: 'ReduceNotifications',
|
||||
},
|
||||
quests: { Type: 'QuestsStateSlice', Reduce: 'ReduceQuests' },
|
||||
lobbyLoader: { Type: 'LobbyLoaderStateSlice', Reduce: 'ReduceLobbyLoader' },
|
||||
purchaseDialogState: { Type: 'PurchaseDialogStateSlice', Reduce: 'ReducePurchaseDialog' },
|
||||
promoCode: { Type: 'PromoCodeStateSlice', Reduce: 'ReducePromoCode' },
|
||||
supportUrl: { Type: 'SupportUrlStateSlice', Reduce: 'ReduceSupportUrl' },
|
||||
rulesUpdated: { Type: 'RulesUpdatedStateSlice', Reduce: 'ReduceRulesUpdated' },
|
||||
notificationByCountry: {
|
||||
Type: 'NotificationByCountryStateSlice',
|
||||
Reduce: 'ReduceNotificationByCountry',
|
||||
},
|
||||
availabilityByRank: { Type: 'AvailabilityByRankStateSlice', Reduce: 'ReduceAvailabilityByRank' },
|
||||
};
|
||||
|
||||
for (const R of Rows) R.Migrated = Object.prototype.hasOwnProperty.call(Migrated, R.Field);
|
||||
@@ -759,6 +778,27 @@ const QuestsAnonymousEffects = {
|
||||
Hrt: '状态不变(`n.au5(cd, cd, t.iu5_1)`;动作类无字段,`t.iu5_1` 与 `cd` 都是 undefined,三个参数全走默认值 → no-op)',
|
||||
};
|
||||
|
||||
// lobbyLoader 切片(KW / VW.es1):FW/GW 没有 toString,按 reducer 分支效果定名。
|
||||
const LobbyLoaderAnonymousEffects = {
|
||||
FW: 'isShowLoader=true、activeLoadersCount = count + 1(`n.fs1(!0, (n.ss0_1 + 1) | 0)`)',
|
||||
GW: 'activeLoadersCount = max(count - 1, 0)、isShowLoader = count != 0(`n.fs1(!(0 === e), e)`)',
|
||||
};
|
||||
|
||||
// purchaseDialogState 切片(CO / DO.ur2):NO 没有 toString,按 reducer 分支效果定名。
|
||||
const PurchaseDialogAnonymousEffects = {
|
||||
NO: 'withEmail=false、goods=emptyList()、emailStatus=null、time=null(`n.jr2(!1, md(), null, null)`)',
|
||||
};
|
||||
|
||||
// supportUrl 切片(I4 / P4.ct7):R4 没有 toString,按 reducer 分支效果定名。
|
||||
const SupportUrlAnonymousEffects = {
|
||||
R4: 'url=""(`n.at7()`)',
|
||||
};
|
||||
|
||||
// notificationByCountry 切片(gJ / $J.ssd):qJ 没有 toString,按 reducer 分支效果定名。
|
||||
const NotificationByCountryAnonymousEffects = {
|
||||
qJ: 'homeScreenDialogShown=true(`n.isd(cd, cd, cd, !0)`)',
|
||||
};
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(P, 'names', 'root_wiring.json'),
|
||||
JSON.stringify(
|
||||
@@ -859,6 +899,30 @@ fs.writeFileSync(
|
||||
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
|
||||
Effect,
|
||||
})),
|
||||
...Object.entries(LobbyLoaderAnonymousEffects).map(([Local, Effect]) => ({
|
||||
Local,
|
||||
Name: AnonymousActionNames.get(Local),
|
||||
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
|
||||
Effect,
|
||||
})),
|
||||
...Object.entries(PurchaseDialogAnonymousEffects).map(([Local, Effect]) => ({
|
||||
Local,
|
||||
Name: AnonymousActionNames.get(Local),
|
||||
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
|
||||
Effect,
|
||||
})),
|
||||
...Object.entries(SupportUrlAnonymousEffects).map(([Local, Effect]) => ({
|
||||
Local,
|
||||
Name: AnonymousActionNames.get(Local),
|
||||
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
|
||||
Effect,
|
||||
})),
|
||||
...Object.entries(NotificationByCountryAnonymousEffects).map(([Local, Effect]) => ({
|
||||
Local,
|
||||
Name: AnonymousActionNames.get(Local),
|
||||
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
|
||||
Effect,
|
||||
})),
|
||||
],
|
||||
Markers: [
|
||||
{
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// AvailabilityByRankReducer.kt —— 按等级可购买项切片(对应 module_10032 的 tet 状态 + set.xth)
|
||||
//
|
||||
// 证据链:
|
||||
// 状态类 tet:1 个字段,toString 名 "AvailabilityByRankState(data=…)";copy 是 pu5(1 字段)。
|
||||
// 默认:data = ag() = emptyMap()。
|
||||
// 根委派:`$p.xth(t, n.cpy_1)`。
|
||||
// reducer:`t instanceof eet ? n.pu5(t.wu5_1) : n`,`eet` 是 SetLoadedData。
|
||||
package App.State.Reducers
|
||||
|
||||
import App.State.SetLoadedData
|
||||
|
||||
/* ------------------------------------------------------------------ 状态 */
|
||||
|
||||
data class AvailabilityByRankStateSlice(
|
||||
val Data: Map<Any?, Any?> = emptyMap(),
|
||||
)
|
||||
|
||||
/* ------------------------------------------------------------------ reducer */
|
||||
|
||||
fun ReduceAvailabilityByRank(
|
||||
State0: AvailabilityByRankStateSlice,
|
||||
Action: Any,
|
||||
): AvailabilityByRankStateSlice = when (Action) {
|
||||
is SetLoadedData -> State0.copy(Data = Action.loadedData as Map<Any?, Any?>)
|
||||
else -> State0
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// LobbyLoaderReducer.kt —— 大厅加载遮罩切片(对应 module_10032 的 KW 状态 + VW.es1)
|
||||
//
|
||||
// 证据链:
|
||||
// 状态类 KW:2 个字段,toString 名 "LobbyLoader(isShowLoader=…, activeLoadersCount=…)";copy 是 fs1(2 字段)。
|
||||
// 默认:isShowLoader=false、activeLoadersCount=0。
|
||||
// 根委派:`Bs.es1(t, n.fpw_1)`。
|
||||
// reducer:
|
||||
// FW → `n.fs1(!0, (n.ss0_1 + 1) | 0)` 显示并计数 +1
|
||||
// GW → `n.fs1(!(0 === e), e)`,e = max(count - 1, 0) 计数 -1(夹到 0),显示 = 计数非 0
|
||||
package App.State.Reducers
|
||||
|
||||
/* ------------------------------------------------------------------ 匿名动作 */
|
||||
|
||||
/** 原 `FW`:有东西开始加载。 */
|
||||
object ShowLoader
|
||||
|
||||
/** 原 `GW`:有东西结束加载。 */
|
||||
object HideLoader
|
||||
|
||||
/* ------------------------------------------------------------------ 状态 */
|
||||
|
||||
data class LobbyLoaderStateSlice(
|
||||
val IsShowLoader: Boolean = false,
|
||||
val ActiveLoadersCount: Int = 0,
|
||||
)
|
||||
|
||||
/* ------------------------------------------------------------------ reducer */
|
||||
|
||||
fun ReduceLobbyLoader(
|
||||
State0: LobbyLoaderStateSlice,
|
||||
Action: Any,
|
||||
): LobbyLoaderStateSlice = when (Action) {
|
||||
is ShowLoader -> State0.copy(
|
||||
IsShowLoader = true,
|
||||
ActiveLoadersCount = State0.ActiveLoadersCount + 1,
|
||||
)
|
||||
is HideLoader -> {
|
||||
val Count = maxOf(State0.ActiveLoadersCount - 1, 0)
|
||||
State0.copy(IsShowLoader = Count != 0, ActiveLoadersCount = Count)
|
||||
}
|
||||
else -> State0
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// NotificationByCountryReducer.kt —— 按国家屏蔽提示切片(对应 module_10032 的 gJ 状态 + $J.ssd)
|
||||
//
|
||||
// 证据链:
|
||||
// 状态类 gJ:4 个字段,toString 名 "NotificationByCountry(...)";copy 是 isd(4 字段 + 回调)。
|
||||
// 默认:三条消息都是 null、homeScreenDialogShown=false。
|
||||
// 根委派:`R_.ssd(t, n.ppx_1)`。
|
||||
// reducer:
|
||||
// bJ(Init)→ `n.isd(t.psd_1, t.qsd_1, t.rsd_1)`,写三条消息
|
||||
// qJ → `n.isd(cd, cd, cd, !0)`,只把 homeScreenDialogShown 置 true
|
||||
package App.State.Reducers
|
||||
|
||||
import App.State.Init
|
||||
|
||||
/* ------------------------------------------------------------------ 匿名动作 */
|
||||
|
||||
/** 原 `qJ`:首页的国家提示弹窗已经展示过。 */
|
||||
object MarkHomeScreenDialogShown
|
||||
|
||||
/* ------------------------------------------------------------------ 状态 */
|
||||
|
||||
data class NotificationByCountryStateSlice(
|
||||
val DisableRegistrationMessage: String? = null,
|
||||
val DisableShopMessage: String? = null,
|
||||
val HomeScreenMessage: String? = null,
|
||||
val HomeScreenDialogShown: Boolean = false,
|
||||
)
|
||||
|
||||
/* ------------------------------------------------------------------ reducer */
|
||||
|
||||
fun ReduceNotificationByCountry(
|
||||
State0: NotificationByCountryStateSlice,
|
||||
Action: Any,
|
||||
): NotificationByCountryStateSlice = when (Action) {
|
||||
is Init -> State0.copy(
|
||||
DisableRegistrationMessage = Action.disableRegistrationMessage,
|
||||
DisableShopMessage = Action.disableShopMessage,
|
||||
HomeScreenMessage = Action.homeScreenMessage,
|
||||
)
|
||||
is MarkHomeScreenDialogShown -> State0.copy(HomeScreenDialogShown = true)
|
||||
else -> State0
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// PromoCodeReducer.kt —— 促销码切片(对应 module_10032 的 cZ 状态 + wZ.ysg)
|
||||
//
|
||||
// 证据链:
|
||||
// 状态类 cZ:1 个字段,toString 名 "PromoCodeState(...)";copy 是 vsg(1 字段)。
|
||||
// 默认:validateStatus = dZ() = NONE。
|
||||
// 根委派:`Q_.ysg(t, n.gpy_1)`。
|
||||
// reducer:
|
||||
// vZ → `n.vsg((lZ(), Y_))`,状态置 WAIT(提交校验中)
|
||||
// pZ → `n.vsg(t.xsg_1)`,写入服务端返回的状态
|
||||
// `dZ()` 是 `fZ` 枚举:NONE / INVALID / WAIT / NOT_ACTIVATED / ACTIVATED。
|
||||
package App.State.Reducers
|
||||
|
||||
import App.State.ActivatePromoCode
|
||||
import App.State.SetCodeValidateStatus
|
||||
|
||||
/* ------------------------------------------------------------------ 枚举 */
|
||||
|
||||
/** 原 `fZ`:促销码校验状态。 */
|
||||
enum class PromoCodeStatus { NONE, INVALID, WAIT, NOT_ACTIVATED, ACTIVATED }
|
||||
|
||||
/* ------------------------------------------------------------------ 状态 */
|
||||
|
||||
data class PromoCodeStateSlice(
|
||||
val ValidateStatus: PromoCodeStatus = PromoCodeStatus.NONE,
|
||||
)
|
||||
|
||||
/* ------------------------------------------------------------------ reducer */
|
||||
|
||||
fun ReducePromoCode(
|
||||
State0: PromoCodeStateSlice,
|
||||
Action: Any,
|
||||
): PromoCodeStateSlice = when (Action) {
|
||||
is ActivatePromoCode -> State0.copy(ValidateStatus = PromoCodeStatus.WAIT)
|
||||
is SetCodeValidateStatus -> State0.copy(ValidateStatus = Action.promoCodeStatus as PromoCodeStatus)
|
||||
else -> State0
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// PurchaseDialogReducer.kt —— 购买对话框切片(对应 module_10032 的 CO 状态 + DO.ur2)
|
||||
//
|
||||
// 证据链:
|
||||
// 状态类 CO:4 个字段,toString 名 "PurchaseDialogState(...)";copy 是 kr2(4 字段 + 回调)。
|
||||
// 默认:withEmail=false、goods=emptyList()、emailStatus=null、time=null。
|
||||
// 根委派:`je.ur2(t, n.bpx_1)`。
|
||||
// reducer:
|
||||
// NO → `n.jr2(!1, md(), null, null)`,四个字段全部复位
|
||||
// MO → `n.kr2(cd, cd, t.pr2_1)`,只写 emailStatus
|
||||
// OO → `n.kr2(u, Im(n.gr2_1, t.rr2_1), cd, s)`,withEmail/goods/time 三个字段;
|
||||
// `Im` 是 List.plus,goods 是「追加」而不是替换
|
||||
// emailStatus 是 `TO` 枚举(BUSY / FORBIDDEN / FREE)。
|
||||
package App.State.Reducers
|
||||
|
||||
import App.State.ChangeEmailStatus
|
||||
import App.State.SuccessfullyPurchase
|
||||
|
||||
/* ------------------------------------------------------------------ 匿名动作 */
|
||||
|
||||
/** 原 `NO`:关闭购买对话框并清空。 */
|
||||
object ResetPurchaseDialog
|
||||
|
||||
/* ------------------------------------------------------------------ 枚举 */
|
||||
|
||||
/** 原 `TO`:邮箱状态。 */
|
||||
enum class PurchaseEmailStatus { BUSY, FORBIDDEN, FREE }
|
||||
|
||||
/* ------------------------------------------------------------------ 状态 */
|
||||
|
||||
data class PurchaseDialogStateSlice(
|
||||
val WithEmail: Boolean = false,
|
||||
val Goods: List<Any?> = emptyList(),
|
||||
val EmailStatus: PurchaseEmailStatus? = null,
|
||||
val Time: Long? = null,
|
||||
)
|
||||
|
||||
/* ------------------------------------------------------------------ reducer */
|
||||
|
||||
fun ReducePurchaseDialog(
|
||||
State0: PurchaseDialogStateSlice,
|
||||
Action: Any,
|
||||
): PurchaseDialogStateSlice = when (Action) {
|
||||
is ResetPurchaseDialog ->
|
||||
PurchaseDialogStateSlice()
|
||||
is ChangeEmailStatus ->
|
||||
State0.copy(EmailStatus = Action.status as PurchaseEmailStatus?)
|
||||
is SuccessfullyPurchase -> State0.copy(
|
||||
WithEmail = Action.withEmail,
|
||||
Goods = State0.Goods + (Action.goods as List<Any?>),
|
||||
Time = Action.time,
|
||||
)
|
||||
else -> State0
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// RulesUpdatedReducer.kt —— 规则更新提示切片(对应 module_10032 的 i1 状态 + u1.dsm)
|
||||
//
|
||||
// 证据链:
|
||||
// 状态类 i1:1 个字段,toString 名 "RulesUpdatedState(text=…)";copy 是 bsm(1 字段)。
|
||||
// 默认:text=null。
|
||||
// 根委派:`Ch.dsm(t, n.npx_1)`。
|
||||
// reducer:`t instanceof e1 ? n.bsm(t.csm_1) : n`,`e1` 是 UpdateRules。
|
||||
package App.State.Reducers
|
||||
|
||||
import App.State.UpdateRules
|
||||
|
||||
/* ------------------------------------------------------------------ 状态 */
|
||||
|
||||
data class RulesUpdatedStateSlice(
|
||||
val Text: String? = null,
|
||||
)
|
||||
|
||||
/* ------------------------------------------------------------------ reducer */
|
||||
|
||||
fun ReduceRulesUpdated(
|
||||
State0: RulesUpdatedStateSlice,
|
||||
Action: Any,
|
||||
): RulesUpdatedStateSlice = when (Action) {
|
||||
is UpdateRules -> State0.copy(Text = Action.text as String?)
|
||||
else -> State0
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// SupportUrlReducer.kt —— 客服链接切片(对应 module_10032 的 I4 状态 + P4.ct7)
|
||||
//
|
||||
// 证据链:
|
||||
// 状态类 I4:1 个字段,toString 名 "SupportUrlState(url=…)";copy 是 at7(1 字段)。
|
||||
// 默认:url=""。
|
||||
// 根委派:`Yl.ct7(t, n.ipy_1)`。
|
||||
// reducer:
|
||||
// A4 → `n.zt6(t.bt7_1)`,写入链接
|
||||
// R4 → `n.at7()`,清成空串
|
||||
package App.State.Reducers
|
||||
|
||||
import App.State.SetUrl
|
||||
|
||||
/* ------------------------------------------------------------------ 匿名动作 */
|
||||
|
||||
/** 原 `R4`:清空客服链接。 */
|
||||
object ClearSupportUrl
|
||||
|
||||
/* ------------------------------------------------------------------ 状态 */
|
||||
|
||||
data class SupportUrlStateSlice(
|
||||
val Url: String = "",
|
||||
)
|
||||
|
||||
/* ------------------------------------------------------------------ reducer */
|
||||
|
||||
fun ReduceSupportUrl(
|
||||
State0: SupportUrlStateSlice,
|
||||
Action: Any,
|
||||
): SupportUrlStateSlice = when (Action) {
|
||||
is SetUrl -> State0.copy(Url = Action.url as String)
|
||||
is ClearSupportUrl -> State0.copy(Url = "")
|
||||
else -> State0
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// RootReducer.kt —— 根 reducer 骨架(对应 module_10032 的根状态 z6 / TOState 与根 reducer k6)
|
||||
//
|
||||
// 原包 k6 对 89 个切片逐个调用各自的 reducer,再 `return n.cti(...)`(copy 出新状态)。
|
||||
// 这里采用同一形状。已迁移的 21 个切片里,20 个真正调用 reducer,launchParams 只做类型收口:
|
||||
// 这里采用同一形状。已迁移的 28 个切片里,27 个真正调用 reducer,launchParams 只做类型收口:
|
||||
//
|
||||
// inviteState → ReduceInvite(aD / pD.er5)
|
||||
// loginByPassword → ReduceLoginByPassword(gD / MD.cr6)
|
||||
@@ -23,9 +23,16 @@
|
||||
// news → ReduceNews(ZX / E_.asd,根委派 `E_.asd(t, n.tpv_1)`)
|
||||
// notifications → ReduceNotifications(jJ / B_.ise,根委派 `B_.ise(t, n.xpv_1)`)
|
||||
// quests → ReduceQuests(Frt / Yrt.otg,根委派 `(null == zp && new Yrt(), zp).otg(t, n.npv_1)`)
|
||||
// lobbyLoader → ReduceLobbyLoader(KW / VW.es1,根委派 `Bs.es1(t, n.fpw_1)`)
|
||||
// purchaseDialogState → ReducePurchaseDialog(CO / DO.ur2,根委派 `je.ur2(t, n.bpx_1)`)
|
||||
// promoCode → ReducePromoCode(cZ / wZ.ysg,根委派 `Q_.ysg(t, n.gpy_1)`)
|
||||
// supportUrl → ReduceSupportUrl(I4 / P4.ct7,根委派 `Yl.ct7(t, n.ipy_1)`)
|
||||
// rulesUpdated → ReduceRulesUpdated(i1 / u1.dsm,根委派 `Ch.dsm(t, n.npx_1)`)
|
||||
// notificationByCountry → ReduceNotificationByCountry(gJ / $J.ssd,根委派 `R_.ssd(t, n.ppx_1)`)
|
||||
// availabilityByRank → ReduceAvailabilityByRank(tet / set.xth,根委派 `$p.xth(t, n.cpy_1)`)
|
||||
// launchParams → 无 reducer(原根 reducer 原样透传;默认 `ag()` = emptyMap())
|
||||
//
|
||||
// 其余 68 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||
// 其余 61 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
|
||||
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
|
||||
package App.State
|
||||
@@ -41,11 +48,18 @@ import App.State.Reducers.ReduceLanguage
|
||||
import App.State.Reducers.ReduceLocalBattleUser
|
||||
import App.State.Reducers.ReduceLoginByPassword
|
||||
import App.State.Reducers.ReduceLootBoxes
|
||||
import App.State.Reducers.ReduceAvailabilityByRank
|
||||
import App.State.Reducers.ReduceLobbyLoader
|
||||
import App.State.Reducers.ReduceNews
|
||||
import App.State.Reducers.ReduceNotificationByCountry
|
||||
import App.State.Reducers.ReduceNotifications
|
||||
import App.State.Reducers.ReduceProBattle
|
||||
import App.State.Reducers.ReducePromoCode
|
||||
import App.State.Reducers.ReducePurchaseDialog
|
||||
import App.State.Reducers.ReduceQuests
|
||||
import App.State.Reducers.ReduceRulesUpdated
|
||||
import App.State.Reducers.ReduceScreen
|
||||
import App.State.Reducers.ReduceSupportUrl
|
||||
import App.State.Reducers.ReduceSettings
|
||||
import App.State.Reducers.ReduceShop
|
||||
import App.State.Reducers.ReduceTwitch
|
||||
@@ -84,6 +98,13 @@ object RootReducer {
|
||||
News = ReduceNews(State0.News, Action),
|
||||
Notifications = ReduceNotifications(State0.Notifications, Action),
|
||||
Quests = ReduceQuests(State0.Quests, Action),
|
||||
LobbyLoader = ReduceLobbyLoader(State0.LobbyLoader, Action),
|
||||
PurchaseDialogState = ReducePurchaseDialog(State0.PurchaseDialogState, Action),
|
||||
PromoCode = ReducePromoCode(State0.PromoCode, Action),
|
||||
SupportUrl = ReduceSupportUrl(State0.SupportUrl, Action),
|
||||
RulesUpdated = ReduceRulesUpdated(State0.RulesUpdated, Action),
|
||||
NotificationByCountry = ReduceNotificationByCountry(State0.NotificationByCountry, Action),
|
||||
AvailabilityByRank = ReduceAvailabilityByRank(State0.AvailabilityByRank, Action),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,21 +6,28 @@ package App.State
|
||||
|
||||
import App.State.Reducers.AccountSettingsStateSlice
|
||||
import App.State.Reducers.AnnouncementStateSlice
|
||||
import App.State.Reducers.AvailabilityByRankStateSlice
|
||||
import App.State.Reducers.EntranceStateSlice
|
||||
import App.State.Reducers.GarageStateSlice
|
||||
import App.State.Reducers.GaragePreviewStateSlice
|
||||
import App.State.Reducers.HomeStateSlice
|
||||
import App.State.Reducers.InviteStateSlice
|
||||
import App.State.Reducers.LobbyLoaderStateSlice
|
||||
import App.State.Reducers.LocalBattleUserStateSlice
|
||||
import App.State.Reducers.LoginByPasswordStateSlice
|
||||
import App.State.Reducers.LootBoxesStateSlice
|
||||
import App.State.Reducers.NewsStateSlice
|
||||
import App.State.Reducers.NotificationByCountryStateSlice
|
||||
import App.State.Reducers.NotificationsStateSlice
|
||||
import App.State.Reducers.ProBattleStateSlice
|
||||
import App.State.Reducers.PromoCodeStateSlice
|
||||
import App.State.Reducers.PurchaseDialogStateSlice
|
||||
import App.State.Reducers.QuestsStateSlice
|
||||
import App.State.Reducers.RulesUpdatedStateSlice
|
||||
import App.State.Reducers.ScreenStateSlice
|
||||
import App.State.Reducers.SettingsStateSlice
|
||||
import App.State.Reducers.ShopStateSlice
|
||||
import App.State.Reducers.SupportUrlStateSlice
|
||||
import App.State.Reducers.TwitchStateSlice
|
||||
import App.State.Reducers.UserStateSlice
|
||||
|
||||
@@ -95,8 +102,8 @@ data class RootState(
|
||||
val BattleCreateState: Any? = null,
|
||||
/** 原 battleSearchState(Itt / Htt().vtg)。未迁移:原样透传。 */
|
||||
val BattleSearchState: Any? = null,
|
||||
/** 原 lobbyLoader(KW / Bs.es1)。未迁移:原样透传。 */
|
||||
val LobbyLoader: Any? = null,
|
||||
/** 原 lobbyLoader(KW / Bs.es1)。已迁移。 */
|
||||
val LobbyLoader: LobbyLoaderStateSlice = LobbyLoaderStateSlice(),
|
||||
/** 原 battleResult(sR / Fi.aqp)。未迁移:原样透传。 */
|
||||
val BattleResult: Any? = null,
|
||||
/** 原 battleUsers(uP / sr.hqs)。未迁移:原样透传。 */
|
||||
@@ -139,8 +146,8 @@ data class RootState(
|
||||
val ControlSettings: Any? = null,
|
||||
/** 原 securitySettings(V2 / pc.qsv)。未迁移:原样透传。 */
|
||||
val SecuritySettings: Any? = null,
|
||||
/** 原 purchaseDialogState(CO / je.ur2)。未迁移:原样透传。 */
|
||||
val PurchaseDialogState: Any? = null,
|
||||
/** 原 purchaseDialogState(CO / je.ur2)。已迁移。 */
|
||||
val PurchaseDialogState: PurchaseDialogStateSlice = PurchaseDialogStateSlice(),
|
||||
/** 原 battleInviteState(att / Ttt().nth)。未迁移:原样透传。 */
|
||||
val BattleInviteState: Any? = null,
|
||||
/** 原 keyboardSettings(Sut / bw.oth)。未迁移:原样透传。 */
|
||||
@@ -163,12 +170,12 @@ data class RootState(
|
||||
val Spectator: Any? = null,
|
||||
/** 原 moderation(NI / Ei.oqh)。未迁移:原样透传。 */
|
||||
val Moderation: Any? = null,
|
||||
/** 原 rulesUpdated(i1 / Ch.dsm)。未迁移:原样透传。 */
|
||||
val RulesUpdated: Any? = null,
|
||||
/** 原 rulesUpdated(i1 / Ch.dsm)。已迁移。 */
|
||||
val RulesUpdated: RulesUpdatedStateSlice = RulesUpdatedStateSlice(),
|
||||
/** 原 announcement(lC / PC().rq5)。已迁移。 */
|
||||
val Announcement: AnnouncementStateSlice = AnnouncementStateSlice(),
|
||||
/** 原 notificationByCountry(gJ / R_.ssd)。未迁移:原样透传。 */
|
||||
val NotificationByCountry: Any? = null,
|
||||
/** 原 notificationByCountry(gJ / R_.ssd)。已迁移。 */
|
||||
val NotificationByCountry: NotificationByCountryStateSlice = NotificationByCountryStateSlice(),
|
||||
/** 原 userRenameState(O8 / Rf.pte)。未迁移:原样透传。 */
|
||||
val UserRenameState: Any? = null,
|
||||
/** 原 eventGoods(W7 / Wc.esx)。未迁移:原样透传。 */
|
||||
@@ -193,18 +200,18 @@ data class RootState(
|
||||
val QuestCategories: Any? = null,
|
||||
/** 原 proBattle(Wtt / uv.wth)。已迁移。 */
|
||||
val ProBattle: ProBattleStateSlice = ProBattleStateSlice(),
|
||||
/** 原 availabilityByRank(tet / $p.xth)。未迁移:原样透传。 */
|
||||
val AvailabilityByRank: Any? = null,
|
||||
/** 原 availabilityByRank(tet / $p.xth)。已迁移。 */
|
||||
val AvailabilityByRank: AvailabilityByRankStateSlice = AvailabilityByRankStateSlice(),
|
||||
/** 原 userReportsState(Zst / qot.zth)。未迁移:原样透传。 */
|
||||
val UserReportsState: Any? = null,
|
||||
/** 原 tankBuilderState(brt / wp.ati)。未迁移:原样透传。 */
|
||||
val TankBuilderState: Any? = null,
|
||||
/** 原 contractState(Ont / xv.bti)。未迁移:原样透传。 */
|
||||
val ContractState: Any? = null,
|
||||
/** 原 promoCode(cZ / Q_.ysg)。未迁移:原样透传。 */
|
||||
val PromoCode: Any? = null,
|
||||
/** 原 promoCode(cZ / Q_.ysg)。已迁移。 */
|
||||
val PromoCode: PromoCodeStateSlice = PromoCodeStateSlice(),
|
||||
/** 原 referralEvent(pQ / lh.osk)。未迁移:原样透传。 */
|
||||
val ReferralEvent: Any? = null,
|
||||
/** 原 supportUrl(I4 / Yl.ct7)。未迁移:原样透传。 */
|
||||
val SupportUrl: Any? = null,
|
||||
/** 原 supportUrl(I4 / Yl.ct7)。已迁移。 */
|
||||
val SupportUrl: SupportUrlStateSlice = SupportUrlStateSlice(),
|
||||
)
|
||||
@@ -47,7 +47,7 @@ object RootWiring {
|
||||
SliceWiring("matchmaking", "cpw_1", "TH", "JH().qrx", "JH().qrx(t, n.cpw_1)", listOf("?WH", "?nW", "?tW", "Configure", "EnterToBattleApply", "Play", "SelectMatchmakingMode", "SetRankIndexToMinGS", "WasRegistered")),
|
||||
SliceWiring("battleCreateState", "dpw_1", "\$6", "Df.ttg", "Df.ttg(t, n.dpw_1, n.bpw_1, n.ypu_1)", listOf("?A6", "?B6", "?C6", "?D6", "?E6", "?F6", "?I6", "?L6", "?M6", "?N6", "?P6", "?R6", "?T6", "?U6", "Configure", "CreateBattle", "SetRestrictions")),
|
||||
SliceWiring("battleSearchState", "epw_1", "Itt", "Htt().vtg", "Htt().vtg(t, n.epw_1)", listOf("?Dtt", "EnterToBattle", "Fight", "RemoveBattle", "SelectBattle", "SelectBattleFormat", "SetFilter", "SetSearch", "SetSuspiciousFilter")),
|
||||
SliceWiring("lobbyLoader", "fpw_1", "KW", "Bs.es1", "Bs.es1(t, n.fpw_1)", listOf("?FW", "?GW")),
|
||||
SliceWiring("lobbyLoader", "fpw_1", "KW", "Bs.es1", "Bs.es1(t, n.fpw_1)", listOf("HideLoader", "ShowLoader")),
|
||||
SliceWiring("battleResult", "gpw_1", "sR", "Fi.aqp", "Fi.aqp(t, n.gpw_1)", listOf("?cR", "?fR", "?lR", "DoInit", "SetHasBattleResult", "SetRewards")),
|
||||
SliceWiring("battleUsers", "hpw_1", "uP", "sr.hqs", "sr.hqs(t, n.hpw_1)", listOf("?_P", "?hP", "?lP", "?oP", "?qP", "?sP", "InitSuspiciousState", "InitUserLevels", "RemoveTankInfo", "SetGearScore", "SetTankInfo", "SetTankResistance", "UpdateUserLevel", "UpdateUserRank", "UpdateUserScoreKillsAndDeaths", "UpdateUserSuspiciousState", "UpdateUsersClanTag", "UpdateUsersScoreKillsAndDeaths")),
|
||||
SliceWiring("suppliesState", "ipw_1", "wE", "\$t.fq9", "\$t.fq9(t, n.ipw_1)", listOf("?qE")),
|
||||
@@ -69,7 +69,7 @@ object RootWiring {
|
||||
SliceWiring("graphicsSettings", "ypw_1", "?", "lc.isu", "lc.isu(t, n.ypw_1)", listOf("UpdateSettings")),
|
||||
SliceWiring("controlSettings", "zpw_1", "i2", "_2().ess", "_2().ess(t, n.zpw_1)", listOf("?_C", "UpdateSettings")),
|
||||
SliceWiring("securitySettings", "apx_1", "V2", "pc.qsv", "pc.qsv(t, n.apx_1)", listOf("SetProfileBlockStatus")),
|
||||
SliceWiring("purchaseDialogState", "bpx_1", "CO", "je.ur2", "je.ur2(t, n.bpx_1)", listOf("?NO", "ChangeEmailStatus", "SuccessfullyPurchase")),
|
||||
SliceWiring("purchaseDialogState", "bpx_1", "CO", "je.ur2", "je.ur2(t, n.bpx_1)", listOf("ChangeEmailStatus", "ResetPurchaseDialog", "SuccessfullyPurchase")),
|
||||
SliceWiring("battleInviteState", "cpx_1", "att", "Ttt().nth", "Ttt().nth(t, n.cpx_1)", listOf("?dtt", "EnableSetBattleInviteNotifications", "InvitationResponse", "InviteToBattle", "RemoveUserFromInvited", "SelectUserToInvite")),
|
||||
SliceWiring("keyboardSettings", "dpx_1", "Sut", "bw.oth", "bw.oth(t, n.dpx_1)", listOf("?Iut", "?_C", "ChangeAction", "RemoveAction", "ResetAction")),
|
||||
SliceWiring("touchscreenHudSettings", "epx_1", "j7", "N7().xsv", "N7().xsv(t, n.epx_1)", listOf("?E7", "?_C", "UpdateSettings")),
|
||||
@@ -83,7 +83,7 @@ object RootWiring {
|
||||
SliceWiring("moderation", "mpx_1", "NI", "Ei.oqh", "Ei.oqh(t, n.mpx_1)", listOf("?FI", "?GI", "?JI", "?KI", "?QI", "?UI", "?WI", "?XI", "?YI", "?ZI", "?nA", "?tA", "?uA")),
|
||||
SliceWiring("rulesUpdated", "npx_1", "i1", "Ch.dsm", "Ch.dsm(t, n.npx_1)", listOf("UpdateRules")),
|
||||
SliceWiring("announcement", "opx_1", "lC", "PC().rq5", "PC().rq5(t, n.opx_1)", listOf("DisableAnnouncement", "SetAnnouncementItems", "SetAnnouncementModelLoaded", "SetSkipNextPromotedAnnounce")),
|
||||
SliceWiring("notificationByCountry", "ppx_1", "gJ", "R_.ssd", "R_.ssd(t, n.ppx_1)", listOf("?qJ", "Init")),
|
||||
SliceWiring("notificationByCountry", "ppx_1", "gJ", "R_.ssd", "R_.ssd(t, n.ppx_1)", listOf("Init", "MarkHomeScreenDialogShown")),
|
||||
SliceWiring("userRenameState", "qpx_1", "O8", "Rf.pte", "Rf.pte(t, n.qpx_1)", listOf("?B8", "?F8", "?G8", "?Q8", "?U8", "ChangeUidInputState", "ResponseActual", "ResponseDelivered", "SetChangeUidParams", "SetRubiesForRename", "SetValidationStatus")),
|
||||
SliceWiring("eventGoods", "rpx_1", "W7", "Wc.esx", "Wc.esx(t, n.rpx_1)", listOf("InitEventGoods")),
|
||||
SliceWiring("creatorTagState", "spx_1", "qO", "ze.er2", "ze.er2(t, n.spx_1)", listOf("?jO", "SetAttachedCreatorTag", "SetCreatorTag", "SetCreatorTags")),
|
||||
@@ -102,11 +102,11 @@ object RootWiring {
|
||||
SliceWiring("contractState", "fpy_1", "Ont", "xv.bti", "xv.bti(t, n.fpy_1)", listOf("?Hnt", "GetReward", "Init", "SetActiveContract", "SetBattleProgress", "SetContract", "UpdateProgress")),
|
||||
SliceWiring("promoCode", "gpy_1", "cZ", "Q_.ysg", "Q_.ysg(t, n.gpy_1)", listOf("ActivatePromoCode", "SetCodeValidateStatus")),
|
||||
SliceWiring("referralEvent", "hpy_1", "pQ", "lh.osk", "lh.osk(t, n.hpy_1)", listOf("SetPromoCode", "SetReferralEventData", "SetReferrals", "SetReferrer")),
|
||||
SliceWiring("supportUrl", "ipy_1", "I4", "Yl.ct7", "Yl.ct7(t, n.ipy_1)", listOf("?R4", "SetUrl")),
|
||||
SliceWiring("supportUrl", "ipy_1", "I4", "Yl.ct7", "Yl.ct7(t, n.ipy_1)", listOf("ClearSupportUrl", "SetUrl")),
|
||||
)
|
||||
|
||||
/** 已迁移(强类型;launchParams 为透传,不调用 reducer)的字段。 */
|
||||
val Migrated: Set<String> = setOf("inviteState", "loginByPassword", "twitchState", "home", "localBattleUserState", "shop", "screen", "lootBoxes", "garage", "garagePreview", "entrance", "user", "settings", "accountSettings", "proBattle", "language", "announcement", "launchParams", "news", "notifications", "quests")
|
||||
val Migrated: Set<String> = setOf("inviteState", "loginByPassword", "twitchState", "home", "localBattleUserState", "shop", "screen", "lootBoxes", "garage", "garagePreview", "entrance", "user", "settings", "accountSettings", "proBattle", "language", "announcement", "launchParams", "news", "notifications", "quests", "lobbyLoader", "purchaseDialogState", "promoCode", "supportUrl", "rulesUpdated", "notificationByCountry", "availabilityByRank")
|
||||
|
||||
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
|
||||
val Unmigrated: List<SliceWiring> get() = All.filter { it.Field !in Migrated }
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
// ⑥ 动作目录:816 个类与 138 个真实派发名对齐
|
||||
// ⑦ 回放:真实 toString 样例的类/字段/类型校验
|
||||
// ⑧ 全目录动作类都能按 KClass 路由
|
||||
// ⑨~㉛ 切片 reducer:InviteState / LoginByPasswordState / TwitchState / HomeScreen / LocalBattleUserState /
|
||||
// ⑨~㊳ 切片 reducer:InviteState / LoginByPasswordState / TwitchState / HomeScreen / LocalBattleUserState /
|
||||
// Shop(含 U7 类别表与 w3 物品表两个子 reducer)/ Screen / LootBoxes / Garage / GaragePreview / Entrance / User / Settings /
|
||||
// AccountSettings / ProBattle / Language / Announcement / News / Notifications / Quests;
|
||||
// AccountSettings / ProBattle / Language / Announcement / News / Notifications / Quests /
|
||||
// LobbyLoader / PurchaseDialogState / PromoCode / SupportUrl / RulesUpdated / NotificationByCountry / AvailabilityByRank;
|
||||
// ㉑ launchParams 是纯透传切片(无 reducer/动作),只做 Map 类型收口;⑭ 根 reducer 的 89 槽位委派
|
||||
//
|
||||
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
|
||||
@@ -165,14 +166,22 @@ import App.State.DisplayMainEntranceScreen
|
||||
import App.State.DisplayReauthorizationMessage
|
||||
import App.State.EntranceScreen
|
||||
import App.State.GoTo
|
||||
import App.State.ActivatePromoCode
|
||||
import App.State.AddNewsItemAction
|
||||
import App.State.AddNotification
|
||||
import App.State.ChangeEmailStatus
|
||||
import App.State.Init as InitNotificationByCountry
|
||||
import App.State.InitNotificationSound
|
||||
import App.State.NewsItemData
|
||||
import App.State.RemoveNewsItemAction
|
||||
import App.State.SetCodeValidateStatus
|
||||
import App.State.SetCurrentCategory
|
||||
import App.State.SetLoadedData
|
||||
import App.State.SetNewsItemAction
|
||||
import App.State.SetUnreadNewsCounter
|
||||
import App.State.SetUrl
|
||||
import App.State.SuccessfullyPurchase
|
||||
import App.State.UpdateRules
|
||||
import App.State.LoaderScreen
|
||||
import App.State.NamedScreen
|
||||
import App.State.PressedAnyButton
|
||||
@@ -325,6 +334,27 @@ import App.State.Reducers.RemoveFirstNotification
|
||||
import App.State.Reducers.RemoveNotifications
|
||||
import App.State.Reducers.SetNewsFlag
|
||||
import App.State.Reducers.SetNotCompletedQuests
|
||||
import App.State.Reducers.AvailabilityByRankStateSlice
|
||||
import App.State.Reducers.ClearSupportUrl
|
||||
import App.State.Reducers.HideLoader
|
||||
import App.State.Reducers.LobbyLoaderStateSlice
|
||||
import App.State.Reducers.MarkHomeScreenDialogShown
|
||||
import App.State.Reducers.NotificationByCountryStateSlice
|
||||
import App.State.Reducers.PromoCodeStateSlice
|
||||
import App.State.Reducers.PromoCodeStatus
|
||||
import App.State.Reducers.PurchaseDialogStateSlice
|
||||
import App.State.Reducers.PurchaseEmailStatus
|
||||
import App.State.Reducers.ReduceAvailabilityByRank
|
||||
import App.State.Reducers.ReduceLobbyLoader
|
||||
import App.State.Reducers.ReduceNotificationByCountry
|
||||
import App.State.Reducers.ReducePromoCode
|
||||
import App.State.Reducers.ReducePurchaseDialog
|
||||
import App.State.Reducers.ReduceRulesUpdated
|
||||
import App.State.Reducers.ReduceSupportUrl
|
||||
import App.State.Reducers.ResetPurchaseDialog
|
||||
import App.State.Reducers.RulesUpdatedStateSlice
|
||||
import App.State.Reducers.ShowLoader
|
||||
import App.State.Reducers.SupportUrlStateSlice
|
||||
import App.State.Reducers.UserRankBounds
|
||||
import App.State.Reducers.UserStateSlice
|
||||
import App.State.Reducers.ClearHullData
|
||||
@@ -752,7 +782,7 @@ object StateCheck {
|
||||
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
|
||||
}
|
||||
|
||||
// ⑭ 根 reducer:89 个槽位的委派表 + 21 个已迁移切片(20 归约 + launchParams 透传)
|
||||
// ⑭ 根 reducer:89 个槽位的委派表 + 28 个已迁移切片(27 归约 + launchParams 透传)
|
||||
run {
|
||||
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
|
||||
Check(
|
||||
@@ -816,7 +846,7 @@ object StateCheck {
|
||||
LocalBattleW.Reducer == "(inline@localBattleUserState)"
|
||||
)
|
||||
Check(
|
||||
"根:迁移表 = 21 个已迁移字段,其余 68 个待迁",
|
||||
"根:迁移表 = 28 个已迁移字段,其余 61 个待迁",
|
||||
RootReducer.Migrated ==
|
||||
setOf(
|
||||
"inviteState", "loginByPassword", "twitchState", "home",
|
||||
@@ -824,8 +854,10 @@ object StateCheck {
|
||||
"garagePreview", "entrance", "user", "settings", "accountSettings",
|
||||
"proBattle", "language", "announcement", "launchParams",
|
||||
"news", "notifications", "quests",
|
||||
"lobbyLoader", "purchaseDialogState", "promoCode", "supportUrl",
|
||||
"rulesUpdated", "notificationByCountry", "availabilityByRank",
|
||||
) &&
|
||||
RootWiring.Unmigrated.size == 68
|
||||
RootWiring.Unmigrated.size == 61
|
||||
)
|
||||
|
||||
// 一次 ReduceRoot 里多个切片各归约自己的动作,互不串片
|
||||
@@ -881,7 +913,7 @@ object StateCheck {
|
||||
S5.User.Uid == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
|
||||
)
|
||||
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
|
||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 21/89")
|
||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 28/89")
|
||||
|
||||
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
|
||||
Store.Dispatch(SetStatus("LOGGING_IN"))
|
||||
@@ -3103,6 +3135,238 @@ object StateCheck {
|
||||
)
|
||||
}
|
||||
|
||||
// ㉜ 切片 reducer:LobbyLoader(KW / VW.es1,2 字段;FW/GW 两个匿名类)
|
||||
run {
|
||||
val S0 = LobbyLoaderStateSlice()
|
||||
Check("切片 LobbyLoader:默认不显示、计数 0", !S0.IsShowLoader && S0.ActiveLoadersCount == 0)
|
||||
val S1 = ReduceLobbyLoader(ReduceLobbyLoader(S0, ShowLoader), ShowLoader)
|
||||
Check(
|
||||
"切片 LobbyLoader:ShowLoader 计数 +1 并显示",
|
||||
S1.IsShowLoader && S1.ActiveLoadersCount == 2
|
||||
)
|
||||
val S2 = ReduceLobbyLoader(S1, HideLoader)
|
||||
Check(
|
||||
"切片 LobbyLoader:HideLoader 计数 -1,仍大于 0 时继续显示",
|
||||
S2.IsShowLoader && S2.ActiveLoadersCount == 1
|
||||
)
|
||||
val S3 = ReduceLobbyLoader(ReduceLobbyLoader(S2, HideLoader), HideLoader)
|
||||
Check(
|
||||
"切片 LobbyLoader:计数夹到 0 并隐藏",
|
||||
!S3.IsShowLoader && S3.ActiveLoadersCount == 0
|
||||
)
|
||||
Check("切片 LobbyLoader:无关动作不改状态", ReduceLobbyLoader(S1, AddFrags(1)) == S1)
|
||||
|
||||
val Root0 = RootState()
|
||||
val Root1 = ReduceRoot(Root0, ShowLoader)
|
||||
Check(
|
||||
"根:ShowLoader 只动 lobbyLoader",
|
||||
Root1.LobbyLoader.IsShowLoader && Root1.PurchaseDialogState == Root0.PurchaseDialogState
|
||||
)
|
||||
val W = RootWiring.ByField("lobbyLoader")!!
|
||||
Check(
|
||||
"根:lobbyLoader 委派到 KW / Bs.es1 且 2 个动作全部具名",
|
||||
W.SliceClass == "KW" && W.Reducer == "Bs.es1" &&
|
||||
W.Actions.toSet() == setOf("ShowLoader", "HideLoader")
|
||||
)
|
||||
}
|
||||
|
||||
// ㉝ 切片 reducer:PurchaseDialogState(CO / DO.ur2,4 字段;NO + 2 个实例臂)
|
||||
run {
|
||||
val S0 = PurchaseDialogStateSlice()
|
||||
Check(
|
||||
"切片 PurchaseDialog:默认值完整",
|
||||
!S0.WithEmail && S0.Goods.isEmpty() && S0.EmailStatus == null && S0.Time == null
|
||||
)
|
||||
val S1 = ReducePurchaseDialog(S0, ChangeEmailStatus(PurchaseEmailStatus.BUSY))
|
||||
Check(
|
||||
"切片 PurchaseDialog:ChangeEmailStatus 只写邮箱状态",
|
||||
S1.EmailStatus == PurchaseEmailStatus.BUSY && S1.Goods.isEmpty() && S1.Time == null
|
||||
)
|
||||
val S2 = ReducePurchaseDialog(S1, SuccessfullyPurchase(listOf("g1"), true, 123L))
|
||||
Check(
|
||||
"切片 PurchaseDialog:SuccessfullyPurchase 追加商品并写时间",
|
||||
S2.WithEmail && S2.Goods == listOf<Any?>("g1") && S2.Time == 123L &&
|
||||
S2.EmailStatus == PurchaseEmailStatus.BUSY
|
||||
)
|
||||
val S3 = ReducePurchaseDialog(S2, SuccessfullyPurchase(listOf("g2"), false, 456L))
|
||||
Check(
|
||||
"切片 PurchaseDialog:商品是追加而不是替换",
|
||||
S3.Goods == listOf<Any?>("g1", "g2") && !S3.WithEmail && S3.Time == 456L
|
||||
)
|
||||
Check(
|
||||
"切片 PurchaseDialog:ResetPurchaseDialog 复位四个字段",
|
||||
ReducePurchaseDialog(S3, ResetPurchaseDialog) == PurchaseDialogStateSlice()
|
||||
)
|
||||
Check("切片 PurchaseDialog:无关动作不改状态", ReducePurchaseDialog(S3, AddFrags(1)) == S3)
|
||||
|
||||
val Root0 = RootState()
|
||||
val Root1 = ReduceRoot(Root0, ChangeEmailStatus(PurchaseEmailStatus.FREE))
|
||||
Check(
|
||||
"根:ChangeEmailStatus 只动 purchaseDialogState",
|
||||
Root1.PurchaseDialogState.EmailStatus == PurchaseEmailStatus.FREE &&
|
||||
Root1.LobbyLoader == Root0.LobbyLoader
|
||||
)
|
||||
val W = RootWiring.ByField("purchaseDialogState")!!
|
||||
Check(
|
||||
"根:purchaseDialogState 委派到 CO / je.ur2 且 3 个动作全部具名",
|
||||
W.SliceClass == "CO" && W.Reducer == "je.ur2" &&
|
||||
W.Actions.toSet() == setOf(
|
||||
"ChangeEmailStatus", "ResetPurchaseDialog", "SuccessfullyPurchase",
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// ㉞ 切片 reducer:PromoCode(cZ / wZ.ysg,1 字段;2 个实例臂)
|
||||
run {
|
||||
val S0 = PromoCodeStateSlice()
|
||||
Check("切片 PromoCode:默认 NONE", S0.ValidateStatus == PromoCodeStatus.NONE)
|
||||
Check(
|
||||
"切片 PromoCode:ActivatePromoCode 进入 WAIT",
|
||||
ReducePromoCode(S0, ActivatePromoCode("CODE-1")).ValidateStatus == PromoCodeStatus.WAIT
|
||||
)
|
||||
Check(
|
||||
"切片 PromoCode:SetCodeValidateStatus 写服务端状态",
|
||||
ReducePromoCode(
|
||||
PromoCodeStateSlice(PromoCodeStatus.WAIT),
|
||||
SetCodeValidateStatus(PromoCodeStatus.ACTIVATED),
|
||||
).ValidateStatus == PromoCodeStatus.ACTIVATED
|
||||
)
|
||||
Check("切片 PromoCode:无关动作不改状态", ReducePromoCode(S0, AddFrags(1)) == S0)
|
||||
|
||||
val Root0 = RootState()
|
||||
val Root1 = ReduceRoot(Root0, ActivatePromoCode("X"))
|
||||
Check(
|
||||
"根:ActivatePromoCode 只动 promoCode",
|
||||
Root1.PromoCode.ValidateStatus == PromoCodeStatus.WAIT &&
|
||||
Root1.SupportUrl == Root0.SupportUrl
|
||||
)
|
||||
val W = RootWiring.ByField("promoCode")!!
|
||||
Check(
|
||||
"根:promoCode 委派到 cZ / Q_.ysg 且 2 个动作",
|
||||
W.SliceClass == "cZ" && W.Reducer == "Q_.ysg" &&
|
||||
W.Actions == listOf("ActivatePromoCode", "SetCodeValidateStatus")
|
||||
)
|
||||
}
|
||||
|
||||
// ㉟ 切片 reducer:SupportUrl(I4 / P4.ct7,1 字段;A4 + R4)
|
||||
run {
|
||||
val S0 = SupportUrlStateSlice()
|
||||
Check("切片 SupportUrl:默认为空串", S0.Url == "")
|
||||
Check(
|
||||
"切片 SupportUrl:SetUrl 写入链接",
|
||||
ReduceSupportUrl(S0, SetUrl("https://support")).Url == "https://support"
|
||||
)
|
||||
Check(
|
||||
"切片 SupportUrl:ClearSupportUrl 清空",
|
||||
ReduceSupportUrl(SupportUrlStateSlice("https://support"), ClearSupportUrl).Url == ""
|
||||
)
|
||||
Check("切片 SupportUrl:无关动作不改状态", ReduceSupportUrl(S0, AddFrags(1)) == S0)
|
||||
|
||||
val Root0 = RootState()
|
||||
val Root1 = ReduceRoot(Root0, SetUrl("u"))
|
||||
Check(
|
||||
"根:SetUrl 只动 supportUrl",
|
||||
Root1.SupportUrl.Url == "u" && Root1.RulesUpdated == Root0.RulesUpdated
|
||||
)
|
||||
val W = RootWiring.ByField("supportUrl")!!
|
||||
Check(
|
||||
"根:supportUrl 委派到 I4 / Yl.ct7 且 2 个动作",
|
||||
W.SliceClass == "I4" && W.Reducer == "Yl.ct7" &&
|
||||
W.Actions.toSet() == setOf("ClearSupportUrl", "SetUrl")
|
||||
)
|
||||
}
|
||||
|
||||
// ㊱ 切片 reducer:RulesUpdated(i1 / u1.dsm,1 字段;1 个实例臂)
|
||||
run {
|
||||
val S0 = RulesUpdatedStateSlice()
|
||||
Check("切片 RulesUpdated:默认 text = null", S0.Text == null)
|
||||
Check(
|
||||
"切片 RulesUpdated:UpdateRules 写入正文",
|
||||
ReduceRulesUpdated(S0, UpdateRules("新规则")).Text == "新规则"
|
||||
)
|
||||
Check("切片 RulesUpdated:无关动作不改状态", ReduceRulesUpdated(S0, AddFrags(1)) == S0)
|
||||
|
||||
val Root0 = RootState()
|
||||
val Root1 = ReduceRoot(Root0, UpdateRules("t"))
|
||||
Check(
|
||||
"根:UpdateRules 只动 rulesUpdated",
|
||||
Root1.RulesUpdated.Text == "t" && Root1.NotificationByCountry == Root0.NotificationByCountry
|
||||
)
|
||||
val W = RootWiring.ByField("rulesUpdated")!!
|
||||
Check(
|
||||
"根:rulesUpdated 委派到 i1 / Ch.dsm",
|
||||
W.SliceClass == "i1" && W.Reducer == "Ch.dsm" && W.Actions == listOf("UpdateRules")
|
||||
)
|
||||
}
|
||||
|
||||
// ㊲ 切片 reducer:NotificationByCountry(gJ / $J.ssd,4 字段;Init + qJ)
|
||||
run {
|
||||
val S0 = NotificationByCountryStateSlice()
|
||||
Check(
|
||||
"切片 NotificationByCountry:默认三条消息为 null、弹窗未展示",
|
||||
S0.DisableRegistrationMessage == null && S0.DisableShopMessage == null &&
|
||||
S0.HomeScreenMessage == null && !S0.HomeScreenDialogShown
|
||||
)
|
||||
val S1 = ReduceNotificationByCountry(
|
||||
S0,
|
||||
InitNotificationByCountry("r", "s", "h"),
|
||||
)
|
||||
Check(
|
||||
"切片 NotificationByCountry:Init 写三条消息且不动弹窗标志",
|
||||
S1.DisableRegistrationMessage == "r" && S1.DisableShopMessage == "s" &&
|
||||
S1.HomeScreenMessage == "h" && !S1.HomeScreenDialogShown
|
||||
)
|
||||
Check(
|
||||
"切片 NotificationByCountry:qJ 只把弹窗标志置 true",
|
||||
ReduceNotificationByCountry(S1, MarkHomeScreenDialogShown) ==
|
||||
S1.copy(HomeScreenDialogShown = true)
|
||||
)
|
||||
Check(
|
||||
"切片 NotificationByCountry:无关动作不改状态",
|
||||
ReduceNotificationByCountry(S1, AddFrags(1)) == S1
|
||||
)
|
||||
|
||||
val Root0 = RootState()
|
||||
val Root1 = ReduceRoot(Root0, MarkHomeScreenDialogShown)
|
||||
Check(
|
||||
"根:qJ 只动 notificationByCountry",
|
||||
Root1.NotificationByCountry.HomeScreenDialogShown &&
|
||||
Root1.RulesUpdated == Root0.RulesUpdated
|
||||
)
|
||||
val W = RootWiring.ByField("notificationByCountry")!!
|
||||
Check(
|
||||
"根:notificationByCountry 委派到 gJ / R_.ssd 且 2 个动作",
|
||||
W.SliceClass == "gJ" && W.Reducer == "R_.ssd" &&
|
||||
W.Actions.toSet() == setOf("Init", "MarkHomeScreenDialogShown")
|
||||
)
|
||||
}
|
||||
|
||||
// ㊳ 切片 reducer:AvailabilityByRank(tet / set.xth,1 字段;1 个实例臂)
|
||||
run {
|
||||
val S0 = AvailabilityByRankStateSlice()
|
||||
Check("切片 AvailabilityByRank:默认空表", S0.Data.isEmpty())
|
||||
val S1 = ReduceAvailabilityByRank(S0, SetLoadedData(mapOf(1 to "a", 2 to "b")))
|
||||
Check(
|
||||
"切片 AvailabilityByRank:SetLoadedData 写整张表",
|
||||
S1.Data == mapOf<Any?, Any?>(1 to "a", 2 to "b")
|
||||
)
|
||||
Check("切片 AvailabilityByRank:无关动作不改状态", ReduceAvailabilityByRank(S1, AddFrags(1)) == S1)
|
||||
|
||||
val Root0 = RootState()
|
||||
val Root1 = ReduceRoot(Root0, SetLoadedData(mapOf(3 to "c")))
|
||||
Check(
|
||||
"根:SetLoadedData 只动 availabilityByRank",
|
||||
Root1.AvailabilityByRank.Data == mapOf<Any?, Any?>(3 to "c") &&
|
||||
Root1.PromoCode == Root0.PromoCode
|
||||
)
|
||||
val W = RootWiring.ByField("availabilityByRank")!!
|
||||
Check(
|
||||
"根:availabilityByRank 委派到 tet / \$p.xth",
|
||||
W.SliceClass == "tet" && W.Reducer == "\$p.xth" &&
|
||||
W.Actions == listOf("SetLoadedData")
|
||||
)
|
||||
}
|
||||
|
||||
val Failed = Results.filter { !it.second }
|
||||
println("TankiOffline 状态层自检:${Results.size} 项")
|
||||
for ((Name, Ok) in Results) println((if (Ok) " ✓ " else " ✗ ") + Name)
|
||||
|
||||
Reference in new issue
Block a user