feat(state): 迁移 remoteUserDataStorage(复用 RemoteUserData 协议模型)
This commit is contained in:
1 parent
5327312b13
commit
9e51b695aa
8 files changed
+235
-18
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` 动作名。
|
||||
- 已迁移切片 **78/89**:
|
||||
- 已迁移切片 **79/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)
|
||||
@@ -392,12 +392,18 @@
|
||||
`ChatLoaded` 写两个频道开关并置 `loaded`;`AddMessage` 只保留最后 120 条再追加;
|
||||
`ett`/`utt` 开关输入框、`stt` 取反历史面板、`rtt`→`ResetBattleChat` 整片复位。
|
||||
消息类型直接复用生成物里的协议模型 `ChatMessage`(13 字段)。
|
||||
- `remoteUserDataStorage` → `$Q` / `vh.isl`(`RemoteUserDataStorageReducer.kt`):1 字段,8 个分支。
|
||||
按 userId 就地更新缓存里的用户数据:uid / rank / hasPremium / clanTag / onlineStatus / battle;
|
||||
`UserLeaveBattle` 在「仍在组里」时保留 `inGroup` 标记,`UserLeaveGroup` 清空 battle;
|
||||
没有缓存条目的 userId 不新建(原 reducer 直接返回原状态)。
|
||||
用户数据模型直接复用生成物 `RemoteUserData`,战斗 / 在线 / 会员状态用
|
||||
`BattleNotifierData` / `OnlineNotifierData` / `PremiumNotifierData`。
|
||||
- `launchParams` → `(pass-through)`:原根 reducer 对 `hpx_1` 没有调用,缺省是 `ag()` 空 Map;
|
||||
`RootState.LaunchParams` 收口为 `LaunchParamsStateSlice = Map<String, String>`,不新增 reducer/动作。
|
||||
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 77 个有 reducer 的切片,
|
||||
`launchParams` 与其余 **11 个字段原样透传**;
|
||||
迁移进度用 `RootReducer.Coverage`(`已迁移 78/89`)与 `RootWiring.Unmigrated` 暴露。
|
||||
- 自检:`TankiOffline/tools/RunStateTest.ps1` **848 项全绿**,含 89 槽位完整性、77 个归约切片一次归约互不串片、未迁移字段透传,
|
||||
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 78 个有 reducer 的切片,
|
||||
`launchParams` 与其余 **10 个字段原样透传**;
|
||||
迁移进度用 `RootReducer.Coverage`(`已迁移 79/89`)与 `RootWiring.Unmigrated` 暴露。
|
||||
- 自检:`TankiOffline/tools/RunStateTest.ps1` **859 项全绿**,含 89 槽位完整性、78 个归约切片一次归约互不串片、未迁移字段透传,
|
||||
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
|
||||
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
|
||||
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"generatedAt": "2026-10-24T12:02:07.703Z",
|
||||
"generatedAt": "2026-10-24T12:12:10.770Z",
|
||||
"module": "module_10032.js",
|
||||
"RootState": {
|
||||
"Class": "z6",
|
||||
@@ -84,7 +84,8 @@
|
||||
"tankBuilderState",
|
||||
"battleMessage",
|
||||
"maps",
|
||||
"battleChat"
|
||||
"battleChat",
|
||||
"remoteUserDataStorage"
|
||||
],
|
||||
"AnonymousActions": [
|
||||
{
|
||||
@@ -2127,7 +2128,7 @@
|
||||
"UserLeaveGroup"
|
||||
],
|
||||
"Default": "new $Q()",
|
||||
"Migrated": false
|
||||
"Migrated": true
|
||||
},
|
||||
{
|
||||
"Field": "battlesSelect",
|
||||
|
||||
@@ -802,6 +802,10 @@ const Migrated = {
|
||||
battleMessage: { Type: 'BattleMessageStateSlice', Reduce: 'ReduceBattleMessage' },
|
||||
maps: { Type: 'MapsStateSlice', Reduce: 'ReduceMaps' },
|
||||
battleChat: { Type: 'BattleChatStateSlice', Reduce: 'ReduceBattleChat' },
|
||||
remoteUserDataStorage: {
|
||||
Type: 'RemoteUserDataStorageStateSlice',
|
||||
Reduce: 'ReduceRemoteUserDataStorage',
|
||||
},
|
||||
};
|
||||
|
||||
for (const R of Rows) R.Migrated = Object.prototype.hasOwnProperty.call(Migrated, R.Field);
|
||||
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
// RemoteUserDataStorageReducer.kt —— 远端用户数据缓存切片
|
||||
// (对应 module_10032 的 $Q 状态 + RQ.isl)
|
||||
//
|
||||
// 证据链:
|
||||
// 状态类 $Q:1 个字段,toString 名 "RemoteUserDataStorage(userIdToData=…)",缺省 emptyMap()。
|
||||
// 根委派:`vh.isl(t, n.zpv_1)`。
|
||||
// reducer(8 个实例臂,全部是「按 userId 更新缓存里的 RemoteUserData」):
|
||||
// xQ(UpdateUids) → uid
|
||||
// jQ(UpdateRanks) → rank
|
||||
// SQ(UpdatePremiumStatus)→ hasPremium
|
||||
// CQ(UpdateClanTags) → clanTag
|
||||
// EQ(UpdateOnlineStatus) → onlineStatus
|
||||
// TQ(UserEnterBattle) → battle(inBattle / inGroup / proBattle)
|
||||
// IQ(UserLeaveBattle) → 仍在组里就保留组标记,否则清空 battle
|
||||
// AQ(UserLeaveGroup) → 清空 battle
|
||||
//
|
||||
// 用户数据模型直接复用生成物里的协议模型 `RemoteUserData`
|
||||
// (userId / uid / rank / hasPremium / clanTag / onlineStatus / battle)。
|
||||
package App.State.Reducers
|
||||
|
||||
import App.State.BattleNotifierData
|
||||
import App.State.OnlineNotifierData
|
||||
import App.State.PremiumNotifierData
|
||||
import App.State.RemoteUserData
|
||||
import App.State.UpdateClanTags
|
||||
import App.State.UpdateOnlineStatus
|
||||
import App.State.UpdatePremiumStatus
|
||||
import App.State.UpdateRanks
|
||||
import App.State.UpdateUids
|
||||
import App.State.UserEnterBattle
|
||||
import App.State.UserLeaveBattle
|
||||
import App.State.UserLeaveGroup
|
||||
|
||||
/* ------------------------------------------------------------------ 状态 */
|
||||
|
||||
data class RemoteUserDataStorageStateSlice(
|
||||
val UserIdToData: Map<Any?, Any?> = emptyMap(),
|
||||
)
|
||||
|
||||
/* ------------------------------------------------------------------ reducer */
|
||||
|
||||
private fun Upsert(
|
||||
Data: Map<Any?, Any?>,
|
||||
UserId: Any?,
|
||||
Transform: (RemoteUserData?) -> RemoteUserData,
|
||||
): Map<Any?, Any?> = Data + (UserId to Transform(Data[UserId] as? RemoteUserData))
|
||||
|
||||
/** 缓存里缺这条用户时用空壳补上(原 `new qQ(userId, …)`)。 */
|
||||
private fun Empty(UserId: Any?): RemoteUserData = RemoteUserData(
|
||||
userId = UserId,
|
||||
uid = null,
|
||||
rank = null,
|
||||
hasPremium = false,
|
||||
clanTag = null,
|
||||
onlineStatus = null,
|
||||
battle = null,
|
||||
)
|
||||
|
||||
private fun MergeInto(
|
||||
Data: Map<Any?, Any?>,
|
||||
Updates: Map<*, *>?,
|
||||
Apply: (RemoteUserData, Any?) -> RemoteUserData,
|
||||
): Map<Any?, Any?> {
|
||||
var Result = Data
|
||||
for ((Key, Value) in Updates ?: emptyMap<Any?, Any?>()) {
|
||||
Result = Upsert(Result, Key) { Existing -> Apply(Existing ?: Empty(Key), Value) }
|
||||
}
|
||||
return Result
|
||||
}
|
||||
|
||||
fun ReduceRemoteUserDataStorage(
|
||||
State0: RemoteUserDataStorageStateSlice,
|
||||
Action: Any,
|
||||
): RemoteUserDataStorageStateSlice = when (Action) {
|
||||
is UpdateUids -> State0.copy(
|
||||
UserIdToData = MergeInto(State0.UserIdToData, Action.uids as? Map<*, *>) { User, Value ->
|
||||
User.copy(uid = Value)
|
||||
},
|
||||
)
|
||||
is UpdateRanks -> State0.copy(
|
||||
UserIdToData = MergeInto(State0.UserIdToData, Action.ranks) { User, Value ->
|
||||
User.copy(rank = Value)
|
||||
},
|
||||
)
|
||||
is UpdatePremiumStatus -> State0.copy(
|
||||
UserIdToData = MergeInto(State0.UserIdToData, Action.premiums) { User, Value ->
|
||||
val HasPremium = (Value as? PremiumNotifierData)?.hasPremium?.toBoolean() ?: false
|
||||
User.copy(hasPremium = HasPremium)
|
||||
},
|
||||
)
|
||||
is UpdateClanTags -> State0.copy(
|
||||
UserIdToData = MergeInto(State0.UserIdToData, Action.clans as? Map<*, *>) { User, Value ->
|
||||
User.copy(clanTag = Value)
|
||||
},
|
||||
)
|
||||
is UpdateOnlineStatus -> State0.copy(
|
||||
UserIdToData = MergeInto(State0.UserIdToData, Action.status) { User, Value ->
|
||||
User.copy(onlineStatus = (Value as? OnlineNotifierData)?.online)
|
||||
},
|
||||
)
|
||||
is UserEnterBattle -> State0.copy(
|
||||
UserIdToData = MergeInto(State0.UserIdToData, Action.battles) { User, Value ->
|
||||
User.copy(battle = Value)
|
||||
},
|
||||
)
|
||||
is UserLeaveBattle -> State0.copy(
|
||||
UserIdToData =
|
||||
if (!State0.UserIdToData.containsKey(Action.userId)) State0.UserIdToData
|
||||
else Upsert(State0.UserIdToData, Action.userId) { Existing ->
|
||||
val User = Existing ?: Empty(Action.userId)
|
||||
val Battle = User.battle as? BattleNotifierData
|
||||
if (Battle?.inGroup == true) {
|
||||
User.copy(battle = BattleNotifierData(inBattle = false, inGroup = true, proBattle = Battle.proBattle))
|
||||
} else User.copy(battle = null)
|
||||
},
|
||||
)
|
||||
is UserLeaveGroup -> State0.copy(
|
||||
UserIdToData =
|
||||
if (!State0.UserIdToData.containsKey(Action.userId)) State0.UserIdToData
|
||||
else Upsert(State0.UserIdToData, Action.userId) { Existing ->
|
||||
(Existing ?: Empty(Action.userId)).copy(battle = null)
|
||||
},
|
||||
)
|
||||
else -> State0
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// RootReducer.kt —— 根 reducer 骨架(对应 module_10032 的根状态 z6 / TOState 与根 reducer k6)
|
||||
//
|
||||
// 原包 k6 对 89 个切片逐个调用各自的 reducer,再 `return n.cti(...)`(copy 出新状态)。
|
||||
// 这里采用同一形状。已迁移的 78 个切片里,77 个真正调用 reducer,launchParams 只做类型收口:
|
||||
// 这里采用同一形状。已迁移的 79 个切片里,78 个真正调用 reducer,launchParams 只做类型收口:
|
||||
//
|
||||
// inviteState → ReduceInvite(aD / pD.er5)
|
||||
// loginByPassword → ReduceLoginByPassword(gD / MD.cr6)
|
||||
@@ -80,9 +80,10 @@
|
||||
// battleMessage → ReduceBattleMessage(FE / vT().rqb,根委派 `vT().rqb(t, n.qpw_1)`)
|
||||
// maps → ReduceMaps(OA / Oi.sql,根委派 `Oi.sql(t, n.bpw_1)`)
|
||||
// battleChat → ReduceBattleChat(Q6 / Hf.wtg,根委派 `Hf.wtg(t, n.rpw_1)`)
|
||||
// remoteUserDataStorage → ReduceRemoteUserDataStorage($Q / vh.isl,根委派 `vh.isl(t, n.zpv_1)`)
|
||||
// launchParams → 无 reducer(原根 reducer 原样透传;默认 `ag()` = emptyMap())
|
||||
//
|
||||
// 其余 11 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||
// 其余 10 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
|
||||
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
|
||||
package App.State
|
||||
@@ -142,6 +143,7 @@ import App.State.Reducers.ReduceQuestCategories
|
||||
import App.State.Reducers.ReduceQuests
|
||||
import App.State.Reducers.ReduceRankUpDialog
|
||||
import App.State.Reducers.ReduceReferralEvent
|
||||
import App.State.Reducers.ReduceRemoteUserDataStorage
|
||||
import App.State.Reducers.ReduceRegister
|
||||
import App.State.Reducers.ReduceRewardedVideos
|
||||
import App.State.Reducers.ReduceRulesUpdated
|
||||
@@ -255,6 +257,7 @@ object RootReducer {
|
||||
BattleMessage = ReduceBattleMessage(State0.BattleMessage, Action),
|
||||
Maps = ReduceMaps(State0.Maps, Action),
|
||||
BattleChat = ReduceBattleChat(State0.BattleChat, Action),
|
||||
RemoteUserDataStorage = ReduceRemoteUserDataStorage(State0.RemoteUserDataStorage, Action),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ import App.State.Reducers.QuestsStateSlice
|
||||
import App.State.Reducers.RankUpDialogStateSlice
|
||||
import App.State.Reducers.ReferralEventStateSlice
|
||||
import App.State.Reducers.RegisterStateSlice
|
||||
import App.State.Reducers.RemoteUserDataStorageStateSlice
|
||||
import App.State.Reducers.RewardedVideosStateSlice
|
||||
import App.State.Reducers.RulesUpdatedStateSlice
|
||||
import App.State.Reducers.ScreenStateSlice
|
||||
@@ -140,8 +141,8 @@ data class RootState(
|
||||
val Notifications: NotificationsStateSlice = NotificationsStateSlice(),
|
||||
/** 原 friends(KD / NL().cr9)。已迁移。 */
|
||||
val Friends: FriendsStateSlice = FriendsStateSlice(),
|
||||
/** 原 remoteUserDataStorage($Q / vh.isl)。未迁移:原样透传。 */
|
||||
val RemoteUserDataStorage: Any? = null,
|
||||
/** 原 remoteUserDataStorage($Q / vh.isl)。已迁移。 */
|
||||
val RemoteUserDataStorage: RemoteUserDataStorageStateSlice = RemoteUserDataStorageStateSlice(),
|
||||
/** 原 battlesSelect(fA / Mi.vqk)。未迁移:原样透传。 */
|
||||
val BattlesSelect: Any? = null,
|
||||
/** 原 maps(OA / Oi.sql)。已迁移。 */
|
||||
|
||||
@@ -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")
|
||||
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")
|
||||
|
||||
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
|
||||
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;
|
||||
// BattlePause / BattleView / TankActionsLog / SuppliesTutorial / ClanChat / BattleSearch / BattleStatistics / SoundSettings / GameSettings / KeyboardSettings / TouchscreenHudSettings / GraphicsSettings / ControlSettings / BattleSettings / ContractState / TankBuilder / BattleMessage / Maps / BattleChat / RemoteUserDataStorage;
|
||||
// ㉑ launchParams 是纯透传切片(无 reducer/动作),只做 Map 类型收口;⑭ 根 reducer 的 89 槽位委派
|
||||
//
|
||||
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
|
||||
@@ -745,6 +745,17 @@ import App.State.Reducers.OpenBattleChatInput
|
||||
import App.State.Reducers.ReduceBattleChat
|
||||
import App.State.Reducers.ResetBattleChat
|
||||
import App.State.Reducers.ToggleBattleChatHistory
|
||||
import App.State.BattleNotifierData
|
||||
import App.State.RemoteUserData
|
||||
import App.State.Reducers.ReduceRemoteUserDataStorage
|
||||
import App.State.Reducers.RemoteUserDataStorageStateSlice
|
||||
import App.State.UpdateClanTags
|
||||
import App.State.UpdateOnlineStatus
|
||||
import App.State.UpdateRanks
|
||||
import App.State.UpdateUids
|
||||
import App.State.UserEnterBattle
|
||||
import App.State.UserLeaveBattle
|
||||
import App.State.UserLeaveGroup
|
||||
import App.State.Reducers.UserRankBounds
|
||||
import App.State.Reducers.UserStateSlice
|
||||
import App.State.Reducers.ClearHullData
|
||||
@@ -1172,7 +1183,7 @@ object StateCheck {
|
||||
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
|
||||
}
|
||||
|
||||
// ⑭ 根 reducer:89 个槽位的委派表 + 78 个已迁移切片(77 归约 + launchParams 透传)
|
||||
// ⑭ 根 reducer:89 个槽位的委派表 + 79 个已迁移切片(78 归约 + launchParams 透传)
|
||||
run {
|
||||
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
|
||||
Check(
|
||||
@@ -1236,7 +1247,7 @@ object StateCheck {
|
||||
LocalBattleW.Reducer == "(inline@localBattleUserState)"
|
||||
)
|
||||
Check(
|
||||
"根:迁移表 = 78 个已迁移字段,其余 11 个待迁",
|
||||
"根:迁移表 = 79 个已迁移字段,其余 10 个待迁",
|
||||
RootReducer.Migrated ==
|
||||
setOf(
|
||||
"inviteState", "loginByPassword", "twitchState", "home",
|
||||
@@ -1260,8 +1271,9 @@ object StateCheck {
|
||||
"soundSettings", "gameSettings", "keyboardSettings", "touchscreenHudSettings",
|
||||
"graphicsSettings", "controlSettings", "battleSettings", "contractState",
|
||||
"tankBuilderState", "battleMessage", "maps", "battleChat",
|
||||
"remoteUserDataStorage",
|
||||
) &&
|
||||
RootWiring.Unmigrated.size == 11
|
||||
RootWiring.Unmigrated.size == 10
|
||||
)
|
||||
|
||||
// 一次 ReduceRoot 里多个切片各归约自己的动作,互不串片
|
||||
@@ -1317,7 +1329,7 @@ object StateCheck {
|
||||
S5.User.Uid == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
|
||||
)
|
||||
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
|
||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 78/89")
|
||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 79/89")
|
||||
|
||||
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
|
||||
Store.Dispatch(SetStatus("LOGGING_IN"))
|
||||
@@ -6469,6 +6481,71 @@ object StateCheck {
|
||||
)
|
||||
}
|
||||
|
||||
// 89 切片 reducer:RemoteUserDataStorage($Q / vh.isl,1 字段;8 个动作)
|
||||
run {
|
||||
val S0 = RemoteUserDataStorageStateSlice()
|
||||
Check("切片 RemoteUserDataStorage:默认空缓存", S0.UserIdToData.isEmpty())
|
||||
val S1 = ReduceRemoteUserDataStorage(S0, UpdateUids(mapOf("1" to "uid1")))
|
||||
Check(
|
||||
"切片 RemoteUserDataStorage:UpdateUids 建条目并写 uid",
|
||||
(S1.UserIdToData["1"] as RemoteUserData).uid == "uid1" &&
|
||||
(S1.UserIdToData["1"] as RemoteUserData).userId == "1"
|
||||
)
|
||||
val S2 = ReduceRemoteUserDataStorage(S1, UpdateRanks(mapOf("1" to 5)))
|
||||
Check(
|
||||
"切片 RemoteUserDataStorage:UpdateRanks 就地更新",
|
||||
(S2.UserIdToData["1"] as RemoteUserData).rank == 5 &&
|
||||
(S2.UserIdToData["1"] as RemoteUserData).uid == "uid1"
|
||||
)
|
||||
Check(
|
||||
"切片 RemoteUserDataStorage:UpdateClanTags / UpdateOnlineStatus",
|
||||
(ReduceRemoteUserDataStorage(S2, UpdateClanTags(mapOf("1" to "TAG")))
|
||||
.UserIdToData["1"] as RemoteUserData).clanTag == "TAG" &&
|
||||
(ReduceRemoteUserDataStorage(S2, UpdateOnlineStatus(mapOf("1" to App.State.OnlineNotifierData("online"))))
|
||||
.UserIdToData["1"] as RemoteUserData).onlineStatus == "online"
|
||||
)
|
||||
val S3 = ReduceRemoteUserDataStorage(
|
||||
S2,
|
||||
UserEnterBattle(mapOf("1" to BattleNotifierData(true, true, ""))),
|
||||
)
|
||||
Check(
|
||||
"切片 RemoteUserDataStorage:UserEnterBattle 写 battle",
|
||||
(S3.UserIdToData["1"] as RemoteUserData).battle is BattleNotifierData
|
||||
)
|
||||
val S4 = ReduceRemoteUserDataStorage(S3, UserLeaveBattle("1"))
|
||||
Check(
|
||||
"切片 RemoteUserDataStorage:UserLeaveBattle 保留组标记、清战斗位",
|
||||
(S4.UserIdToData["1"] as RemoteUserData).battle ==
|
||||
BattleNotifierData(false, true, "")
|
||||
)
|
||||
Check(
|
||||
"切片 RemoteUserDataStorage:UserLeaveGroup 清空 battle",
|
||||
(ReduceRemoteUserDataStorage(S4, UserLeaveGroup("1"))
|
||||
.UserIdToData["1"] as RemoteUserData).battle == null
|
||||
)
|
||||
Check(
|
||||
"切片 RemoteUserDataStorage:未知用户不新建条目",
|
||||
ReduceRemoteUserDataStorage(S0, UserLeaveBattle("9")).UserIdToData.isEmpty()
|
||||
)
|
||||
Check(
|
||||
"切片 RemoteUserDataStorage:无关动作不改状态",
|
||||
ReduceRemoteUserDataStorage(S4, AddFrags(1)) == S4
|
||||
)
|
||||
|
||||
val Root0 = RootState()
|
||||
val Root1 = ReduceRoot(Root0, UpdateUids(mapOf("2" to "u2")))
|
||||
Check(
|
||||
"根:UpdateUids 只动 remoteUserDataStorage",
|
||||
(Root1.RemoteUserDataStorage.UserIdToData["2"] as RemoteUserData).uid == "u2" &&
|
||||
Root1.BattleChat == Root0.BattleChat
|
||||
)
|
||||
val W = RootWiring.ByField("remoteUserDataStorage")!!
|
||||
Check(
|
||||
"根:remoteUserDataStorage 委派到 \$Q / vh.isl 且 8 个动作",
|
||||
W.SliceClass == "\$Q" && W.Reducer == "vh.isl" && W.Actions.size == 8
|
||||
)
|
||||
}
|
||||
|
||||
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