feat(state): 迁移 chatState(复用 ChatMessage 模型)
This commit is contained in:
1 parent
2f38609a98
commit
bb544643b5
8 files changed
+399
-32
No files matched your search
@@ -143,7 +143,7 @@
|
|||||||
`names/root_wiring.json` + `TankiOffline/.../App/State/RootState.kt`、`RootWiring.kt`。
|
`names/root_wiring.json` + `TankiOffline/.../App/State/RootState.kt`、`RootWiring.kt`。
|
||||||
- 根状态 `z6`(`TOState`)的 **89 个槽位**全部入表;字段顺序取 `toString`,槽位取构造函数 `<名>_1`。
|
- 根状态 `z6`(`TOState`)的 **89 个槽位**全部入表;字段顺序取 `toString`,槽位取构造函数 `<名>_1`。
|
||||||
- 根 reducer `k6` 的逐切片委派记全:reducer 引用、委派表达式、顶层 `instanceof` 动作名。
|
- 根 reducer `k6` 的逐切片委派记全:reducer 引用、委派表达式、顶层 `instanceof` 动作名。
|
||||||
- 已迁移切片 **85/89**:
|
- 已迁移切片 **86/89**:
|
||||||
- `inviteState` → `aD` / `pD.er5`(`InviteReducer.kt`)
|
- `inviteState` → `aD` / `pD.er5`(`InviteReducer.kt`)
|
||||||
- `loginByPassword` → `gD` / `MD.cr6`(`LoginReducer.kt`,含 `SetLogin`/`SetPassword` 把 status 打回 `NONE` 的原语义)
|
- `loginByPassword` → `gD` / `MD.cr6`(`LoginReducer.kt`,含 `SetLogin`/`SetPassword` 把 status 打回 `NONE` 的原语义)
|
||||||
- `twitchState` → `g9` / `y9.gta`(`TwitchReducer.kt`;`SetAccount` 是 Twitch 账号,不是 UID)
|
- `twitchState` → `g9` / `y9.gta`(`TwitchReducer.kt`;`SetAccount` 是 Twitch 账号,不是 UID)
|
||||||
@@ -429,12 +429,18 @@
|
|||||||
成员 / 待审用户 / 人员 / 外战队 / 新申请各有增删两臂;校验状态 4 个 setter + 2 个复位;
|
成员 / 待审用户 / 人员 / 外战队 / 新申请各有增删两臂;校验状态 4 个 setter + 2 个复位;
|
||||||
`ClanInfoLoaded` 写 6 个身份字段(复用 `ClanInfo`);`UpdateHasClan(false)` 整片复位;
|
`ClanInfoLoaded` 写 6 个身份字段(复用 `ClanInfo`);`UpdateHasClan(false)` 整片复位;
|
||||||
通知计数 3 个直接写 + 2 个已读 + 计数 ±1;其余为中间态三元组、封禁、弹窗与改名价格。
|
通知计数 3 个直接写 + 2 个已读 + 计数 ±1;其余为中间态三元组、封禁、弹窗与改名价格。
|
||||||
|
- `chatState` → `LP` / `$r.wqv`(`ChatStateReducer.kt`):16 字段,24 个分支。
|
||||||
|
`SaveChatMessage` 写消息并优先选中消息里的目标用户;`SetMessages` 整体替换频道表;
|
||||||
|
`CleanUsersMessage` 按 uid 过滤每个频道;键盘 / 滚动 / 右键菜单各有开关;
|
||||||
|
`IgnoreUserMessages`/`ShowUserMessages` 增删屏蔽名单(`_C` 重载时清空);
|
||||||
|
`rN`(用户资料刷新)依赖消息内的用户对象,离线重建按 no-op;`SendMessageToServer` 的时间戳留给中间件。
|
||||||
|
消息复用生成物 `ChatMessage`。
|
||||||
- `launchParams` → `(pass-through)`:原根 reducer 对 `hpx_1` 没有调用,缺省是 `ag()` 空 Map;
|
- `launchParams` → `(pass-through)`:原根 reducer 对 `hpx_1` 没有调用,缺省是 `ag()` 空 Map;
|
||||||
`RootState.LaunchParams` 收口为 `LaunchParamsStateSlice = Map<String, String>`,不新增 reducer/动作。
|
`RootState.LaunchParams` 收口为 `LaunchParamsStateSlice = Map<String, String>`,不新增 reducer/动作。
|
||||||
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 84 个有 reducer 的切片,
|
- `App/State/RootReducer.kt`:`ReduceRoot(State0, Action)` 归约上述 85 个有 reducer 的切片,
|
||||||
`launchParams` 与其余 **4 个字段原样透传**;
|
`launchParams` 与其余 **3 个字段原样透传**;
|
||||||
迁移进度用 `RootReducer.Coverage`(`已迁移 85/89`)与 `RootWiring.Unmigrated` 暴露。
|
迁移进度用 `RootReducer.Coverage`(`已迁移 86/89`)与 `RootWiring.Unmigrated` 暴露。
|
||||||
- 自检:`TankiOffline/tools/RunStateTest.ps1` **939 项全绿**,含 89 槽位完整性、84 个归约切片一次归约互不串片、未迁移字段透传,
|
- 自检:`TankiOffline/tools/RunStateTest.ps1` **953 项全绿**,含 89 槽位完整性、85 个归约切片一次归约互不串片、未迁移字段透传,
|
||||||
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
|
以及用 `action_trace_merged.json` 真实派发的两条商店动作(`SetCoinPaymentProviderUrl` / `ToggleShopActivity`)回放。
|
||||||
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
|
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
|
||||||
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
|
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"generatedAt": "2026-10-24T14:00:39.799Z",
|
"generatedAt": "2026-10-24T14:08:05.684Z",
|
||||||
"module": "module_10032.js",
|
"module": "module_10032.js",
|
||||||
"RootState": {
|
"RootState": {
|
||||||
"Class": "z6",
|
"Class": "z6",
|
||||||
@@ -91,7 +91,8 @@
|
|||||||
"battleUsers",
|
"battleUsers",
|
||||||
"challenges",
|
"challenges",
|
||||||
"clansStorage",
|
"clansStorage",
|
||||||
"clan"
|
"clan",
|
||||||
|
"chatState"
|
||||||
],
|
],
|
||||||
"AnonymousActions": [
|
"AnonymousActions": [
|
||||||
{
|
{
|
||||||
@@ -1563,6 +1564,78 @@
|
|||||||
"Name": "DecrementClanNotifications",
|
"Name": "DecrementClanNotifications",
|
||||||
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
"Effect": "notificationsCount - 1,下限 0"
|
"Effect": "notificationsCount - 1,下限 0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "XP",
|
||||||
|
"Name": "OpenChat",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "open=true(`n.cqu(!0)`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "QP",
|
||||||
|
"Name": "CloseChat",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "open=false(`n.cqu(!1)`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "nN",
|
||||||
|
"Name": "ClearChatContextMenu",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "contextMenuData=null(`n.cqu(cd, cd, null)`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "lN",
|
||||||
|
"Name": "ResetChat",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "new LP() 整片复位"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "fN",
|
||||||
|
"Name": "ShowChatKeyboard",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "isShowKeyboard=true、isNeedToHideKeyboard=false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "vN",
|
||||||
|
"Name": "HideChatKeyboard",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "isShowKeyboard=false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "pN",
|
||||||
|
"Name": "RequireHideChatKeyboard",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "isNeedToHideKeyboard=true"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "wN",
|
||||||
|
"Name": "CancelHideChatKeyboard",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "isNeedToHideKeyboard=false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "bN",
|
||||||
|
"Name": "DisableChatScroll",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "isNeedScroll=false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "qN",
|
||||||
|
"Name": "EnableChatScroll",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "isNeedScroll=true"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "sN",
|
||||||
|
"Name": "MarkFirstChannelMessagesLoaded",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "isFirstChannelMessagesLoad=false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Local": "YP",
|
||||||
|
"Name": "SetChatConfig",
|
||||||
|
"DispatchedAt": "未在 action_trace_merged.json 中出现;按 reducer 分支效果定名",
|
||||||
|
"Effect": "config(原名 Configure,与匹配大厅同名类冲突,改名 SetChatConfig)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Markers": [
|
"Markers": [
|
||||||
@@ -2006,33 +2079,33 @@
|
|||||||
"Reducer": "$r.wqv",
|
"Reducer": "$r.wqv",
|
||||||
"Delegate": "$r.wqv(t, n.opv_1)",
|
"Delegate": "$r.wqv(t, n.opv_1)",
|
||||||
"Actions": [
|
"Actions": [
|
||||||
"?QP",
|
"CancelHideChatKeyboard",
|
||||||
"?XP",
|
|
||||||
"?bN",
|
|
||||||
"?fN",
|
|
||||||
"?lN",
|
|
||||||
"?nN",
|
|
||||||
"?pN",
|
|
||||||
"?qN",
|
|
||||||
"?sN",
|
|
||||||
"?vN",
|
|
||||||
"?wN",
|
|
||||||
"ChangeChannel",
|
"ChangeChannel",
|
||||||
"CleanUsersMessage",
|
"CleanUsersMessage",
|
||||||
"Configure",
|
"ClearChatContextMenu",
|
||||||
|
"CloseChat",
|
||||||
|
"DisableChatScroll",
|
||||||
|
"EnableChatScroll",
|
||||||
|
"HideChatKeyboard",
|
||||||
"IgnoreUserMessages",
|
"IgnoreUserMessages",
|
||||||
|
"MarkFirstChannelMessagesLoaded",
|
||||||
|
"OpenChat",
|
||||||
|
"RequireHideChatKeyboard",
|
||||||
|
"ResetChat",
|
||||||
"ResetSettingsFromStorage",
|
"ResetSettingsFromStorage",
|
||||||
"SaveChatMessage",
|
"SaveChatMessage",
|
||||||
"SendMessageToServer",
|
"SendMessageToServer",
|
||||||
|
"SetChatConfig",
|
||||||
"SetMessages",
|
"SetMessages",
|
||||||
"SetSelectedUid",
|
"SetSelectedUid",
|
||||||
|
"ShowChatKeyboard",
|
||||||
"ShowContextMenu",
|
"ShowContextMenu",
|
||||||
"ShowUserMessages",
|
"ShowUserMessages",
|
||||||
"UpdateTypingSpeedAntifloodParams",
|
"UpdateTypingSpeedAntifloodParams",
|
||||||
"UpdateUsers"
|
"UpdateUsers"
|
||||||
],
|
],
|
||||||
"Default": "new LP()",
|
"Default": "new LP()",
|
||||||
"Migrated": false
|
"Migrated": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Field": "clanChatState",
|
"Field": "clanChatState",
|
||||||
|
|||||||
@@ -354,6 +354,20 @@ const AnonymousActionNames = new Map([
|
|||||||
['VM', 'SetClanNotificationsCount'],
|
['VM', 'SetClanNotificationsCount'],
|
||||||
['KM', 'IncrementClanNotifications'],
|
['KM', 'IncrementClanNotifications'],
|
||||||
['YM', 'DecrementClanNotifications'],
|
['YM', 'DecrementClanNotifications'],
|
||||||
|
// chatState 切片(LP / $r.wqv):11 个类没有 toString(hN 有名字,不需要)。
|
||||||
|
['XP', 'OpenChat'],
|
||||||
|
['QP', 'CloseChat'],
|
||||||
|
['nN', 'ClearChatContextMenu'],
|
||||||
|
['lN', 'ResetChat'],
|
||||||
|
['fN', 'ShowChatKeyboard'],
|
||||||
|
['vN', 'HideChatKeyboard'],
|
||||||
|
['pN', 'RequireHideChatKeyboard'],
|
||||||
|
['wN', 'CancelHideChatKeyboard'],
|
||||||
|
['bN', 'DisableChatScroll'],
|
||||||
|
['qN', 'EnableChatScroll'],
|
||||||
|
['sN', 'MarkFirstChannelMessagesLoaded'],
|
||||||
|
// YP 的 toString 名 Configure 与匹配大厅同名类冲突,改名避免字段错配。
|
||||||
|
['YP', 'SetChatConfig'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 非动作的 `instanceof`:屏幕标识类,用于 GoTo 分支判断(不是 dispatch 的动作)。
|
// 非动作的 `instanceof`:屏幕标识类,用于 GoTo 分支判断(不是 dispatch 的动作)。
|
||||||
@@ -843,6 +857,7 @@ const Migrated = {
|
|||||||
challenges: { Type: 'ChallengesStateSlice', Reduce: 'ReduceChallenges' },
|
challenges: { Type: 'ChallengesStateSlice', Reduce: 'ReduceChallenges' },
|
||||||
clansStorage: { Type: 'ClanStorageStateSlice', Reduce: 'ReduceClanStorage' },
|
clansStorage: { Type: 'ClanStorageStateSlice', Reduce: 'ReduceClanStorage' },
|
||||||
clan: { Type: 'ClanStateSlice', Reduce: 'ReduceClan' },
|
clan: { Type: 'ClanStateSlice', Reduce: 'ReduceClan' },
|
||||||
|
chatState: { Type: 'ChatStateSlice', Reduce: 'ReduceChat' },
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const R of Rows) R.Migrated = Object.prototype.hasOwnProperty.call(Migrated, R.Field);
|
for (const R of Rows) R.Migrated = Object.prototype.hasOwnProperty.call(Migrated, R.Field);
|
||||||
@@ -1279,6 +1294,18 @@ const AppSettingsAnonymousEffects = {
|
|||||||
VM: 'notificationsCount(`t.ir0_1`)',
|
VM: 'notificationsCount(`t.ir0_1`)',
|
||||||
KM: 'notificationsCount + 1(`(n.bqx_1 + 1) | 0`)',
|
KM: 'notificationsCount + 1(`(n.bqx_1 + 1) | 0`)',
|
||||||
YM: 'notificationsCount - 1,下限 0',
|
YM: 'notificationsCount - 1,下限 0',
|
||||||
|
XP: 'open=true(`n.cqu(!0)`)',
|
||||||
|
QP: 'open=false(`n.cqu(!1)`)',
|
||||||
|
nN: 'contextMenuData=null(`n.cqu(cd, cd, null)`)',
|
||||||
|
lN: 'new LP() 整片复位',
|
||||||
|
fN: 'isShowKeyboard=true、isNeedToHideKeyboard=false',
|
||||||
|
vN: 'isShowKeyboard=false',
|
||||||
|
pN: 'isNeedToHideKeyboard=true',
|
||||||
|
wN: 'isNeedToHideKeyboard=false',
|
||||||
|
bN: 'isNeedScroll=false',
|
||||||
|
qN: 'isNeedScroll=true',
|
||||||
|
sN: 'isFirstChannelMessagesLoad=false',
|
||||||
|
YP: 'config(原名 Configure,与匹配大厅同名类冲突,改名 SetChatConfig)',
|
||||||
};
|
};
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(P, 'names', 'root_wiring.json'),
|
path.join(P, 'names', 'root_wiring.json'),
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
// ChatStateReducer.kt —— 大厅聊天切片(对应 module_10032 的 LP 状态 + yN.wqv)
|
||||||
|
//
|
||||||
|
// 证据链:
|
||||||
|
// 状态类 LP:16 个字段,toString 名 "ChatState(…)";copy 是 cqu(16 字段 + 回调)。
|
||||||
|
// 根委派:`$r.wqv(t, n.opv_1)`。
|
||||||
|
// reducer(24 个实例臂):
|
||||||
|
// YP(Configure) → config
|
||||||
|
// XP / QP(无 toString) → open = true / false
|
||||||
|
// aN(ChangeChannel) → currentChannel
|
||||||
|
// oN(SaveChatMessage) → chatMessage + selectedUid(优先消息里的目标用户)
|
||||||
|
// uN(SetMessages) → messagesByChannel
|
||||||
|
// iN(CleanUsersMessage) → 从所有频道里删掉该用户的消息
|
||||||
|
// rN(UpdateUsers) → 用户资料批量刷新(依赖消息内的用户对象,离线重建按 no-op)
|
||||||
|
// tN / nN(ShowContextMenu / 无 toString)→ contextMenuData / null
|
||||||
|
// lN(无 toString) → `new LP()` 整片复位
|
||||||
|
// fN / vN(无 toString) → 显示 / 收起键盘
|
||||||
|
// pN / wN(无 toString) → 需要隐藏键盘 / 取消需要
|
||||||
|
// bN / qN(无 toString) → 滚动位 false / true
|
||||||
|
// hN(SendMessageToServer) → 需要隐藏键盘 + 需要滚动(时间戳由中间件补)
|
||||||
|
// dN(UpdateTypingSpeedAntifloodParams)→ symbolCost / enterCost
|
||||||
|
// gN(SetSelectedUid) → selectedUid(转小写)
|
||||||
|
// sN(无 toString) → isFirstChannelMessagesLoad=false
|
||||||
|
// mN / kN(IgnoreUserMessages / ShowUserMessages)→ ignoredUserIds ±
|
||||||
|
// _C(ResetSettingsFromStorage)→ 从存储重载屏蔽名单(离线重建按清空收口)
|
||||||
|
//
|
||||||
|
// 消息模型复用生成物 `ChatMessage`(channel / id / messageType / sourceUser / targetUser / text …)。
|
||||||
|
package App.State.Reducers
|
||||||
|
|
||||||
|
import App.State.ChangeChannel
|
||||||
|
import App.State.ChatMessage
|
||||||
|
import App.State.CleanUsersMessage
|
||||||
|
import App.State.IgnoreUserMessages
|
||||||
|
import App.State.SaveChatMessage
|
||||||
|
import App.State.SendMessageToServer
|
||||||
|
import App.State.SetMessages
|
||||||
|
import App.State.SetSelectedUid
|
||||||
|
import App.State.ShowContextMenu
|
||||||
|
import App.State.ShowUserMessages
|
||||||
|
import App.State.UpdateTypingSpeedAntifloodParams
|
||||||
|
import App.State.UpdateUsers
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ 匿名动作 */
|
||||||
|
|
||||||
|
object OpenChat
|
||||||
|
object CloseChat
|
||||||
|
object ClearChatContextMenu
|
||||||
|
object ResetChat
|
||||||
|
object ShowChatKeyboard
|
||||||
|
object HideChatKeyboard
|
||||||
|
object RequireHideChatKeyboard
|
||||||
|
object CancelHideChatKeyboard
|
||||||
|
object DisableChatScroll
|
||||||
|
object EnableChatScroll
|
||||||
|
object MarkFirstChannelMessagesLoaded
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ 状态 */
|
||||||
|
|
||||||
|
data class ChatStateSlice(
|
||||||
|
val Open: Boolean = false,
|
||||||
|
val CurrentChannel: Any? = null,
|
||||||
|
val ContextMenuData: Any? = null,
|
||||||
|
val ChatMessage: Any? = null,
|
||||||
|
val SelectedUid: String = "",
|
||||||
|
val Config: Any? = null,
|
||||||
|
val MessagesByChannel: Map<Any?, Any?> = emptyMap(),
|
||||||
|
val IsShowKeyboard: Boolean = false,
|
||||||
|
val IsNeedToHideKeyboard: Boolean = false,
|
||||||
|
val IsNeedScroll: Boolean = false,
|
||||||
|
val HaveNewDirectMessages: Boolean = false,
|
||||||
|
val SymbolCost: Any? = 0,
|
||||||
|
val EnterCost: Any? = 0,
|
||||||
|
val LastMessageTime: Any? = null,
|
||||||
|
val IsFirstChannelMessagesLoad: Boolean = true,
|
||||||
|
val IgnoredUserIds: Set<Any?> = emptySet(),
|
||||||
|
)
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ reducer */
|
||||||
|
|
||||||
|
/** 原 `YP`(原名 `Configure`,与匹配大厅的同名类冲突):写聊天配置。 */
|
||||||
|
data class SetChatConfig(val config: Any?)
|
||||||
|
|
||||||
|
/** 原 `iN`:按 uid 从每个频道里过滤消息(sourceUser / targetUser 命中即删)。 */
|
||||||
|
private fun CleanByUid(
|
||||||
|
Channels: Map<Any?, Any?>,
|
||||||
|
Uid: Any?,
|
||||||
|
): Map<Any?, Any?> = Channels.mapValues { (_, Messages) ->
|
||||||
|
(Messages as? List<Any?>)?.filterNot {
|
||||||
|
val Message = it as? ChatMessage
|
||||||
|
Message != null && (Message.sourceUser == Uid || Message.targetUser == Uid)
|
||||||
|
} ?: Messages
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ReduceChat(
|
||||||
|
State0: ChatStateSlice,
|
||||||
|
Action: Any,
|
||||||
|
): ChatStateSlice = when (Action) {
|
||||||
|
is SetChatConfig -> State0.copy(Config = Action.config)
|
||||||
|
is OpenChat -> State0.copy(Open = true)
|
||||||
|
is CloseChat -> State0.copy(Open = false)
|
||||||
|
is ChangeChannel -> State0.copy(CurrentChannel = Action.channel)
|
||||||
|
is SaveChatMessage -> {
|
||||||
|
val Message = Action.message as? ChatMessage
|
||||||
|
State0.copy(
|
||||||
|
ChatMessage = Action.message,
|
||||||
|
SelectedUid = Message?.targetUser ?: State0.SelectedUid,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is SetMessages -> State0.copy(
|
||||||
|
MessagesByChannel = (Action.messages as? Map<Any?, Any?>) ?: emptyMap(),
|
||||||
|
)
|
||||||
|
is CleanUsersMessage -> State0.copy(
|
||||||
|
MessagesByChannel = CleanByUid(State0.MessagesByChannel, Action.uid),
|
||||||
|
)
|
||||||
|
is UpdateUsers -> State0
|
||||||
|
is ShowContextMenu -> State0.copy(ContextMenuData = Action.contextMenuData)
|
||||||
|
is ClearChatContextMenu -> State0.copy(ContextMenuData = null)
|
||||||
|
is ResetChat -> ChatStateSlice()
|
||||||
|
is ShowChatKeyboard -> State0.copy(IsShowKeyboard = true, IsNeedToHideKeyboard = false)
|
||||||
|
is HideChatKeyboard -> State0.copy(IsShowKeyboard = false)
|
||||||
|
is RequireHideChatKeyboard -> State0.copy(IsNeedToHideKeyboard = true)
|
||||||
|
is CancelHideChatKeyboard -> State0.copy(IsNeedToHideKeyboard = false)
|
||||||
|
is DisableChatScroll -> State0.copy(IsNeedScroll = false)
|
||||||
|
is EnableChatScroll -> State0.copy(IsNeedScroll = true)
|
||||||
|
// 原分支把 lastMessageTime 写成「当前时间」;纯 reducer 没有时钟,时间戳留给中间件。
|
||||||
|
is SendMessageToServer -> State0.copy(
|
||||||
|
IsNeedToHideKeyboard = true,
|
||||||
|
IsNeedScroll = true,
|
||||||
|
)
|
||||||
|
is UpdateTypingSpeedAntifloodParams -> State0.copy(
|
||||||
|
SymbolCost = Action.symbolCost,
|
||||||
|
EnterCost = Action.enterCost,
|
||||||
|
)
|
||||||
|
is SetSelectedUid -> State0.copy(SelectedUid = Action.selectedUid.toString().lowercase())
|
||||||
|
is MarkFirstChannelMessagesLoaded -> State0.copy(IsFirstChannelMessagesLoad = false)
|
||||||
|
is IgnoreUserMessages -> State0.copy(IgnoredUserIds = State0.IgnoredUserIds + Action.userId)
|
||||||
|
is ShowUserMessages -> State0.copy(IgnoredUserIds = State0.IgnoredUserIds - Action.userId)
|
||||||
|
is ResetSettingsFromStorage -> State0.copy(IgnoredUserIds = emptySet())
|
||||||
|
else -> State0
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// RootReducer.kt —— 根 reducer 骨架(对应 module_10032 的根状态 z6 / TOState 与根 reducer k6)
|
// RootReducer.kt —— 根 reducer 骨架(对应 module_10032 的根状态 z6 / TOState 与根 reducer k6)
|
||||||
//
|
//
|
||||||
// 原包 k6 对 89 个切片逐个调用各自的 reducer,再 `return n.cti(...)`(copy 出新状态)。
|
// 原包 k6 对 89 个切片逐个调用各自的 reducer,再 `return n.cti(...)`(copy 出新状态)。
|
||||||
// 这里采用同一形状。已迁移的 85 个切片里,84 个真正调用 reducer,launchParams 只做类型收口:
|
// 这里采用同一形状。已迁移的 86 个切片里,85 个真正调用 reducer,launchParams 只做类型收口:
|
||||||
//
|
//
|
||||||
// inviteState → ReduceInvite(aD / pD.er5)
|
// inviteState → ReduceInvite(aD / pD.er5)
|
||||||
// loginByPassword → ReduceLoginByPassword(gD / MD.cr6)
|
// loginByPassword → ReduceLoginByPassword(gD / MD.cr6)
|
||||||
@@ -87,9 +87,10 @@
|
|||||||
// challenges → ReduceChallenges(CZ / nh.hsj,根委派 `nh.hsj(t, n.fpv_1)`)
|
// challenges → ReduceChallenges(CZ / nh.hsj,根委派 `nh.hsj(t, n.fpv_1)`)
|
||||||
// clansStorage → ReduceClanStorage(lO / me.vr1,根委派 `me.vr1(t, n.rpv_1)`)
|
// clansStorage → ReduceClanStorage(lO / me.vr1,根委派 `me.vr1(t, n.rpv_1)`)
|
||||||
// clan → ReduceClan(jN / we.hr0,根委派 `we.hr0(t, n.qpv_1)`)
|
// clan → ReduceClan(jN / we.hr0,根委派 `we.hr0(t, n.qpv_1)`)
|
||||||
|
// chatState → ReduceChat(LP / $r.wqv,根委派 `$r.wqv(t, n.opv_1)`)
|
||||||
// launchParams → 无 reducer(原根 reducer 原样透传;默认 `ag()` = emptyMap())
|
// launchParams → 无 reducer(原根 reducer 原样透传;默认 `ag()` = emptyMap())
|
||||||
//
|
//
|
||||||
// 其余 4 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
// 其余 3 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||||
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
|
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
|
||||||
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
|
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
|
||||||
package App.State
|
package App.State
|
||||||
@@ -110,6 +111,7 @@ import App.State.Reducers.ReduceBattleUsers
|
|||||||
import App.State.Reducers.ReduceBattleView
|
import App.State.Reducers.ReduceBattleView
|
||||||
import App.State.Reducers.ReduceBattleInvite
|
import App.State.Reducers.ReduceBattleInvite
|
||||||
import App.State.Reducers.ReduceCaptcha
|
import App.State.Reducers.ReduceCaptcha
|
||||||
|
import App.State.Reducers.ReduceChat
|
||||||
import App.State.Reducers.ReduceChallenges
|
import App.State.Reducers.ReduceChallenges
|
||||||
import App.State.Reducers.ReduceClan
|
import App.State.Reducers.ReduceClan
|
||||||
import App.State.Reducers.ReduceClanStorage
|
import App.State.Reducers.ReduceClanStorage
|
||||||
@@ -276,6 +278,7 @@ object RootReducer {
|
|||||||
Challenges = ReduceChallenges(State0.Challenges, Action),
|
Challenges = ReduceChallenges(State0.Challenges, Action),
|
||||||
ClansStorage = ReduceClanStorage(State0.ClansStorage, Action),
|
ClansStorage = ReduceClanStorage(State0.ClansStorage, Action),
|
||||||
Clan = ReduceClan(State0.Clan, Action),
|
Clan = ReduceClan(State0.Clan, Action),
|
||||||
|
ChatState = ReduceChat(State0.ChatState, Action),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import App.State.Reducers.BattleViewStateSlice
|
|||||||
import App.State.Reducers.BattlesSelectStateSlice
|
import App.State.Reducers.BattlesSelectStateSlice
|
||||||
import App.State.Reducers.CaptchaStateSlice
|
import App.State.Reducers.CaptchaStateSlice
|
||||||
import App.State.Reducers.ChallengesStateSlice
|
import App.State.Reducers.ChallengesStateSlice
|
||||||
|
import App.State.Reducers.ChatStateSlice
|
||||||
import App.State.Reducers.ClanStateSlice
|
import App.State.Reducers.ClanStateSlice
|
||||||
import App.State.Reducers.ClanChatStateSlice
|
import App.State.Reducers.ClanChatStateSlice
|
||||||
import App.State.Reducers.ClanUserInfoStateSlice
|
import App.State.Reducers.ClanUserInfoStateSlice
|
||||||
@@ -125,8 +126,8 @@ data class RootState(
|
|||||||
val System: SystemStateSlice = SystemStateSlice(),
|
val System: SystemStateSlice = SystemStateSlice(),
|
||||||
/** 原 quests(Frt / Yrt.otg)。已迁移。 */
|
/** 原 quests(Frt / Yrt.otg)。已迁移。 */
|
||||||
val Quests: QuestsStateSlice = QuestsStateSlice(),
|
val Quests: QuestsStateSlice = QuestsStateSlice(),
|
||||||
/** 原 chatState(LP / $r.wqv)。未迁移:原样透传。 */
|
/** 原 chatState(LP / $r.wqv)。已迁移。 */
|
||||||
val ChatState: Any? = null,
|
val ChatState: ChatStateSlice = ChatStateSlice(),
|
||||||
/** 原 clanChatState(qnt / yv.qtg)。已迁移。 */
|
/** 原 clanChatState(qnt / yv.qtg)。已迁移。 */
|
||||||
val ClanChatState: ClanChatStateSlice = ClanChatStateSlice(),
|
val ClanChatState: ClanChatStateSlice = ClanChatStateSlice(),
|
||||||
/** 原 clan(jN / we.hr0)。已迁移。 */
|
/** 原 clan(jN / we.hr0)。已迁移。 */
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ object RootWiring {
|
|||||||
SliceWiring("passwordChangeState", "lpv_1", "VD", "uu.pr6", "uu.pr6(t, n.lpv_1)", listOf("SetPasswordChangeHash", "ShowEmailAndPasswordChangeDialog")),
|
SliceWiring("passwordChangeState", "lpv_1", "VD", "uu.pr6", "uu.pr6(t, n.lpv_1)", listOf("SetPasswordChangeHash", "ShowEmailAndPasswordChangeDialog")),
|
||||||
SliceWiring("system", "mpv_1", "_9", "w9().xt9", "w9().xt9(t, n.mpv_1)", listOf("DoShowMessage", "SetFatalError")),
|
SliceWiring("system", "mpv_1", "_9", "w9().xt9", "w9().xt9(t, n.mpv_1)", listOf("DoShowMessage", "SetFatalError")),
|
||||||
SliceWiring("quests", "npv_1", "Frt", "Yrt.otg", "(null == zp && new Yrt(), zp).otg(t, n.npv_1)", listOf("GoTo", "SetCurrentCategory", "SetNotCompletedQuests")),
|
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("chatState", "opv_1", "LP", "\$r.wqv", "\$r.wqv(t, n.opv_1)", listOf("CancelHideChatKeyboard", "ChangeChannel", "CleanUsersMessage", "ClearChatContextMenu", "CloseChat", "DisableChatScroll", "EnableChatScroll", "HideChatKeyboard", "IgnoreUserMessages", "MarkFirstChannelMessagesLoaded", "OpenChat", "RequireHideChatKeyboard", "ResetChat", "ResetSettingsFromStorage", "SaveChatMessage", "SendMessageToServer", "SetChatConfig", "SetMessages", "SetSelectedUid", "ShowChatKeyboard", "ShowContextMenu", "ShowUserMessages", "UpdateTypingSpeedAntifloodParams", "UpdateUsers")),
|
||||||
SliceWiring("clanChatState", "ppv_1", "qnt", "yv.qtg", "yv.qtg(t, n.ppv_1)", listOf("CloseClanChat", "Configure", "DoAddMessages", "OpenClanChat", "ResetClanChat")),
|
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("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("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("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")),
|
||||||
@@ -106,7 +106,7 @@ object RootWiring {
|
|||||||
)
|
)
|
||||||
|
|
||||||
/** 已迁移(强类型;launchParams 为透传,不调用 reducer)的字段。 */
|
/** 已迁移(强类型;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", "clan")
|
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", "chatState")
|
||||||
|
|
||||||
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
|
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
|
||||||
val Unmigrated: List<SliceWiring> get() = All.filter { it.Field !in Migrated }
|
val Unmigrated: List<SliceWiring> get() = All.filter { it.Field !in Migrated }
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
// System / ContextTutorial / BattleTutorial / Spectator / TwoStepVerification / AntiAddiction /
|
// System / ContextTutorial / BattleTutorial / Spectator / TwoStepVerification / AntiAddiction /
|
||||||
// Moderation / MigrationState / Register / UserReports /
|
// Moderation / MigrationState / Register / UserReports /
|
||||||
// Friends / Supplies / ClanUserInfo / BattleInvite / Partner / UserRename /
|
// 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 / Clan;
|
// 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 / ChatState;
|
||||||
// ㉑ launchParams 是纯透传切片(无 reducer/动作),只做 Map 类型收口;⑭ 根 reducer 的 89 槽位委派
|
// ㉑ launchParams 是纯透传切片(无 reducer/动作),只做 Map 类型收口;⑭ 根 reducer 的 89 槽位委派
|
||||||
//
|
//
|
||||||
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
|
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
|
||||||
@@ -879,6 +879,31 @@ import App.State.Reducers.SetClanNotificationsCount
|
|||||||
import App.State.Reducers.UpdateIntermediateDescription
|
import App.State.Reducers.UpdateIntermediateDescription
|
||||||
import App.State.Reducers.UpdateIntermediateMinRank
|
import App.State.Reducers.UpdateIntermediateMinRank
|
||||||
import App.State.Reducers.UpdateIntermediateRequests
|
import App.State.Reducers.UpdateIntermediateRequests
|
||||||
|
import App.State.ChangeChannel
|
||||||
|
import App.State.CleanUsersMessage
|
||||||
|
import App.State.IgnoreUserMessages
|
||||||
|
import App.State.SaveChatMessage
|
||||||
|
import App.State.SendMessageToServer
|
||||||
|
import App.State.SetMessages
|
||||||
|
import App.State.SetSelectedUid
|
||||||
|
import App.State.ShowContextMenu
|
||||||
|
import App.State.ShowUserMessages
|
||||||
|
import App.State.UpdateTypingSpeedAntifloodParams
|
||||||
|
import App.State.UpdateUsers
|
||||||
|
import App.State.Reducers.CancelHideChatKeyboard
|
||||||
|
import App.State.Reducers.ChatStateSlice
|
||||||
|
import App.State.Reducers.ClearChatContextMenu
|
||||||
|
import App.State.Reducers.CloseChat
|
||||||
|
import App.State.Reducers.DisableChatScroll
|
||||||
|
import App.State.Reducers.EnableChatScroll
|
||||||
|
import App.State.Reducers.HideChatKeyboard
|
||||||
|
import App.State.Reducers.MarkFirstChannelMessagesLoaded
|
||||||
|
import App.State.Reducers.OpenChat
|
||||||
|
import App.State.Reducers.ReduceChat
|
||||||
|
import App.State.Reducers.RequireHideChatKeyboard
|
||||||
|
import App.State.Reducers.ResetChat
|
||||||
|
import App.State.Reducers.SetChatConfig
|
||||||
|
import App.State.Reducers.ShowChatKeyboard
|
||||||
import App.State.Reducers.UserRankBounds
|
import App.State.Reducers.UserRankBounds
|
||||||
import App.State.Reducers.UserStateSlice
|
import App.State.Reducers.UserStateSlice
|
||||||
import App.State.Reducers.ClearHullData
|
import App.State.Reducers.ClearHullData
|
||||||
@@ -1306,7 +1331,7 @@ object StateCheck {
|
|||||||
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
|
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⑭ 根 reducer:89 个槽位的委派表 + 85 个已迁移切片(84 归约 + launchParams 透传)
|
// ⑭ 根 reducer:89 个槽位的委派表 + 86 个已迁移切片(85 归约 + launchParams 透传)
|
||||||
run {
|
run {
|
||||||
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
|
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
|
||||||
Check(
|
Check(
|
||||||
@@ -1370,7 +1395,7 @@ object StateCheck {
|
|||||||
LocalBattleW.Reducer == "(inline@localBattleUserState)"
|
LocalBattleW.Reducer == "(inline@localBattleUserState)"
|
||||||
)
|
)
|
||||||
Check(
|
Check(
|
||||||
"根:迁移表 = 85 个已迁移字段,其余 4 个待迁",
|
"根:迁移表 = 86 个已迁移字段,其余 3 个待迁",
|
||||||
RootReducer.Migrated ==
|
RootReducer.Migrated ==
|
||||||
setOf(
|
setOf(
|
||||||
"inviteState", "loginByPassword", "twitchState", "home",
|
"inviteState", "loginByPassword", "twitchState", "home",
|
||||||
@@ -1394,9 +1419,9 @@ object StateCheck {
|
|||||||
"soundSettings", "gameSettings", "keyboardSettings", "touchscreenHudSettings",
|
"soundSettings", "gameSettings", "keyboardSettings", "touchscreenHudSettings",
|
||||||
"graphicsSettings", "controlSettings", "battleSettings", "contractState",
|
"graphicsSettings", "controlSettings", "battleSettings", "contractState",
|
||||||
"tankBuilderState", "battleMessage", "maps", "battleChat",
|
"tankBuilderState", "battleMessage", "maps", "battleChat",
|
||||||
"remoteUserDataStorage", "matchmaking", "battlesSelect", "battleUsers", "challenges", "clansStorage", "clan",
|
"remoteUserDataStorage", "matchmaking", "battlesSelect", "battleUsers", "challenges", "clansStorage", "clan", "chatState",
|
||||||
) &&
|
) &&
|
||||||
RootWiring.Unmigrated.size == 4
|
RootWiring.Unmigrated.size == 3
|
||||||
)
|
)
|
||||||
|
|
||||||
// 一次 ReduceRoot 里多个切片各归约自己的动作,互不串片
|
// 一次 ReduceRoot 里多个切片各归约自己的动作,互不串片
|
||||||
@@ -1452,7 +1477,7 @@ object StateCheck {
|
|||||||
S5.User.Uid == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
|
S5.User.Uid == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
|
||||||
)
|
)
|
||||||
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
|
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
|
||||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 85/89")
|
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 86/89")
|
||||||
|
|
||||||
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
|
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
|
||||||
Store.Dispatch(SetStatus("LOGGING_IN"))
|
Store.Dispatch(SetStatus("LOGGING_IN"))
|
||||||
@@ -7228,6 +7253,99 @@ object StateCheck {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 96 切片 reducer:ChatState(LP / $r.wqv,16 字段;24 个动作)
|
||||||
|
run {
|
||||||
|
fun Msg(Id: Int, Source: String, Target: String? = null): ChatMessage = ChatMessage(
|
||||||
|
addressMode = "", battleLinks = emptyList(), channel = "general", id = Id,
|
||||||
|
links = emptyList(), messageType = "text", sourceUser = Source, ip = "",
|
||||||
|
uid = null, userId = "1", targetUser = Target, text = "m", timePassedInSec = 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
val S0 = ChatStateSlice()
|
||||||
|
Check(
|
||||||
|
"切片 ChatState:默认值完整",
|
||||||
|
!S0.Open && S0.CurrentChannel == null && S0.ChatMessage == null &&
|
||||||
|
S0.SelectedUid == "" && S0.MessagesByChannel.isEmpty() &&
|
||||||
|
!S0.IsShowKeyboard && !S0.IsNeedToHideKeyboard && !S0.IsNeedScroll &&
|
||||||
|
S0.IsFirstChannelMessagesLoad && S0.IgnoredUserIds.isEmpty()
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 ChatState:开关 / 频道 / 配置",
|
||||||
|
ReduceChat(S0, OpenChat).Open && !ReduceChat(S0, CloseChat).Open &&
|
||||||
|
ReduceChat(S0, ChangeChannel("general")).CurrentChannel == "general" &&
|
||||||
|
ReduceChat(S0, SetChatConfig(null)).Config == null
|
||||||
|
)
|
||||||
|
val S1 = ReduceChat(S0, SaveChatMessage(Msg(1, "u1", "u2")))
|
||||||
|
Check(
|
||||||
|
"切片 ChatState:SaveChatMessage 写消息并选中目标用户",
|
||||||
|
(S1.ChatMessage as ChatMessage).id == 1 && S1.SelectedUid == "u2"
|
||||||
|
)
|
||||||
|
val S2 = ReduceChat(S0, SetMessages(mapOf("general" to listOf(Msg(1, "u1")))))
|
||||||
|
Check(
|
||||||
|
"切片 ChatState:SetMessages 整体替换频道表",
|
||||||
|
(S2.MessagesByChannel["general"] as List<Any?>).size == 1
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 ChatState:CleanUsersMessage 按 sourceUser 过滤",
|
||||||
|
(ReduceChat(S2, CleanUsersMessage("u1"))
|
||||||
|
.MessagesByChannel["general"] as List<Any?>).isEmpty()
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 ChatState:右键菜单设置 / 清除",
|
||||||
|
ReduceChat(S0, ShowContextMenu("ctx")).ContextMenuData == "ctx" &&
|
||||||
|
ReduceChat(S0, ClearChatContextMenu).ContextMenuData == null
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 ChatState:键盘 / 滚动开关",
|
||||||
|
ReduceChat(S0, ShowChatKeyboard).let { it.IsShowKeyboard && !it.IsNeedToHideKeyboard } &&
|
||||||
|
!ReduceChat(S0, HideChatKeyboard).IsShowKeyboard &&
|
||||||
|
ReduceChat(S0, RequireHideChatKeyboard).IsNeedToHideKeyboard &&
|
||||||
|
!ReduceChat(S0, CancelHideChatKeyboard).IsNeedToHideKeyboard &&
|
||||||
|
ReduceChat(S0, EnableChatScroll).IsNeedScroll &&
|
||||||
|
!ReduceChat(S0, DisableChatScroll).IsNeedScroll
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 ChatState:SendMessageToServer 置两个待处理位",
|
||||||
|
ReduceChat(S0, SendMessageToServer("t", "mode", "m")).let {
|
||||||
|
it.IsNeedToHideKeyboard && it.IsNeedScroll
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 ChatState:速率参数 / 选中 uid 转小写 / 首屏标记",
|
||||||
|
ReduceChat(S0, UpdateTypingSpeedAntifloodParams(1, 2)).let {
|
||||||
|
it.SymbolCost == 1 && it.EnterCost == 2
|
||||||
|
} &&
|
||||||
|
ReduceChat(S0, SetSelectedUid("ABC")).SelectedUid == "abc" &&
|
||||||
|
!ReduceChat(S0, MarkFirstChannelMessagesLoaded).IsFirstChannelMessagesLoad
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 ChatState:屏蔽 / 取消屏蔽 / 重载清空",
|
||||||
|
ReduceChat(S0, IgnoreUserMessages("u1")).IgnoredUserIds == setOf<Any?>("u1") &&
|
||||||
|
ReduceChat(ReduceChat(S0, IgnoreUserMessages("u1")), ShowUserMessages("u1"))
|
||||||
|
.IgnoredUserIds.isEmpty() &&
|
||||||
|
ReduceChat(ReduceChat(S0, IgnoreUserMessages("u1")), ResetSettingsFromStorage)
|
||||||
|
.IgnoredUserIds.isEmpty()
|
||||||
|
)
|
||||||
|
Check(
|
||||||
|
"切片 ChatState:UpdateUsers 是 no-op、ResetChat 复位",
|
||||||
|
ReduceChat(S2, UpdateUsers(listOf("u1"))) == S2 &&
|
||||||
|
ReduceChat(S2, ResetChat) == ChatStateSlice()
|
||||||
|
)
|
||||||
|
Check("切片 ChatState:无关动作不改状态", ReduceChat(S2, AddFrags(1)) == S2)
|
||||||
|
|
||||||
|
val Root0 = RootState()
|
||||||
|
val Root1 = ReduceRoot(Root0, OpenChat)
|
||||||
|
Check(
|
||||||
|
"根:OpenChat 只动 chatState",
|
||||||
|
Root1.ChatState.Open && Root1.Clan == Root0.Clan
|
||||||
|
)
|
||||||
|
val W = RootWiring.ByField("chatState")!!
|
||||||
|
Check(
|
||||||
|
"根:chatState 委派到 LP / \$r.wqv 且 24 个动作",
|
||||||
|
W.SliceClass == "LP" && W.Reducer == "\$r.wqv" && W.Actions.size == 24
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val Failed = Results.filter { !it.second }
|
val Failed = Results.filter { !it.second }
|
||||||
println("TankiOffline 状态层自检:${Results.size} 项")
|
println("TankiOffline 状态层自检:${Results.size} 项")
|
||||||
for ((Name, Ok) in Results) println((if (Ok) " ✓ " else " ✗ ") + Name)
|
for ((Name, Ok) in Results) println((if (Ok) " ✓ " else " ✗ ") + Name)
|
||||||
|
|||||||
Reference in new issue
Block a user