feat(state): 迁移 friends/suppliesState/clanUserInfo/battleInviteState/partner/userRenameState

This commit is contained in:
TermiNexus committed 2026-10-24 16:00:39 +08:00
1 parent af005ab019
commit 0183f191d3
13 files changed
+1149 -54

No files matched your search

+28 -5
View File
@@ -143,7 +143,7 @@
`names/root_wiring.json` + `TankiOffline/.../App/State/RootState.kt`、`RootWiring.kt`。
- 根状态 `z6`(`TOState`)的 **89 个槽位**全部入表;字段顺序取 `toString`,槽位取构造函数 `<名>_1`。
- 根 reducer `k6` 的逐切片委派记全:reducer 引用、委派表达式、顶层 `instanceof` 动作名。
- 已迁移切片 **53/89**:
- 已迁移切片 **59/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)
@@ -309,12 +309,33 @@
新增 `ReportKind { BATTLE_NAME, LOBBY_CHAT_MESSAGE, BATTLE_CHAT_MESSAGE }` 与 `ReportEntry(kind, targetId, expireTime)`;
三类举报都追加一条记录、有效期 +1 天。原 reducer 还把列表写回 localStorage 的 `SENT_REPORTS`,
属 storage port 职责,纯 reducer 不做 IO(Init 保留现有列表)。
- `friends` → `KD` / `NL().cr9`(`FriendsReducer.kt`):8 字段,13 个分支。
新增 `FriendSearchState { HIDDEN, SEARCHING, FOUND, NOT_FOUND }`(对应 `YD()`);
四个集合分别做集合 / 单值增删;`SetValidationStatus` 原判据是 `status.equals(pk())`,
`pk()` 是校验通过单例,离线重建按「非空即 FOUND」收口。
`AddUsersToNewAccepted` 与 clan 切片的同名动作合并成一个类(`clanId, userIds`),friends 只读 `userIds`。
- `suppliesState` → `wE` / `$t.fq9`(`SuppliesStateReducer.kt`):1 字段 `Map`,
`qE`→`SetSupplyCount(supplyId, count)` 写一个键值。
- `clanUserInfo` → `hO` / `de.gr1`(`ClanUserInfoReducer.kt`):1 字段 `Map`,
`Loaded(users)` 是**合并**(`jm` = copy + `putAll`),不是覆盖。
- `battleInviteState` → `att` / `Ttt().nth`(`BattleInviteStateReducer.kt`):3 字段,6 个分支。
`mtt` 的 `toString` 名 `InviteToBattle` 与另一个只有 `battleId` 的同名动作冲突,
改用 `AddInvitedUser(receiverId)`;`dtt`→`ClearSelectedForInvite`。
- `partner` → `UJ` / `(inline@partner)`(`PartnerReducer.kt`):15 字段,11 个内联分支。
`_Z`→`UpdatePartnerInviteAction`、`tZ`/`JJ`→`MarkAccountLinked`/`MarkAccountNotLinked`、
`hZ`→`SetPartnerItemEnabled`(`Km` = Pair,写 `itemsEnabledForPartner`);
`loginForBindingStatus` 复用登录切片的 `ByPasswordStatus`。
- `userRenameState` → `O8` / `Rf.pte`(`UserRenameReducer.kt`):10 字段,11 个分支。
新增 `RenameUidState { EMPTY, SHORT, RESERVED, FORBIDDEN, NOT_VALID, FREE }`(对应 `D8()`);
`SetValidationStatus` 按 ordinal 映射输入状态(EMPTY→SHORT、SHORT→NOT_VALID、RESERVED→RESERVED、
FORBIDDEN/NOT_VALID→FORBIDDEN、FREE→FREE)并把 `isResponseDelivered` 置 true;
`G8`→`ResetRenameDialog` 复位弹窗字段但保留 rubies / hash / email。
- `launchParams` → `(pass-through)`:原根 reducer 对 `hpx_1` 没有调用,缺省是 `ag()` 空 Map;
`RootState.LaunchParams` 收口为 `LaunchParamsStateSlice = Map<String, String>`,不新增 reducer/动作。
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 52 个有 reducer 的切片,
`launchParams` 与其余 **36 个字段原样透传**;
迁移进度用 `RootReducer.Coverage`(`已迁移 53/89`)与 `RootWiring.Unmigrated` 暴露。
- 自检:`TankiOffline/tools/RunStateTest.ps1` **627 项全绿**,含 89 槽位完整性、52 个归约切片一次归约互不串片、未迁移字段透传,
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 58 个有 reducer 的切片,
`launchParams` 与其余 **30 个字段原样透传**;
迁移进度用 `RootReducer.Coverage`(`已迁移 59/89`)与 `RootWiring.Unmigrated` 暴露。
- 自检:`TankiOffline/tools/RunStateTest.ps1` **685 项全绿**,含 89 槽位完整性、58 个归约切片一次归约互不串片、未迁移字段透传,
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
@@ -331,6 +352,8 @@
captchaState 的 5 个、modalScreen 的 1 个、droppableGold 的 2 个、creatorTagState 的 1 个、
battleTutorial 的 2 个、spectator 的 4 个、antiAddiction 的 3 个、moderation 的 13 个、
迁移 / 注册共用的 10 个(`Mit`…`_rt`)匿名类 / 单例同样按效果定名并登记。
friends 的 2 个、suppliesState 的 1 个、battleInviteState 的 2 个、partner 的 4 个、
userRenameState 的 5 个也已登记(其中 `mtt` 因同名类冲突改名为 `AddInvitedUser`)。
### S4 引擎核心
- 模块:32088(3D 数学)、92980(渲染)、61378(ECS)、55088(物理)、36659 + 71105(资源)、36734(音频)、3569(输入)
+113 -23
View File
@@ -1,5 +1,5 @@
{
"generatedAt": "2026-10-24T07:23:39.141Z",
"generatedAt": "2026-10-24T07:48:13.464Z",
"module": "module_10032.js",
"RootState": {
"Class": "z6",
@@ -59,7 +59,13 @@
"moderation",
"migrationState",
"register",
"userReportsState"
"userReportsState",
"friends",
"suppliesState",
"clanUserInfo",
"battleInviteState",
"partner",
"userRenameState"
],
"AnonymousActions": [
{
@@ -998,6 +1004,90 @@
"Name": "ResetAuthenticationForm",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "login=\"\"、两个状态复位(两个切片都读)"
},
{
"Local": "_L",
"Name": "SetContextMenuUser",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "contextMenuUser=参数(`n.er7(cd, t.er9_1)`)"
},
{
"Local": "hL",
"Name": "ClearContextMenuUser",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "contextMenuUser=null(`n.er7(cd, null)`)"
},
{
"Local": "qE",
"Name": "SetSupplyCount",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "count 写键值(`n.bq9(cg(n.aq9_1, Vd(t.gq9_1, t.hq9_1)))`)"
},
{
"Local": "mtt",
"Name": "AddInvitedUser",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "invitedUsers += receiverId(原名 InviteToBattle,与另一个只有 battleId 的同名动作冲突,改名 AddInvitedUser)"
},
{
"Local": "dtt",
"Name": "ClearSelectedForInvite",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "selectedForInvite = emptySet()(`Yg()`)"
},
{
"Local": "_Z",
"Name": "UpdatePartnerInviteAction",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "inviteAction=参数(`n.osf(…, t.osg_1)`)"
},
{
"Local": "tZ",
"Name": "MarkAccountLinked",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "isAccountAlreadyLinked=true(`n.osf(…, !0)`)"
},
{
"Local": "JJ",
"Name": "MarkAccountNotLinked",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "isAccountAlreadyLinked=false(`n.osf(…, !1)`)"
},
{
"Local": "hZ",
"Name": "SetPartnerItemEnabled",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "itemsEnabledForPartner 写键值(`cg(n.ksf_1, new Km(t.msg_1, t.nsg_1))`)"
},
{
"Local": "B8",
"Name": "MarkFreeRenameAvailable",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "isFreeRenameAvailable=true(`n.cte(!0)`)"
},
{
"Local": "F8",
"Name": "MarkRenameSucceeded",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "isFreeRenameAvailable=false、renameSuccessful=true(`n.cte(!1, cd, cd, !0)`)"
},
{
"Local": "U8",
"Name": "MarkRenameFailed",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "renameSuccessful=false(`n.cte(cd, cd, cd, !1)`)"
},
{
"Local": "G8",
"Name": "ResetRenameDialog",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "复位 shopItemId / uidInputState / renameSuccessful / 两个 response 位(`n.cte(cd, null, n6(), null, cd, !1, !1)`)"
},
{
"Local": "Q8",
"Name": "HideLinkErrorWarning",
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
"Effect": "showLinkErrorWarning=false(`n.cte(…, !1)`)"
}
],
"Markers": [
@@ -1679,22 +1769,22 @@
"Reducer": "NL().cr9",
"Delegate": "NL().cr9(t, n.ypv_1)",
"Actions": [
"?_L",
"?hL",
"AddUsersToAccepted",
"AddUsersToIncoming",
"AddUsersToNewAccepted",
"AddUsersToNewIncoming",
"ChangeFriendAddFindState",
"ClearContextMenuUser",
"RemoveUserFromAccepted",
"RemoveUserFromIncoming",
"RemoveUserFromNewAccepted",
"RemoveUsersFromNewIncoming",
"SetContextMenuUser",
"SetLimits",
"SetValidationStatus"
],
"Default": "new KD()",
"Migrated": false
"Migrated": true
},
{
"Field": "remoteUserDataStorage",
@@ -1892,10 +1982,10 @@
"Reducer": "$t.fq9",
"Delegate": "$t.fq9(t, n.ipw_1)",
"Actions": [
"?qE"
"SetSupplyCount"
],
"Default": "new wE()",
"Migrated": false
"Migrated": true
},
{
"Field": "matchmakingGroup",
@@ -2064,7 +2154,7 @@
"Loaded"
],
"Default": "new hO()",
"Migrated": false
"Migrated": true
},
{
"Field": "tankActionsLogState",
@@ -2253,15 +2343,15 @@
"Reducer": "Ttt().nth",
"Delegate": "Ttt().nth(t, n.cpx_1)",
"Actions": [
"?dtt",
"AddInvitedUser",
"ClearSelectedForInvite",
"EnableSetBattleInviteNotifications",
"InvitationResponse",
"InviteToBattle",
"RemoveUserFromInvited",
"SelectUserToInvite"
],
"Default": "new att()",
"Migrated": false
"Migrated": true
},
{
"Field": "keyboardSettings",
@@ -2340,20 +2430,20 @@
"Reducer": "(inline@partner)",
"Delegate": "(function (t, n) { var i; if (t instanceof VJ) i = n.osf(t.rsf_1, cd, cd, cd, cd, cd, cd, cd, cd, cd, t.ssf_1); else if (t instanceof nZ) { var r = t.bsg_1, e = t.csg_1, u = t.dsg_1, s = t.esg_1, o = t.fsg_1, _ = t.gsg_1; i = n.osf(cd, cd, cd, r, e, u, cd, cd, o, _, s); } else i = t instanceof iZ ? n.osf(cd, t.hsg_1) : t instanceof uZ ? n.osf(cd, cd, t.ksg_1) : t instanceof eZ ? n.osf(cd, cd, cd, cd, cd, cd, cd, t.jsg_1) : t instanceof _Z ? n.osf(cd, cd, cd, cd, cd, cd, t.osg_1) : t instanceof X …",
"Actions": [
"?JJ",
"?_Z",
"?hZ",
"?tZ",
"InitPartnerInviteAction",
"InitSocialNetworkParameters",
"MarkAccountLinked",
"MarkAccountNotLinked",
"SetLoginParams",
"SetLoginStatusForBinding",
"SetPartnerId",
"SetPartnerItemEnabled",
"SetPartnerParameters",
"SetPartnerReady"
"SetPartnerReady",
"UpdatePartnerInviteAction"
],
"Default": "new UJ()",
"Migrated": false
"Migrated": true
},
{
"Field": "entrance",
@@ -2477,12 +2567,12 @@
"Reducer": "Rf.pte",
"Delegate": "Rf.pte(t, n.qpx_1)",
"Actions": [
"?B8",
"?F8",
"?G8",
"?Q8",
"?U8",
"ChangeUidInputState",
"HideLinkErrorWarning",
"MarkFreeRenameAvailable",
"MarkRenameFailed",
"MarkRenameSucceeded",
"ResetRenameDialog",
"ResponseActual",
"ResponseDelivered",
"SetChangeUidParams",
@@ -2490,7 +2580,7 @@
"SetValidationStatus"
],
"Default": "new O8()",
"Migrated": false
"Migrated": true
},
{
"Field": "eventGoods",
+90
View File
@@ -237,6 +237,26 @@ const AnonymousActionNames = new Map([
['Git', 'RegisterFailed'],
['hrt', 'ResetRegisterStatus'],
['_rt', 'ResetAuthenticationForm'],
// friends 切片(KD / PL.cr9):_L/hL 没有 toString。
['_L', 'SetContextMenuUser'],
['hL', 'ClearContextMenuUser'],
// suppliesState 切片(wE / yE.fq9):qE 没有 toString。
['qE', 'SetSupplyCount'],
// battleInviteState 切片(att / Ett.nth):dtt 没有 toString;
// mtt 的 toString 名 InviteToBattle 与另一个只有 battleId 的同名动作冲突,这里改用不冲突的类名。
['mtt', 'AddInvitedUser'],
['dtt', 'ClearSelectedForInvite'],
// partner 切片(UJ / 内联 reducer):_Z/tZ/JJ/hZ 没有 toString。
['_Z', 'UpdatePartnerInviteAction'],
['tZ', 'MarkAccountLinked'],
['JJ', 'MarkAccountNotLinked'],
['hZ', 'SetPartnerItemEnabled'],
// userRenameState 切片(O8 / Rf.pte):B8/F8/U8/G8/Q8 没有 toString。
['B8', 'MarkFreeRenameAvailable'],
['F8', 'MarkRenameSucceeded'],
['U8', 'MarkRenameFailed'],
['G8', 'ResetRenameDialog'],
['Q8', 'HideLinkErrorWarning'],
]);
// 非动作的 `instanceof`:屏幕标识类,用于 GoTo 分支判断(不是 dispatch 的动作)。
@@ -678,6 +698,12 @@ const Migrated = {
migrationState: { Type: 'MigrationStateSlice', Reduce: 'ReduceMigrationState' },
register: { Type: 'RegisterStateSlice', Reduce: 'ReduceRegister' },
userReportsState: { Type: 'UserReportsStateSlice', Reduce: 'ReduceUserReports' },
friends: { Type: 'FriendsStateSlice', Reduce: 'ReduceFriends' },
suppliesState: { Type: 'SuppliesStateSlice', Reduce: 'ReduceSupplies' },
clanUserInfo: { Type: 'ClanUserInfoStateSlice', Reduce: 'ReduceClanUserInfo' },
battleInviteState: { Type: 'BattleInviteStateSlice', Reduce: 'ReduceBattleInvite' },
partner: { Type: 'PartnerStateSlice', Reduce: 'ReducePartner' },
userRenameState: { Type: 'UserRenameStateSlice', Reduce: 'ReduceUserRename' },
};
for (const R of Rows) R.Migrated = Object.prototype.hasOwnProperty.call(Migrated, R.Field);
@@ -979,6 +1005,40 @@ const AuthAnonymousEffects = {
_rt: 'login=""、两个状态复位(两个切片都读)',
};
// friends 切片(KD / PL.cr9):_L/hL 没有 toString。
const FriendsAnonymousEffects = {
_L: 'contextMenuUser=参数(`n.er7(cd, t.er9_1)`)',
hL: 'contextMenuUser=null(`n.er7(cd, null)`)',
};
// suppliesState 切片(wE / yE.fq9):qE 没有 toString。
const SuppliesAnonymousEffects = {
qE: 'count 写键值(`n.bq9(cg(n.aq9_1, Vd(t.gq9_1, t.hq9_1)))`)',
};
// battleInviteState 切片(att / Ett.nth):dtt 没有 toString;mtt 名字冲突另收口。
const BattleInviteAnonymousEffects = {
mtt: 'invitedUsers += receiverId(原名 InviteToBattle,与另一个只有 battleId 的同名动作冲突,改名 AddInvitedUser)',
dtt: 'selectedForInvite = emptySet()(`Yg()`)',
};
// partner 切片(UJ / 内联 reducer):_Z/tZ/JJ/hZ 没有 toString。
const PartnerAnonymousEffects = {
_Z: 'inviteAction=参数(`n.osf(…, t.osg_1)`)',
tZ: 'isAccountAlreadyLinked=true(`n.osf(…, !0)`)',
JJ: 'isAccountAlreadyLinked=false(`n.osf(…, !1)`)',
hZ: 'itemsEnabledForPartner 写键值(`cg(n.ksf_1, new Km(t.msg_1, t.nsg_1))`)',
};
// userRenameState 切片(O8 / Rf.pte):B8/F8/U8/G8/Q8 没有 toString。
const UserRenameAnonymousEffects = {
B8: 'isFreeRenameAvailable=true(`n.cte(!0)`)',
F8: 'isFreeRenameAvailable=false、renameSuccessful=true(`n.cte(!1, cd, cd, !0)`)',
U8: 'renameSuccessful=false(`n.cte(cd, cd, cd, !1)`)',
G8: '复位 shopItemId / uidInputState / renameSuccessful / 两个 response 位(`n.cte(cd, null, n6(), null, cd, !1, !1)`)',
Q8: 'showLinkErrorWarning=false(`n.cte(…, !1)`)',
};
fs.writeFileSync(
path.join(P, 'names', 'root_wiring.json'),
JSON.stringify(
@@ -1175,6 +1235,36 @@ fs.writeFileSync(
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
...Object.entries(FriendsAnonymousEffects).map(([Local, Effect]) => ({
Local,
Name: AnonymousActionNames.get(Local),
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
...Object.entries(SuppliesAnonymousEffects).map(([Local, Effect]) => ({
Local,
Name: AnonymousActionNames.get(Local),
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
...Object.entries(BattleInviteAnonymousEffects).map(([Local, Effect]) => ({
Local,
Name: AnonymousActionNames.get(Local),
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
...Object.entries(PartnerAnonymousEffects).map(([Local, Effect]) => ({
Local,
Name: AnonymousActionNames.get(Local),
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
...Object.entries(UserRenameAnonymousEffects).map(([Local, Effect]) => ({
Local,
Name: AnonymousActionNames.get(Local),
DispatchedAt: '未在 action_trace_merged.json 中出现;按 reducer 分支效果定名',
Effect,
})),
],
Markers: [
{
@@ -0,0 +1,56 @@
// BattleInviteStateReducer.kt —— 战斗邀请切片(对应 module_10032 的 att 状态 + Ett.nth)
//
// 证据链:
// 状态类 att:3 个字段,toString 名 "BattleInviteState(…)"。
// 默认:isEnabled=true、invitedUsers=emptySet()、selectedForInvite=emptySet()。
// 根委派:`Ttt().nth(t, n.cpx_1)`。
// reducer(6 个实例臂):
// mtt(InviteToBattle) → invitedUsers += receiverId
// ytt(InvitationResponse) → invitedUsers -= userId
// wtt(SelectUserToInvite) → selectedForInvite += userId
// ptt(RemoveUserFromInvited) → selectedForInvite -= userId
// dtt(无 toString) → selectedForInvite = emptySet()
// ftt(EnableSetBattleInviteNotifications)→ isEnabled
package App.State.Reducers
import App.State.EnableSetBattleInviteNotifications
import App.State.InvitationResponse
import App.State.RemoveUserFromInvited
import App.State.SelectUserToInvite
/* ------------------------------------------------------------------ 匿名动作 */
/** 原 `mtt`(`InviteToBattle`):把被邀请者加入已邀请集合。
* 生成物里的 `InviteToBattle` 只保留了另一个同名的 `battleId` 动作,缺 `receiverId`,
* 因此这里单独收口成 `AddInvitedUser(userId)`。 */
data class AddInvitedUser(val userId: Any?)
/** 原 `dtt`:清空待邀请列表。 */
object ClearSelectedForInvite
/* ------------------------------------------------------------------ 状态 */
data class BattleInviteStateSlice(
val IsEnabled: Boolean = true,
val InvitedUsers: Set<Any?> = emptySet(),
val SelectedForInvite: Set<Any?> = emptySet(),
)
/* ------------------------------------------------------------------ reducer */
fun ReduceBattleInvite(
State0: BattleInviteStateSlice,
Action: Any,
): BattleInviteStateSlice = when (Action) {
is AddInvitedUser ->
State0.copy(InvitedUsers = State0.InvitedUsers + Action.userId)
is InvitationResponse ->
State0.copy(InvitedUsers = State0.InvitedUsers - Action.userId)
is SelectUserToInvite ->
State0.copy(SelectedForInvite = State0.SelectedForInvite + Action.userId)
is RemoveUserFromInvited ->
State0.copy(SelectedForInvite = State0.SelectedForInvite - Action.userId)
is ClearSelectedForInvite -> State0.copy(SelectedForInvite = emptySet())
is EnableSetBattleInviteNotifications -> State0.copy(IsEnabled = Action.isEnabled)
else -> State0
}
@@ -0,0 +1,31 @@
// ClanUserInfoReducer.kt —— 战队成员信息切片(对应 module_10032 的 hO 状态 + cO.gr1)
//
// 证据链:
// 状态类 hO:1 个字段,toString 名 "ClanUserInfo(users=…)"。
// 默认:users = ag() = emptyMap()。
// 根委派:`de.gr1(t, n.opw_1)`。
// reducer:`t instanceof aO ? n.dr1(jm(n.cr1_1, t.fr1_1)) : n`
// —— `jm` = Map.copy + putAll(`i3` 是 putAll),即把动作里的用户表合并进来。
// 动作 `aO` = Loaded(users)。
package App.State.Reducers
import App.State.Loaded
/* ------------------------------------------------------------------ 状态 */
data class ClanUserInfoStateSlice(
val Users: Map<Any?, Any?> = emptyMap(),
)
/* ------------------------------------------------------------------ reducer */
fun ReduceClanUserInfo(
State0: ClanUserInfoStateSlice,
Action: Any,
): ClanUserInfoStateSlice = when (Action) {
is Loaded -> {
val Incoming = Action.users as? Map<Any?, Any?> ?: emptyMap()
State0.copy(Users = State0.Users + Incoming)
}
else -> State0
}
@@ -0,0 +1,106 @@
// FriendsReducer.kt —— 好友切片(对应 module_10032 的 KD 状态 + PL.cr9)
//
// 证据链:
// 状态类 KD:8 个字段,toString 名 "FriendsState(…)"。
// 默认:userSearchState=HIDDEN、contextMenuUser=null、两个 limit=0、四个集合 emptySet()。
// 根委派:`NL().cr9(t, n.ypv_1)`。
// reducer(13 个实例臂):
// uL(ChangeFriendAddFindState)→ userSearchState
// _L(无 toString) → contextMenuUser = 参数
// hL(无 toString) → contextMenuUser = null
// aL(SetLimits) → acceptedLimit / incomingLimit
// cL / pL → accepted ∓
// lL / vL → newAccepted ∓
// dL / zL → incoming ∓
// gL / kL → newIncoming ∓
// U9(SetValidationStatus) → userSearchState = FOUND / NOT_FOUND
//
// 枚举直接对应源码 `YD()`:HIDDEN / SEARCHING / FOUND / NOT_FOUND。
// 原判据是 `status.equals(pk())`,`pk()` 是校验通过的单例(`new tJ()`);
// 离线重建没有这个单例的 Kotlin 值,这里按「传了非空状态即视为找到」收口。
package App.State.Reducers
import App.State.AddUsersToAccepted
import App.State.AddUsersToIncoming
import App.State.AddUsersToNewAccepted
import App.State.AddUsersToNewIncoming
import App.State.ChangeFriendAddFindState
import App.State.RemoveUserFromAccepted
import App.State.RemoveUserFromIncoming
import App.State.RemoveUserFromNewAccepted
import App.State.RemoveUsersFromNewIncoming
import App.State.SetLimits
import App.State.SetValidationStatus
/* ------------------------------------------------------------------ 枚举 */
enum class FriendSearchState {
HIDDEN,
SEARCHING,
FOUND,
NOT_FOUND,
}
/* ------------------------------------------------------------------ 匿名动作 */
/** 原 `_L`:打开某个好友的右键菜单。 */
data class SetContextMenuUser(val user: Any?)
/** 原 `hL`:关闭右键菜单。 */
object ClearContextMenuUser
/* ------------------------------------------------------------------ 状态 */
data class FriendsStateSlice(
val UserSearchState: FriendSearchState = FriendSearchState.HIDDEN,
val ContextMenuUser: Any? = null,
val AcceptedLimit: Int = 0,
val IncomingLimit: Int = 0,
val Accepted: Set<Any?> = emptySet(),
val NewAccepted: Set<Any?> = emptySet(),
val Incoming: Set<Any?> = emptySet(),
val NewIncoming: Set<Any?> = emptySet(),
)
/* ------------------------------------------------------------------ reducer */
/** 原 `Zg`:集合 += 集合 / 单值。 */
private fun Plus(Target: Set<Any?>, Value: Any?): Set<Any?> =
if (Value is Iterable<*>) Target + Value.toSet() else Target + Value
/** 原 `Qg`:集合 -= 集合 / 单值。 */
private fun Minus(Target: Set<Any?>, Value: Any?): Set<Any?> =
if (Value is Iterable<*>) Target - Value.toSet() else Target - Value
fun ReduceFriends(
State0: FriendsStateSlice,
Action: Any,
): FriendsStateSlice = when (Action) {
is ChangeFriendAddFindState ->
State0.copy(UserSearchState = Action.state as FriendSearchState)
is SetContextMenuUser -> State0.copy(ContextMenuUser = Action.user)
is ClearContextMenuUser -> State0.copy(ContextMenuUser = null)
is SetLimits -> State0.copy(
AcceptedLimit = Action.acceptedLimit,
IncomingLimit = Action.incomingLimit,
)
is AddUsersToAccepted -> State0.copy(Accepted = Plus(State0.Accepted, Action.usersId))
is RemoveUserFromAccepted -> State0.copy(Accepted = Minus(State0.Accepted, Action.userId))
// 生成物里 `AddUsersToNewAccepted` 与 clan 切片的同名动作合并成了一个类
// (`clanId, userIds`),friends 只读 `userIds`。
is AddUsersToNewAccepted ->
State0.copy(NewAccepted = Plus(State0.NewAccepted, Action.userIds))
is RemoveUserFromNewAccepted ->
State0.copy(NewAccepted = Minus(State0.NewAccepted, Action.userId))
is AddUsersToIncoming -> State0.copy(Incoming = Plus(State0.Incoming, Action.usersId))
is RemoveUserFromIncoming -> State0.copy(Incoming = Minus(State0.Incoming, Action.userId))
is AddUsersToNewIncoming ->
State0.copy(NewIncoming = Plus(State0.NewIncoming, Action.usersId))
is RemoveUsersFromNewIncoming ->
State0.copy(NewIncoming = Minus(State0.NewIncoming, Action.usersId))
is SetValidationStatus -> State0.copy(
UserSearchState =
if (Action.status == null) FriendSearchState.NOT_FOUND else FriendSearchState.FOUND,
)
else -> State0
}
@@ -0,0 +1,101 @@
// PartnerReducer.kt —— 合作方(partner)切片(对应 module_10032 的 UJ 状态 + 内联 reducer)
//
// 证据链:
// 状态类 UJ:15 个字段,toString 名 "PartnerState(…)";copy 是 osf(15 字段 + 回调)。
// 默认:partnerId=null、loginParams=emptyMap()、params=emptyMap()、三个能力位 false、
// paymentAction=null、inviteAction=null、canOpenExternalLinks=false、
// hasAccountBinding=false、failedRedirectUrl=null、loginForBindingStatus=OD()(NONE)、
// isAccountAlreadyLinked=false、itemsEnabledForPartner=emptyMap()、isPartnerReady=false。
// 根委派:内联 IIFE,字段 `fpy_1`;委派表记作 `(inline@partner)`,11 个动作。
// reducer:
// VJ(SetPartnerId) → partnerId + failedRedirectUrl
// nZ(InitSocialNetworkParameters)→ 3 个能力位 + canOpenExternalLinks + hasAccountBinding + failedRedirectUrl
// iZ(SetLoginParams) → loginParams
// uZ(SetPartnerParameters) → params
// eZ(InitPartnerInviteAction) → inviteAction
// _Z(无 toString) → inviteAction(另一个派发点)
// XJ(SetLoginStatusForBinding) → loginForBindingStatus(复用登录切片的 ByPasswordStatus)
// tZ / JJ(无 toString) → isAccountAlreadyLinked = true / false
// hZ(无 toString) → itemsEnabledForPartner 写一个键值(`Km` = Pair)
// rZ(SetPartnerReady) → isPartnerReady
package App.State.Reducers
import App.State.InitPartnerInviteAction
import App.State.InitSocialNetworkParameters
import App.State.SetLoginParams
import App.State.SetLoginStatusForBinding
import App.State.SetPartnerId
import App.State.SetPartnerParameters
import App.State.SetPartnerReady
/* ------------------------------------------------------------------ 匿名动作 */
/** 原 `_Z`:更新邀请动作(与 InitPartnerInviteAction 同字段,另一个派发点)。 */
data class UpdatePartnerInviteAction(val action: Any?)
/** 原 `tZ`:标记账号已绑定。 */
object MarkAccountLinked
/** 原 `JJ`:标记账号未绑定。 */
object MarkAccountNotLinked
/** 原 `hZ`:写某个合作方物品的启用状态。 */
data class SetPartnerItemEnabled(val itemId: Any?, val enabled: Boolean)
/* ------------------------------------------------------------------ 状态 */
data class PartnerStateSlice(
val PartnerId: Any? = null,
val LoginParams: Map<Any?, Any?> = emptyMap(),
val Params: Map<Any?, Any?> = emptyMap(),
val HasOwnPaymentSystem: Boolean = false,
val HasSocialFunction: Boolean = false,
val HasClientPaymentAction: Boolean = false,
val PaymentAction: Any? = null,
val InviteAction: Any? = null,
val CanOpenExternalLinks: Boolean = false,
val HasAccountBinding: Boolean = false,
val FailedRedirectUrl: Any? = null,
val LoginForBindingStatus: ByPasswordStatus = ByPasswordStatus.NONE,
val IsAccountAlreadyLinked: Boolean = false,
val ItemsEnabledForPartner: Map<Any?, Any?> = emptyMap(),
val IsPartnerReady: Boolean = false,
)
/* ------------------------------------------------------------------ reducer */
@Suppress("UNCHECKED_CAST")
private fun AsMap(Value: Any?): Map<Any?, Any?> =
Value as? Map<Any?, Any?> ?: emptyMap()
fun ReducePartner(
State0: PartnerStateSlice,
Action: Any,
): PartnerStateSlice = when (Action) {
is SetPartnerId -> State0.copy(
PartnerId = Action.partnerId,
FailedRedirectUrl = Action.failedRedirectUrl,
)
is InitSocialNetworkParameters -> State0.copy(
HasOwnPaymentSystem = Action.hasOwnPaymentSystem,
HasSocialFunction = Action.hasSocialFunction,
HasClientPaymentAction = Action.hasClientPaymentAction,
CanOpenExternalLinks = Action.canOpenExternalLinks,
HasAccountBinding = Action.hasAccountBinding,
FailedRedirectUrl = Action.failedRedirectUrl,
)
is SetLoginParams -> State0.copy(LoginParams = AsMap(Action.params))
is SetPartnerParameters -> State0.copy(Params = AsMap(Action.params))
is InitPartnerInviteAction -> State0.copy(InviteAction = Action.action)
is UpdatePartnerInviteAction -> State0.copy(InviteAction = Action.action)
is SetLoginStatusForBinding ->
State0.copy(LoginForBindingStatus = Action.status as ByPasswordStatus)
is MarkAccountLinked -> State0.copy(IsAccountAlreadyLinked = true)
is MarkAccountNotLinked -> State0.copy(IsAccountAlreadyLinked = false)
is SetPartnerItemEnabled -> State0.copy(
ItemsEnabledForPartner =
State0.ItemsEnabledForPartner + (Action.itemId to Action.enabled),
)
is SetPartnerReady -> State0.copy(IsPartnerReady = Action.isReady)
else -> State0
}
@@ -0,0 +1,31 @@
// SuppliesStateReducer.kt —— 补给计数切片(对应 module_10032 的 wE 状态 + yE.fq9)
//
// 证据链:
// 状态类 wE:1 个字段,toString 名 "SuppliesState(count=…)"。
// 默认:count = ag() = emptyMap()。
// 根委派:`$t.fq9(t, n.ipw_1)`。
// reducer:`t instanceof qE ? n.bq9(cg(n.aq9_1, Vd(t.gq9_1, t.hq9_1))) : n`
// —— `qE(key, value)` 往 Map 里写一个键值(`cg` = put,`Vd` = Pair)。
package App.State.Reducers
/* ------------------------------------------------------------------ 匿名动作 */
/** 原 `qE`:写一个补给 id 的剩余次数。 */
data class SetSupplyCount(val supplyId: Any?, val count: Any?)
/* ------------------------------------------------------------------ 状态 */
data class SuppliesStateSlice(
val Count: Map<Any?, Any?> = emptyMap(),
)
/* ------------------------------------------------------------------ reducer */
fun ReduceSupplies(
State0: SuppliesStateSlice,
Action: Any,
): SuppliesStateSlice = when (Action) {
is SetSupplyCount ->
State0.copy(Count = State0.Count + (Action.supplyId to Action.count))
else -> State0
}
@@ -0,0 +1,125 @@
// UserRenameReducer.kt —— 改名切片(对应 module_10032 的 O8 状态 + t6.pte)
//
// 证据链:
// 状态类 O8:10 个字段,toString 名 "UserRenameState(…)";copy 是 cte(10 字段 + 回调)。
// 默认:isFreeRenameAvailable=false、userRenameShopItemId=null、uidInputState=EMPTY、
// renameSuccessful=null、rubiesForRename=Int.MAX_VALUE、两个 response 位 false、
// changeUidHash=null、email=null、showLinkErrorWarning=true。
// 根委派:`Rf.pte(t, n.qpx_1)`。
// reducer(11 个实例臂):
// H8(ChangeUidInputState)→ uidInputState
// B8(无 toString) → isFreeRenameAvailable=true
// F8(无 toString) → isFreeRenameAvailable=false、renameSuccessful=true
// U8(无 toString) → renameSuccessful=false
// U9(SetValidationStatus)→ isResponseDelivered=true;
// uidInputState 按校验状态映射
// (源码 `switch (status.k3_1)`:EMPTY→SHORT、SHORT→NOT_VALID、
// RESERVED→RESERVED、FORBIDDEN/NOT_VALID→FORBIDDEN、FREE→FREE)
// V8(SetRubiesForRename) → rubiesForRename
// G8(无 toString) → 整片复位(shopItemId=null、uidInputState=EMPTY、renameSuccessful=null、
// 两个 response 位 false)
// K8(ResponseDelivered) → isResponseDelivered
// Y8(ResponseActual) → isResponseActual
// X8(SetChangeUidParams) → changeUidHash、email
// Q8(无 toString) → showLinkErrorWarning=false
//
// 枚举 `UidInputState` 直接对应源码 `D8()`:EMPTY / SHORT / RESERVED / FORBIDDEN / NOT_VALID / FREE。
// `SetValidationStatus.status` 在原包里是同一枚举;离线重建按该枚举的 ordinal 映射。
package App.State.Reducers
import App.State.ChangeUidInputState
import App.State.ResponseActual
import App.State.ResponseDelivered
import App.State.SetChangeUidParams
import App.State.SetRubiesForRename
import App.State.SetValidationStatus
/* ------------------------------------------------------------------ 枚举 */
enum class RenameUidState {
EMPTY,
SHORT,
RESERVED,
FORBIDDEN,
NOT_VALID,
FREE,
}
/* ------------------------------------------------------------------ 匿名动作 */
/** 原 `B8`:免费改名可用。 */
object MarkFreeRenameAvailable
/** 原 `F8`:改名成功(同时把免费名额用掉)。 */
object MarkRenameSucceeded
/** 原 `U8`:改名失败。 */
object MarkRenameFailed
/** 原 `G8`:复位改名弹窗(保留 rubiesForRename / email 等外层字段)。 */
object ResetRenameDialog
/** 原 `Q8`:隐藏链接错误提示。 */
object HideLinkErrorWarning
/* ------------------------------------------------------------------ 状态 */
data class UserRenameStateSlice(
val IsFreeRenameAvailable: Boolean = false,
val UserRenameShopItemId: Any? = null,
val UidInputState: RenameUidState = RenameUidState.EMPTY,
val RenameSuccessful: Any? = null,
val RubiesForRename: Int = Int.MAX_VALUE,
val IsResponseDelivered: Boolean = false,
val IsResponseActual: Boolean = false,
val ChangeUidHash: Any? = null,
val Email: Any? = null,
val ShowLinkErrorWarning: Boolean = true,
)
/* ------------------------------------------------------------------ reducer */
/** 原 `switch (status.k3_1)`:校验状态 → 输入框状态。 */
private fun MapValidationStatus(Status: Any?): RenameUidState? =
when ((Status as? RenameUidState)?.ordinal) {
0 -> RenameUidState.SHORT
1 -> RenameUidState.NOT_VALID
2 -> RenameUidState.RESERVED
3, 4 -> RenameUidState.FORBIDDEN
5 -> RenameUidState.FREE
else -> null
}
fun ReduceUserRename(
State0: UserRenameStateSlice,
Action: Any,
): UserRenameStateSlice = when (Action) {
is ChangeUidInputState ->
State0.copy(UidInputState = Action.uidInputState as RenameUidState)
is MarkFreeRenameAvailable -> State0.copy(IsFreeRenameAvailable = true)
is MarkRenameSucceeded -> State0.copy(
IsFreeRenameAvailable = false,
RenameSuccessful = true,
)
is MarkRenameFailed -> State0.copy(RenameSuccessful = false)
is SetValidationStatus -> State0.copy(
UidInputState = MapValidationStatus(Action.status) ?: State0.UidInputState,
IsResponseDelivered = true,
)
is SetRubiesForRename -> State0.copy(RubiesForRename = Action.rubiesForRename)
is ResetRenameDialog -> State0.copy(
UserRenameShopItemId = null,
UidInputState = RenameUidState.EMPTY,
RenameSuccessful = null,
IsResponseDelivered = false,
IsResponseActual = false,
)
is ResponseDelivered -> State0.copy(IsResponseDelivered = Action.isDelivered)
is ResponseActual -> State0.copy(IsResponseActual = Action.isResponseActual)
is SetChangeUidParams -> State0.copy(
ChangeUidHash = Action.changeUidHash,
Email = Action.email,
)
is HideLinkErrorWarning -> State0.copy(ShowLinkErrorWarning = false)
else -> State0
}
@@ -1,7 +1,7 @@
// RootReducer.kt —— 根 reducer 骨架(对应 module_10032 的根状态 z6 / TOState 与根 reducer k6)
//
// 原包 k6 对 89 个切片逐个调用各自的 reducer,再 `return n.cti(...)`(copy 出新状态)。
// 这里采用同一形状。已迁移的 53 个切片里,52 个真正调用 reducer,launchParams 只做类型收口:
// 这里采用同一形状。已迁移的 59 个切片里,58 个真正调用 reducer,launchParams 只做类型收口:
//
// inviteState → ReduceInvite(aD / pD.er5)
// loginByPassword → ReduceLoginByPassword(gD / MD.cr6)
@@ -55,9 +55,15 @@
// migrationState → ReduceMigrationState($it / Uv.rtg,根委派 `Uv.rtg(t, n.ipv_1)`)
// register → ReduceRegister(Cit / cp.stg,根委派 `cp.stg(t, n.hpv_1)`)
// userReportsState → ReduceUserReports(Zst / qot.zth,根委派 `(null == sd && new qot(), sd).zth(t, n.dpy_1)`)
// friends → ReduceFriends(KD / NL().cr9,根委派 `NL().cr9(t, n.ypv_1)`)
// suppliesState → ReduceSupplies(wE / $t.fq9,根委派 `$t.fq9(t, n.ipw_1)`)
// clanUserInfo → ReduceClanUserInfo(hO / de.gr1,根委派 `de.gr1(t, n.opw_1)`)
// battleInviteState → ReduceBattleInvite(att / Ttt().nth,根委派 `Ttt().nth(t, n.cpx_1)`)
// partner → ReducePartner(UJ / `(inline@partner)`,根委派内联 IIFE)
// userRenameState → ReduceUserRename(O8 / Rf.pte,根委派 `Rf.pte(t, n.qpx_1)`)
// launchParams → 无 reducer(原根 reducer 原样透传;默认 `ag()` = emptyMap())
//
// 其余 36 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
// 其余 30 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
package App.State
@@ -67,7 +73,9 @@ import App.State.Reducers.ReduceAnnouncement
import App.State.Reducers.ReduceAntiAddiction
import App.State.Reducers.ReduceBattleResult
import App.State.Reducers.ReduceBattleTutorial
import App.State.Reducers.ReduceBattleInvite
import App.State.Reducers.ReduceCaptcha
import App.State.Reducers.ReduceClanUserInfo
import App.State.Reducers.ReduceContextTutorial
import App.State.Reducers.ReduceCreatorTag
import App.State.Reducers.ReduceDroppableGold
@@ -75,6 +83,7 @@ import App.State.Reducers.ReduceEntrance
import App.State.Reducers.ReduceEventGoods
import App.State.Reducers.ReduceGarage
import App.State.Reducers.ReduceGaragePreview
import App.State.Reducers.ReduceFriends
import App.State.Reducers.ReduceHome
import App.State.Reducers.ReduceIcons
import App.State.Reducers.ReduceInvite
@@ -92,6 +101,7 @@ import App.State.Reducers.ReduceNews
import App.State.Reducers.ReduceNotificationByCountry
import App.State.Reducers.ReduceNotifications
import App.State.Reducers.ReducePasswordChange
import App.State.Reducers.ReducePartner
import App.State.Reducers.ReduceProBattle
import App.State.Reducers.ReducePromoCode
import App.State.Reducers.ReducePurchaseDialog
@@ -108,11 +118,13 @@ import App.State.Reducers.ReduceSpectator
import App.State.Reducers.ReduceSupportUrl
import App.State.Reducers.ReduceSettings
import App.State.Reducers.ReduceShop
import App.State.Reducers.ReduceSupplies
import App.State.Reducers.ReduceSystem
import App.State.Reducers.ReduceTips
import App.State.Reducers.ReduceTwitch
import App.State.Reducers.ReduceTwoStepVerification
import App.State.Reducers.ReduceUser
import App.State.Reducers.ReduceUserRename
import App.State.Reducers.ReduceUserReports
/** 根 reducer:`k6`。 */
@@ -180,6 +192,12 @@ object RootReducer {
MigrationState = ReduceMigrationState(State0.MigrationState, Action),
Register = ReduceRegister(State0.Register, Action),
UserReportsState = ReduceUserReports(State0.UserReportsState, Action),
Friends = ReduceFriends(State0.Friends, Action),
SuppliesState = ReduceSupplies(State0.SuppliesState, Action),
ClanUserInfo = ReduceClanUserInfo(State0.ClanUserInfo, Action),
BattleInviteState = ReduceBattleInvite(State0.BattleInviteState, Action),
Partner = ReducePartner(State0.Partner, Action),
UserRenameState = ReduceUserRename(State0.UserRenameState, Action),
)
}
@@ -8,14 +8,17 @@ import App.State.Reducers.AccountSettingsStateSlice
import App.State.Reducers.AnnouncementStateSlice
import App.State.Reducers.AntiAddictionStateSlice
import App.State.Reducers.AvailabilityByRankStateSlice
import App.State.Reducers.BattleInviteStateSlice
import App.State.Reducers.BattleResultStateSlice
import App.State.Reducers.BattleTutorialStateSlice
import App.State.Reducers.CaptchaStateSlice
import App.State.Reducers.ClanUserInfoStateSlice
import App.State.Reducers.ContextTutorialStateSlice
import App.State.Reducers.CreatorTagStateSlice
import App.State.Reducers.DroppableGoldStateSlice
import App.State.Reducers.EntranceStateSlice
import App.State.Reducers.EventGoodsStateSlice
import App.State.Reducers.FriendsStateSlice
import App.State.Reducers.GarageStateSlice
import App.State.Reducers.GaragePreviewStateSlice
import App.State.Reducers.HomeStateSlice
@@ -32,6 +35,7 @@ import App.State.Reducers.ModerationStateSlice
import App.State.Reducers.NewsStateSlice
import App.State.Reducers.NotificationByCountryStateSlice
import App.State.Reducers.NotificationsStateSlice
import App.State.Reducers.PartnerStateSlice
import App.State.Reducers.PasswordChangeStateSlice
import App.State.Reducers.ProBattleStateSlice
import App.State.Reducers.PromoCodeStateSlice
@@ -48,12 +52,14 @@ import App.State.Reducers.SecuritySettingsStateSlice
import App.State.Reducers.SettingsStateSlice
import App.State.Reducers.ShopStateSlice
import App.State.Reducers.SpectatorStateSlice
import App.State.Reducers.SuppliesStateSlice
import App.State.Reducers.SupportUrlStateSlice
import App.State.Reducers.SystemStateSlice
import App.State.Reducers.TipsStateSlice
import App.State.Reducers.TwitchStateSlice
import App.State.Reducers.TwoStepVerificationStateSlice
import App.State.Reducers.UserStateSlice
import App.State.Reducers.UserRenameStateSlice
import App.State.Reducers.UserReportsStateSlice
/**
@@ -113,8 +119,8 @@ data class RootState(
val LootBoxes: LootBoxesStateSlice = LootBoxesStateSlice(),
/** 原 notifications(jJ / B_.ise)。已迁移。 */
val Notifications: NotificationsStateSlice = NotificationsStateSlice(),
/** 原 friends(KD / NL().cr9)。未迁移:原样透传。 */
val Friends: Any? = null,
/** 原 friends(KD / NL().cr9)。已迁移。 */
val Friends: FriendsStateSlice = FriendsStateSlice(),
/** 原 remoteUserDataStorage($Q / vh.isl)。未迁移:原样透传。 */
val RemoteUserDataStorage: Any? = null,
/** 原 battlesSelect(fA / Mi.vqk)。未迁移:原样透传。 */
@@ -133,8 +139,8 @@ data class RootState(
val BattleResult: BattleResultStateSlice = BattleResultStateSlice(),
/** 原 battleUsers(uP / sr.hqs)。未迁移:原样透传。 */
val BattleUsers: Any? = null,
/** 原 suppliesState(wE / $t.fq9)。未迁移:原样透传。 */
val SuppliesState: Any? = null,
/** 原 suppliesState(wE / $t.fq9)。已迁移。 */
val SuppliesState: SuppliesStateSlice = SuppliesStateSlice(),
/** 原 matchmakingGroup(rW / NW().is0)。未迁移:原样透传。 */
val MatchmakingGroup: Any? = null,
/** 原 shop(j4 / (inline@shop))。已迁移。 */
@@ -145,8 +151,8 @@ data class RootState(
val BattleViewState: Any? = null,
/** 原 rankUpDialogState(LO / Se.br3)。已迁移。 */
val RankUpDialogState: RankUpDialogStateSlice = RankUpDialogStateSlice(),
/** 原 clanUserInfo(hO / de.gr1)。未迁移:原样透传。 */
val ClanUserInfo: Any? = null,
/** 原 clanUserInfo(hO / de.gr1)。已迁移。 */
val ClanUserInfo: ClanUserInfoStateSlice = ClanUserInfoStateSlice(),
/** 原 tankActionsLogState(vI / wi.gqe)。未迁移:原样透传。 */
val TankActionsLogState: Any? = null,
/** 原 battleMessage(FE / vT().rqb)。未迁移:原样透传。 */
@@ -173,8 +179,8 @@ data class RootState(
val SecuritySettings: SecuritySettingsStateSlice = SecuritySettingsStateSlice(),
/** 原 purchaseDialogState(CO / je.ur2)。已迁移。 */
val PurchaseDialogState: PurchaseDialogStateSlice = PurchaseDialogStateSlice(),
/** 原 battleInviteState(att / Ttt().nth)。未迁移:原样透传。 */
val BattleInviteState: Any? = null,
/** 原 battleInviteState(att / Ttt().nth)。已迁移。 */
val BattleInviteState: BattleInviteStateSlice = BattleInviteStateSlice(),
/** 原 keyboardSettings(Sut / bw.oth)。未迁移:原样透传。 */
val KeyboardSettings: Any? = null,
/** 原 touchscreenHudSettings(j7 / N7().xsv)。未迁移:原样透传。 */
@@ -185,8 +191,8 @@ data class RootState(
val Language: Language = App.State.Language.EN,
/** 原 launchParams(? / (pass-through))。已迁移(透传,强类型)。 */
val LaunchParams: LaunchParamsStateSlice = emptyMap(),
/** 原 partner(UJ / (inline@partner))。未迁移:原样透传。 */
val Partner: Any? = null,
/** 原 partner(UJ / (inline@partner))。已迁移。 */
val Partner: PartnerStateSlice = PartnerStateSlice(),
/** 原 entrance(hD / Bv.pth)。已迁移。 */
val Entrance: EntranceStateSlice = EntranceStateSlice(),
/** 原 antiAddiction(UC / W.iq8)。已迁移。 */
@@ -201,8 +207,8 @@ data class RootState(
val Announcement: AnnouncementStateSlice = AnnouncementStateSlice(),
/** 原 notificationByCountry(gJ / R_.ssd)。已迁移。 */
val NotificationByCountry: NotificationByCountryStateSlice = NotificationByCountryStateSlice(),
/** 原 userRenameState(O8 / Rf.pte)。未迁移:原样透传。 */
val UserRenameState: Any? = null,
/** 原 userRenameState(O8 / Rf.pte)。已迁移。 */
val UserRenameState: UserRenameStateSlice = UserRenameStateSlice(),
/** 原 eventGoods(W7 / Wc.esx)。已迁移。 */
val EventGoods: EventGoodsStateSlice = EventGoodsStateSlice(),
/** 原 creatorTagState(qO / ze.er2)。已迁移。 */
@@ -40,7 +40,7 @@ object RootWiring {
SliceWiring("accountSettings", "vpv_1", "oet", "Lp.mtg", "Lp.mtg(t, n.vpv_1)", listOf("ClearAccountRemovalTimer", "ClearInputUid", "ResetPasswordFields", "SetAccountRemovalDays", "SetCountry", "SetCurrentPassword", "SetCurrentPasswordMandatory", "SetEmail", "SetEmailConfirmed", "SetNewPassword", "SetPasswordStatus", "SetRepeatNewPassword", "SetUnlockButtonForRenameUid")),
SliceWiring("lootBoxes", "wpv_1", "aH", "ws.wrv", "ws.wrv(t, n.wpv_1)", listOf("AddKeys", "ClearRewards", "LootBoxOpeningFailed", "SelectCategory", "SetContainerData", "SetCurrentContainer", "SetGoToGarage", "SetIsAccelerated", "SetIsBlockOpenButtons", "SetIsOpening", "SetIsReopening", "SetIsShopVisited", "SetKeys", "SetPeriodOfGiveLootBox", "SetPossibleRewards", "SetRewards", "SetTimeToGiveLootBox", "ShownRewards")),
SliceWiring("notifications", "xpv_1", "jJ", "B_.ise", "B_.ise(t, n.xpv_1)", listOf("AddNotification", "InitNotificationSound", "RemoveFirstNotification", "RemoveNotifications")),
SliceWiring("friends", "ypv_1", "KD", "NL().cr9", "NL().cr9(t, n.ypv_1)", listOf("?_L", "?hL", "AddUsersToAccepted", "AddUsersToIncoming", "AddUsersToNewAccepted", "AddUsersToNewIncoming", "ChangeFriendAddFindState", "RemoveUserFromAccepted", "RemoveUserFromIncoming", "RemoveUserFromNewAccepted", "RemoveUsersFromNewIncoming", "SetLimits", "SetValidationStatus")),
SliceWiring("friends", "ypv_1", "KD", "NL().cr9", "NL().cr9(t, n.ypv_1)", listOf("AddUsersToAccepted", "AddUsersToIncoming", "AddUsersToNewAccepted", "AddUsersToNewIncoming", "ChangeFriendAddFindState", "ClearContextMenuUser", "RemoveUserFromAccepted", "RemoveUserFromIncoming", "RemoveUserFromNewAccepted", "RemoveUsersFromNewIncoming", "SetContextMenuUser", "SetLimits", "SetValidationStatus")),
SliceWiring("remoteUserDataStorage", "zpv_1", "\$Q", "vh.isl", "vh.isl(t, n.zpv_1)", listOf("UpdateClanTags", "UpdateOnlineStatus", "UpdatePremiumStatus", "UpdateRanks", "UpdateUids", "UserEnterBattle", "UserLeaveBattle", "UserLeaveGroup")),
SliceWiring("battlesSelect", "apw_1", "fA", "Mi.vqk", "Mi.vqk(t, n.apw_1)", listOf("?dA", "?gA", "AddBattle", "AddUser", "AddUsers", "ChangeName", "EnterToBattleInfo", "InitProBattleRewardLimit", "LimitReached", "RemoveBattle", "RemoveUser", "RoundFinish", "RoundStarted", "SetReEnterProtectBattleId", "SwapTeam", "UpdateFund", "UpdateSuspicion", "UpdateTeamScore", "UpdateUserScore", "UpdateUserSuspicion")),
SliceWiring("maps", "bpw_1", "OA", "Oi.sql", "Oi.sql(t, n.bpw_1)", listOf("AddMap")),
@@ -50,7 +50,7 @@ object RootWiring {
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("DoInit", "ResetBattleRewards", "SetHasBattleResult", "SetIncreasedReward", "SetRewards", "SetSimpleReward")),
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")),
SliceWiring("suppliesState", "ipw_1", "wE", "\$t.fq9", "\$t.fq9(t, n.ipw_1)", listOf("SetSupplyCount")),
SliceWiring("matchmakingGroup", "jpw_1", "rW", "NW().is0", "NW().is0(t, n.jpw_1)", listOf("?zW", "AcceptedInviteToGroup", "AddUser", "AddUserToSelected", "InitMatchmakingGroup", "Invite", "RejectedInviteToGroup", "RemoveUserToSelected", "SetAllowedFromFriends", "SetSelectedUsers", "UpdateMountItems", "UpdateUserReadiness", "UserRemoved")),
SliceWiring("shop", "kpw_1", "j4", "(inline@shop)", "(function (t, n) { var i; if (t instanceof w4) i = n.qt6(cd, cd, cd, cd, cd, cd, cd, t.lt6_1); else if (t instanceof Yut) i = n.qt6(cd, cd, cd, cd, !0); else if (t instanceof F5) i = new j4(cd, cd, cd, cd, cd, cd, cd, n.qq3_1); else if (t instanceof h4) i = n.qt6(cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, t.st5_1); else if (t instanceof c4) i = n.qt6(cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, cd, t.ut5_1); else if (t instanceof u4) i = n.qt6( cd, cd, cd, cd, cd, cd, …", listOf("?x4", "AddCategoriesWithBonus", "AddItemToOrderProcessing", "BlockBackButton", "BraintreeLoadingInterrupt", "ClearPreviousScreen", "ClearTargetCategory", "ClearTargetProductId", "CurrentPayMode", "DeleteOneTimePurchaseItem", "InitAdditionalDescription", "InitAmountString", "InitCategory", "InitCoinPackage", "InitCompensation", "InitCrystalKitPackage", "InitFeatureItem", "InitGroup", "InitItemViewType", "InitKeysPackage", "InitKitBundle", "InitKitPackage", "InitLootBoxAndPaint", "InitOneTimePurchaseItem", "InitPayModeAction", "InitPaymentModeAsShopItem", "InitPaymentProviderItems", "InitPaymentViewType", "InitRestrictionByPayMode", "InitRewardedVideosBlocks", "InitShopCard", "InitShopCardDiscount", "InitShopCardView", "InitShopItem", "InitShopItemName", "InitSingleItemKit", "InitSpecialKitPackage", "MobilePaymentStateChanged", "PaymentProviderFinishFail", "PaymentProviderFinishSuccess", "PaymentProviderInProgress", "PaymentUrlReceiveFailed", "PaymentUrlReceived", "RemoveDiscountItemForItem", "RemoveItemFromOrderProcessing", "ResetPaymentState", "ResetShop", "SavePreviousScreen", "SaveTargetProductId", "SaveTargetVisitedCategory", "SelectCategory", "SelectCategoryId", "SelectItem", "SetBraintreeToken", "SetCoinPaymentProviderUrl", "SetGoToHomeAfterBuy", "SetInitShopItemId", "SetPurchaseCompleted", "SetStripeIntentParams", "SetupDiscountCategoryByItem", "SetupDiscountItem", "SetupDiscountItemForItem", "ShopCardExpired", "ShopCardRemoved", "ShopCardSold", "ShopLoaded", "ToggleShopActivity", "TryToBuyOneTimePurchase", "UnblockBackButton")),
SliceWiring("battlePauseState", "lpw_1", "dT", "NT().mqc", "NT().mqc(t, n.lpw_1)", listOf("?TT", "?xT", "OnPauseMode", "SetIdleKickPeriod")),
@@ -70,13 +70,13 @@ object RootWiring {
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("ChangeEmailStatus", "ResetPurchaseDialog", "SuccessfullyPurchase")),
SliceWiring("battleInviteState", "cpx_1", "att", "Ttt().nth", "Ttt().nth(t, n.cpx_1)", listOf("?dtt", "EnableSetBattleInviteNotifications", "InvitationResponse", "InviteToBattle", "RemoveUserFromInvited", "SelectUserToInvite")),
SliceWiring("battleInviteState", "cpx_1", "att", "Ttt().nth", "Ttt().nth(t, n.cpx_1)", listOf("AddInvitedUser", "ClearSelectedForInvite", "EnableSetBattleInviteNotifications", "InvitationResponse", "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")),
SliceWiring("mainQuest", "fpx_1", "WZ", "ih.bsk", "ih.bsk(t, n.fpx_1)", listOf("AddRewardedQuest", "QuestsLoaded", "ResetQuestsLoaded", "SetBattleUpdatedQuests", "SetEventPassShopItemId", "SetQuests", "UpdateQuests")),
SliceWiring("language", "gpx_1", "?", "m6().jtg", "m6().jtg(t, n.gpx_1)", listOf("SetLanguage")),
SliceWiring("launchParams", "hpx_1", "?", "(pass-through)", "n.hpx_1", listOf()),
SliceWiring("partner", "ipx_1", "UJ", "(inline@partner)", "(function (t, n) { var i; if (t instanceof VJ) i = n.osf(t.rsf_1, cd, cd, cd, cd, cd, cd, cd, cd, cd, t.ssf_1); else if (t instanceof nZ) { var r = t.bsg_1, e = t.csg_1, u = t.dsg_1, s = t.esg_1, o = t.fsg_1, _ = t.gsg_1; i = n.osf(cd, cd, cd, r, e, u, cd, cd, o, _, s); } else i = t instanceof iZ ? n.osf(cd, t.hsg_1) : t instanceof uZ ? n.osf(cd, cd, t.ksg_1) : t instanceof eZ ? n.osf(cd, cd, cd, cd, cd, cd, cd, t.jsg_1) : t instanceof _Z ? n.osf(cd, cd, cd, cd, cd, cd, t.osg_1) : t instanceof X …", listOf("?JJ", "?_Z", "?hZ", "?tZ", "InitPartnerInviteAction", "InitSocialNetworkParameters", "SetLoginParams", "SetLoginStatusForBinding", "SetPartnerId", "SetPartnerParameters", "SetPartnerReady")),
SliceWiring("partner", "ipx_1", "UJ", "(inline@partner)", "(function (t, n) { var i; if (t instanceof VJ) i = n.osf(t.rsf_1, cd, cd, cd, cd, cd, cd, cd, cd, cd, t.ssf_1); else if (t instanceof nZ) { var r = t.bsg_1, e = t.csg_1, u = t.dsg_1, s = t.esg_1, o = t.fsg_1, _ = t.gsg_1; i = n.osf(cd, cd, cd, r, e, u, cd, cd, o, _, s); } else i = t instanceof iZ ? n.osf(cd, t.hsg_1) : t instanceof uZ ? n.osf(cd, cd, t.ksg_1) : t instanceof eZ ? n.osf(cd, cd, cd, cd, cd, cd, cd, t.jsg_1) : t instanceof _Z ? n.osf(cd, cd, cd, cd, cd, cd, t.osg_1) : t instanceof X …", listOf("InitPartnerInviteAction", "InitSocialNetworkParameters", "MarkAccountLinked", "MarkAccountNotLinked", "SetLoginParams", "SetLoginStatusForBinding", "SetPartnerId", "SetPartnerItemEnabled", "SetPartnerParameters", "SetPartnerReady", "UpdatePartnerInviteAction")),
SliceWiring("entrance", "jpx_1", "hD", "Bv.pth", "Bv.pth(t, n.jpx_1)", listOf("HideSocialNetworks", "SetAgeWarnShow", "SetAuthorizedByChinaSdkToken", "SetClassic", "SetControlsForChina", "SetRunningInChinaDataCenter", "SetRunningInChinaPartners")),
SliceWiring("antiAddiction", "kpx_1", "UC", "W.iq8", "W.iq8(t, n.kpx_1)", listOf("EnableRealNameCheck", "RequireUserIdVerification", "SetRealNameAndIdNumber", "SetValidateAntiAddictionUserDataStatus", "UpdatePlayingTime")),
SliceWiring("spectator", "lpx_1", "HA", "Li.jqm", "Li.jqm(t, n.lpx_1)", listOf("AddSpectator", "RemoveSpectator", "SetSpectator", "SetSpectators", "SetSpectatorsListVisible", "UpdateHudLayout")),
@@ -84,7 +84,7 @@ object RootWiring {
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("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("userRenameState", "qpx_1", "O8", "Rf.pte", "Rf.pte(t, n.qpx_1)", listOf("ChangeUidInputState", "HideLinkErrorWarning", "MarkFreeRenameAvailable", "MarkRenameFailed", "MarkRenameSucceeded", "ResetRenameDialog", "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("ClearCreatorTag", "SetAttachedCreatorTag", "SetCreatorTag", "SetCreatorTags")),
SliceWiring("twoStepVerificationState", "tpx_1", "x9", "_f.atb", "_f.atb(t, n.tpx_1)", listOf("ActivationStatus2FA", "Enable2FaRequest", "SetSecretAndQRCode", "SetUse2FA", "VerificationStatus")),
@@ -106,7 +106,7 @@ object RootWiring {
)
/** 已迁移(强类型;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", "lobbyLoader", "purchaseDialogState", "promoCode", "supportUrl", "rulesUpdated", "notificationByCountry", "availabilityByRank", "icons", "questCategories", "mainQuest", "securitySettings", "referralEvent", "tipsState", "rankUpDialogState", "battleResult", "captchaState", "modalScreen", "droppableGold", "eventGoods", "creatorTagState", "rewardedVideosState", "passwordChangeState", "system", "contextTutorial", "battleTutorial", "spectator", "twoStepVerificationState", "antiAddiction", "moderation", "migrationState", "register", "userReportsState")
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", "icons", "questCategories", "mainQuest", "securitySettings", "referralEvent", "tipsState", "rankUpDialogState", "battleResult", "captchaState", "modalScreen", "droppableGold", "eventGoods", "creatorTagState", "rewardedVideosState", "passwordChangeState", "system", "contextTutorial", "battleTutorial", "spectator", "twoStepVerificationState", "antiAddiction", "moderation", "migrationState", "register", "userReportsState", "friends", "suppliesState", "clanUserInfo", "battleInviteState", "partner", "userRenameState")
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
val Unmigrated: List<SliceWiring> get() = All.filter { it.Field !in Migrated }
@@ -9,7 +9,7 @@
// ⑥ 动作目录:816 个类与 138 个真实派发名对齐
// ⑦ 回放:真实 toString 样例的类/字段/类型校验
// ⑧ 全目录动作类都能按 KClass 路由
// ⑨~㊿/51-63 切片 reducer:InviteState / LoginByPasswordState / TwitchState / HomeScreen / LocalBattleUserState /
// ⑨~㊿/51-69 切片 reducer:InviteState / LoginByPasswordState / TwitchState / HomeScreen / LocalBattleUserState /
// Shop(含 U7 类别表与 w3 物品表两个子 reducer)/ Screen / LootBoxes / Garage / GaragePreview / Entrance / User / Settings /
// AccountSettings / ProBattle / Language / Announcement / News / Notifications / Quests /
// LobbyLoader / PurchaseDialogState / PromoCode / SupportUrl / RulesUpdated / NotificationByCountry / AvailabilityByRank /
@@ -17,7 +17,8 @@
// TipsState / RankUpDialogState / BattleResult / CaptchaState / ModalScreen / DroppableGold / EventGoods /
// CreatorTagState / RewardedVideosState / PasswordChangeState /
// System / ContextTutorial / BattleTutorial / Spectator / TwoStepVerification / AntiAddiction /
// Moderation / MigrationState / Register / UserReports;
// Moderation / MigrationState / Register / UserReports /
// Friends / Supplies / ClanUserInfo / BattleInvite / Partner / UserRename;
// ㉑ launchParams 是纯透传切片(无 reducer/动作),只做 Map 类型收口;⑭ 根 reducer 的 89 槽位委派
//
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
@@ -534,6 +535,63 @@ import App.State.Reducers.ReduceUserReports
import App.State.Reducers.ReportEntry
import App.State.Reducers.ReportKind
import App.State.Reducers.UserReportsStateSlice
import App.State.ChangeFriendAddFindState
import App.State.AddUsersToAccepted
import App.State.AddUsersToIncoming
import App.State.AddUsersToNewAccepted
import App.State.AddUsersToNewIncoming
import App.State.RemoveUserFromAccepted
import App.State.RemoveUserFromIncoming
import App.State.RemoveUserFromNewAccepted
import App.State.RemoveUsersFromNewIncoming
import App.State.SetLimits
import App.State.SetValidationStatus
import App.State.Reducers.ClearContextMenuUser
import App.State.Reducers.FriendSearchState
import App.State.Reducers.FriendsStateSlice
import App.State.Reducers.ReduceFriends
import App.State.Reducers.SetContextMenuUser
import App.State.Reducers.ReduceSupplies
import App.State.Reducers.SetSupplyCount
import App.State.Reducers.SuppliesStateSlice
import App.State.ClanNotifierData
import App.State.Loaded
import App.State.Reducers.ClanUserInfoStateSlice
import App.State.Reducers.ReduceClanUserInfo
import App.State.EnableSetBattleInviteNotifications
import App.State.InvitationResponse
import App.State.RemoveUserFromInvited
import App.State.SelectUserToInvite
import App.State.Reducers.AddInvitedUser
import App.State.Reducers.BattleInviteStateSlice
import App.State.Reducers.ClearSelectedForInvite
import App.State.Reducers.ReduceBattleInvite
import App.State.InitPartnerInviteAction
import App.State.InitSocialNetworkParameters
import App.State.SetLoginParams
import App.State.SetLoginStatusForBinding
import App.State.SetPartnerId
import App.State.SetPartnerParameters
import App.State.SetPartnerReady
import App.State.Reducers.MarkAccountLinked
import App.State.Reducers.MarkAccountNotLinked
import App.State.Reducers.PartnerStateSlice
import App.State.Reducers.ReducePartner
import App.State.Reducers.SetPartnerItemEnabled
import App.State.Reducers.UpdatePartnerInviteAction
import App.State.ChangeUidInputState
import App.State.ResponseActual
import App.State.ResponseDelivered
import App.State.SetChangeUidParams
import App.State.SetRubiesForRename
import App.State.Reducers.HideLinkErrorWarning
import App.State.Reducers.MarkFreeRenameAvailable
import App.State.Reducers.MarkRenameFailed
import App.State.Reducers.MarkRenameSucceeded
import App.State.Reducers.ReduceUserRename
import App.State.Reducers.ResetRenameDialog
import App.State.Reducers.RenameUidState
import App.State.Reducers.UserRenameStateSlice
import App.State.Reducers.UserRankBounds
import App.State.Reducers.UserStateSlice
import App.State.Reducers.ClearHullData
@@ -961,7 +1019,7 @@ object StateCheck {
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
}
// ⑭ 根 reducer:89 个槽位的委派表 + 53 个已迁移切片(52 归约 + launchParams 透传)
// ⑭ 根 reducer:89 个槽位的委派表 + 59 个已迁移切片(58 归约 + launchParams 透传)
run {
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
Check(
@@ -1025,7 +1083,7 @@ object StateCheck {
LocalBattleW.Reducer == "(inline@localBattleUserState)"
)
Check(
"根:迁移表 = 53 个已迁移字段,其余 36 个待迁",
"根:迁移表 = 59 个已迁移字段,其余 30 个待迁",
RootReducer.Migrated ==
setOf(
"inviteState", "loginByPassword", "twitchState", "home",
@@ -1042,8 +1100,10 @@ object StateCheck {
"system", "contextTutorial", "battleTutorial", "spectator",
"twoStepVerificationState", "antiAddiction", "moderation",
"migrationState", "register", "userReportsState",
"friends", "suppliesState", "clanUserInfo", "battleInviteState",
"partner", "userRenameState",
) &&
RootWiring.Unmigrated.size == 36
RootWiring.Unmigrated.size == 30
)
// 一次 ReduceRoot 里多个切片各归约自己的动作,互不串片
@@ -1099,7 +1159,7 @@ object StateCheck {
S5.User.Uid == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
)
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 53/89")
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 59/89")
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
Store.Dispatch(SetStatus("LOGGING_IN"))
@@ -4815,6 +4875,364 @@ object StateCheck {
)
}
// 64 切片 reducer:Friends(KD / NL().cr9,8 字段;13 个动作)
run {
val S0 = FriendsStateSlice()
Check(
"切片 Friends:默认值完整",
S0.UserSearchState == FriendSearchState.HIDDEN && S0.ContextMenuUser == null &&
S0.AcceptedLimit == 0 && S0.IncomingLimit == 0 &&
S0.Accepted.isEmpty() && S0.NewAccepted.isEmpty() &&
S0.Incoming.isEmpty() && S0.NewIncoming.isEmpty()
)
val S1 = ReduceFriends(S0, ChangeFriendAddFindState(FriendSearchState.SEARCHING))
Check(
"切片 Friends:ChangeFriendAddFindState 写搜索状态",
S1.UserSearchState == FriendSearchState.SEARCHING
)
Check(
"切片 Friends:SetValidationStatus 命中即 FOUND,null 即 NOT_FOUND",
ReduceFriends(S1, SetValidationStatus("ok")).UserSearchState ==
FriendSearchState.FOUND &&
ReduceFriends(S1, SetValidationStatus(null)).UserSearchState ==
FriendSearchState.NOT_FOUND
)
Check(
"切片 Friends:SetLimits 写两个上限",
ReduceFriends(S1, SetLimits(50, 30)).let {
it.AcceptedLimit == 50 && it.IncomingLimit == 30
}
)
Check(
"切片 Friends:右键菜单设置 / 清除",
ReduceFriends(S1, SetContextMenuUser("u1")).ContextMenuUser == "u1" &&
ReduceFriends(S1, ClearContextMenuUser).ContextMenuUser == null
)
val S2 = ReduceFriends(S1, AddUsersToAccepted(listOf(1L, 2L)))
Check(
"切片 Friends:AddUsersToAccepted 整批入集合",
S2.Accepted == setOf<Any?>(1L, 2L)
)
Check(
"切片 Friends:RemoveUserFromAccepted 删单值",
ReduceFriends(S2, RemoveUserFromAccepted(1L)).Accepted == setOf<Any?>(2L)
)
val S3 = ReduceFriends(S2, AddUsersToNewAccepted(clanId = 0, userIds = listOf(3L, 4L)))
Check(
"切片 Friends:AddUsersToNewAccepted 读 userIds",
S3.NewAccepted == setOf<Any?>(3L, 4L)
)
Check(
"切片 Friends:RemoveUserFromNewAccepted / AddUsersToIncoming",
ReduceFriends(S3, RemoveUserFromNewAccepted(3L)).NewAccepted == setOf<Any?>(4L) &&
ReduceFriends(S3, AddUsersToIncoming(listOf(5L))).Incoming == setOf<Any?>(5L)
)
Check(
"切片 Friends:RemoveUsersFromNewIncoming 按集合减",
ReduceFriends(
ReduceFriends(S3, AddUsersToNewIncoming(listOf(6L, 7L))),
RemoveUsersFromNewIncoming(listOf(6L)),
).NewIncoming == setOf<Any?>(7L)
)
Check(
"切片 Friends:无关动作不改状态",
ReduceFriends(S3, AddFrags(1)) == S3
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, ChangeFriendAddFindState(FriendSearchState.FOUND))
Check(
"根:ChangeFriendAddFindState 只动 friends",
Root1.Friends.UserSearchState == FriendSearchState.FOUND &&
Root1.Partner == Root0.Partner
)
val W = RootWiring.ByField("friends")!!
Check(
"根:friends 委派到 KD / NL().cr9 且 13 个动作",
W.SliceClass == "KD" && W.Reducer == "NL().cr9" && W.Actions.size == 13
)
}
// 65 切片 reducer:Supplies(wE / $t.fq9,1 字段;SetSupplyCount)
run {
val S0 = SuppliesStateSlice()
Check("切片 Supplies:默认空 Map", S0.Count.isEmpty())
val S1 = ReduceSupplies(S0, SetSupplyCount("mine", 3))
Check(
"切片 Supplies:SetSupplyCount 写键值",
S1.Count == mapOf<Any?, Any?>("mine" to 3)
)
Check(
"切片 Supplies:再次写入覆盖旧值",
ReduceSupplies(S1, SetSupplyCount("mine", 1)).Count ==
mapOf<Any?, Any?>("mine" to 1)
)
Check("切片 Supplies:无关动作不改状态", ReduceSupplies(S1, AddFrags(1)) == S1)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, SetSupplyCount("k", 9))
Check(
"根:SetSupplyCount 只动 suppliesState",
Root1.SuppliesState.Count == mapOf<Any?, Any?>("k" to 9) &&
Root1.ClanUserInfo == Root0.ClanUserInfo
)
val W = RootWiring.ByField("suppliesState")!!
Check(
"根:suppliesState 委派到 wE / \$t.fq9",
W.SliceClass == "wE" && W.Reducer == "\$t.fq9" &&
W.Actions == listOf("SetSupplyCount")
)
}
// 66 切片 reducer:ClanUserInfo(hO / de.gr1,1 字段;Loaded 合并)
run {
val S0 = ClanUserInfoStateSlice()
Check("切片 ClanUserInfo:默认空 Map", S0.Users.isEmpty())
val S1 = ReduceClanUserInfo(S0, Loaded(mapOf("u1" to ClanNotifierData(1L, null, 11L))))
Check(
"切片 ClanUserInfo:Loaded 写入用户表",
S1.Users.keys == setOf<Any?>("u1") &&
(S1.Users["u1"] as ClanNotifierData).userId == 11L
)
Check(
"切片 ClanUserInfo:第二次 Loaded 是合并不是覆盖",
ReduceClanUserInfo(S1, Loaded(mapOf("u2" to ClanNotifierData(2L, null, 22L)))).Users.keys ==
setOf<Any?>("u1", "u2")
)
Check(
"切片 ClanUserInfo:无关动作不改状态",
ReduceClanUserInfo(S1, AddFrags(1)) == S1
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, Loaded(mapOf("x" to ClanNotifierData(3L, null, 33L))))
Check(
"根:Loaded 只动 clanUserInfo",
Root1.ClanUserInfo.Users.keys == setOf<Any?>("x") &&
Root1.SuppliesState == Root0.SuppliesState
)
val W = RootWiring.ByField("clanUserInfo")!!
Check(
"根:clanUserInfo 委派到 hO / de.gr1",
W.SliceClass == "hO" && W.Reducer == "de.gr1" &&
W.Actions == listOf("Loaded")
)
}
// 67 切片 reducer:BattleInvite(att / Ttt().nth,3 字段;6 个动作)
run {
val S0 = BattleInviteStateSlice()
Check(
"切片 BattleInvite:默认开启、两个集合为空",
S0.IsEnabled && S0.InvitedUsers.isEmpty() && S0.SelectedForInvite.isEmpty()
)
val S1 = ReduceBattleInvite(S0, AddInvitedUser(1L))
Check("切片 BattleInvite:AddInvitedUser 入已邀请集合", S1.InvitedUsers == setOf<Any?>(1L))
Check(
"切片 BattleInvite:InvitationResponse 从已邀请集合删",
ReduceBattleInvite(S1, InvitationResponse(1L, "ok")).InvitedUsers.isEmpty()
)
val S2 = ReduceBattleInvite(S1, SelectUserToInvite(2L))
Check(
"切片 BattleInvite:SelectUserToInvite 入待邀请集合",
S2.SelectedForInvite == setOf<Any?>(2L)
)
Check(
"切片 BattleInvite:RemoveUserFromInvited 从待邀请集合删",
ReduceBattleInvite(S2, RemoveUserFromInvited(2L)).SelectedForInvite.isEmpty()
)
Check(
"切片 BattleInvite:ClearSelectedForInvite 清空",
ReduceBattleInvite(S2, ClearSelectedForInvite).SelectedForInvite.isEmpty()
)
Check(
"切片 BattleInvite:EnableSetBattleInviteNotifications 写开关",
!ReduceBattleInvite(S2, EnableSetBattleInviteNotifications(false)).IsEnabled
)
Check(
"切片 BattleInvite:无关动作不改状态",
ReduceBattleInvite(S2, AddFrags(1)) == S2
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, AddInvitedUser(9L))
Check(
"根:AddInvitedUser 只动 battleInviteState",
Root1.BattleInviteState.InvitedUsers == setOf<Any?>(9L) &&
Root1.UserRenameState == Root0.UserRenameState
)
val W = RootWiring.ByField("battleInviteState")!!
Check(
"根:battleInviteState 委派到 att / Ttt().nth 且 6 个动作",
W.SliceClass == "att" && W.Reducer == "Ttt().nth" &&
W.Actions.toSet() == setOf(
"AddInvitedUser", "InvitationResponse", "SelectUserToInvite",
"RemoveUserFromInvited", "ClearSelectedForInvite",
"EnableSetBattleInviteNotifications",
)
)
}
// 68 切片 reducer:Partner(UJ / (inline@partner),15 字段;11 个动作)
run {
val S0 = PartnerStateSlice()
Check(
"切片 Partner:默认值完整",
S0.PartnerId == null && S0.LoginParams.isEmpty() && S0.Params.isEmpty() &&
!S0.HasOwnPaymentSystem && !S0.HasSocialFunction &&
!S0.HasClientPaymentAction && S0.PaymentAction == null &&
S0.InviteAction == null && !S0.CanOpenExternalLinks &&
!S0.HasAccountBinding && S0.FailedRedirectUrl == null &&
S0.LoginForBindingStatus == ByPasswordStatus.NONE &&
!S0.IsAccountAlreadyLinked && S0.ItemsEnabledForPartner.isEmpty() &&
!S0.IsPartnerReady
)
val S1 = ReducePartner(S0, SetPartnerId("p1", "https://fail"))
Check(
"切片 Partner:SetPartnerId 同时写失败跳转",
S1.PartnerId == "p1" && S1.FailedRedirectUrl == "https://fail"
)
val S2 = ReducePartner(
S1,
InitSocialNetworkParameters(
hasOwnPaymentSystem = true, hasSocialFunction = true,
hasClientPaymentAction = false, failedRedirectUrl = null,
canOpenExternalLinks = true, hasAccountBinding = true,
),
)
Check(
"切片 Partner:InitSocialNetworkParameters 写 6 个字段",
S2.HasOwnPaymentSystem && S2.HasSocialFunction && !S2.HasClientPaymentAction &&
S2.CanOpenExternalLinks && S2.HasAccountBinding
)
Check(
"切片 Partner:loginParams / params 分别写入",
ReducePartner(S2, SetLoginParams(mapOf("a" to 1))).LoginParams ==
mapOf<Any?, Any?>("a" to 1) &&
ReducePartner(S2, SetPartnerParameters(mapOf("b" to 2))).Params ==
mapOf<Any?, Any?>("b" to 2)
)
Check(
"切片 Partner:两种邀请动作都写 inviteAction",
ReducePartner(S2, InitPartnerInviteAction("i1")).InviteAction == "i1" &&
ReducePartner(S2, UpdatePartnerInviteAction("i2")).InviteAction == "i2"
)
Check(
"切片 Partner:SetLoginStatusForBinding 写绑定状态",
ReducePartner(S2, SetLoginStatusForBinding(ByPasswordStatus.WRONG_PASSWORD))
.LoginForBindingStatus == ByPasswordStatus.WRONG_PASSWORD
)
Check(
"切片 Partner:已绑定标记两态",
ReducePartner(S2, MarkAccountLinked).IsAccountAlreadyLinked &&
!ReducePartner(S2, MarkAccountNotLinked).IsAccountAlreadyLinked
)
Check(
"切片 Partner:SetPartnerItemEnabled 写物品开关 Map",
ReducePartner(S2, SetPartnerItemEnabled("item", true))
.ItemsEnabledForPartner == mapOf<Any?, Any?>("item" to true)
)
Check(
"切片 Partner:SetPartnerReady 写就绪位",
ReducePartner(S2, SetPartnerReady(true)).IsPartnerReady
)
Check("切片 Partner:无关动作不改状态", ReducePartner(S2, AddFrags(1)) == S2)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, SetPartnerReady(true))
Check(
"根:SetPartnerReady 只动 partner",
Root1.Partner.IsPartnerReady && Root1.Friends == Root0.Friends
)
val W = RootWiring.ByField("partner")!!
Check(
"根:partner 是内联 reducer 且 11 个动作",
W.SliceClass == "UJ" && W.Reducer == "(inline@partner)" && W.Actions.size == 11
)
}
// 69 切片 reducer:UserRename(O8 / Rf.pte,10 字段;11 个动作)
run {
val S0 = UserRenameStateSlice()
Check(
"切片 UserRename:默认值完整",
!S0.IsFreeRenameAvailable && S0.UserRenameShopItemId == null &&
S0.UidInputState == RenameUidState.EMPTY && S0.RenameSuccessful == null &&
S0.RubiesForRename == Int.MAX_VALUE && !S0.IsResponseDelivered &&
!S0.IsResponseActual && S0.ChangeUidHash == null && S0.Email == null &&
S0.ShowLinkErrorWarning
)
val S1 = ReduceUserRename(S0, ChangeUidInputState(RenameUidState.SHORT))
Check(
"切片 UserRename:ChangeUidInputState 写输入状态",
S1.UidInputState == RenameUidState.SHORT
)
Check(
"切片 UserRename:免费改名 / 改名成功 / 改名失败",
ReduceUserRename(S1, MarkFreeRenameAvailable).IsFreeRenameAvailable &&
ReduceUserRename(S1, MarkRenameSucceeded).let {
!it.IsFreeRenameAvailable && it.RenameSuccessful == true
} &&
ReduceUserRename(S1, MarkRenameFailed).RenameSuccessful == false
)
Check(
"切片 UserRename:SetValidationStatus 按 ordinal 映射输入状态",
ReduceUserRename(S1, SetValidationStatus(RenameUidState.FREE)).let {
it.UidInputState == RenameUidState.FREE && it.IsResponseDelivered
} &&
ReduceUserRename(S1, SetValidationStatus(RenameUidState.SHORT)).UidInputState ==
RenameUidState.NOT_VALID &&
ReduceUserRename(S1, SetValidationStatus(RenameUidState.NOT_VALID))
.UidInputState == RenameUidState.FORBIDDEN
)
Check(
"切片 UserRename:SetRubiesForRename 写价格",
ReduceUserRename(S1, SetRubiesForRename(99)).RubiesForRename == 99
)
val S2 = ReduceUserRename(
ReduceUserRename(
ReduceUserRename(S1, SetChangeUidParams("hash", "mail")),
ResponseDelivered(true),
),
ResponseActual(true),
)
Check(
"切片 UserRename:改绑参数与两个 response 位",
S2.ChangeUidHash == "hash" && S2.Email == "mail" &&
S2.IsResponseDelivered && S2.IsResponseActual
)
Check(
"切片 UserRename:ResetRenameDialog 复位弹窗字段",
ReduceUserRename(S2, ResetRenameDialog).let {
it.UserRenameShopItemId == null &&
it.UidInputState == RenameUidState.EMPTY &&
it.RenameSuccessful == null && !it.IsResponseDelivered &&
!it.IsResponseActual && it.ChangeUidHash == "hash"
}
)
Check(
"切片 UserRename:HideLinkErrorWarning 关提示",
!ReduceUserRename(S2, HideLinkErrorWarning).ShowLinkErrorWarning
)
Check(
"切片 UserRename:无关动作不改状态",
ReduceUserRename(S2, AddFrags(1)) == S2
)
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, SetRubiesForRename(5))
Check(
"根:SetRubiesForRename 只动 userRenameState",
Root1.UserRenameState.RubiesForRename == 5 &&
Root1.Partner == Root0.Partner
)
val W = RootWiring.ByField("userRenameState")!!
Check(
"根:userRenameState 委派到 O8 / Rf.pte 且 11 个动作",
W.SliceClass == "O8" && W.Reducer == "Rf.pte" && W.Actions.size == 11
)
}
val Failed = Results.filter { !it.second }
println("TankiOffline 状态层自检:${Results.size} 项")
for ((Name, Ok) in Results) println((if (Ok) " ✓ " else " ✗ ") + Name)