Skip to content

Commit c2fceef

Browse files
committed
fix: 딥링크 테스트
1 parent 74d6ffc commit c2fceef

File tree

1 file changed

+84
-1
lines changed

1 file changed

+84
-1
lines changed

apps/tuk-web/src/app/invite/meet/[meetId]/src/components/InviteProposal.tsx

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,86 @@ import { BackgroundTemplate, Button } from '@/shared/components';
1111
import SkeletonGuard from '@/shared/components/SkeletonGuard';
1212
import { useParam } from '@/shared/hooks/useParam';
1313

14+
// === Deep Link: 강화 폴백 유틸 시작 ===
15+
const IOS_STORE_URL =
16+
'https://apps.apple.com/kr/app/%ED%88%AD-%EB%A7%8C%EB%82%8C%EC%9D%84-%EB%84%8C%EC%A7%80%EC%8B%9C/id6749781762';
17+
const ANDROID_STORE_URL = 'https://play.google.com/store/apps/details?id=com.plottwist.tuk';
18+
19+
function buildAndroidIntent(path: string, playUrl: string) {
20+
// path 예: "tuk/proposal-detail/123"
21+
return (
22+
`intent://${path}` +
23+
'#Intent;' +
24+
'scheme=tuk-app;' +
25+
'package=com.plottwist.tuk;' +
26+
`S.browser_fallback_url=${encodeURIComponent(playUrl)};` +
27+
'end'
28+
);
29+
}
30+
31+
function openProposalInApp(proposalId: number | string) {
32+
const ua = navigator.userAgent.toLowerCase();
33+
const isAndroid = ua.includes('android');
34+
35+
// 인앱 브라우저(대략) 감지 — 카톡/인스타 등
36+
const isInApp = /\b(kakaotalk|instagram|line|fb_iab|fbav|twitter|naver(inapp)?|daumapps)\b/.test(
37+
ua
38+
);
39+
40+
// 공통 스킴 (앱에서 열릴 실제 진입 위치)
41+
const iosScheme = `tuk-app://tuk/proposal-detail/${encodeURIComponent(String(proposalId))}`;
42+
const androidPath = `tuk/proposal-detail/${encodeURIComponent(String(proposalId))}`;
43+
44+
// Android: 가능하면 intent:// 로 한 방에 폴백 자동 처리
45+
if (isAndroid) {
46+
const intentUrl = buildAndroidIntent(androidPath, ANDROID_STORE_URL);
47+
// 일부 인앱 브라우저는 intent 처리에 제약이 있어도, 시도 → 스토어 폴백은 작동
48+
window.location.href = intentUrl;
49+
return;
50+
}
51+
52+
// iOS (및 기타): 스킴 시도 → 전환 신호 없으면 스토어로 replace
53+
const fallbackDelayMs = 1700; // 1500~1800 권장
54+
let timer: number | null = null;
55+
56+
const cleanup = () => {
57+
if (timer) {
58+
window.clearTimeout(timer);
59+
timer = null;
60+
}
61+
document.removeEventListener('visibilitychange', onHide, true);
62+
window.removeEventListener('pagehide', onHide, true);
63+
window.removeEventListener('blur', onHide, true);
64+
};
65+
const onHide = () => {
66+
// 앱으로 전환되면 페이지가 숨김/이탈/블러됨 → 타이머 취소
67+
cleanup();
68+
};
69+
70+
document.addEventListener('visibilitychange', onHide, true);
71+
window.addEventListener('pagehide', onHide, true);
72+
window.addEventListener('blur', onHide, true);
73+
74+
// 인앱 브라우저일 때 커스텀 스킴이 막히는 케이스가 흔하지만
75+
// 그래도 한번 시도 후 폴백(UX 유지)
76+
try {
77+
window.location.href = iosScheme;
78+
} catch {
79+
/* empty */
80+
}
81+
82+
timer = window.setTimeout(
83+
() => {
84+
// 전환 신호 없으면 미설치/차단으로 판단 → 스토어로 이동
85+
// replace를 써서 '뒤로 가기' 시 빈 페이지/루프 방지
86+
window.location.replace(IOS_STORE_URL);
87+
cleanup();
88+
},
89+
isInApp ? 1200 : fallbackDelayMs
90+
); // 인앱 브라우저는 약간 더 짧게
91+
}
92+
// === Deep Link: 강화 폴백 유틸 끝 ===
93+
1494
const InviteProposal = () => {
1595
const proposalId = Number(useParam('meetId'));
1696

@@ -34,7 +114,10 @@ const InviteProposal = () => {
34114
<BackgroundTemplate.CTA>
35115
<Button
36116
className="w-full"
37-
onClick={() => (window.location.href = `tuk-app://tuk/proposal-detail/${proposalId}`)}
117+
onClick={() => {
118+
// 사용자 제스처 내부에서 호출해야 브라우저가 내비게이션 차단 안 함
119+
openProposalInApp(proposalId);
120+
}}
38121
>
39122
초대장 확인하기
40123
</Button>

0 commit comments

Comments
 (0)