fix:APP-push点击文案显示home页
This commit is contained in:
77
client/src/services/__tests__/pushNotificationRoute.test.ts
Normal file
77
client/src/services/__tests__/pushNotificationRoute.test.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
vi.mock('../../storage/appStorage', () => ({
|
||||
getLastHandledNotificationId: vi.fn(async () => null),
|
||||
setLastHandledNotificationId: vi.fn(async () => undefined),
|
||||
setPendingHomePushMessage: vi.fn(async () => undefined),
|
||||
}));
|
||||
|
||||
import { buildPendingHomePushMessageFromResponse } from '../pushNotificationRoute';
|
||||
|
||||
describe('pushNotificationRoute.buildPendingHomePushMessageFromResponse', () => {
|
||||
it('能从每日推荐 push payload 提取 home 文案', () => {
|
||||
const result = buildPendingHomePushMessageFromResponse({
|
||||
notification: {
|
||||
request: {
|
||||
identifier: 'notif-1',
|
||||
content: {
|
||||
title: '每日推荐',
|
||||
body: '先用通知正文兜底',
|
||||
data: {
|
||||
scene: 'push',
|
||||
target_screen: 'home',
|
||||
home_text: '点击推送后回到首页展示这句文案',
|
||||
content_id: '42',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
notification_id: 'notif-1',
|
||||
title: '每日推荐',
|
||||
text: '点击推送后回到首页展示这句文案',
|
||||
content_id: 42,
|
||||
scene: 'push',
|
||||
});
|
||||
});
|
||||
|
||||
it('home_text 缺失时回退到通知正文', () => {
|
||||
const result = buildPendingHomePushMessageFromResponse({
|
||||
notification: {
|
||||
request: {
|
||||
identifier: 'notif-2',
|
||||
content: {
|
||||
body: '直接展示通知正文',
|
||||
data: {
|
||||
target_screen: 'home',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result?.text).toBe('直接展示通知正文');
|
||||
expect(result?.notification_id).toBe('notif-2');
|
||||
});
|
||||
|
||||
it('非 home 目标且非 push 场景时忽略', () => {
|
||||
const result = buildPendingHomePushMessageFromResponse({
|
||||
notification: {
|
||||
request: {
|
||||
identifier: 'notif-3',
|
||||
content: {
|
||||
body: '这条不应该进入首页',
|
||||
data: {
|
||||
target_screen: 'profile',
|
||||
scene: 'other',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
100
client/src/services/pushNotificationRoute.ts
Normal file
100
client/src/services/pushNotificationRoute.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import {
|
||||
getLastHandledNotificationId,
|
||||
setLastHandledNotificationId,
|
||||
setPendingHomePushMessage,
|
||||
type PendingHomePushMessage,
|
||||
} from '../storage/appStorage';
|
||||
|
||||
type NotificationContentLike = {
|
||||
title?: string | null;
|
||||
body?: string | null;
|
||||
data?: Record<string, unknown> | null;
|
||||
};
|
||||
|
||||
export type NotificationResponseLike = {
|
||||
notification?: {
|
||||
request?: {
|
||||
identifier?: string;
|
||||
content?: NotificationContentLike;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
type HomePushListener = (message: PendingHomePushMessage) => void;
|
||||
|
||||
const homePushListeners = new Set<HomePushListener>();
|
||||
|
||||
function readString(value: unknown): string | undefined {
|
||||
if (typeof value !== 'string') return undefined;
|
||||
const trimmed = value.trim();
|
||||
return trimmed ? trimmed : undefined;
|
||||
}
|
||||
|
||||
function readContentId(value: unknown): number | undefined {
|
||||
if (typeof value === 'number' && Number.isFinite(value)) {
|
||||
return Math.trunc(value);
|
||||
}
|
||||
if (typeof value === 'string' && value.trim()) {
|
||||
const parsed = Number(value);
|
||||
if (Number.isFinite(parsed)) return Math.trunc(parsed);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function notifyHomePushListeners(message: PendingHomePushMessage): void {
|
||||
for (const listener of homePushListeners) {
|
||||
listener(message);
|
||||
}
|
||||
}
|
||||
|
||||
export function subscribeHomePushMessage(listener: HomePushListener): () => void {
|
||||
homePushListeners.add(listener);
|
||||
return () => {
|
||||
homePushListeners.delete(listener);
|
||||
};
|
||||
}
|
||||
|
||||
export function buildPendingHomePushMessageFromResponse(
|
||||
response: NotificationResponseLike
|
||||
): PendingHomePushMessage | null {
|
||||
const request = response.notification?.request;
|
||||
const content = request?.content;
|
||||
const data =
|
||||
content?.data && typeof content.data === 'object' && !Array.isArray(content.data)
|
||||
? content.data
|
||||
: {};
|
||||
|
||||
const targetScreen = readString(data.target_screen);
|
||||
const scene = readString(data.scene);
|
||||
const shouldOpenHome = targetScreen === 'home' || scene === 'push';
|
||||
if (!shouldOpenHome) return null;
|
||||
|
||||
const text = readString(data.home_text) ?? readString(content?.body);
|
||||
if (!text) return null;
|
||||
|
||||
return {
|
||||
notification_id: readString(request?.identifier) ?? `push-${Date.now()}`,
|
||||
received_at: new Date().toISOString(),
|
||||
text,
|
||||
title: readString(content?.title),
|
||||
content_id: readContentId(data.content_id),
|
||||
scene,
|
||||
};
|
||||
}
|
||||
|
||||
export async function persistHomePushMessageFromResponse(
|
||||
response: NotificationResponseLike
|
||||
): Promise<PendingHomePushMessage | null> {
|
||||
const message = buildPendingHomePushMessageFromResponse(response);
|
||||
if (!message) return null;
|
||||
|
||||
const lastHandledNotificationId = await getLastHandledNotificationId();
|
||||
if (lastHandledNotificationId === message.notification_id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
await setPendingHomePushMessage(message);
|
||||
await setLastHandledNotificationId(message.notification_id);
|
||||
notifyHomePushListeners(message);
|
||||
return message;
|
||||
}
|
||||
Reference in New Issue
Block a user