refactor(chat): decouple chat state from auth and gate quota alerts to guests
- Remove ChatAuthStatusChanged dispatch from auth login flow; chat screen now subscribes directly to auth state machine (loginStatus) to derive isGuest and drive WS lifecycle - Gate quota-exceeded and warning dialogs to guests only (non-guests have no message limit) - Dispatch ChatLoadMoreHistory for non-guest initial load vs ChatInit for guests - Fix AppConstants import path from @/core/constants/app_constants to @/core/app_constants - Add defensive isGuest checks in quota effects to prevent non-guest quota triggers
This commit is contained in:
@@ -13,6 +13,16 @@
|
||||
* - HTTP 操作用 `fromPromise` actor(一次性 Promise)
|
||||
* - 保持事件类型名与原 Dart 一致,便于业务层迁移对照
|
||||
* - actions 全部 inline 在 setup() 中(确保类型推断正确)
|
||||
*
|
||||
* **鉴权解耦**:
|
||||
* chat 机器**不**感知鉴权状态 —— `isGuest` 字段已删(由 chat-screen 派生自 auth.loginStatus)。
|
||||
* WebSocket 管理也由 chat-screen 监听 auth 状态机后驱动。
|
||||
*
|
||||
* **配额(**仅**游客)**:
|
||||
* - `guestRemainingQuota` / `guestTotalQuota` **仅**游客有意义
|
||||
* - 非游客"无消息限制"(业务事实)—— 永不被赋值
|
||||
* - chat-screen 派 `ChatInit` 时(**只**在 guest)→ onDone 条件赋
|
||||
* - `appendUserMessage` 减 `Math.max(0, q - 1)` —— 非游客 0-1=0 天然 no-op
|
||||
*/
|
||||
import { setup, fromPromise, assign } from "xstate";
|
||||
|
||||
@@ -85,6 +95,8 @@ interface InitResult {
|
||||
|
||||
async function readInitData(): Promise<InitResult> {
|
||||
const chatStorage = ChatStorage.getInstance();
|
||||
// isGuest 在 chat 业务层仅用于"是否加载本地历史" + "onDone 条件赋配额" —— **不**是鉴权信号
|
||||
// 鉴权判断已上提到 chat-screen 派生
|
||||
const isGuest = !AuthStorage.getInstance().hasLoginToken();
|
||||
|
||||
const [dailyResult, totalResult, localResult] = await Promise.all([
|
||||
@@ -95,13 +107,14 @@ async function readInitData(): Promise<InitResult> {
|
||||
|
||||
return {
|
||||
isGuest,
|
||||
// fallback 0 —— 非游客路径用不到;onDone 会**条件**赋
|
||||
guestRemainingQuota: mapQuotaResult(
|
||||
dailyResult as Result<{ remaining: number } | null>,
|
||||
40,
|
||||
0,
|
||||
),
|
||||
guestTotalQuota: mapTotalQuotaResult(
|
||||
totalResult as Result<number | null>,
|
||||
50,
|
||||
0,
|
||||
),
|
||||
localMessages:
|
||||
localResult && Result.isOk(localResult) && localResult.data
|
||||
@@ -145,8 +158,7 @@ const loadMoreHistoryActor = fromPromise<
|
||||
};
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// Machine
|
||||
// ============================================================// Machine
|
||||
// ============================================================
|
||||
export const chatMachine = setup({
|
||||
types: {
|
||||
@@ -171,6 +183,8 @@ export const chatMachine = setup({
|
||||
},
|
||||
],
|
||||
isReplyingAI: true,
|
||||
// 游客减配额;非游客 0 - 1 = max(0, -1) = 0 —— **天然 no-op**(业务事实"非游客无限制")
|
||||
guestRemainingQuota: Math.max(0, context.guestRemainingQuota - 1),
|
||||
};
|
||||
}),
|
||||
|
||||
@@ -187,6 +201,8 @@ export const chatMachine = setup({
|
||||
},
|
||||
],
|
||||
isReplyingAI: true,
|
||||
// 游客减配额;非游客 no-op
|
||||
guestRemainingQuota: Math.max(0, context.guestRemainingQuota - 1),
|
||||
};
|
||||
}),
|
||||
|
||||
@@ -223,10 +239,6 @@ export const chatMachine = setup({
|
||||
return { messages, isReplyingAI: false };
|
||||
}),
|
||||
|
||||
refreshAuthStatus: assign(() => ({
|
||||
isGuest: !AuthStorage.getInstance().hasLoginToken(),
|
||||
})),
|
||||
|
||||
incrementQuotaExceeded: assign(({ context }) => ({
|
||||
quotaExceededTrigger: context.quotaExceededTrigger + 1,
|
||||
})),
|
||||
@@ -248,9 +260,6 @@ export const chatMachine = setup({
|
||||
ChatSendImage: {
|
||||
actions: "appendUserImage",
|
||||
},
|
||||
ChatAuthStatusChanged: {
|
||||
actions: "refreshAuthStatus",
|
||||
},
|
||||
ChatAISentenceReceived: {
|
||||
actions: "appendOrUpdateAISentence",
|
||||
},
|
||||
@@ -269,11 +278,18 @@ export const chatMachine = setup({
|
||||
src: "chatInit",
|
||||
onDone: {
|
||||
target: "ready",
|
||||
actions: assign({
|
||||
messages: ({ event }) => event.output.localMessages,
|
||||
isGuest: ({ event }) => event.output.isGuest,
|
||||
guestRemainingQuota: ({ event }) => event.output.guestRemainingQuota,
|
||||
guestTotalQuota: ({ event }) => event.output.guestTotalQuota,
|
||||
// **条件赋**:仅 isGuest 时初始化配额(业务事实"非游客无消息限制")
|
||||
// - 游客:messages + quota 都更新
|
||||
// - 非游客:仅 messages(local 空),quota 保持 default 0(不更新其值)
|
||||
actions: assign(({ event }) => {
|
||||
const updates: Partial<ChatState> = {
|
||||
messages: event.output.localMessages,
|
||||
};
|
||||
if (event.output.isGuest) {
|
||||
updates.guestRemainingQuota = event.output.guestRemainingQuota;
|
||||
updates.guestTotalQuota = event.output.guestTotalQuota;
|
||||
}
|
||||
return updates;
|
||||
}),
|
||||
},
|
||||
onError: {
|
||||
@@ -293,9 +309,6 @@ export const chatMachine = setup({
|
||||
},
|
||||
ChatLoadMoreHistory: "loadingMoreHistory",
|
||||
ChatScreenInvisible: "idle",
|
||||
ChatAuthStatusChanged: {
|
||||
actions: "refreshAuthStatus",
|
||||
},
|
||||
ChatAISentenceReceived: {
|
||||
actions: "appendOrUpdateAISentence",
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user