修复泥点消耗刷新不及时 (#138)
refactor: - 主站复用game agent的zustand store fix: - 修复旧请求覆盖问题 - 修复账号切换问题 - 主站原本已经每 4 秒轮询 external generation 任务状态, 在这里补充触发余额刷新的时机 --------- Co-authored-by: 段舒康 <kdletters@qq.com> Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/138 Co-authored-by: 王德宇 <kvtodev@outlook.com> Co-committed-by: 王德宇 <kvtodev@outlook.com>
This commit was merged in pull request #138.
This commit is contained in:
@@ -51,6 +51,7 @@ export type * from './contracts/visualNovel';
|
||||
export * from './http';
|
||||
export * from './llm/narrativeLanguage';
|
||||
export * from './llm/parsers';
|
||||
export * from './stores/createProfileWalletStore';
|
||||
export * from './utils/signedReadUrlCache';
|
||||
|
||||
// should not export component, instead, they should be ref by relative path
|
||||
|
||||
@@ -0,0 +1,337 @@
|
||||
import { describe, expect, test, vi } from 'vitest';
|
||||
|
||||
import type {
|
||||
ProfileMudPointBalance,
|
||||
ProfileRechargeCenterResponse,
|
||||
} from '../contracts/runtime';
|
||||
import { createProfileWalletStore } from './createProfileWalletStore';
|
||||
|
||||
function balance(totalPoints: number): ProfileMudPointBalance {
|
||||
return {
|
||||
totalPoints,
|
||||
permanentPoints: totalPoints,
|
||||
limitedPoints: 0,
|
||||
limitedExpiresAt: null,
|
||||
dailyFreePoints: 0,
|
||||
dailyFreeResetPoints: 20,
|
||||
dailyFreeResetsAt: '2026-08-04T00:00:00+08:00',
|
||||
};
|
||||
}
|
||||
|
||||
function center(totalPoints: number): ProfileRechargeCenterResponse {
|
||||
return {
|
||||
mudPointBalance: balance(totalPoints),
|
||||
} as ProfileRechargeCenterResponse;
|
||||
}
|
||||
|
||||
function deferred<T>() {
|
||||
let resolve!: (value: T) => void;
|
||||
let reject!: (reason?: unknown) => void;
|
||||
const promise = new Promise<T>((resolvePromise, rejectPromise) => {
|
||||
resolve = resolvePromise;
|
||||
reject = rejectPromise;
|
||||
});
|
||||
return { promise, resolve, reject };
|
||||
}
|
||||
|
||||
describe('createProfileWalletStore', () => {
|
||||
test('reads the first complete wallet snapshot for its owner', async () => {
|
||||
const getRechargeCenter = vi.fn().mockResolvedValue(center(42));
|
||||
const store = createProfileWalletStore({ getRechargeCenter });
|
||||
|
||||
store.getState().setWalletOwner('user-a');
|
||||
await store.getState().onWalletBalanceMayHaveChanged();
|
||||
|
||||
expect(getRechargeCenter).toHaveBeenCalledTimes(1);
|
||||
expect(store.getState()).toMatchObject({
|
||||
ownerUserId: 'user-a',
|
||||
mudPointBalance: balance(42),
|
||||
mudPointBalanceStatus: 'ready',
|
||||
mudPointBalanceError: '',
|
||||
});
|
||||
});
|
||||
|
||||
test('merges concurrent notifications into one request', async () => {
|
||||
const pending = deferred<ProfileRechargeCenterResponse>();
|
||||
const getRechargeCenter = vi.fn(() => pending.promise);
|
||||
const store = createProfileWalletStore({ getRechargeCenter });
|
||||
store.getState().setWalletOwner('user-a');
|
||||
|
||||
const first = store.getState().onWalletBalanceMayHaveChanged();
|
||||
const second = store.getState().onWalletBalanceMayHaveChanged();
|
||||
expect(first).toBe(second);
|
||||
expect(getRechargeCenter).toHaveBeenCalledTimes(1);
|
||||
|
||||
pending.resolve(center(12));
|
||||
await first;
|
||||
expect(getRechargeCenter).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
test('reads again when balance changes during an active request', async () => {
|
||||
const first = deferred<ProfileRechargeCenterResponse>();
|
||||
const getRechargeCenter = vi
|
||||
.fn()
|
||||
.mockImplementationOnce(() => first.promise)
|
||||
.mockResolvedValueOnce(center(30));
|
||||
const store = createProfileWalletStore({ getRechargeCenter });
|
||||
store.getState().setWalletOwner('user-a');
|
||||
|
||||
const refresh = store.getState().onWalletBalanceMayHaveChanged();
|
||||
void store.getState().onWalletBalanceMayHaveChanged();
|
||||
first.resolve(center(99));
|
||||
await refresh;
|
||||
|
||||
expect(getRechargeCenter).toHaveBeenCalledTimes(2);
|
||||
expect(store.getState().mudPointBalance?.totalPoints).toBe(30);
|
||||
});
|
||||
|
||||
test('does not let an older read overwrite an applied mutation snapshot', async () => {
|
||||
const pending = deferred<ProfileRechargeCenterResponse>();
|
||||
const store = createProfileWalletStore({
|
||||
getRechargeCenter: vi.fn(() => pending.promise),
|
||||
});
|
||||
store.getState().setWalletOwner('user-a');
|
||||
|
||||
const refresh = store.getState().onWalletBalanceMayHaveChanged();
|
||||
const snapshot = store.getState().captureWalletBalanceSnapshot('user-a');
|
||||
expect(snapshot).not.toBeNull();
|
||||
store.getState().applyWalletBalanceSnapshot(snapshot!, balance(80));
|
||||
pending.resolve(center(20));
|
||||
await refresh;
|
||||
|
||||
expect(store.getState().mudPointBalance?.totalPoints).toBe(80);
|
||||
});
|
||||
|
||||
test('refreshes the new owner without waiting for a stalled old-owner request', async () => {
|
||||
const oldOwner = deferred<ProfileRechargeCenterResponse>();
|
||||
const newOwner = deferred<ProfileRechargeCenterResponse>();
|
||||
const requestSignals: Array<AbortSignal | undefined> = [];
|
||||
const getRechargeCenter = vi
|
||||
.fn()
|
||||
.mockImplementationOnce((signal?: AbortSignal) => {
|
||||
requestSignals.push(signal);
|
||||
return oldOwner.promise;
|
||||
})
|
||||
.mockImplementationOnce((signal?: AbortSignal) => {
|
||||
requestSignals.push(signal);
|
||||
return newOwner.promise;
|
||||
});
|
||||
const store = createProfileWalletStore({ getRechargeCenter });
|
||||
store.getState().setWalletOwner('user-a');
|
||||
const snapshot = store.getState().captureWalletBalanceSnapshot('user-a');
|
||||
store.getState().applyWalletBalanceSnapshot(snapshot!, balance(50));
|
||||
|
||||
const oldRefresh = store.getState().onWalletBalanceMayHaveChanged();
|
||||
store.getState().setWalletOwner('user-b');
|
||||
expect(store.getState()).toMatchObject({
|
||||
ownerUserId: 'user-b',
|
||||
mudPointBalance: null,
|
||||
mudPointBalanceStatus: 'idle',
|
||||
});
|
||||
const newRefresh = store.getState().onWalletBalanceMayHaveChanged();
|
||||
|
||||
expect(getRechargeCenter).toHaveBeenCalledTimes(2);
|
||||
expect(newRefresh).not.toBe(oldRefresh);
|
||||
expect(requestSignals[0]?.aborted).toBe(true);
|
||||
expect(requestSignals[1]?.aborted).toBe(false);
|
||||
newOwner.resolve(center(7));
|
||||
await newRefresh;
|
||||
expect(store.getState().mudPointBalance?.totalPoints).toBe(7);
|
||||
|
||||
oldOwner.resolve(center(60));
|
||||
await oldRefresh;
|
||||
expect(store.getState().mudPointBalance?.totalPoints).toBe(7);
|
||||
});
|
||||
|
||||
test('keeps an existing snapshot when refresh fails', async () => {
|
||||
const store = createProfileWalletStore({
|
||||
getRechargeCenter: vi.fn().mockRejectedValue(new Error('network down')),
|
||||
});
|
||||
store.getState().setWalletOwner('user-a');
|
||||
const snapshot = store.getState().captureWalletBalanceSnapshot('user-a');
|
||||
store.getState().applyWalletBalanceSnapshot(snapshot!, balance(55));
|
||||
|
||||
await store.getState().onWalletBalanceMayHaveChanged();
|
||||
|
||||
expect(store.getState()).toMatchObject({
|
||||
mudPointBalance: balance(55),
|
||||
mudPointBalanceStatus: 'error',
|
||||
mudPointBalanceError: '泥点明细读取失败',
|
||||
});
|
||||
});
|
||||
|
||||
test('treats a recharge response without the balance breakdown as an error', async () => {
|
||||
const store = createProfileWalletStore({
|
||||
getRechargeCenter: vi
|
||||
.fn()
|
||||
.mockResolvedValue({} as ProfileRechargeCenterResponse),
|
||||
});
|
||||
store.getState().setWalletOwner('user-a');
|
||||
|
||||
await store.getState().onWalletBalanceMayHaveChanged();
|
||||
|
||||
expect(store.getState()).toMatchObject({
|
||||
mudPointBalance: null,
|
||||
mudPointBalanceStatus: 'error',
|
||||
mudPointBalanceError: '泥点明细读取失败',
|
||||
});
|
||||
});
|
||||
|
||||
test('does not capture a snapshot for a different owner', () => {
|
||||
const store = createProfileWalletStore({
|
||||
getRechargeCenter: vi.fn(),
|
||||
});
|
||||
store.getState().setWalletOwner('user-b');
|
||||
|
||||
const snapshot = store.getState().captureWalletBalanceSnapshot('user-a');
|
||||
|
||||
expect(snapshot).toBeNull();
|
||||
expect(store.getState().mudPointBalance).toBeNull();
|
||||
expect(store.getState().mudPointBalanceStatus).toBe('idle');
|
||||
});
|
||||
|
||||
test('normalizes a snapshot owner before matching it', () => {
|
||||
const store = createProfileWalletStore({
|
||||
getRechargeCenter: vi.fn(),
|
||||
});
|
||||
store.getState().setWalletOwner('user-a');
|
||||
|
||||
const snapshot = store
|
||||
.getState()
|
||||
.captureWalletBalanceSnapshot(' user-a ');
|
||||
store.getState().applyWalletBalanceSnapshot(snapshot!, balance(100));
|
||||
|
||||
expect(store.getState()).toMatchObject({
|
||||
mudPointBalance: balance(100),
|
||||
mudPointBalanceStatus: 'ready',
|
||||
});
|
||||
});
|
||||
|
||||
test('does not let an older ordinary snapshot swallow a terminal refresh', async () => {
|
||||
const terminalRefresh = deferred<ProfileRechargeCenterResponse>();
|
||||
const requestSignals: Array<AbortSignal | undefined> = [];
|
||||
const store = createProfileWalletStore({
|
||||
getRechargeCenter: vi.fn((signal?: AbortSignal) => {
|
||||
requestSignals.push(signal);
|
||||
return terminalRefresh.promise;
|
||||
}),
|
||||
});
|
||||
store.getState().setWalletOwner('user-a');
|
||||
const ordinarySnapshot = store
|
||||
.getState()
|
||||
.captureWalletBalanceSnapshot('user-a');
|
||||
|
||||
const refresh = store.getState().onWalletBalanceMayHaveChanged();
|
||||
expect(
|
||||
store
|
||||
.getState()
|
||||
.applyWalletBalanceSnapshot(ordinarySnapshot!, balance(80)),
|
||||
).toBe(false);
|
||||
expect(requestSignals[0]?.aborted).toBe(false);
|
||||
|
||||
terminalRefresh.resolve(center(100));
|
||||
await refresh;
|
||||
|
||||
expect(store.getState().mudPointBalance?.totalPoints).toBe(100);
|
||||
});
|
||||
|
||||
test('rejects an older direct snapshot after a newer operation has applied', () => {
|
||||
const store = createProfileWalletStore({ getRechargeCenter: vi.fn() });
|
||||
store.getState().setWalletOwner('user-a');
|
||||
const olderSnapshot = store
|
||||
.getState()
|
||||
.captureWalletBalanceSnapshot('user-a');
|
||||
const newerSnapshot = store
|
||||
.getState()
|
||||
.captureWalletBalanceSnapshot('user-a');
|
||||
|
||||
expect(
|
||||
store.getState().applyWalletBalanceSnapshot(newerSnapshot!, balance(100)),
|
||||
).toBe(true);
|
||||
expect(
|
||||
store.getState().applyWalletBalanceSnapshot(olderSnapshot!, balance(20)),
|
||||
).toBe(false);
|
||||
expect(store.getState().mudPointBalance?.totalPoints).toBe(100);
|
||||
});
|
||||
|
||||
test('retains a legacy total without inventing a balance breakdown', async () => {
|
||||
const store = createProfileWalletStore({
|
||||
getRechargeCenter: vi.fn().mockResolvedValue({
|
||||
walletBalance: 37,
|
||||
} as ProfileRechargeCenterResponse),
|
||||
});
|
||||
store.getState().setWalletOwner('user-a');
|
||||
|
||||
await store.getState().onWalletBalanceMayHaveChanged();
|
||||
|
||||
expect(store.getState()).toMatchObject({
|
||||
legacyWalletBalance: 37,
|
||||
mudPointBalance: null,
|
||||
mudPointBalanceStatus: 'error',
|
||||
mudPointBalanceError: '泥点明细读取失败',
|
||||
});
|
||||
});
|
||||
|
||||
test('clears a stale balance breakdown when a newer refresh only has a legacy total', async () => {
|
||||
const getRechargeCenter = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(center(90))
|
||||
.mockResolvedValueOnce({
|
||||
walletBalance: 37,
|
||||
} as ProfileRechargeCenterResponse);
|
||||
const store = createProfileWalletStore({ getRechargeCenter });
|
||||
store.getState().setWalletOwner('user-a');
|
||||
|
||||
await store.getState().onWalletBalanceMayHaveChanged();
|
||||
await store.getState().onWalletBalanceMayHaveChanged();
|
||||
|
||||
expect(store.getState()).toMatchObject({
|
||||
legacyWalletBalance: 37,
|
||||
mudPointBalance: null,
|
||||
mudPointBalanceStatus: 'error',
|
||||
mudPointBalanceError: '泥点明细读取失败',
|
||||
});
|
||||
});
|
||||
|
||||
test('applies an owner-scoped legacy mutation snapshot atomically', () => {
|
||||
const store = createProfileWalletStore({ getRechargeCenter: vi.fn() });
|
||||
store.getState().setWalletOwner('user-a');
|
||||
const structuredSnapshot = store
|
||||
.getState()
|
||||
.captureWalletBalanceSnapshot('user-a');
|
||||
store
|
||||
.getState()
|
||||
.applyWalletBalanceSnapshot(structuredSnapshot!, balance(90));
|
||||
const legacySnapshot = store
|
||||
.getState()
|
||||
.captureWalletBalanceSnapshot('user-a');
|
||||
|
||||
expect(
|
||||
store.getState().applyLegacyWalletBalanceSnapshot(legacySnapshot!, 37),
|
||||
).toBe(true);
|
||||
expect(store.getState()).toMatchObject({
|
||||
legacyWalletBalance: 37,
|
||||
mudPointBalance: null,
|
||||
mudPointBalanceStatus: 'ready',
|
||||
mudPointBalanceError: '',
|
||||
});
|
||||
});
|
||||
|
||||
test('rejects a snapshot after switching away from and back to the same owner', () => {
|
||||
const store = createProfileWalletStore({ getRechargeCenter: vi.fn() });
|
||||
store.getState().setWalletOwner('user-a');
|
||||
const oldLifecycleSnapshot = store
|
||||
.getState()
|
||||
.captureWalletBalanceSnapshot('user-a');
|
||||
|
||||
store.getState().setWalletOwner('user-b');
|
||||
store.getState().setWalletOwner('user-a');
|
||||
|
||||
expect(
|
||||
store
|
||||
.getState()
|
||||
.applyWalletBalanceSnapshot(oldLifecycleSnapshot!, balance(100)),
|
||||
).toBe(false);
|
||||
expect(store.getState().mudPointBalance).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,238 @@
|
||||
import { create, type StoreApi, type UseBoundStore } from 'zustand';
|
||||
|
||||
import type {
|
||||
ProfileMudPointBalance,
|
||||
ProfileRechargeCenterResponse,
|
||||
} from '../contracts/runtime';
|
||||
|
||||
export type MudPointBalanceStatus = 'idle' | 'loading' | 'ready' | 'error';
|
||||
|
||||
export type ProfileWalletApi = {
|
||||
getRechargeCenter(
|
||||
signal?: AbortSignal,
|
||||
): Promise<ProfileRechargeCenterResponse>;
|
||||
};
|
||||
|
||||
export type ProfileWalletBalanceSnapshot = Readonly<{
|
||||
ownerUserId: string;
|
||||
ownerVersion: number;
|
||||
invalidationVersion: number;
|
||||
operationSequence: number;
|
||||
}>;
|
||||
|
||||
export type ProfileWalletStore = {
|
||||
ownerUserId: string | null;
|
||||
mudPointBalance: ProfileMudPointBalance | null;
|
||||
legacyWalletBalance: number | null;
|
||||
mudPointBalanceStatus: MudPointBalanceStatus;
|
||||
mudPointBalanceError: string;
|
||||
setWalletOwner: (userId: string | null) => void;
|
||||
captureWalletBalanceSnapshot: (
|
||||
ownerUserId: string,
|
||||
) => ProfileWalletBalanceSnapshot | null;
|
||||
applyWalletBalanceSnapshot: (
|
||||
snapshot: ProfileWalletBalanceSnapshot,
|
||||
balance: ProfileMudPointBalance,
|
||||
) => boolean;
|
||||
applyLegacyWalletBalanceSnapshot: (
|
||||
snapshot: ProfileWalletBalanceSnapshot,
|
||||
balance: number,
|
||||
) => boolean;
|
||||
onWalletBalanceMayHaveChanged: () => Promise<void>;
|
||||
resetWalletBalance: () => void;
|
||||
};
|
||||
|
||||
const EMPTY_WALLET_STATE = {
|
||||
mudPointBalance: null,
|
||||
legacyWalletBalance: null,
|
||||
mudPointBalanceStatus: 'idle',
|
||||
mudPointBalanceError: '',
|
||||
} as const;
|
||||
|
||||
export function createProfileWalletStore(
|
||||
api: ProfileWalletApi,
|
||||
): UseBoundStore<StoreApi<ProfileWalletStore>> {
|
||||
let refreshVersion = 0;
|
||||
let settledRefreshVersion = 0;
|
||||
let ownerVersion = 0;
|
||||
let operationSequence = 0;
|
||||
let latestAppliedOperationSequence = 0;
|
||||
let requestGeneration = 0;
|
||||
let activeRefresh: Promise<void> | null = null;
|
||||
let activeAbortController: AbortController | null = null;
|
||||
|
||||
const invalidateActiveRefresh = () => {
|
||||
requestGeneration += 1;
|
||||
activeAbortController?.abort();
|
||||
activeAbortController = null;
|
||||
activeRefresh = null;
|
||||
};
|
||||
|
||||
return create<ProfileWalletStore>((set, get) => ({
|
||||
ownerUserId: null,
|
||||
...EMPTY_WALLET_STATE,
|
||||
setWalletOwner: (userId) => {
|
||||
const normalizedUserId = userId?.trim() || null;
|
||||
if (get().ownerUserId === normalizedUserId) {
|
||||
return;
|
||||
}
|
||||
|
||||
ownerVersion += 1;
|
||||
latestAppliedOperationSequence = 0;
|
||||
invalidateActiveRefresh();
|
||||
settledRefreshVersion = refreshVersion;
|
||||
set({
|
||||
ownerUserId: normalizedUserId,
|
||||
...EMPTY_WALLET_STATE,
|
||||
});
|
||||
},
|
||||
captureWalletBalanceSnapshot: (ownerUserId) => {
|
||||
const normalizedOwnerUserId = ownerUserId.trim();
|
||||
if (
|
||||
!normalizedOwnerUserId ||
|
||||
get().ownerUserId !== normalizedOwnerUserId
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
ownerUserId: normalizedOwnerUserId,
|
||||
ownerVersion,
|
||||
invalidationVersion: refreshVersion,
|
||||
operationSequence: ++operationSequence,
|
||||
};
|
||||
},
|
||||
applyWalletBalanceSnapshot: (snapshot, balance) => {
|
||||
if (
|
||||
get().ownerUserId !== snapshot.ownerUserId ||
|
||||
ownerVersion !== snapshot.ownerVersion ||
|
||||
snapshot.invalidationVersion < refreshVersion ||
|
||||
snapshot.invalidationVersion < settledRefreshVersion ||
|
||||
snapshot.operationSequence < latestAppliedOperationSequence
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
invalidateActiveRefresh();
|
||||
latestAppliedOperationSequence = Math.max(
|
||||
latestAppliedOperationSequence,
|
||||
snapshot.operationSequence,
|
||||
);
|
||||
settledRefreshVersion = snapshot.invalidationVersion;
|
||||
set({
|
||||
mudPointBalance: balance,
|
||||
legacyWalletBalance: balance.totalPoints,
|
||||
mudPointBalanceStatus: 'ready',
|
||||
mudPointBalanceError: '',
|
||||
});
|
||||
return true;
|
||||
},
|
||||
applyLegacyWalletBalanceSnapshot: (snapshot, balance) => {
|
||||
if (
|
||||
!Number.isFinite(balance) ||
|
||||
get().ownerUserId !== snapshot.ownerUserId ||
|
||||
ownerVersion !== snapshot.ownerVersion ||
|
||||
snapshot.invalidationVersion < refreshVersion ||
|
||||
snapshot.invalidationVersion < settledRefreshVersion ||
|
||||
snapshot.operationSequence < latestAppliedOperationSequence
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
invalidateActiveRefresh();
|
||||
latestAppliedOperationSequence = Math.max(
|
||||
latestAppliedOperationSequence,
|
||||
snapshot.operationSequence,
|
||||
);
|
||||
settledRefreshVersion = snapshot.invalidationVersion;
|
||||
set({
|
||||
mudPointBalance: null,
|
||||
legacyWalletBalance: balance,
|
||||
mudPointBalanceStatus: 'ready',
|
||||
mudPointBalanceError: '',
|
||||
});
|
||||
return true;
|
||||
},
|
||||
onWalletBalanceMayHaveChanged: () => {
|
||||
refreshVersion += 1;
|
||||
if (activeRefresh) {
|
||||
return activeRefresh;
|
||||
}
|
||||
|
||||
const abortController = new AbortController();
|
||||
const refresh = (async () => {
|
||||
while (settledRefreshVersion < refreshVersion) {
|
||||
const ownerUserId = get().ownerUserId;
|
||||
const requestedVersion = refreshVersion;
|
||||
const generation = requestGeneration;
|
||||
if (!ownerUserId) {
|
||||
settledRefreshVersion = requestedVersion;
|
||||
return;
|
||||
}
|
||||
|
||||
set({ mudPointBalanceStatus: 'loading', mudPointBalanceError: '' });
|
||||
try {
|
||||
const center = await api.getRechargeCenter(abortController.signal);
|
||||
if (generation !== requestGeneration) {
|
||||
return;
|
||||
}
|
||||
if (requestedVersion !== refreshVersion) {
|
||||
settledRefreshVersion = requestedVersion;
|
||||
continue;
|
||||
}
|
||||
if (!center.mudPointBalance) {
|
||||
if (Number.isFinite(center.walletBalance)) {
|
||||
set({
|
||||
mudPointBalance: null,
|
||||
legacyWalletBalance: center.walletBalance,
|
||||
});
|
||||
}
|
||||
throw new Error('充值中心响应缺少泥点余额');
|
||||
}
|
||||
|
||||
settledRefreshVersion = requestedVersion;
|
||||
set({
|
||||
mudPointBalance: center.mudPointBalance,
|
||||
legacyWalletBalance: center.mudPointBalance.totalPoints,
|
||||
mudPointBalanceStatus: 'ready',
|
||||
mudPointBalanceError: '',
|
||||
});
|
||||
} catch {
|
||||
if (generation !== requestGeneration) {
|
||||
return;
|
||||
}
|
||||
if (requestedVersion !== refreshVersion) {
|
||||
settledRefreshVersion = requestedVersion;
|
||||
continue;
|
||||
}
|
||||
|
||||
settledRefreshVersion = requestedVersion;
|
||||
set({
|
||||
mudPointBalanceStatus: 'error',
|
||||
mudPointBalanceError: '泥点明细读取失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
||||
activeRefresh = refresh;
|
||||
activeAbortController = abortController;
|
||||
const clearActiveRefresh = () => {
|
||||
if (activeRefresh === refresh) {
|
||||
activeRefresh = null;
|
||||
}
|
||||
if (activeAbortController === abortController) {
|
||||
activeAbortController = null;
|
||||
}
|
||||
};
|
||||
void refresh.then(clearActiveRefresh, clearActiveRefresh);
|
||||
return refresh;
|
||||
},
|
||||
resetWalletBalance: () => {
|
||||
ownerVersion += 1;
|
||||
latestAppliedOperationSequence = 0;
|
||||
invalidateActiveRefresh();
|
||||
settledRefreshVersion = refreshVersion;
|
||||
set({ ownerUserId: null, ...EMPTY_WALLET_STATE });
|
||||
},
|
||||
}));
|
||||
}
|
||||
Reference in New Issue
Block a user