퀘스트 완료 햅틱 · HOME 행성 반응 · Push·라이브 띠 중복 제어 · 알림함 위계 · Push 탭 화면 이동
This commit is contained in:
@@ -25,6 +25,10 @@ export interface InboxItem {
|
||||
title: string;
|
||||
source: string;
|
||||
targetPath?: string;
|
||||
/** 「내 행성」 사건의 팬덤 — Push 탭 시 그 행성 · 이클립스로 가고, 라이브 띠와 같은 사건인지 가린다. */
|
||||
fandomId?: string;
|
||||
/** 이클립스 단계 — `start`(C-78 위기) · `defended`(결과, 통보 N-9). */
|
||||
phase?: 'start' | 'defended';
|
||||
}
|
||||
|
||||
export async function notifyOnce(uid: string, id: string, item: InboxItem): Promise<boolean> {
|
||||
@@ -74,7 +78,7 @@ export async function notifyEclipseTransition(
|
||||
await notifyMembers(fandomId, `eclipse_${fandomId}_${roundId}_${started ? 'start' : 'defended'}`, {
|
||||
eventType: 'eclipse', type: 'fandomEvent',
|
||||
title: started ? '내 행성의 이클립스가 발생했어요.' : '내 행성의 이클립스를 방어했어요.',
|
||||
source: name,
|
||||
source: name, fandomId, phase: started ? 'start' : 'defended',
|
||||
});
|
||||
return started ? 'started' : 'defended';
|
||||
}
|
||||
@@ -84,7 +88,7 @@ export async function notifyHeroDrop(fandomId: string, dropId: string, heroNickn
|
||||
const hero = typeof heroNickname === 'string' && heroNickname ? heroNickname : '팬';
|
||||
return notifyMembers(fandomId, `herodrop_${fandomId}_${dropId}`, {
|
||||
eventType: 'item_used', type: 'fandomEvent',
|
||||
title: `${hero}님의 고귀한 기여로 영웅의 선물이 발동되었습니다!`, source: '아이템',
|
||||
title: `${hero}님의 고귀한 기여로 영웅의 선물이 발동되었습니다!`, source: '아이템', fandomId,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -120,16 +124,21 @@ export async function notifyQuestDone(
|
||||
* 트리거가 한 번만 돈다. 재시도는 두지 않는다 — 재전달 시 같은 Push 가 두 번 갈 수 있다.
|
||||
* ponytail: 회원 전원 알림(이클립스 · 영웅의 선물)은 회원 수만큼 함수가 돈다. 수십만이 되면 토큰 묶음 발송으로.
|
||||
*/
|
||||
export async function pushInboxItem(uid: string, item: Partial<InboxItem>): Promise<UserPushOutcome | null> {
|
||||
export async function pushInboxItem(
|
||||
uid: string, item: Partial<InboxItem> & { campaignId?: string },
|
||||
): Promise<UserPushOutcome | null> {
|
||||
const category = item.type;
|
||||
if (!category || !PUSH_CATEGORIES.includes(category) || !item.title) return null;
|
||||
// 앱이 탭 시 실제 화면으로 가고(v1.5 8장 Deep Link) C-78 단계를 가르는 값만 싣는다.
|
||||
const extra = { targetPath: item.targetPath, campaignId: item.campaignId, phase: item.phase };
|
||||
return sendUserPush(uid, {
|
||||
category,
|
||||
title: 'FANUZU',
|
||||
body: item.title,
|
||||
...(item.fandomId ? { fandomId: item.fandomId } : {}),
|
||||
data: {
|
||||
kind: item.eventType ?? '',
|
||||
...(item.targetPath ? { targetPath: item.targetPath } : {}),
|
||||
...Object.fromEntries(Object.entries(extra).filter((e): e is [string, string] => typeof e[1] === 'string' && e[1] !== '')),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ it('이클립스 발생 · 방어는 회원 전원에게 한 건씩, 재전달
|
||||
const titles = items.map((n) => n.title).sort();
|
||||
expect(titles).toEqual(['내 행성의 이클립스가 발생했어요.', '내 행성의 이클립스를 방어했어요.']);
|
||||
expect(items.every((n) => n.eventType === 'eclipse' && n.source === 'BTS' && !n.targetPath)).toBe(true);
|
||||
// 앱이 단계(발생 = 위기 · 방어 = 결과)와 행성을 가른다.
|
||||
expect(items.map((n) => `${n.phase}:${n.fandomId}`).sort()).toEqual([`defended:${FANDOM}`, `start:${FANDOM}`]);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -54,6 +56,7 @@ it('영웅의 선물 발동은 회원 전원에게 발동자 닉네임으로', a
|
||||
expect(hero).toHaveLength(1);
|
||||
expect(hero[0]!.title).toBe('팜하니짱님의 고귀한 기여로 영웅의 선물이 발동되었습니다!');
|
||||
expect(hero[0]!.source).toBe('아이템');
|
||||
expect(hero[0]!.fandomId).toBe(FANDOM);
|
||||
});
|
||||
|
||||
it('퀘스트 완료는 횟수가 오른 때만 본인에게, 상세 경로를 싣는다', async () => {
|
||||
|
||||
@@ -74,3 +74,11 @@ it('토큰이 없으면 보내지 않는다 · 구분을 모르는 항목은 건
|
||||
expect(await pushInboxItem(U, { title: 'x' })).toBeNull();
|
||||
expect(sent).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('탭 이동 · C-78 단계에 쓰는 값을 싣는다 — 팬덤 · 이클립스 단계 · 캠페인 (v1.5 8장 Deep Link)', async () => {
|
||||
await getFirestore().doc(`users/${U}`).set({ fcmTokens: ['t1'] });
|
||||
expect(await pushInboxItem(U, { ...eclipse, fandomId: 'bts', phase: 'defended' })).toBe('sent');
|
||||
expect(await pushInboxItem(U, { eventType: 'campaign', type: 'essential', title: '정산', source: '', campaignId: 'c1' })).toBe('sent');
|
||||
expect(sent[0]!.data).toEqual({ category: 'fandomEvent', fandomId: 'bts', kind: 'eclipse', phase: 'defended' });
|
||||
expect(sent[1]!.data).toEqual({ category: 'essential', kind: 'campaign', campaignId: 'c1' });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user