feat(state): 迁移 clan(36 字段 / 43 动作)
This commit is contained in:
1 parent
ad5686241d
commit
2f38609a98
8 files changed
+448
-28
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` 动作名。
|
||||
- 已迁移切片 **84/89**:
|
||||
- 已迁移切片 **85/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)
|
||||
@@ -425,12 +425,16 @@
|
||||
`vO`(原名 `ClanLoaded`,与 clan 切片的 `isLoaded` 同名)改名 `LoadClanIntoStorage`;
|
||||
`dO`/`gO`(原名 `AddUser`/`RemoveUser`,与战斗列表同名)改名 `AddClanUser`/`RemoveClanUser`;
|
||||
查找时兼容 Int/Long 两种 clanId。
|
||||
- `clan` → `jN` / `we.hr0`(`ClanReducer.kt`):36 字段,43 个分支。
|
||||
成员 / 待审用户 / 人员 / 外战队 / 新申请各有增删两臂;校验状态 4 个 setter + 2 个复位;
|
||||
`ClanInfoLoaded` 写 6 个身份字段(复用 `ClanInfo`);`UpdateHasClan(false)` 整片复位;
|
||||
通知计数 3 个直接写 + 2 个已读 + 计数 ±1;其余为中间态三元组、封禁、弹窗与改名价格。
|
||||
- `launchParams` → `(pass-through)`:原根 reducer 对 `hpx_1` 没有调用,缺省是 `ag()` 空 Map;
|
||||
`RootState.LaunchParams` 收口为 `LaunchParamsStateSlice = Map<String, String>`,不新增 reducer/动作。
|
||||
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 83 个有 reducer 的切片,
|
||||
`launchParams` 与其余 **5 个字段原样透传**;
|
||||
迁移进度用 `RootReducer.Coverage`(`已迁移 84/89`)与 `RootWiring.Unmigrated` 暴露。
|
||||
- 自检:`TankiOffline/tools/RunStateTest.ps1` **924 项全绿**,含 89 槽位完整性、83 个归约切片一次归约互不串片、未迁移字段透传,
|
||||
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 84 个有 reducer 的切片,
|
||||
`launchParams` 与其余 **4 个字段原样透传**;
|
||||
迁移进度用 `RootReducer.Coverage`(`已迁移 85/89`)与 `RootWiring.Unmigrated` 暴露。
|
||||
- 自检:`TankiOffline/tools/RunStateTest.ps1` **939 项全绿**,含 89 槽位完整性、84 个归约切片一次归约互不串片、未迁移字段透传,
|
||||
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
|
||||
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
|
||||
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"generatedAt": "2026-10-24T13:56:08.668Z",
|
||||
"generatedAt": "2026-10-24T14:00:39.799Z",
|
||||
"module": "module_10032.js",
|
||||
"RootState": {
|
||||
"Class": "z6",
|
||||
@@ -90,7 +90,8 @@
|
||||
"battlesSelect",
|
||||
"battleUsers",
|
||||
"challenges",
|
||||
"clansStorage"
|
||||
"clansStorage",
|
||||
"clan"
|
||||
],
|
||||
"AnonymousActions": [
|
||||
{
|
||||
@@ -1514,6 +1515,54 @@
|
||||
"Name": "RemoveClanUser",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "clan.users -= userId(原名 RemoveUser,同上改名 RemoveClanUser)"
|
||||
},
|
||||
{
|
||||
"Local": "iM",
|
||||
"Name": "ResetClanUserValidation",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "clanUserValidation 复位(`n.iqx(…, sO())`)"
|
||||
},
|
||||
{
|
||||
"Local": "FM",
|
||||
"Name": "ResetClanNameTagValidation",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "clanNameNameValidation / clanTagNameValidation 复位(`oO()`)"
|
||||
},
|
||||
{
|
||||
"Local": "IM",
|
||||
"Name": "UpdateIntermediateMinRank",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "intermediateMinRankIndex(`n.iqx(cd, cd, cd, cd, cd, cd, cd, t.lr0_1)`)"
|
||||
},
|
||||
{
|
||||
"Local": "RM",
|
||||
"Name": "UpdateIntermediateRequests",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "intermediateRequestsEnabled(`n.iqx(cd, cd, cd, cd, cd, cd, t.kr0_1)`)"
|
||||
},
|
||||
{
|
||||
"Local": "PM",
|
||||
"Name": "UpdateIntermediateDescription",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "intermediateDescription(`n.iqx(cd, cd, cd, cd, cd, cd, cd, cd, t.jr0_1)`)"
|
||||
},
|
||||
{
|
||||
"Local": "VM",
|
||||
"Name": "SetClanNotificationsCount",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "notificationsCount(`t.ir0_1`)"
|
||||
},
|
||||
{
|
||||
"Local": "KM",
|
||||
"Name": "IncrementClanNotifications",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "notificationsCount + 1(`(n.bqx_1 + 1) | 0`)"
|
||||
},
|
||||
{
|
||||
"Local": "YM",
|
||||
"Name": "DecrementClanNotifications",
|
||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||
"Effect": "notificationsCount - 1,下限 0"
|
||||
}
|
||||
],
|
||||
"Markers": [
|
||||
@@ -2008,14 +2057,6 @@
|
||||
"Reducer": "we.hr0",
|
||||
"Delegate": "we.hr0(t, n.qpv_1)",
|
||||
"Actions": [
|
||||
"?FM",
|
||||
"?IM",
|
||||
"?KM",
|
||||
"?PM",
|
||||
"?RM",
|
||||
"?VM",
|
||||
"?YM",
|
||||
"?iM",
|
||||
"AcceptedUserViewed",
|
||||
"AddIncomingClan",
|
||||
"AddNewIncoming",
|
||||
@@ -2027,9 +2068,11 @@
|
||||
"ClanInfoLoaded",
|
||||
"ClanLoaded",
|
||||
"ClanModalVisible",
|
||||
"DecrementClanNotifications",
|
||||
"IncomingAdded",
|
||||
"IncomingRemoved",
|
||||
"IncomingUserViewed",
|
||||
"IncrementClanNotifications",
|
||||
"MembersFetched",
|
||||
"NewAcceptedUserNotification",
|
||||
"NewClanInviteNotification",
|
||||
@@ -2042,18 +2085,24 @@
|
||||
"RemoveOutgoingClan",
|
||||
"RemoveUserFromAccepted",
|
||||
"RemoveUsersFromNewAccepted",
|
||||
"ResetClanNameTagValidation",
|
||||
"ResetClanUserValidation",
|
||||
"SetClanEditPrice",
|
||||
"SetClanNotificationsCount",
|
||||
"SetMinRankForCreate",
|
||||
"UpdateClanLicense",
|
||||
"UpdateClanNameValidation",
|
||||
"UpdateClanSearchValidation",
|
||||
"UpdateClanTagValidation",
|
||||
"UpdateHasClan",
|
||||
"UpdateIntermediateDescription",
|
||||
"UpdateIntermediateMinRank",
|
||||
"UpdateIntermediateRequests",
|
||||
"UpdateTimeToJoinRestriction",
|
||||
"UpdateValidation"
|
||||
],
|
||||
"Default": "new jN()",
|
||||
"Migrated": false
|
||||
"Migrated": true
|
||||
},
|
||||
{
|
||||
"Field": "clansStorage",
|
||||
|
||||
@@ -345,6 +345,15 @@ const AnonymousActionNames = new Map([
|
||||
['gO', 'RemoveClanUser'],
|
||||
// vO 的 toString 名 ClanLoaded 与 clan 切片的 isLoaded 动作同名冲突,改名避免字段错配。
|
||||
['vO', 'LoadClanIntoStorage'],
|
||||
// clan 切片(jN / we.hr0):8 个匿名类。
|
||||
['iM', 'ResetClanUserValidation'],
|
||||
['FM', 'ResetClanNameTagValidation'],
|
||||
['IM', 'UpdateIntermediateMinRank'],
|
||||
['RM', 'UpdateIntermediateRequests'],
|
||||
['PM', 'UpdateIntermediateDescription'],
|
||||
['VM', 'SetClanNotificationsCount'],
|
||||
['KM', 'IncrementClanNotifications'],
|
||||
['YM', 'DecrementClanNotifications'],
|
||||
]);
|
||||
|
||||
// 非动作的 `instanceof`:屏幕标识类,用于 GoTo 分支判断(不是 dispatch 的动作)。
|
||||
@@ -833,6 +842,7 @@ const Migrated = {
|
||||
battleUsers: { Type: 'BattleUsersStateSlice', Reduce: 'ReduceBattleUsers' },
|
||||
challenges: { Type: 'ChallengesStateSlice', Reduce: 'ReduceChallenges' },
|
||||
clansStorage: { Type: 'ClanStorageStateSlice', Reduce: 'ReduceClanStorage' },
|
||||
clan: { Type: 'ClanStateSlice', Reduce: 'ReduceClan' },
|
||||
};
|
||||
|
||||
for (const R of Rows) R.Migrated = Object.prototype.hasOwnProperty.call(Migrated, R.Field);
|
||||
@@ -1261,6 +1271,14 @@ const AppSettingsAnonymousEffects = {
|
||||
vO: 'clans[clan.id]=clan(原名 ClanLoaded,与 clan 切片同名类冲突,改名 LoadClanIntoStorage)',
|
||||
dO: 'clan.users += userId(原名 AddUser,与战斗列表同名类冲突,改名 AddClanUser)',
|
||||
gO: 'clan.users -= userId(原名 RemoveUser,同上改名 RemoveClanUser)',
|
||||
iM: 'clanUserValidation 复位(`n.iqx(…, sO())`)',
|
||||
FM: 'clanNameNameValidation / clanTagNameValidation 复位(`oO()`)',
|
||||
IM: 'intermediateMinRankIndex(`n.iqx(cd, cd, cd, cd, cd, cd, cd, t.lr0_1)`)',
|
||||
RM: 'intermediateRequestsEnabled(`n.iqx(cd, cd, cd, cd, cd, cd, t.kr0_1)`)',
|
||||
PM: 'intermediateDescription(`n.iqx(cd, cd, cd, cd, cd, cd, cd, cd, t.jr0_1)`)',
|
||||
VM: 'notificationsCount(`t.ir0_1`)',
|
||||
KM: 'notificationsCount + 1(`(n.bqx_1 + 1) | 0`)',
|
||||
YM: 'notificationsCount - 1,下限 0',
|
||||
};
|
||||
fs.writeFileSync(
|
||||
path.join(P, 'names', 'root_wiring.json'),
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
// ClanReducer.kt —— 战队切片(对应 module_10032 的 jN 状态 + we.hr0)
|
||||
//
|
||||
// 证据链:
|
||||
// 状态类 jN:36 个字段,toString 名 "Clan(…)";copy 是 iqx(36 字段 + 回调)。
|
||||
// 根委派:`we.hr0(t, n.qpv_1)`。
|
||||
// reducer(43 个实例臂)——绝大多数是 `iqx` 的单个位置参数:
|
||||
// 成员/待审:DN/LN(accepted)BN/FN(acceptedNew)
|
||||
// 校验:iM 复位、rM/uM/sM/oM 直接写、FM 复位 name/tag
|
||||
// 载入:HN(ClanInfoLoaded 写 id/creatorId/name/createTime/description/tag)、fM(isLoaded)
|
||||
// 人员:WN/VN/KN/YN(incoming/outgoing)
|
||||
// 外战队:vM/pM/wM/dM/gM/mM
|
||||
// 中间态:IM/RM/PM(minRank / requests / description)
|
||||
// 通知计数:VM/KM/YM/UN/WM/XM/JM/ZM
|
||||
// 其他:GN 最小创建等级、ON 成员统计合并、CM 入队限制时间、
|
||||
// lM hasClan(false 时整片复位)、NM 封禁、GM/HM 新申请、QM 弹窗、tO 改名价格
|
||||
//
|
||||
// `ClanInfoLoaded.info` 复用生成物 `ClanInfo`;`ON` 的 `jm` 是 putAll(合并)。
|
||||
package App.State.Reducers
|
||||
|
||||
import App.State.AddIncomingClan
|
||||
import App.State.AddNewIncoming
|
||||
import App.State.AddNewIncomingClan
|
||||
import App.State.AddOutgoingClan
|
||||
import App.State.AddUsersToAccepted
|
||||
import App.State.AddUsersToNewAccepted
|
||||
import App.State.AcceptedUserViewed
|
||||
import App.State.ClanBanned
|
||||
import App.State.ClanInfo
|
||||
import App.State.ClanInfoLoaded
|
||||
import App.State.ClanLoaded
|
||||
import App.State.ClanModalVisible
|
||||
import App.State.IncomingAdded
|
||||
import App.State.IncomingRemoved
|
||||
import App.State.IncomingUserViewed
|
||||
import App.State.MembersFetched
|
||||
import App.State.NewAcceptedUserNotification
|
||||
import App.State.NewClanInviteNotification
|
||||
import App.State.NewIncomingUserNotification
|
||||
import App.State.OutgoingAdded
|
||||
import App.State.OutgoingRemoved
|
||||
import App.State.RemoveIncomingClan
|
||||
import App.State.RemoveNewIncoming
|
||||
import App.State.RemoveNewIncomingClan
|
||||
import App.State.RemoveOutgoingClan
|
||||
import App.State.RemoveUserFromAccepted
|
||||
import App.State.RemoveUsersFromNewAccepted
|
||||
import App.State.SetClanEditPrice
|
||||
import App.State.SetMinRankForCreate
|
||||
import App.State.UpdateClanLicense
|
||||
import App.State.UpdateClanNameValidation
|
||||
import App.State.UpdateClanSearchValidation
|
||||
import App.State.UpdateClanTagValidation
|
||||
import App.State.UpdateHasClan
|
||||
import App.State.UpdateTimeToJoinRestriction
|
||||
import App.State.UpdateValidation
|
||||
|
||||
/* ------------------------------------------------------------------ 匿名动作 */
|
||||
|
||||
/** 原 `iM`:复位用户名校验状态。 */
|
||||
object ResetClanUserValidation
|
||||
|
||||
/** 原 `FM`:复位战队名 / 标签校验状态。 */
|
||||
object ResetClanNameTagValidation
|
||||
|
||||
/** 原 `IM`:写中间态最小等级。 */
|
||||
data class UpdateIntermediateMinRank(val rankIndex: Any?)
|
||||
|
||||
/** 原 `RM`:写「可自由加入」开关。 */
|
||||
data class UpdateIntermediateRequests(val enabled: Any?)
|
||||
|
||||
/** 原 `PM`:写中间态说明。 */
|
||||
data class UpdateIntermediateDescription(val description: Any?)
|
||||
|
||||
/** 原 `VM`:直接写通知总数。 */
|
||||
data class SetClanNotificationsCount(val count: Int)
|
||||
|
||||
/** 原 `KM` / `YM`:通知总数 +1 / -1。 */
|
||||
object IncrementClanNotifications
|
||||
object DecrementClanNotifications
|
||||
|
||||
/* ------------------------------------------------------------------ 状态 */
|
||||
|
||||
data class ClanStateSlice(
|
||||
val CurrentClanId: Any? = null,
|
||||
val CreatorId: Any? = null,
|
||||
val Name: Any? = null,
|
||||
val CreateTime: Any? = null,
|
||||
val Description: Any? = null,
|
||||
val Tag: Any? = null,
|
||||
val IntermediateRequestsEnabled: Any? = null,
|
||||
val IntermediateMinRankIndex: Any? = null,
|
||||
val IntermediateDescription: Any? = null,
|
||||
val RulesConfirmed: Boolean = false,
|
||||
val HasLicense: Boolean = false,
|
||||
val HasClan: Boolean = false,
|
||||
val IsLoaded: Any? = null,
|
||||
val IncomingUsers: Set<Any?> = emptySet(),
|
||||
val OutgoingUsers: Set<Any?> = emptySet(),
|
||||
val ClanUserValidation: Any? = null,
|
||||
val ClanNameNameValidation: Any? = null,
|
||||
val ClanTagNameValidation: Any? = null,
|
||||
val ClanSearchValidation: Any? = null,
|
||||
val ClanAcceptedMembers: Set<Any?> = emptySet(),
|
||||
val ClanAcceptedMembersNew: Set<Any?> = emptySet(),
|
||||
val MinRankForCreateClan: Int = 0,
|
||||
val ClanMembersStatistics: Map<Any?, Any?> = emptyMap(),
|
||||
val OutgoingClans: Set<Any?> = emptySet(),
|
||||
val IncomingClans: Set<Any?> = emptySet(),
|
||||
val RestrictionToJoinInClan: Any? = null,
|
||||
val Banned: Boolean = false,
|
||||
val BanReason: Any? = null,
|
||||
val NewIncomingClans: Set<Any?> = emptySet(),
|
||||
val NewIncoming: Set<Any?> = emptySet(),
|
||||
val NotificationsCount: Int = 0,
|
||||
val IncomingUserNotificationCount: Int = 0,
|
||||
val AcceptedUserNotificationCount: Int = 0,
|
||||
val ClanInviteNotificationCount: Int = 0,
|
||||
val ClanModalVisible: Boolean = false,
|
||||
val ClanEditPrice: Any? = null,
|
||||
)
|
||||
|
||||
/* ------------------------------------------------------------------ reducer */
|
||||
|
||||
private fun Plus(Target: Set<Any?>, Value: Any?): Set<Any?> =
|
||||
if (Value is Iterable<*>) Target + Value.toSet() else Target + Value
|
||||
|
||||
private fun Minus(Target: Set<Any?>, Value: Any?): Set<Any?> =
|
||||
if (Value is Iterable<*>) Target - Value.toSet() else Target - Value
|
||||
|
||||
fun ReduceClan(
|
||||
State0: ClanStateSlice,
|
||||
Action: Any,
|
||||
): ClanStateSlice = when (Action) {
|
||||
is AddUsersToAccepted -> State0.copy(ClanAcceptedMembers = Plus(State0.ClanAcceptedMembers, Action.usersId))
|
||||
is RemoveUserFromAccepted -> State0.copy(ClanAcceptedMembers = Minus(State0.ClanAcceptedMembers, Action.userId))
|
||||
is AddUsersToNewAccepted -> State0.copy(ClanAcceptedMembersNew = Plus(State0.ClanAcceptedMembersNew, Action.userIds))
|
||||
is RemoveUsersFromNewAccepted -> State0.copy(ClanAcceptedMembersNew = Minus(State0.ClanAcceptedMembersNew, Action.userId))
|
||||
is SetMinRankForCreate -> State0.copy(MinRankForCreateClan = (Action.rankIndex as? Number)?.toInt() ?: State0.MinRankForCreateClan)
|
||||
is MembersFetched -> State0.copy(
|
||||
ClanMembersStatistics =
|
||||
State0.ClanMembersStatistics + ((Action.users as? Map<Any?, Any?>) ?: emptyMap()),
|
||||
)
|
||||
is ClanInfoLoaded -> {
|
||||
val Info = Action.info as? ClanInfo
|
||||
if (Info == null) State0
|
||||
else State0.copy(
|
||||
CurrentClanId = Info.id,
|
||||
CreatorId = Info.creatorId,
|
||||
Name = Info.name,
|
||||
CreateTime = Info.createTime,
|
||||
Description = Info.description,
|
||||
Tag = Info.tag,
|
||||
)
|
||||
}
|
||||
is IncomingAdded -> State0.copy(IncomingUsers = Plus(State0.IncomingUsers, Action.incomingUsers))
|
||||
is IncomingRemoved -> State0.copy(IncomingUsers = Minus(State0.IncomingUsers, Action.incomingUsers))
|
||||
is OutgoingAdded -> State0.copy(OutgoingUsers = Plus(State0.OutgoingUsers, Action.outgoingUsers))
|
||||
is OutgoingRemoved -> State0.copy(OutgoingUsers = Minus(State0.OutgoingUsers, Action.outgoingUser))
|
||||
is ResetClanUserValidation -> State0.copy(ClanUserValidation = null)
|
||||
is UpdateValidation -> State0.copy(ClanUserValidation = Action.state)
|
||||
is UpdateClanNameValidation -> State0.copy(ClanNameNameValidation = Action.state)
|
||||
is UpdateClanTagValidation -> State0.copy(ClanTagNameValidation = Action.state)
|
||||
is UpdateClanSearchValidation -> State0.copy(ClanSearchValidation = Action.state)
|
||||
is UpdateClanLicense -> State0.copy(HasLicense = Action.hasLicense)
|
||||
is UpdateHasClan -> if (Action.hasClan) State0.copy(HasClan = true) else ClanStateSlice()
|
||||
is ClanLoaded -> State0.copy(IsLoaded = Action.isLoaded)
|
||||
is AddOutgoingClan -> State0.copy(OutgoingClans = Plus(State0.OutgoingClans, Action.clanId))
|
||||
is RemoveOutgoingClan -> State0.copy(OutgoingClans = Minus(State0.OutgoingClans, Action.clanId))
|
||||
is AddIncomingClan -> State0.copy(IncomingClans = Plus(State0.IncomingClans, Action.clanIds))
|
||||
is RemoveIncomingClan -> State0.copy(IncomingClans = Minus(State0.IncomingClans, Action.clanId))
|
||||
is AddNewIncomingClan -> State0.copy(NewIncomingClans = Plus(State0.NewIncomingClans, Action.clanIds))
|
||||
is RemoveNewIncomingClan -> State0.copy(NewIncomingClans = Minus(State0.NewIncomingClans, Action.clanId))
|
||||
is UpdateTimeToJoinRestriction ->
|
||||
State0.copy(RestrictionToJoinInClan = Action.restrictionTimeJoinClanInSec)
|
||||
is UpdateIntermediateMinRank -> State0.copy(IntermediateMinRankIndex = Action.rankIndex)
|
||||
is UpdateIntermediateRequests -> State0.copy(IntermediateRequestsEnabled = Action.enabled)
|
||||
is UpdateIntermediateDescription -> State0.copy(IntermediateDescription = Action.description)
|
||||
is ClanBanned -> State0.copy(Banned = true, BanReason = Action.reason)
|
||||
is ResetClanNameTagValidation -> State0.copy(
|
||||
ClanNameNameValidation = null,
|
||||
ClanTagNameValidation = null,
|
||||
)
|
||||
is AddNewIncoming -> State0.copy(NewIncoming = Plus(State0.NewIncoming, Action.newIncoming))
|
||||
is RemoveNewIncoming -> State0.copy(NewIncoming = Minus(State0.NewIncoming, Action.newIncoming))
|
||||
is SetClanNotificationsCount -> State0.copy(NotificationsCount = Action.count)
|
||||
is IncrementClanNotifications -> State0.copy(NotificationsCount = State0.NotificationsCount + 1)
|
||||
is DecrementClanNotifications ->
|
||||
State0.copy(NotificationsCount = (State0.NotificationsCount - 1).coerceAtLeast(0))
|
||||
is AcceptedUserViewed ->
|
||||
State0.copy(AcceptedUserNotificationCount = State0.AcceptedUserNotificationCount - 1)
|
||||
is IncomingUserViewed ->
|
||||
State0.copy(IncomingUserNotificationCount = State0.IncomingUserNotificationCount - 1)
|
||||
is NewIncomingUserNotification -> State0.copy(IncomingUserNotificationCount = Action.count)
|
||||
is NewAcceptedUserNotification -> State0.copy(AcceptedUserNotificationCount = Action.count)
|
||||
is NewClanInviteNotification -> State0.copy(ClanInviteNotificationCount = Action.count)
|
||||
is ClanModalVisible -> State0.copy(ClanModalVisible = Action.visible)
|
||||
is SetClanEditPrice -> State0.copy(ClanEditPrice = Action.clanEditPrice)
|
||||
else -> State0
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// RootReducer.kt —— 根 reducer 骨架(对应 module_10032 的根状态 z6 / TOState 与根 reducer k6)
|
||||
//
|
||||
// 原包 k6 对 89 个切片逐个调用各自的 reducer,再 `return n.cti(...)`(copy 出新状态)。
|
||||
// 这里采用同一形状。已迁移的 84 个切片里,83 个真正调用 reducer,launchParams 只做类型收口:
|
||||
// 这里采用同一形状。已迁移的 85 个切片里,84 个真正调用 reducer,launchParams 只做类型收口:
|
||||
//
|
||||
// inviteState → ReduceInvite(aD / pD.er5)
|
||||
// loginByPassword → ReduceLoginByPassword(gD / MD.cr6)
|
||||
@@ -86,9 +86,10 @@
|
||||
// battleUsers → ReduceBattleUsers(uP / sr.hqs,根委派 `sr.hqs(t, n.hpw_1)`)
|
||||
// challenges → ReduceChallenges(CZ / nh.hsj,根委派 `nh.hsj(t, n.fpv_1)`)
|
||||
// clansStorage → ReduceClanStorage(lO / me.vr1,根委派 `me.vr1(t, n.rpv_1)`)
|
||||
// clan → ReduceClan(jN / we.hr0,根委派 `we.hr0(t, n.qpv_1)`)
|
||||
// launchParams → 无 reducer(原根 reducer 原样透传;默认 `ag()` = emptyMap())
|
||||
//
|
||||
// 其余 5 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||
// 其余 4 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
|
||||
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
|
||||
package App.State
|
||||
@@ -110,6 +111,7 @@ import App.State.Reducers.ReduceBattleView
|
||||
import App.State.Reducers.ReduceBattleInvite
|
||||
import App.State.Reducers.ReduceCaptcha
|
||||
import App.State.Reducers.ReduceChallenges
|
||||
import App.State.Reducers.ReduceClan
|
||||
import App.State.Reducers.ReduceClanStorage
|
||||
import App.State.Reducers.ReduceClanUserInfo
|
||||
import App.State.Reducers.ReduceClanChat
|
||||
@@ -273,6 +275,7 @@ object RootReducer {
|
||||
BattleUsers = ReduceBattleUsers(State0.BattleUsers, Action),
|
||||
Challenges = ReduceChallenges(State0.Challenges, Action),
|
||||
ClansStorage = ReduceClanStorage(State0.ClansStorage, Action),
|
||||
Clan = ReduceClan(State0.Clan, Action),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import App.State.Reducers.BattleViewStateSlice
|
||||
import App.State.Reducers.BattlesSelectStateSlice
|
||||
import App.State.Reducers.CaptchaStateSlice
|
||||
import App.State.Reducers.ChallengesStateSlice
|
||||
import App.State.Reducers.ClanStateSlice
|
||||
import App.State.Reducers.ClanChatStateSlice
|
||||
import App.State.Reducers.ClanUserInfoStateSlice
|
||||
import App.State.Reducers.ClanStorageStateSlice
|
||||
@@ -128,8 +129,8 @@ data class RootState(
|
||||
val ChatState: Any? = null,
|
||||
/** 原 clanChatState(qnt / yv.qtg)。已迁移。 */
|
||||
val ClanChatState: ClanChatStateSlice = ClanChatStateSlice(),
|
||||
/** 原 clan(jN / we.hr0)。未迁移:原样透传。 */
|
||||
val Clan: Any? = null,
|
||||
/** 原 clan(jN / we.hr0)。已迁移。 */
|
||||
val Clan: ClanStateSlice = ClanStateSlice(),
|
||||
/** 原 clansStorage(lO / me.vr1)。已迁移。 */
|
||||
val ClansStorage: ClanStorageStateSlice = ClanStorageStateSlice(),
|
||||
/** 原 tipsState(dst / Hw.vth)。已迁移。 */
|
||||
|
||||
@@ -32,7 +32,7 @@ object RootWiring {
|
||||
SliceWiring("quests", "npv_1", "Frt", "Yrt.otg", "(null == zp && new Yrt(), zp).otg(t, n.npv_1)", listOf("GoTo", "SetCurrentCategory", "SetNotCompletedQuests")),
|
||||
SliceWiring("chatState", "opv_1", "LP", "\$r.wqv", "\$r.wqv(t, n.opv_1)", listOf("?QP", "?XP", "?bN", "?fN", "?lN", "?nN", "?pN", "?qN", "?sN", "?vN", "?wN", "ChangeChannel", "CleanUsersMessage", "Configure", "IgnoreUserMessages", "ResetSettingsFromStorage", "SaveChatMessage", "SendMessageToServer", "SetMessages", "SetSelectedUid", "ShowContextMenu", "ShowUserMessages", "UpdateTypingSpeedAntifloodParams", "UpdateUsers")),
|
||||
SliceWiring("clanChatState", "ppv_1", "qnt", "yv.qtg", "yv.qtg(t, n.ppv_1)", listOf("CloseClanChat", "Configure", "DoAddMessages", "OpenClanChat", "ResetClanChat")),
|
||||
SliceWiring("clan", "qpv_1", "jN", "we.hr0", "we.hr0(t, n.qpv_1)", listOf("?FM", "?IM", "?KM", "?PM", "?RM", "?VM", "?YM", "?iM", "AcceptedUserViewed", "AddIncomingClan", "AddNewIncoming", "AddNewIncomingClan", "AddOutgoingClan", "AddUsersToAccepted", "AddUsersToNewAccepted", "ClanBanned", "ClanInfoLoaded", "ClanLoaded", "ClanModalVisible", "IncomingAdded", "IncomingRemoved", "IncomingUserViewed", "MembersFetched", "NewAcceptedUserNotification", "NewClanInviteNotification", "NewIncomingUserNotification", "OutgoingAdded", "OutgoingRemoved", "RemoveIncomingClan", "RemoveNewIncoming", "RemoveNewIncomingClan", "RemoveOutgoingClan", "RemoveUserFromAccepted", "RemoveUsersFromNewAccepted", "SetClanEditPrice", "SetMinRankForCreate", "UpdateClanLicense", "UpdateClanNameValidation", "UpdateClanSearchValidation", "UpdateClanTagValidation", "UpdateHasClan", "UpdateTimeToJoinRestriction", "UpdateValidation")),
|
||||
SliceWiring("clan", "qpv_1", "jN", "we.hr0", "we.hr0(t, n.qpv_1)", listOf("AcceptedUserViewed", "AddIncomingClan", "AddNewIncoming", "AddNewIncomingClan", "AddOutgoingClan", "AddUsersToAccepted", "AddUsersToNewAccepted", "ClanBanned", "ClanInfoLoaded", "ClanLoaded", "ClanModalVisible", "DecrementClanNotifications", "IncomingAdded", "IncomingRemoved", "IncomingUserViewed", "IncrementClanNotifications", "MembersFetched", "NewAcceptedUserNotification", "NewClanInviteNotification", "NewIncomingUserNotification", "OutgoingAdded", "OutgoingRemoved", "RemoveIncomingClan", "RemoveNewIncoming", "RemoveNewIncomingClan", "RemoveOutgoingClan", "RemoveUserFromAccepted", "RemoveUsersFromNewAccepted", "ResetClanNameTagValidation", "ResetClanUserValidation", "SetClanEditPrice", "SetClanNotificationsCount", "SetMinRankForCreate", "UpdateClanLicense", "UpdateClanNameValidation", "UpdateClanSearchValidation", "UpdateClanTagValidation", "UpdateHasClan", "UpdateIntermediateDescription", "UpdateIntermediateMinRank", "UpdateIntermediateRequests", "UpdateTimeToJoinRestriction", "UpdateValidation")),
|
||||
SliceWiring("clansStorage", "rpv_1", "lO", "me.vr1", "me.vr1(t, n.rpv_1)", listOf("AddClanUser", "AddUsersToNewAccepted", "BuyAdditionalPlace", "ClanUnloaded", "ClearForeignClan", "ForeignClanLoaded", "LoadClanIntoStorage", "MinimalRankChanged", "RecruitmentChanged", "RemoveClanUser", "RemoveUsersFromNewAccepted", "ShowForeignClan", "UpdateClan")),
|
||||
SliceWiring("tipsState", "spv_1", "dst", "Hw.vth", "Hw.vth(t, n.spv_1)", listOf("UpdateTips")),
|
||||
SliceWiring("news", "tpv_1", "ZX", "E_.asd", "E_.asd(t, n.tpv_1)", listOf("AddNewsItemAction", "RemoveNewsItemAction", "SetNewsFlag", "SetNewsItemAction", "SetUnreadNewsCounter")),
|
||||
@@ -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", "friends", "suppliesState", "clanUserInfo", "battleInviteState", "partner", "userRenameState", "battlePauseState", "battleViewState", "tankActionsLogState", "suppliesTutorial", "clanChatState", "battleSearchState", "battleStatistics", "soundSettings", "gameSettings", "keyboardSettings", "touchscreenHudSettings", "graphicsSettings", "controlSettings", "battleSettings", "contractState", "tankBuilderState", "battleMessage", "maps", "battleChat", "remoteUserDataStorage", "matchmaking", "battlesSelect", "battleUsers", "challenges", "clansStorage")
|
||||
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", "battlePauseState", "battleViewState", "tankActionsLogState", "suppliesTutorial", "clanChatState", "battleSearchState", "battleStatistics", "soundSettings", "gameSettings", "keyboardSettings", "touchscreenHudSettings", "graphicsSettings", "controlSettings", "battleSettings", "contractState", "tankBuilderState", "battleMessage", "maps", "battleChat", "remoteUserDataStorage", "matchmaking", "battlesSelect", "battleUsers", "challenges", "clansStorage", "clan")
|
||||
|
||||
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
|
||||
val Unmigrated: List<SliceWiring> get() = All.filter { it.Field !in Migrated }
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
// System / ContextTutorial / BattleTutorial / Spectator / TwoStepVerification / AntiAddiction /
|
||||
// Moderation / MigrationState / Register / UserReports /
|
||||
// Friends / Supplies / ClanUserInfo / BattleInvite / Partner / UserRename /
|
||||
// BattlePause / BattleView / TankActionsLog / SuppliesTutorial / ClanChat / BattleSearch / BattleStatistics / SoundSettings / GameSettings / KeyboardSettings / TouchscreenHudSettings / GraphicsSettings / ControlSettings / BattleSettings / ContractState / TankBuilder / BattleMessage / Maps / BattleChat / RemoteUserDataStorage / Matchmaking / BattlesSelect / BattleUsers / Challenges / ClansStorage;
|
||||
// BattlePause / BattleView / TankActionsLog / SuppliesTutorial / ClanChat / BattleSearch / BattleStatistics / SoundSettings / GameSettings / KeyboardSettings / TouchscreenHudSettings / GraphicsSettings / ControlSettings / BattleSettings / ContractState / TankBuilder / BattleMessage / Maps / BattleChat / RemoteUserDataStorage / Matchmaking / BattlesSelect / BattleUsers / Challenges / ClansStorage / Clan;
|
||||
// ㉑ launchParams 是纯透传切片(无 reducer/动作),只做 Map 类型收口;⑭ 根 reducer 的 89 槽位委派
|
||||
//
|
||||
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
|
||||
@@ -826,6 +826,7 @@ import App.State.UpdateStars
|
||||
import App.State.Reducers.ChallengesStateSlice
|
||||
import App.State.Reducers.ReduceChallenges
|
||||
import App.State.Reducers.RefreshBattlePasses
|
||||
import App.State.ClanEditPriceCC
|
||||
import App.State.ClanInfo
|
||||
import App.State.ClanUnloaded
|
||||
import App.State.BuyAdditionalPlace
|
||||
@@ -840,6 +841,44 @@ import App.State.Reducers.ClanStorageStateSlice
|
||||
import App.State.Reducers.ClearForeignClan
|
||||
import App.State.Reducers.ReduceClanStorage
|
||||
import App.State.Reducers.RemoveClanUser
|
||||
import App.State.AddIncomingClan
|
||||
import App.State.AddNewIncoming
|
||||
import App.State.AddNewIncomingClan
|
||||
import App.State.AddOutgoingClan
|
||||
import App.State.AcceptedUserViewed
|
||||
import App.State.ClanBanned
|
||||
import App.State.ClanModalVisible
|
||||
import App.State.IncomingAdded
|
||||
import App.State.IncomingRemoved
|
||||
import App.State.IncomingUserViewed
|
||||
import App.State.MembersFetched
|
||||
import App.State.NewAcceptedUserNotification
|
||||
import App.State.NewClanInviteNotification
|
||||
import App.State.NewIncomingUserNotification
|
||||
import App.State.OutgoingAdded
|
||||
import App.State.OutgoingRemoved
|
||||
import App.State.ClanInfoLoaded
|
||||
import App.State.RemoveIncomingClan
|
||||
import App.State.RemoveNewIncoming
|
||||
import App.State.RemoveNewIncomingClan
|
||||
import App.State.RemoveOutgoingClan
|
||||
import App.State.SetClanEditPrice
|
||||
import App.State.SetMinRankForCreate
|
||||
import App.State.UpdateClanLicense
|
||||
import App.State.UpdateClanNameValidation
|
||||
import App.State.UpdateHasClan
|
||||
import App.State.UpdateTimeToJoinRestriction
|
||||
import App.State.UpdateValidation
|
||||
import App.State.Reducers.ClanStateSlice
|
||||
import App.State.Reducers.DecrementClanNotifications
|
||||
import App.State.Reducers.IncrementClanNotifications
|
||||
import App.State.Reducers.ReduceClan
|
||||
import App.State.Reducers.ResetClanNameTagValidation
|
||||
import App.State.Reducers.ResetClanUserValidation
|
||||
import App.State.Reducers.SetClanNotificationsCount
|
||||
import App.State.Reducers.UpdateIntermediateDescription
|
||||
import App.State.Reducers.UpdateIntermediateMinRank
|
||||
import App.State.Reducers.UpdateIntermediateRequests
|
||||
import App.State.Reducers.UserRankBounds
|
||||
import App.State.Reducers.UserStateSlice
|
||||
import App.State.Reducers.ClearHullData
|
||||
@@ -1267,7 +1306,7 @@ object StateCheck {
|
||||
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
|
||||
}
|
||||
|
||||
// ⑭ 根 reducer:89 个槽位的委派表 + 84 个已迁移切片(83 归约 + launchParams 透传)
|
||||
// ⑭ 根 reducer:89 个槽位的委派表 + 85 个已迁移切片(84 归约 + launchParams 透传)
|
||||
run {
|
||||
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
|
||||
Check(
|
||||
@@ -1331,7 +1370,7 @@ object StateCheck {
|
||||
LocalBattleW.Reducer == "(inline@localBattleUserState)"
|
||||
)
|
||||
Check(
|
||||
"根:迁移表 = 84 个已迁移字段,其余 5 个待迁",
|
||||
"根:迁移表 = 85 个已迁移字段,其余 4 个待迁",
|
||||
RootReducer.Migrated ==
|
||||
setOf(
|
||||
"inviteState", "loginByPassword", "twitchState", "home",
|
||||
@@ -1355,9 +1394,9 @@ object StateCheck {
|
||||
"soundSettings", "gameSettings", "keyboardSettings", "touchscreenHudSettings",
|
||||
"graphicsSettings", "controlSettings", "battleSettings", "contractState",
|
||||
"tankBuilderState", "battleMessage", "maps", "battleChat",
|
||||
"remoteUserDataStorage", "matchmaking", "battlesSelect", "battleUsers", "challenges", "clansStorage",
|
||||
"remoteUserDataStorage", "matchmaking", "battlesSelect", "battleUsers", "challenges", "clansStorage", "clan",
|
||||
) &&
|
||||
RootWiring.Unmigrated.size == 5
|
||||
RootWiring.Unmigrated.size == 4
|
||||
)
|
||||
|
||||
// 一次 ReduceRoot 里多个切片各归约自己的动作,互不串片
|
||||
@@ -1413,7 +1452,7 @@ object StateCheck {
|
||||
S5.User.Uid == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
|
||||
)
|
||||
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
|
||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 84/89")
|
||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 85/89")
|
||||
|
||||
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
|
||||
Store.Dispatch(SetStatus("LOGGING_IN"))
|
||||
@@ -7082,6 +7121,113 @@ object StateCheck {
|
||||
)
|
||||
}
|
||||
|
||||
// 95 切片 reducer:Clan(jN / we.hr0,36 字段;43 个动作)
|
||||
run {
|
||||
val S0 = ClanStateSlice()
|
||||
Check(
|
||||
"切片 Clan:默认值完整",
|
||||
S0.CurrentClanId == null && !S0.HasClan && !S0.HasLicense &&
|
||||
S0.ClanAcceptedMembers.isEmpty()
|
||||
)
|
||||
val S1 = ReduceClan(S0, SetMinRankForCreate(5))
|
||||
Check("切片 Clan:SetMinRankForCreate 写最小等级", S1.MinRankForCreateClan == 5)
|
||||
val S2 = ReduceClan(S1, AddUsersToAccepted(listOf(1L, 2L)))
|
||||
Check(
|
||||
"切片 Clan:AddUsersToAccepted 批量入集合",
|
||||
S2.ClanAcceptedMembers == setOf<Any?>(1L, 2L)
|
||||
)
|
||||
Check(
|
||||
"切片 Clan:RemoveUserFromAccepted 删单值",
|
||||
ReduceClan(S2, RemoveUserFromAccepted(1L))
|
||||
.ClanAcceptedMembers == setOf<Any?>(2L)
|
||||
)
|
||||
val Info = ClanInfo(
|
||||
id = 7L, name = "n", tag = "t", description = "d",
|
||||
minRankIndexForAddClan = 1, incomingRequestEnabled = true,
|
||||
creatorId = 9L, createTime = 0L, users = emptySet<Any?>(),
|
||||
gameObject = null, maxCharactersDescription = 100, blocked = false,
|
||||
reasonForBlocking = "", self = false, maxMembers = 10,
|
||||
newUsers = emptySet<Any?>(),
|
||||
)
|
||||
val S3 = ReduceClan(S2, ClanInfoLoaded(Info))
|
||||
Check(
|
||||
"切片 Clan:ClanInfoLoaded 写 6 个身份字段",
|
||||
S3.CurrentClanId == 7L && S3.CreatorId == 9L && S3.Name == "n" &&
|
||||
S3.Tag == "t" && S3.Description == "d"
|
||||
)
|
||||
Check(
|
||||
"切片 Clan:人员 / 战队列表四臂",
|
||||
ReduceClan(S3, IncomingAdded(listOf(3L))).IncomingUsers == setOf<Any?>(3L) &&
|
||||
ReduceClan(S3, OutgoingAdded(listOf(4L))).OutgoingUsers == setOf<Any?>(4L) &&
|
||||
ReduceClan(S3, AddOutgoingClan(11L)).OutgoingClans == setOf<Any?>(11L) &&
|
||||
ReduceClan(S3, AddNewIncomingClan(listOf(12L)))
|
||||
.NewIncomingClans == setOf<Any?>(12L)
|
||||
)
|
||||
Check(
|
||||
"切片 Clan:中间态三臂",
|
||||
ReduceClan(S3, UpdateIntermediateMinRank(2)).IntermediateMinRankIndex == 2 &&
|
||||
ReduceClan(S3, UpdateIntermediateRequests(true))
|
||||
.IntermediateRequestsEnabled == true &&
|
||||
ReduceClan(S3, UpdateIntermediateDescription("x"))
|
||||
.IntermediateDescription == "x"
|
||||
)
|
||||
Check(
|
||||
"切片 Clan:校验状态写 / 复位",
|
||||
ReduceClan(S3, UpdateValidation("bad")).ClanUserValidation == "bad" &&
|
||||
ReduceClan(ReduceClan(S3, UpdateValidation("bad")), ResetClanUserValidation)
|
||||
.ClanUserValidation == null &&
|
||||
ReduceClan(ReduceClan(S3, UpdateClanNameValidation("bad")),
|
||||
ResetClanNameTagValidation).ClanNameNameValidation == null
|
||||
)
|
||||
Check(
|
||||
"切片 Clan:HasClan=false 整片复位、true 只置位",
|
||||
ReduceClan(
|
||||
ReduceClan(S3, UpdateClanLicense(true)),
|
||||
UpdateHasClan(false),
|
||||
) == ClanStateSlice() &&
|
||||
ReduceClan(S3, UpdateHasClan(true)).HasClan
|
||||
)
|
||||
Check(
|
||||
"切片 Clan:封禁 / 弹窗 / 改名价格 / 入队限制",
|
||||
ReduceClan(S3, ClanBanned("why")).let { it.Banned && it.BanReason == "why" } &&
|
||||
ReduceClan(S3, ClanModalVisible(true)).ClanModalVisible &&
|
||||
ReduceClan(S3, SetClanEditPrice(ClanEditPriceCC(1, 2, null, 3, 4, 5)))
|
||||
.ClanEditPrice == ClanEditPriceCC(1, 2, null, 3, 4, 5) &&
|
||||
ReduceClan(S3, UpdateTimeToJoinRestriction(60))
|
||||
.RestrictionToJoinInClan == 60
|
||||
)
|
||||
Check(
|
||||
"切片 Clan:通知计数 +1 / -1 / 直接写",
|
||||
ReduceClan(S3, IncrementClanNotifications).NotificationsCount == 1 &&
|
||||
ReduceClan(ReduceClan(S3, IncrementClanNotifications),
|
||||
DecrementClanNotifications).NotificationsCount == 0 &&
|
||||
ReduceClan(S3, SetClanNotificationsCount(4)).NotificationsCount == 4
|
||||
)
|
||||
Check(
|
||||
"切片 Clan:三个通知徽标与两个已读动作",
|
||||
ReduceClan(S3, NewIncomingUserNotification(2))
|
||||
.IncomingUserNotificationCount == 2 &&
|
||||
ReduceClan(ReduceClan(S3, NewAcceptedUserNotification(3)),
|
||||
AcceptedUserViewed(1L)).AcceptedUserNotificationCount == 2 &&
|
||||
ReduceClan(ReduceClan(S3, NewIncomingUserNotification(2)),
|
||||
IncomingUserViewed(1L)).IncomingUserNotificationCount == 1 &&
|
||||
ReduceClan(S3, NewClanInviteNotification(1)).ClanInviteNotificationCount == 1
|
||||
)
|
||||
Check("切片 Clan:无关动作不改状态", ReduceClan(S3, AddFrags(1)) == S3)
|
||||
|
||||
val Root0 = RootState()
|
||||
val Root1 = ReduceRoot(Root0, SetMinRankForCreate(3))
|
||||
Check(
|
||||
"根:SetMinRankForCreate 只动 clan",
|
||||
Root1.Clan.MinRankForCreateClan == 3 && Root1.ClansStorage == Root0.ClansStorage
|
||||
)
|
||||
val W = RootWiring.ByField("clan")!!
|
||||
Check(
|
||||
"根:clan 委派到 jN / we.hr0 且 43 个动作",
|
||||
W.SliceClass == "jN" && W.Reducer == "we.hr0" && W.Actions.size == 43
|
||||
)
|
||||
}
|
||||
|
||||
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