接入微信 Native 充值与订单过期处理
调整网页端充值渠道,微信内浏览器在开放平台接入前走 Native 支付。 拆分 Native 支付二维码弹窗,展示金额和充值内容并取消无效 code_url 跳转。 修复微信 Native 下单 time_expire 秒级 RFC3339 格式,避免小数秒参数错误。 新增充值订单 5 分钟过期调度,过期前查询微信真实订单状态。 补充微信授权绑定、路由加载恢复和后台表时间显示等配套逻辑。 更新 SpacetimeDB schema、绑定和相关文档测试。
This commit is contained in:
@@ -15,6 +15,7 @@ import { RouteLoadingScreen } from './RouteLoadingScreen';
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
window.localStorage.clear();
|
||||
window.sessionStorage.clear();
|
||||
});
|
||||
|
||||
describe('RouteImageReadyGate image url helpers', () => {
|
||||
@@ -87,6 +88,56 @@ describe('RouteImageReadyGate image url helpers', () => {
|
||||
expect(visibilityGate?.style.visibility).toBe('visible');
|
||||
});
|
||||
|
||||
it('does not show the full screen route loader again after the same route was revealed', () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const firstRender = render(
|
||||
createElement(
|
||||
RouteImageReadyGate,
|
||||
{
|
||||
eyebrow: 'Loading',
|
||||
text: 'Please wait',
|
||||
},
|
||||
createElement(
|
||||
'section',
|
||||
{
|
||||
'data-testid': 'route-content',
|
||||
},
|
||||
createElement('img', {
|
||||
src: '/generated-characters/slow-cover.png',
|
||||
alt: 'slow cover',
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(screen.getByText('Please wait')).toBeTruthy();
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(1600);
|
||||
});
|
||||
|
||||
expect(screen.queryByText('Please wait')).toBeNull();
|
||||
firstRender.unmount();
|
||||
|
||||
render(
|
||||
createElement(
|
||||
RouteImageReadyGate,
|
||||
{
|
||||
eyebrow: 'Loading',
|
||||
text: 'Please wait',
|
||||
},
|
||||
createElement('section', { 'data-testid': 'route-content' }, 'Ready'),
|
||||
),
|
||||
);
|
||||
|
||||
const content = screen.getByTestId('route-content');
|
||||
const visibilityGate = content.parentElement;
|
||||
expect(visibilityGate?.getAttribute('aria-hidden')).toBe('false');
|
||||
expect(visibilityGate?.style.visibility).toBe('visible');
|
||||
expect(screen.queryByText('Please wait')).toBeNull();
|
||||
});
|
||||
|
||||
it('uses the saved platform theme tokens for the route loading screen', () => {
|
||||
window.localStorage.setItem(
|
||||
'tavernrealms.settings.v1',
|
||||
|
||||
@@ -17,10 +17,40 @@ const IMAGE_GATE_QUIET_MS = 140;
|
||||
const IMAGE_GATE_MIN_VISIBLE_WAIT_MS = 260;
|
||||
const IMAGE_GATE_MAX_BLOCK_MS = 1400;
|
||||
const IMAGE_PRELOAD_TIMEOUT_MS = 12000;
|
||||
const ROUTE_IMAGE_READY_SESSION_PREFIX = 'genarrative:route-image-ready:';
|
||||
|
||||
const settledImageUrls = new Set<string>();
|
||||
const imagePreloadTasks = new Map<string, Promise<ImagePreloadResult>>();
|
||||
|
||||
function getRouteImageReadySessionKey() {
|
||||
if (typeof window === 'undefined') {
|
||||
return ROUTE_IMAGE_READY_SESSION_PREFIX;
|
||||
}
|
||||
return `${ROUTE_IMAGE_READY_SESSION_PREFIX}${window.location.pathname}`;
|
||||
}
|
||||
|
||||
function hasSeenRouteImageReady() {
|
||||
if (typeof window === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return window.sessionStorage.getItem(getRouteImageReadySessionKey()) === '1';
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function markRouteImageReadySeen() {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
window.sessionStorage.setItem(getRouteImageReadySessionKey(), '1');
|
||||
} catch {
|
||||
// sessionStorage may be blocked in embedded browsers; the visual gate still works without it.
|
||||
}
|
||||
}
|
||||
|
||||
function preloadImageUrl(url: string) {
|
||||
const existingTask = imagePreloadTasks.get(url);
|
||||
if (existingTask) {
|
||||
@@ -80,13 +110,18 @@ export function RouteImageReadyGate({
|
||||
const revealTimerRef = useRef<number | null>(null);
|
||||
const scanVersionRef = useRef(0);
|
||||
const revealedRef = useRef(false);
|
||||
const [ready, setReady] = useState(false);
|
||||
const [ready, setReady] = useState(() => hasSeenRouteImageReady());
|
||||
|
||||
useEffect(() => {
|
||||
const root = rootRef.current;
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
if (hasSeenRouteImageReady()) {
|
||||
revealedRef.current = true;
|
||||
setReady(true);
|
||||
return;
|
||||
}
|
||||
|
||||
let disposed = false;
|
||||
startTimeRef.current = window.performance.now();
|
||||
@@ -133,6 +168,7 @@ export function RouteImageReadyGate({
|
||||
}
|
||||
|
||||
revealedRef.current = true;
|
||||
markRouteImageReadySeen();
|
||||
setReady(true);
|
||||
}, delay);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user