feat(state): 迁移屏幕导航切片(13 个动作 + ScreenId)
This commit is contained in:
1 parent
b7afe07894
commit
974a9f49ad
12 files changed
+446
-36
No files matched your search
@@ -422,7 +422,7 @@ object ActionCatalog {
|
||||
ActionMeta("ReferralEventEnterPromoCodeStatus", ReferralEventEnterPromoCodeStatus::class, listOf(ActionFieldMeta("referralEventEnterCodeStatus", "Any?"))),
|
||||
ActionMeta("TemporaryCashExpiredInfo", TemporaryCashExpiredInfo::class, listOf(ActionFieldMeta("temporaryCashData", "Any?"))),
|
||||
ActionMeta("Screen", Screen::class, listOf(ActionFieldMeta("current", "Any?"), ActionFieldMeta("loaderBackgroundImage", "Any?"), ActionFieldMeta("foreground", "Any?"), ActionFieldMeta("screenStack", "Any?"), ActionFieldMeta("enableBack", "Any?"), ActionFieldMeta("isResetBackStackOnNextGoTo", "Any?"), ActionFieldMeta("isDelayedGoto", "Any?"), ActionFieldMeta("pressedAnyButton", "Any?"), ActionFieldMeta("displayMainEntranceScreen", "Any?"), ActionFieldMeta("needReauthorization", "Any?"))),
|
||||
ActionMeta("GoTo", GoTo::class, listOf(ActionFieldMeta("screen", "Any?"), ActionFieldMeta("history", "Boolean"))),
|
||||
ActionMeta("GoTo", GoTo::class, listOf(ActionFieldMeta("screen", "ScreenId"), ActionFieldMeta("history", "Boolean"))),
|
||||
ActionMeta("DisplayMainEntranceScreen", DisplayMainEntranceScreen::class, listOf(ActionFieldMeta("isDisplayed", "Boolean"))),
|
||||
ActionMeta("PressedAnyButton", PressedAnyButton::class, listOf(ActionFieldMeta("isPressed", "Boolean"))),
|
||||
ActionMeta("DisplayReauthorizationMessage", DisplayReauthorizationMessage::class, listOf(ActionFieldMeta("isDisplay", "Boolean"))),
|
||||
|
||||
@@ -5,7 +5,7 @@ package App.State
|
||||
|
||||
import Core.Vector3
|
||||
|
||||
// 动作/状态类共 816 个,字段 2150 个(已定类型 508 个)
|
||||
// 动作/状态类共 816 个,字段 2150 个(已定类型 509 个)
|
||||
data class AnnouncementState(
|
||||
val isEnabled: Any?, // 仅 copy 转发(无类型信息)
|
||||
val modelLoaded: Any?, // 仅 copy 转发(无类型信息)
|
||||
@@ -2408,7 +2408,7 @@ data class Screen(
|
||||
)
|
||||
|
||||
data class GoTo(
|
||||
val screen: Any?, // 未收录类型: Any?(runtime)
|
||||
val screen: ScreenId, // override
|
||||
val history: Boolean, // runtime
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
// ScreenReducer.kt —— 屏幕导航切片(对应 module_10032 的 WK 状态 + wY.ks5 reducer)
|
||||
//
|
||||
// 证据链:
|
||||
// 状态类 WK:`function WK(t, n, i, r, e, u, s, o, _, h)`,10 个字段,toString 名为 `Screen(...)`。
|
||||
// 默认值取自构造:current = PX()(LoaderScreen)、loaderBackgroundImage = null、
|
||||
// foreground = false、screenStack = md()(emptyList)、enableBack = true(`e === cd || e`)、
|
||||
// isResetBackStackOnNextGoTo / isDelayedGoto / pressedAnyButton / displayMainEntranceScreen /
|
||||
// needReauthorization 均 false。
|
||||
// reducer:`ld(wY).ks5`(module_10032 第 34950 行)13 个 instanceof 臂。
|
||||
//
|
||||
// sC → foreground = false oC → foreground = true
|
||||
// eY → screenStack = [] uY → isResetBackStackOnNextGoTo = true
|
||||
// sY → isDelayedGoto = true lY → enableBack = true
|
||||
// cY → enableBack = false fY → screenStack = dropLast(1)(原 `f$(stack, 1)`)
|
||||
// oY → screenStack = stack.filter(predicate)(原 `e(o) && u.y(o)`,kd() 是 ArrayList)
|
||||
// QK → GoTo(见下) hY → pressedAnyButton = action.isPressed
|
||||
// _Y → displayMainEntranceScreen = action.isDisplayed
|
||||
// aY → needReauthorization = action.isDisplay
|
||||
//
|
||||
// GoTo 分支(原 IIFE,`ks5` 内调用参数为 (0, t, n)):
|
||||
// 新栈 = isResetBackStackOnNextGoTo ? [] : history ? stack + current : stack
|
||||
// current = 目标;enableBack 不变;isResetBackStackOnNextGoTo / isDelayedGoto 归 false。
|
||||
// 原代码还会用 `rk().boz(u)` 发一条 screen_show analytics(目标等于当前时不发,
|
||||
// Entrance 带 isModal=true,System.Loader 不发);这是副作用,纯 reducer 只保留状态变化。
|
||||
//
|
||||
// 无 toString 的 9 个类按 reducer 效果定名,原名写在注释里;名字同时登记在
|
||||
// 3DTank/tools/gen_root_wiring.js 的 AnonymousActionNames,重新生成 RootWiring 后显示。
|
||||
package App.State.Reducers
|
||||
|
||||
import App.State.DisplayMainEntranceScreen
|
||||
import App.State.DisplayReauthorizationMessage
|
||||
import App.State.GoTo
|
||||
import App.State.LoaderScreen
|
||||
import App.State.PressedAnyButton
|
||||
import App.State.ScreenId
|
||||
|
||||
/** 原 `sC`:无字段;`foreground = false`。 */
|
||||
object DisableForeground
|
||||
|
||||
/** 原 `oC`:无字段;`foreground = true`。 */
|
||||
object EnableForeground
|
||||
|
||||
/** 原 `eY`:无字段;`screenStack = emptyList()`。 */
|
||||
object ClearScreenStack
|
||||
|
||||
/** 原 `uY`:无字段;`isResetBackStackOnNextGoTo = true`。 */
|
||||
object ResetScreenStackOnNextGoTo
|
||||
|
||||
/** 原 `sY`:无字段;`isDelayedGoto = true`。 */
|
||||
object DelayNextGoTo
|
||||
|
||||
/**
|
||||
* 原 `oY(predicate)`:只保留 predicate 为 true 的屏幕(`for (…) e(o) && u.y(o)`)。
|
||||
* 默认谓词是原 `YK`(恒 false),所以缺省调用会清空历史栈。
|
||||
*/
|
||||
data class FilterScreenStack(val predicate: (ScreenId) -> Boolean = { false })
|
||||
|
||||
/** 原 `lY`:无字段;`enableBack = true`。 */
|
||||
object EnableBack
|
||||
|
||||
/** 原 `cY`:无字段;`enableBack = false`。 */
|
||||
object DisableBack
|
||||
|
||||
/** 原 `fY`:无字段;`screenStack = dropLast(1)`。 */
|
||||
object PopScreen
|
||||
|
||||
/**
|
||||
* 原 `WK` / `Screen`:`Screen(current, loaderBackgroundImage, foreground, screenStack, enableBack,
|
||||
* isResetBackStackOnNextGoTo, isDelayedGoto, pressedAnyButton, displayMainEntranceScreen, needReauthorization)`。
|
||||
*/
|
||||
data class ScreenStateSlice(
|
||||
val Current: ScreenId = LoaderScreen,
|
||||
/** 本 reducer 只 copy 转发、从不写;源码没有给出元素类型,保留 Any?。 */
|
||||
val LoaderBackgroundImage: Any? = null,
|
||||
val Foreground: Boolean = false,
|
||||
val ScreenStack: List<ScreenId> = emptyList(),
|
||||
val EnableBack: Boolean = true,
|
||||
val IsResetBackStackOnNextGoTo: Boolean = false,
|
||||
val IsDelayedGoto: Boolean = false,
|
||||
val PressedAnyButton: Boolean = false,
|
||||
val DisplayMainEntranceScreen: Boolean = false,
|
||||
val NeedReauthorization: Boolean = false,
|
||||
)
|
||||
|
||||
/** 根 reducer 的 screen 分支(原 `dY().ks5(t, n.zpu_1)`)。 */
|
||||
fun ReduceScreen(State0: ScreenStateSlice, A: Any): ScreenStateSlice = when (A) {
|
||||
is DisableForeground -> State0.copy(Foreground = false)
|
||||
is EnableForeground -> State0.copy(Foreground = true)
|
||||
is ClearScreenStack -> State0.copy(ScreenStack = emptyList())
|
||||
is ResetScreenStackOnNextGoTo -> State0.copy(IsResetBackStackOnNextGoTo = true)
|
||||
is DelayNextGoTo -> State0.copy(IsDelayedGoto = true)
|
||||
|
||||
is GoTo -> State0.copy(
|
||||
Current = A.screen,
|
||||
ScreenStack = when {
|
||||
State0.IsResetBackStackOnNextGoTo -> emptyList()
|
||||
A.history -> State0.ScreenStack + State0.Current
|
||||
else -> State0.ScreenStack
|
||||
},
|
||||
IsResetBackStackOnNextGoTo = false,
|
||||
IsDelayedGoto = false,
|
||||
)
|
||||
|
||||
is FilterScreenStack -> State0.copy(ScreenStack = State0.ScreenStack.filter(A.predicate))
|
||||
is EnableBack -> State0.copy(EnableBack = true)
|
||||
is DisableBack -> State0.copy(EnableBack = false)
|
||||
is PopScreen -> State0.copy(ScreenStack = State0.ScreenStack.dropLast(1))
|
||||
is PressedAnyButton -> State0.copy(PressedAnyButton = A.isPressed)
|
||||
is DisplayMainEntranceScreen -> State0.copy(DisplayMainEntranceScreen = A.isDisplayed)
|
||||
is DisplayReauthorizationMessage -> State0.copy(NeedReauthorization = A.isDisplay)
|
||||
else -> State0
|
||||
}
|
||||
@@ -9,8 +9,9 @@
|
||||
// home → ReduceHome(s1 / 根 reducer 的 `(inline@home)` 分支)
|
||||
// localBattleUserState → ReduceLocalBattleUser(lE / `(inline@localBattleUserState)` 分支)
|
||||
// shop → ReduceShop(j4 / `(inline@shop)` 分支)
|
||||
// screen → ReduceScreen(WK / dY().ks5)
|
||||
//
|
||||
// 其余 83 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||
// 其余 82 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
|
||||
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
|
||||
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
|
||||
package App.State
|
||||
@@ -19,6 +20,7 @@ import App.State.Reducers.ReduceHome
|
||||
import App.State.Reducers.ReduceInvite
|
||||
import App.State.Reducers.ReduceLocalBattleUser
|
||||
import App.State.Reducers.ReduceLoginByPassword
|
||||
import App.State.Reducers.ReduceScreen
|
||||
import App.State.Reducers.ReduceShop
|
||||
import App.State.Reducers.ReduceTwitch
|
||||
|
||||
@@ -41,6 +43,7 @@ object RootReducer {
|
||||
Home = ReduceHome(State0.Home, Action),
|
||||
LocalBattleUserState = ReduceLocalBattleUser(State0.LocalBattleUserState, Action),
|
||||
Shop = ReduceShop(State0.Shop, Action),
|
||||
Screen = ReduceScreen(State0.Screen, Action),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import App.State.Reducers.HomeStateSlice
|
||||
import App.State.Reducers.InviteStateSlice
|
||||
import App.State.Reducers.LocalBattleUserStateSlice
|
||||
import App.State.Reducers.LoginByPasswordStateSlice
|
||||
import App.State.Reducers.ScreenStateSlice
|
||||
import App.State.Reducers.ShopStateSlice
|
||||
import App.State.Reducers.TwitchStateSlice
|
||||
|
||||
@@ -18,8 +19,8 @@ import App.State.Reducers.TwitchStateSlice
|
||||
data class RootState(
|
||||
/** 原 user(t8 / M8().qtd)。未迁移:原样透传。 */
|
||||
val User: Any? = null,
|
||||
/** 原 screen(WK / dY().ks5)。未迁移:原样透传。 */
|
||||
val Screen: Any? = null,
|
||||
/** 原 screen(WK / dY().ks5)。已迁移。 */
|
||||
val Screen: ScreenStateSlice = ScreenStateSlice(),
|
||||
/** 原 modalScreen(nV / Fs.js1)。未迁移:原样透传。 */
|
||||
val ModalScreen: Any? = null,
|
||||
/** 原 battleStatistics(jR / rr.fqr)。未迁移:原样透传。 */
|
||||
|
||||
@@ -15,7 +15,7 @@ data class SliceWiring(
|
||||
object RootWiring {
|
||||
val All: List<SliceWiring> = listOf(
|
||||
SliceWiring("user", "ypu_1", "t8", "M8().qtd", "M8().qtd(t, n.ypu_1)", listOf("?R8", "?p8", "?v8", "AddCoins", "AddRubies", "SetBatteryLevel", "SetClanInfo", "SetCoins", "SetDisabledWithoutPremiumLocations", "SetHasTemporaryCash", "SetPremiumExpirationTime", "SetRankNames", "SetRubies", "SetUserId", "SetUserPropertiesAction", "SpendCoins", "SpendCrystals", "SpendRubies", "UpdateClanTag", "UpdateCrystals", "UpdateEventPass", "UpdateGearScore", "UpdatePremium", "UpdateRank", "UpdateScore", "UpdateUid", "UpdateUserRating")),
|
||||
SliceWiring("screen", "zpu_1", "WK", "dY().ks5", "dY().ks5(t, n.zpu_1)", listOf("?LX", "?RX", "?cY", "?eY", "?fY", "?lY", "?oC", "?oY", "?sC", "?sY", "?uY", "DisplayMainEntranceScreen", "DisplayReauthorizationMessage", "GoTo", "PressedAnyButton")),
|
||||
SliceWiring("screen", "zpu_1", "WK", "dY().ks5", "dY().ks5(t, n.zpu_1)", listOf("ClearScreenStack", "DelayNextGoTo", "DisableBack", "DisableForeground", "DisplayMainEntranceScreen", "DisplayReauthorizationMessage", "EnableBack", "EnableForeground", "FilterScreenStack", "GoTo", "PopScreen", "PressedAnyButton", "ResetScreenStackOnNextGoTo")),
|
||||
SliceWiring("modalScreen", "apv_1", "nV", "Fs.js1", "Fs.js1(t, n.apv_1)", listOf("?XW", "AddModal", "RemoveCurrentByName", "RemoveModal")),
|
||||
SliceWiring("battleStatistics", "bpv_1", "jR", "rr.fqr", "rr.fqr(t, n.bpv_1)", listOf("?DR", "?HR", "?KR", "?PR", "?QR", "?UR", "?XR", "?YR", "?ZR", "?nP", "?tP", "Init", "InitBattleModeWithLevels", "SetBattleMode", "SetDisplayClientInfo", "SetFlagOrBallOwner", "SetRankRange", "SetReArmor", "SetRemainingTime", "SetRoundRestartTime", "UpdateFund", "UpdateScore")),
|
||||
SliceWiring("battleTutorial", "cpv_1", "rE", "Q.sq8", "Q.sq8(t, n.cpv_1)", listOf("?oE", "?sE", "SetFragsCount", "ShowMessage")),
|
||||
@@ -106,7 +106,7 @@ object RootWiring {
|
||||
)
|
||||
|
||||
/** 已迁移(由 RootReducer 真正归约)的字段。 */
|
||||
val Migrated: Set<String> = setOf("inviteState", "loginByPassword", "twitchState", "home", "localBattleUserState", "shop")
|
||||
val Migrated: Set<String> = setOf("inviteState", "loginByPassword", "twitchState", "home", "localBattleUserState", "shop", "screen")
|
||||
|
||||
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
|
||||
val Unmigrated: List<SliceWiring> get() = All.filter { it.Field !in Migrated }
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// ScreenId.kt —— 屏幕标识(原 module_10032 的 XX 体系里,screen 切片真正用到的部分)
|
||||
//
|
||||
// 证据链:
|
||||
// · WK.current 默认 `PX()` → `RX` 单例,`ld(RX).m()` 返回 "System.Loader"。
|
||||
// · 根 reducer 的 GoTo 分支只对目标做 `instanceof LX`(Entrance,`m()` = "Entrance")
|
||||
// 与 `instanceof RX`(System.Loader)判别,其余屏幕只取 `m()` 当 analytics 的 screen 名。
|
||||
// · 其余屏幕类(UX=Shop、FX=Garage、eX=Lobby.Home …)的字段只在各自切片里使用;
|
||||
// 在那些切片迁移之前,这里先用「名字」承载身份,避免凭空补一层用不到的字段。
|
||||
//
|
||||
// 因此 ScreenId 只保留该 reducer 需要的三种能力:名字(analytics)、Entrance/Loader 判别、相等性。
|
||||
// 注意:同名的两个不同 Shop 类别在真实包里有不同字段,这里会折叠成同一个 NamedScreen;
|
||||
// 等 shop 屏幕对象(UX)本身成为状态切片时再替换成带字段的类。
|
||||
package App.State
|
||||
|
||||
sealed interface ScreenId {
|
||||
val name: String
|
||||
}
|
||||
|
||||
/** 原 `LX`:`m() = "Entrance"`。 */
|
||||
object EntranceScreen : ScreenId {
|
||||
override val name: String = "Entrance"
|
||||
}
|
||||
|
||||
/** 原 `RX`:`m() = "System.Loader"`,也是 `WK` 的默认 current(`PX()`)。 */
|
||||
object LoaderScreen : ScreenId {
|
||||
override val name: String = "System.Loader"
|
||||
}
|
||||
|
||||
/** 其余屏幕:`m()` 返回带层级的路径,例如 "Lobby.Home" / "Lobby.Quests" / "Shop" / "Garage"。 */
|
||||
data class NamedScreen(override val name: String) : ScreenId
|
||||
@@ -9,8 +9,8 @@
|
||||
// ⑥ 动作目录:816 个类与 138 个真实派发名对齐
|
||||
// ⑦ 回放:真实 toString 样例的类/字段/类型校验
|
||||
// ⑧ 全目录动作类都能按 KClass 路由
|
||||
// ⑨~⑮ 切片 reducer:InviteState / LoginByPasswordState / TwitchState / HomeScreen / LocalBattleUserState /
|
||||
// Shop(含 U7 类别表与 w3 物品表两个子 reducer);⑭ 根 reducer 的 89 槽位委派
|
||||
// ⑨~⑰ 切片 reducer:InviteState / LoginByPasswordState / TwitchState / HomeScreen / LocalBattleUserState /
|
||||
// Shop(含 U7 类别表与 w3 物品表两个子 reducer)/ Screen;⑭ 根 reducer 的 89 槽位委派
|
||||
//
|
||||
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
|
||||
package App
|
||||
@@ -126,6 +126,24 @@ import App.State.SetReArmorTemporaryDisabled
|
||||
import App.State.SetRememberMe
|
||||
import App.State.SetStatus
|
||||
import App.State.SetTwitchEnabled
|
||||
import App.State.DisplayMainEntranceScreen
|
||||
import App.State.DisplayReauthorizationMessage
|
||||
import App.State.EntranceScreen
|
||||
import App.State.GoTo
|
||||
import App.State.LoaderScreen
|
||||
import App.State.NamedScreen
|
||||
import App.State.PressedAnyButton
|
||||
import App.State.Reducers.ClearScreenStack
|
||||
import App.State.Reducers.DelayNextGoTo
|
||||
import App.State.Reducers.DisableBack
|
||||
import App.State.Reducers.DisableForeground
|
||||
import App.State.Reducers.EnableBack
|
||||
import App.State.Reducers.EnableForeground
|
||||
import App.State.Reducers.FilterScreenStack
|
||||
import App.State.Reducers.PopScreen
|
||||
import App.State.Reducers.ReduceScreen
|
||||
import App.State.Reducers.ResetScreenStackOnNextGoTo
|
||||
import App.State.Reducers.ScreenStateSlice
|
||||
|
||||
private data class AddFrags(val fragsCount: Int)
|
||||
private data class SetScreen(val screen: String)
|
||||
@@ -563,14 +581,14 @@ object StateCheck {
|
||||
LocalBattleW.Reducer == "(inline@localBattleUserState)"
|
||||
)
|
||||
Check(
|
||||
"根:迁移表 = 六个已迁移字段,其余 83 个待迁",
|
||||
"根:迁移表 = 七个已迁移字段,其余 82 个待迁",
|
||||
RootReducer.Migrated ==
|
||||
setOf("inviteState", "loginByPassword", "twitchState", "home", "localBattleUserState", "shop") &&
|
||||
RootWiring.Unmigrated.size == 83
|
||||
setOf("inviteState", "loginByPassword", "twitchState", "home", "localBattleUserState", "shop", "screen") &&
|
||||
RootWiring.Unmigrated.size == 82
|
||||
)
|
||||
|
||||
// 一次 ReduceRoot 里五个切片各归约自己的动作,互不串片
|
||||
val S0 = RootState(User = "user-1", Screen = "Preloader")
|
||||
// 一次 ReduceRoot 里六个切片各归约自己的动作,互不串片
|
||||
val S0 = RootState(User = "user-1", Screen = ScreenStateSlice(Current = NamedScreen("Preloader")))
|
||||
val S1 = ReduceRoot(S0, ApplyInvite("uid-1"))
|
||||
Check(
|
||||
"根:ApplyInvite 只动 inviteState",
|
||||
@@ -619,10 +637,10 @@ object StateCheck {
|
||||
// 未迁移字段原样透传;无关动作不改变任何字段
|
||||
Check(
|
||||
"根:未迁移字段原样透传",
|
||||
S5.User == "user-1" && S5.Screen == "Preloader"
|
||||
S5.User == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
|
||||
)
|
||||
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
|
||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 6/89")
|
||||
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 7/89")
|
||||
|
||||
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
|
||||
Store.Dispatch(SetStatus("LOGGING_IN"))
|
||||
@@ -924,6 +942,131 @@ object StateCheck {
|
||||
// 69 个动作里只有 x4 是标记接口(没有独立效果),其余都已有名字
|
||||
val ShopUnknown = RootWiring.ByField("shop")!!.Actions.filter { it.startsWith("?") }
|
||||
Check("shop 回放:唯一未具名的是标记 x4", ShopUnknown == listOf("?x4"))
|
||||
|
||||
// ⑰ 切片 reducer:Screen(原 module_10032 的 WK / wY.ks5,13 个 instanceof 臂)
|
||||
run {
|
||||
val Lobby = NamedScreen("Lobby.Home")
|
||||
val Shop = NamedScreen("Shop")
|
||||
|
||||
val S0 = ScreenStateSlice()
|
||||
Check(
|
||||
"切片 Screen:默认值与原构造一致(current=System.Loader、enableBack=true)",
|
||||
S0.Current == LoaderScreen && S0.LoaderBackgroundImage == null && !S0.Foreground &&
|
||||
S0.ScreenStack.isEmpty() && S0.EnableBack && !S0.IsResetBackStackOnNextGoTo &&
|
||||
!S0.IsDelayedGoto && !S0.PressedAnyButton && !S0.DisplayMainEntranceScreen &&
|
||||
!S0.NeedReauthorization
|
||||
)
|
||||
|
||||
Check(
|
||||
"切片 Screen:前景开关只改 foreground",
|
||||
!ReduceScreen(S0, DisableForeground).Foreground &&
|
||||
ReduceScreen(S0, EnableForeground).Foreground &&
|
||||
ReduceScreen(S0, EnableForeground).Current == LoaderScreen
|
||||
)
|
||||
|
||||
val S1 = S0.copy(Current = Lobby, ScreenStack = listOf(NamedScreen("Preloader")))
|
||||
Check(
|
||||
"切片 Screen:ClearScreenStack 清历史但保留 current",
|
||||
ReduceScreen(S1, ClearScreenStack).let { it.ScreenStack.isEmpty() && it.Current == Lobby }
|
||||
)
|
||||
|
||||
val S2 = ReduceScreen(S1, GoTo(Shop, false))
|
||||
Check(
|
||||
"切片 Screen:GoTo 无 history 只换 current",
|
||||
S2.Current == Shop && S2.ScreenStack == S1.ScreenStack
|
||||
)
|
||||
|
||||
val S3 = ReduceScreen(S1, GoTo(Shop, true))
|
||||
Check(
|
||||
"切片 Screen:GoTo history 把原 current 压栈",
|
||||
S3.Current == Shop && S3.ScreenStack == S1.ScreenStack + Lobby
|
||||
)
|
||||
Check(
|
||||
"切片 Screen:GoTo 到当前屏仍按 history 压栈(原包只在 analytics 里去重)",
|
||||
ReduceScreen(S1, GoTo(Lobby, true)).ScreenStack == listOf(NamedScreen("Preloader"), Lobby)
|
||||
)
|
||||
|
||||
val S4 = ReduceScreen(S3.copy(IsResetBackStackOnNextGoTo = true), GoTo(Lobby, true))
|
||||
Check(
|
||||
"切片 Screen:复位标志优先于 history,且 GoTo 后归 false",
|
||||
S4.Current == Lobby && S4.ScreenStack.isEmpty() && !S4.IsResetBackStackOnNextGoTo
|
||||
)
|
||||
Check(
|
||||
"切片 Screen:GoTo 清 isDelayedGoto",
|
||||
!ReduceScreen(S3.copy(IsDelayedGoto = true), GoTo(Shop, false)).IsDelayedGoto
|
||||
)
|
||||
|
||||
Check(
|
||||
"切片 Screen:ResetScreenStackOnNextGoTo / DelayNextGoTo 各置本标志",
|
||||
ReduceScreen(S0, ResetScreenStackOnNextGoTo).IsResetBackStackOnNextGoTo &&
|
||||
ReduceScreen(S0, DelayNextGoTo).IsDelayedGoto
|
||||
)
|
||||
|
||||
val S5 = S1.copy(ScreenStack = listOf(NamedScreen("Battle"), Lobby, Shop))
|
||||
Check(
|
||||
"切片 Screen:FilterScreenStack 只保留 predicate 命中项",
|
||||
ReduceScreen(S5, FilterScreenStack { it.name == "Battle" }).ScreenStack ==
|
||||
listOf(NamedScreen("Battle"))
|
||||
)
|
||||
Check(
|
||||
"切片 Screen:FilterScreenStack 缺省谓词(原 YK)清空历史",
|
||||
ReduceScreen(S5, FilterScreenStack()).ScreenStack.isEmpty()
|
||||
)
|
||||
|
||||
Check(
|
||||
"切片 Screen:EnableBack / DisableBack 切返回键",
|
||||
ReduceScreen(S0.copy(EnableBack = false), EnableBack).EnableBack &&
|
||||
!ReduceScreen(S0, DisableBack).EnableBack
|
||||
)
|
||||
Check(
|
||||
"切片 Screen:PopScreen 丢最后一个,空栈安全",
|
||||
ReduceScreen(S1, PopScreen).ScreenStack.isEmpty() &&
|
||||
ReduceScreen(S0, PopScreen).ScreenStack.isEmpty()
|
||||
)
|
||||
Check(
|
||||
"切片 Screen:PressedAnyButton 写 action.isPressed(false 也生效)",
|
||||
!ReduceScreen(S0.copy(PressedAnyButton = true), PressedAnyButton(false)).PressedAnyButton &&
|
||||
ReduceScreen(S0, PressedAnyButton(true)).PressedAnyButton
|
||||
)
|
||||
Check(
|
||||
"切片 Screen:DisplayMainEntranceScreen / DisplayReauthorizationMessage 各写本字段",
|
||||
ReduceScreen(S0, DisplayMainEntranceScreen(true)).DisplayMainEntranceScreen &&
|
||||
ReduceScreen(S0, DisplayReauthorizationMessage(true)).NeedReauthorization
|
||||
)
|
||||
Check("切片 Screen:无关动作不改状态", ReduceScreen(S1, AddFrags(1)) == S1)
|
||||
|
||||
// 根:screen 动作只动 screen,且与已迁移的其余六片共存
|
||||
val R0 = RootState()
|
||||
val R1 = ReduceRoot(R0, GoTo(Shop, false))
|
||||
Check(
|
||||
"根:GoTo 只动 screen",
|
||||
R1.Screen.Current == Shop && R1.User == null && R1.Shop == R0.Shop &&
|
||||
R1.InviteState == R0.InviteState && R1.Home == R0.Home
|
||||
)
|
||||
val R2 = ReduceRoot(R1, EnableForeground)
|
||||
Check(
|
||||
"根:EnableForeground 只动 screen 前景位",
|
||||
R2.Screen.Foreground && R2.Screen.Current == Shop &&
|
||||
R2.Screen.ScreenStack == R1.Screen.ScreenStack && R2.Shop == R1.Shop
|
||||
)
|
||||
Check("根:screen 迁移后无关动作仍返回等值状态", ReduceRoot(R2, AddFrags(1)) == R2)
|
||||
|
||||
val W = RootWiring.ByField("screen")!!
|
||||
Check(
|
||||
"根:screen 委派到 WK / dY().ks5 且 13 个动作全部具名",
|
||||
W.SliceClass == "WK" && W.Reducer == "dY().ks5" && W.Actions.size == 13 &&
|
||||
W.Actions.none { it.startsWith("?") }
|
||||
)
|
||||
Check(
|
||||
"根:screen 的动作名覆盖 13 个 instanceof 臂",
|
||||
listOf(
|
||||
"DisableForeground", "EnableForeground", "ClearScreenStack",
|
||||
"ResetScreenStackOnNextGoTo", "DelayNextGoTo", "GoTo", "FilterScreenStack",
|
||||
"EnableBack", "DisableBack", "PopScreen", "PressedAnyButton",
|
||||
"DisplayMainEntranceScreen", "DisplayReauthorizationMessage",
|
||||
).all { it in W.Actions }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val Failed = Results.filter { !it.second }
|
||||
|
||||
Reference in new issue
Block a user