@cshah18/sdk 4.14.0 → 4.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cobuy-sdk.esm.js +1136 -60
- package/dist/cobuy-sdk.esm.js.map +1 -1
- package/dist/cobuy-sdk.umd.js +1136 -60
- package/dist/cobuy-sdk.umd.js.map +1 -1
- package/dist/types/core/api-client.d.ts +23 -1
- package/dist/types/core/endpoints.d.ts +3 -0
- package/dist/types/core/socket.d.ts +3 -2
- package/dist/types/core/types.d.ts +39 -0
- package/dist/types/ui/lobby/lobby-modal.d.ts +54 -2
- package/dist/types/ui/widget/widget-root.d.ts +6 -0
- package/package.json +1 -1
package/dist/cobuy-sdk.umd.js
CHANGED
|
@@ -840,6 +840,7 @@ var CoBuySDK = (function (exports) {
|
|
|
840
840
|
this.onGroupMemberJoined = (event) => {
|
|
841
841
|
const detail = event.detail || {};
|
|
842
842
|
console.log("eventttttt", detail);
|
|
843
|
+
const eventType = event.type;
|
|
843
844
|
const productId = detail.product_id;
|
|
844
845
|
const groupData = detail.group;
|
|
845
846
|
// Only process if this is for the current product
|
|
@@ -861,6 +862,16 @@ var CoBuySDK = (function (exports) {
|
|
|
861
862
|
if (typeof participantsCount === "number") {
|
|
862
863
|
this.groups[groupIndex].joined = participantsCount;
|
|
863
864
|
}
|
|
865
|
+
// If this session left the currently joined group, clear stale local membership state.
|
|
866
|
+
const leavingSessionId = detail.session_id;
|
|
867
|
+
if (eventType === "group:member:left" &&
|
|
868
|
+
this.currentSessionId &&
|
|
869
|
+
leavingSessionId === this.currentSessionId &&
|
|
870
|
+
this.currentJoinedGroupId === groupId) {
|
|
871
|
+
this.currentJoinedGroupId = null;
|
|
872
|
+
this.groups[groupIndex].isMember = false;
|
|
873
|
+
this.updateStartButtonState(false, this.isLoading);
|
|
874
|
+
}
|
|
864
875
|
this.logger.info(`[GroupListModal] Updated group ${groupId} - participants: ${participantsCount}`);
|
|
865
876
|
// Re-render the specific group card
|
|
866
877
|
this.updateGroupCard(groupId);
|
|
@@ -902,6 +913,7 @@ var CoBuySDK = (function (exports) {
|
|
|
902
913
|
return;
|
|
903
914
|
}
|
|
904
915
|
window.addEventListener("group:member:joined", this.handleGroupMemberJoinedEvent);
|
|
916
|
+
window.addEventListener("group:member:left", this.handleGroupMemberJoinedEvent);
|
|
905
917
|
this.socketListenerRegistered = true;
|
|
906
918
|
this.logger.debug("[GroupListModal] Socket event listeners registered");
|
|
907
919
|
}
|
|
@@ -911,6 +923,7 @@ var CoBuySDK = (function (exports) {
|
|
|
911
923
|
return;
|
|
912
924
|
}
|
|
913
925
|
window.removeEventListener("group:member:joined", this.handleGroupMemberJoinedEvent);
|
|
926
|
+
window.removeEventListener("group:member:left", this.handleGroupMemberJoinedEvent);
|
|
914
927
|
this.socketListenerRegistered = false;
|
|
915
928
|
this.logger.debug("[GroupListModal] Socket event listeners unregistered");
|
|
916
929
|
}
|
|
@@ -1042,13 +1055,11 @@ var CoBuySDK = (function (exports) {
|
|
|
1042
1055
|
console.log("groupsss", groups);
|
|
1043
1056
|
// If API reports membership, prefer it over cached state
|
|
1044
1057
|
const memberGroup = groups.find((g) => g.isMember);
|
|
1045
|
-
|
|
1046
|
-
this.currentJoinedGroupId = memberGroup.groupId;
|
|
1047
|
-
}
|
|
1058
|
+
this.currentJoinedGroupId = memberGroup ? memberGroup.groupId : null;
|
|
1048
1059
|
this.groups = groups;
|
|
1049
1060
|
this.liveCount = groups.length;
|
|
1050
1061
|
this.logger.info(`Fetched ${groups.length} active groups`);
|
|
1051
|
-
const hasMembership = Boolean(
|
|
1062
|
+
const hasMembership = Boolean(memberGroup);
|
|
1052
1063
|
this.updateStartButtonState(hasMembership, false);
|
|
1053
1064
|
// Update and show live count display
|
|
1054
1065
|
const liveCountText = document.getElementById("cobuy-live-count-text");
|
|
@@ -1406,7 +1417,7 @@ var CoBuySDK = (function (exports) {
|
|
|
1406
1417
|
createGroupCard(group) {
|
|
1407
1418
|
const card = document.createElement("div");
|
|
1408
1419
|
card.className = "cobuy-group-card";
|
|
1409
|
-
card.dataset.groupId = group.
|
|
1420
|
+
card.dataset.groupId = group.groupId;
|
|
1410
1421
|
const header = document.createElement("div");
|
|
1411
1422
|
header.className = "cobuy-group-card-header";
|
|
1412
1423
|
const groupId = document.createElement("div");
|
|
@@ -2159,15 +2170,24 @@ var CoBuySDK = (function (exports) {
|
|
|
2159
2170
|
}
|
|
2160
2171
|
}
|
|
2161
2172
|
|
|
2162
|
-
var css_248z = ".sr-only{clip:rect(0,0,0,0);border-width:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.cb-lobby-modal-container{height:100%;left:0;overflow-y:auto!important;position:fixed;top:0;width:100%;z-index:10000}.cb-lobby-modal-container *{box-sizing:border-box;margin:0;padding:0}.cb-lobby-modal-container body,.cb-lobby-modal-container html{font-family:Inter,sans-serif;height:100%;overflow-x:hidden;width:100%}.cb-lobby-main{backdrop-filter:blur(4px);background-color:color-mix(in oklab,#fff 5%,transparent);border-radius:50px;box-shadow:0 25px 50px -12px #00000040;padding:80px;position:relative;width:100%;z-index:1}.cb-lobby-bg{background-image:url(https://cobuy-dev.s3.af-south-1.amazonaws.com/public/sdk/cb-back-image.png);background-position:50%;background-repeat:no-repeat;background-size:cover;height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:-1}.cb-lobby-main-wp{align-items:center;display:flex;justify-content:center;padding:40px 80px}.lobby-indicator{align-items:center;backdrop-filter:blur(8px);background:rgba(16,185,129,.1);border:1px solid rgba(16,185,129,.3);border-radius:24px;display:flex;gap:12px;margin-bottom:16px;padding:6px 12px;width:fit-content}.lobby-indicator-logo{display:block;flex-shrink:0;height:34px;width:auto}.pulsing-dot{align-items:center;display:flex;height:8px;justify-content:center;position:relative;width:8px}.pulsing-dot:after{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.pulsing-dot:after,.pulsing-dot:before{background:#10b981;border-radius:50%;content:\"\";height:100%;position:absolute;width:100%}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.indicator-text{color:#047857;font-size:11px;font-weight:800;letter-spacing:.5px;line-height:1;text-transform:uppercase}.lobby-status-section{display:flex;flex-direction:column;margin-bottom:24px}.lobby-status{background:#fff;border-radius:4px;color:#000;font-size:10px;font-weight:600;line-height:1.2;padding:6px 10px;text-transform:uppercase}.lobby-status.active{background:#155dfc;color:#fff}.lobby-status.complete{background:#10b981;color:#fff}.lobby-status-wp{align-items:center;display:flex;flex-wrap:wrap;gap:10px}.lobby-number{backdrop-filter:blur(3px);background:hsla(0,0%,100%,.2);border-radius:4px;box-shadow:0 25px 50px -12px #00000040;font-size:12px;font-weight:700;letter-spacing:1px;line-height:1.2;padding:5px 8px;text-transform:uppercase}.title-wp{margin-top:25px}.title-wp h2{font-size:60px;font-weight:900;line-height:1;margin-bottom:15px}.sub-title{-webkit-text-fill-color:transparent;animation:gradient-flow 4s linear infinite;background-clip:text;-webkit-background-clip:text;background-image:linear-gradient(90deg,#1e293b,#2563eb 25%,#1e293b 50%);background-size:200% auto;color:#1e293b;font-size:16px;font-weight:700;line-height:1.5;margin-bottom:0}@keyframes gradient-flow{0%{background-position:200% 0}to{background-position:-200% 0}}.sub-title.completed{-webkit-text-fill-color:unset;background-clip:unset;-webkit-background-clip:unset;background-image:none;color:#1e293b}.connected-section{backdrop-filter:blur(4px);background:hsla(0,0%,100%,.05);border:1px solid hsla(0,0%,100%,.1);border-radius:24px;box-shadow:0 1px 2px 0 rgba(0,0,0,.05);display:flex;flex-direction:column;gap:20px;margin-top:24px;padding:24px;transition:all .3s ease}.connected-section:hover{background:hsla(0,0%,100%,.1);box-shadow:0 2px 4px 0 rgba(0,0,0,.08)}.link-share-container{display:flex;flex-direction:column}.link-share-wrapper{align-items:center;display:flex;gap:12px;width:100%}.lobby-link-box{align-items:center;backdrop-filter:blur(8px);background-color:hsla(0,0%,100%,.4);border:1px solid hsla(0,0%,100%,.3);border-radius:14px;box-shadow:0 1px 2px 0 rgba(0,0,0,.05);display:flex;flex:1;gap:16px;justify-content:space-between;min-height:50px;padding:16px 20px;transition:all .2s ease}.lobby-link-box:hover{background-color:hsla(0,0%,100%,.5);box-shadow:0 2px 4px 0 rgba(0,0,0,.08)}.lobby-link-text{color:#71717a;font-size:10px;font-weight:700;letter-spacing:.6px;line-height:1.2;margin-bottom:6px;text-transform:uppercase}.copy-link-btn{align-items:center;background:transparent;border:none;border-radius:6px;color:#64748b;cursor:pointer;display:flex;flex-shrink:0;font-size:13px;font-weight:600;gap:6px;justify-content:center;padding:0;transition:all .2s ease;white-space:nowrap}.copy-link-btn:hover{color:#1e293b}.copy-link-btn .copy-text{display:none}.copy-link-btn svg{flex-shrink:0;height:18px;width:18px}@media (min-width:640px){.copy-link-btn .copy-text{display:inline}}.lobby-link-url{color:#1e293b;font-size:15px;font-weight:700;line-height:1.4;word-break:break-all}.link-box-container{flex:1;min-width:0}.share-btn{align-items:center;background:#1e293b;border:none;border-radius:14px;box-shadow:0 2px 4px 0 rgba(0,0,0,.1);color:#fff;cursor:pointer;display:flex;flex-shrink:0;font-size:15px;font-weight:600;gap:8px;height:50px;justify-content:center;min-width:auto;padding:35px 24px;transition:all .2s ease}.share-btn .share-text{color:#fff;display:inline;font-size:18px;margin:0!important}.share-btn svg{color:#fff;flex-shrink:0;height:18px;width:18px}@media (max-width:640px){.share-btn{font-size:14px;height:50px;padding:0 18px}.share-btn .share-text{color:#fff;display:inline}}.share-btn:hover{background:#0f172a;box-shadow:0 4px 8px 0 rgba(0,0,0,.15);transform:translateY(-1px)}.share-btn:active{transform:scale(.95)}.lobby-offer-box{align-items:center;backdrop-filter:blur(8px);background-color:rgba(59,130,246,.063);border:1px solid rgba(59,130,246,.125);border-radius:1rem;display:flex;gap:30px;margin-top:30px;padding:15px 20px}.offer-box-icon{align-items:center;background:linear-gradient(135deg,#3b82f6,rgba(59,130,246,.867));border-radius:10px;box-shadow:0 10px 15px -3px rgba(59,130,246,.314);color:#fff;cursor:pointer;display:flex;height:56px;justify-content:center;padding:5px;width:56px}.offer-lock-status{align-items:center;display:flex;gap:6px;margin-bottom:2px}.offer-lock-status span{font-size:14px;font-weight:700;line-height:1}.offer-box-content h3{font-size:30px;font-weight:900;line-height:1.2}.cb-lobby-top{display:grid;gap:100px;grid-template-columns:7fr 5fr}.group-info{backdrop-filter:blur(8px);background-color:color-mix(in oklab,#fff 5%,transparent);border-radius:20px;box-shadow:0 10px 15px -3px #0000001a;padding:30px}.progress-header{align-items:center;display:flex;justify-content:space-between;margin-bottom:14px}.progress-header .title{align-items:center;color:#000;display:flex;font-size:14px;font-weight:900;justify-content:center;letter-spacing:.7px;position:relative}.progress-badge{background:#3b82f6;border-radius:999px;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;color:#fff;font-size:13px;font-weight:700;padding:6px 12px}.cb-lobby-modal-container .progress-bar{background:#fff;border-radius:999px;height:14px;overflow:hidden;width:100%}.cb-lobby-modal-container .progress-fill{animation:shimmer 2.7s linear infinite;background:#3b82f6;background-image:linear-gradient(120deg,hsla(0,0%,100%,.15) 25%,hsla(0,0%,100%,.45) 37%,hsla(0,0%,100%,.15) 63%);background-size:200% 100%;border-radius:999px;height:100%;position:relative;transition:width .6s ease;width:0}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.progress-labels{color:#474d56;display:flex;font-size:12px;justify-content:space-between;margin-top:8px}.team-card{margin-top:40px}.team-card .icon-box svg{width:16px}.team-card .team-header{align-items:center;display:flex;justify-content:space-between}.team-card .team-title{align-items:center;display:flex;font-size:14px;font-weight:600;gap:12px;letter-spacing:1px}.team-card .icon-box{align-items:center;background-color:#3b82f6;border-radius:.5rem;box-shadow:0 10px 15px -3px #3b82f64d;color:#fff;display:flex;font-size:18px;height:2rem;justify-content:center;width:2rem}.team-card .team-title span{color:#000;font-size:14px;font-weight:900;letter-spacing:.7px}.team-card .team-count{background-color:#3b82f6;border-radius:9999px;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;color:#fff;font-size:13px;font-weight:700;letter-spacing:.7px;padding:6px 12px}.team-card .team-members{align-items:center;display:flex;gap:14px;justify-content:center;margin-top:20px}.team-card .member{border:3px solid #fff;border-radius:50%;box-shadow:0 4px 12px #0000001a;font-size:22px;height:56px;position:relative;width:56px}.team-card .member,.team-card .member:after{align-items:center;color:#fff;display:flex;justify-content:center}.team-card .member:after{background:#3b82f6;background-image:url(https://cobuy-dev.s3.af-south-1.amazonaws.com/public/sdk/tick-mark-icon.svg);background-position:50%;background-repeat:no-repeat;background-size:75%;border:2px solid #fff;border-radius:50%;bottom:-4px;content:\"\";height:12px;padding:2px;position:absolute;right:-4px;width:12px}.team-card .member.blue{background:#2563eb}.team-card .member.purple{background:#9333ea}.team-card .member.pink{background:#ec4899}.team-card .member.orange{background:#f97316}.team-card .member.empty{background:#f8fafc;color:#9ca3af}.team-card .member.empty:after{display:none}.time-card{align-items:center;backdrop-filter:blur(8px);background:hsla(0,0%,100%,.2);border:1px solid hsla(0,0%,100%,.2);border-radius:20px;display:flex;gap:14px;margin-top:30px;padding:15px}.time-card .time-icon{align-items:center;background:#3b82f6;border-radius:14px;color:#fff;display:flex;flex-shrink:0;font-size:22px;height:48px;justify-content:center;width:48px}.time-card .time-content{display:flex;flex-direction:column}.time-card .time-label{color:#4b5563;font-size:12px;font-weight:600;letter-spacing:.6px}.time-card .time-value{color:#111827;font-size:22px;font-weight:900}.group-info-box{backdrop-filter:blur(8px);background:color-mix(in oklab,#fff 5%,transparent);border:1px solid color-mix(in oklab,#fff 20%,transparent);border-radius:1.5rem;box-shadow:0 10px 15px -3px #0000001a;padding:30px}.offer-lock-status svg{height:16px;width:16px}.cb-lobby-modal-container .offer-box-content .reward-text{background:unset;border:none;color:#000;font-size:14px;line-height:1;margin-top:4px}.progress-header .title:before{background:#3b82f6;border-radius:50%;content:\"\";display:inline-block;height:8px;margin-right:8px;position:relative;width:8px}.live-activity-wrapper{backdrop-filter:blur(8px);background-color:color-mix(in oklab,#fff 80%,transparent);border-radius:18px;box-shadow:0 30px 80px rgba(0,0,0,.15);padding:16px}.live-activity-header{display:flex;gap:10px;justify-content:space-between;margin-bottom:12px}.live-activity-header .title{align-items:center;color:#000;display:flex;font-size:14px;font-weight:900;gap:8px;letter-spacing:.7px}.live-activity-header .dot{background:#3b82f6;border-radius:50%;height:8px;position:relative;width:8px}.live-activity-header .dot:after{animation:livePulse 1.6s ease-out infinite;background:rgba(59,130,246,.6);border-radius:50%;content:\"\";inset:0;position:absolute}@keyframes livePulse{0%{opacity:.8;transform:scale(1)}70%{opacity:0;transform:scale(2.4)}to{opacity:0}}.activity-stats{align-items:center;display:flex;gap:8px}.activity-stats-badge{background-color:rgba(59,130,246,.082);border-radius:999px;color:#3b82f6;font-size:12px;font-weight:500;line-height:1;padding:4px 10px}.activity-stats-badge.light{background-color:#f9f3f4;color:#45556c}.activity-list{height:104px;overflow:hidden;position:relative}.activity-card{align-items:center;background:linear-gradient(90deg,#fff,#f2f6ff);border:1px solid #dbeafe;border-radius:14px;display:flex;gap:10px;height:58px;inset:0;padding:12px 10px;position:absolute;transition:transform .6s cubic-bezier(.22,.61,.36,1),opacity .6s}.activity-card .text{font-size:14px}.activity-card .avatar{align-items:center;border-radius:50%;color:#fff;display:flex;flex:0 0 30px;height:30px;justify-content:center;width:30px}.activity-card .pink{background:linear-gradient(135deg,#ec4899,#f472b6)}.activity-card .purple{background:linear-gradient(135deg,#8b5cf6,#a78bfa)}.activity-card .blue{background:linear-gradient(135deg,#3b82f6,#60a5fa)}.activity-card .green{background:linear-gradient(135deg,#10b981,#34d399)}.activity-card .orange{background:linear-gradient(135deg,#f97316,#fb923c)}.activity-card .text p{color:#6b7280;font-size:12px;margin:2px 0 0}.activity-card .time{color:#94a3b8;font-size:10px;margin-left:auto}.lobby-activity-wp{backdrop-filter:blur(12px);background-color:hsla(0,0%,100%,.4);border:1px solid hsla(0,0%,100%,.2);border-radius:25px;box-shadow:0 1px 3px 0 #0000001a;margin-top:50px;padding:8px}.lobby-close-icon{align-items:center;background-color:color-mix(in oklab,#000 80%,transparent);border-radius:50%;color:#fff;cursor:pointer;display:flex;height:34px;justify-content:center;position:fixed;right:30px;top:30px;width:34px;z-index:99}.lobby-close-icon svg{height:20px;width:20px}@media screen and (max-width:1600px){.cb-lobby-main{border-radius:30px;padding:50px}.title-wp h2{font-size:48px;margin-bottom:12px}.sub-title{font-size:16px}.title-wp{margin-top:20px}.lobby-link-section{margin-top:30px}.offer-box-content h3{font-size:26px}.lobby-offer-box{gap:24px;padding:15px}.cb-lobby-top{gap:80px}.group-info-box{border-radius:20px;padding:22px}.team-card{margin-top:30px}.team-card .team-members{margin-top:12px}.lobby-activity-wp{margin-top:40px}.lobby-close-icon{height:30px;top:20px;width:30px}}@media screen and (max-width:1280px){.cb-lobby-main,.cb-lobby-main-wp{padding:40px}.title-wp h2{font-size:42px}.cb-lobby-top{gap:60px}}@media screen and (max-width:1120px){.title-wp h2{font-size:38px}}@media screen and (max-width:991px){.cb-lobby-top{gap:30px;grid-template-columns:1fr}.cb-lobby-main{border-radius:15px;padding:30px}.cb-lobby-main-wp{padding:30px}.lobby-close-icon{right:20px}.lobby-link-box{border-radius:10px}.share-btn{border-radius:8px}.offer-box-content h3{font-size:24px}.lobby-activity-wp,.lobby-offer-box,.time-card{border-radius:15px;margin-top:20px}.live-activity-wrapper{border-radius:10px}.group-info-box{border-radius:15px}}@media screen and (max-width:767px){.link-share-wrapper{flex-direction:column!important}}@media screen and (max-width:575px){.cb-lobby-main,.cb-lobby-main-wp{padding:20px}.title-wp h2{font-size:30px}.lobby-link-text{font-size:11px;margin-bottom:4px}.lobby-link-url{font-size:14px}.lobby-link-box{min-height:48px;padding:12px 16px}.link-share-wrapper{gap:10px}.share-btn{border-radius:8px;font-size:13px;height:48px;min-width:auto;padding:0 14px}.lobby-offer-box{padding:10px}.offer-box-content h3{font-size:20px}.offer-box-content .reward-text{font-size:13px}.group-info-box{padding:13px}.progress-header .title,.team-card .team-title span{font-size:13px}.team-card .member{height:40px;width:40px}.team-card .member:after{height:8px;width:8px}.team-card .team-members{gap:10px;margin-top:12px}.time-card{padding:13px}.team-card{margin-top:22px}.activity-card .text{font-size:12px}.activity-card .text p{font-size:11px}.live-activity-header .title{font-size:12px}.time-card .time-value{font-size:20px}}@media screen and (max-width:480px){.cb-lobby-main-wp{padding:20px 12px}.cb-lobby-main{padding:15px 12px}.lobby-status{font-size:9px}.lobby-number{font-size:10px}.title-wp h2{font-size:28px;margin-bottom:7px}.sub-title{font-size:14px}.lobby-link-section{gap:10px;margin-top:20px}.lobby-offer-box{gap:12px}.offer-box-icon{height:45px;width:45px}.offer-box-content .reward-text,.offer-lock-status span{font-size:12px}.cb-lobby-top{gap:20px}.lobby-close-icon{right:10px;top:10px}.live-activity-header{flex-direction:column;gap:5px}.activity-card{border-radius:9px;padding:6px}}.share-overlay{align-items:center;background:rgba(0,0,0,.45);display:flex;inset:0;justify-content:center;opacity:0;position:fixed;transition:.3s ease;visibility:hidden;z-index:999}.share-overlay.active{opacity:1;visibility:visible}.share-popup{background:#fff;border-radius:18px;max-height:90%;overflow:auto;padding:24px;position:relative;transform:translateY(30px);transition:.35s ease;width:420px}.share-overlay.active .share-popup{transform:translateY(0)}.share-popup .close-btn{align-items:center;background:#f3f4f6;border:none;border-radius:50%;cursor:pointer;display:flex;height:32px;justify-content:center;position:absolute;right:14px;top:14px;transition:.3s;width:32px}.share-popup .close-btn:hover{background:#eeecec}.share-popup h2{font-size:22px;font-weight:600;line-height:1;margin-bottom:10px}.share-popup .subtitle{color:#4a5565;font-size:14px;margin:6px 0 30px}.share-popup .share-grid{display:grid;gap:14px;grid-template-columns:repeat(2,1fr)}.share-popup .share-card{align-items:center;background:#f2f6f9;border-radius:14px;cursor:pointer;display:flex;flex-direction:column;padding:16px;position:relative;text-align:center;transition:.25s ease}.share-popup .share-card:hover{transform:translateY(-3px)}.share-popup .share-card .icon{align-items:center;background:#1877f2;border-radius:50%;color:#fff;display:flex;font-size:30px;height:50px;justify-content:center;margin-bottom:8px;text-align:center;width:50px}.share-popup .share-card span{font-size:13px;font-weight:500}.share-popup .share-card.whatsapp{background:#ecfdf5;border:2px solid #22c55e;color:#22c55e}.share-popup .share-card.whatsapp .icon{background:#22c55e}.share-popup .share-card .badge{background:#2563eb;border-radius:12px;color:#fff;font-size:11px;padding:4px 10px;position:absolute;right:10px;top:-8px}.share-popup .link-box{background:#fbf9fa;border:1px solid #ebe6e7;border-radius:14px;margin-top:18px;padding:14px}.share-popup .link-box label{color:#64748b;font-size:13px}.share-popup .link-row{display:flex;gap:8px;margin-top:6px}.share-popup .link-row input{background:transparent;border:none;color:#334155;flex:1;font-size:13px;letter-spacing:.8px;line-height:1.2;outline:none}.share-popup .link-row button{align-items:center;background:#fff;border:1px solid #ebe6e7;border-radius:10px;cursor:pointer;display:flex;height:35px;justify-content:center;padding:6px 8px;width:35px}.share-popup .success{color:#2563eb;display:block;font-size:12px;margin-top:6px;opacity:0;transition:opacity .3s}.share-popup .success.show{opacity:1}.share-popup .footer-text{color:#64748b;font-size:12px;margin-top:14px;text-align:center}.share-popup .share-card.twitter .icon{background:#000}.share-popup .share-card.facebook .icon{background:#1877f2}.share-popup .share-card.sms .icon{background:#059669}.share-popup .share-card.copied .icon{background:#2563eb}@keyframes entrance-fade-in{0%{opacity:0}to{opacity:1}}@keyframes entrance-scale-in{0%{opacity:0;transform:scale(.9) translateY(20px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes entrance-text-fade-in{0%{opacity:0}to{opacity:1}}@keyframes exit-blur-scale{0%{filter:blur(0);opacity:1;transform:scale(1)}to{filter:blur(10px);opacity:0;transform:scale(1.1)}}@keyframes pulse-dot{0%,to{opacity:1}50%{opacity:.5}}.entrance-animation-overlay{align-items:center;animation:entrance-fade-in .8s ease-in-out forwards;background-color:#0f172a;color:#fff;display:flex;flex-direction:column;inset:0;justify-content:center;position:fixed;z-index:9999}.entrance-animation-overlay.exit{animation:exit-blur-scale .8s ease-in-out forwards}.entrance-content{align-items:center;animation:entrance-scale-in .5s ease-out .2s both;display:flex;flex-direction:column}.entrance-icon-box{align-items:center;background:#2563eb;border-radius:16px;box-shadow:0 10px 25px rgba(37,99,235,.2);display:flex;height:64px;justify-content:center;margin-bottom:24px;width:64px}.entrance-icon-box svg{color:#fff;height:32px;width:32px}.entrance-title{font-size:32px;font-weight:700;letter-spacing:-.5px;margin-bottom:12px}@media (min-width:768px){.entrance-title{font-size:48px}}.entrance-message{align-items:center;animation:entrance-text-fade-in .5s ease-out .5s both;color:#60a5fa;display:flex;font-size:18px;font-weight:500;gap:8px}.entrance-pulse-dot{animation:pulse-dot 1.5s ease-in-out infinite;background-color:#60a5fa;border-radius:50%;height:8px;width:8px}";
|
|
2173
|
+
var css_248z = ".sr-only{clip:rect(0,0,0,0);border-width:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.cb-lobby-modal-container{height:100%;left:0;overflow-y:auto!important;position:fixed;top:0;width:100%;z-index:10000}.cb-lobby-modal-container *{box-sizing:border-box;margin:0;padding:0}.cb-lobby-modal-container body,.cb-lobby-modal-container html{font-family:Inter,sans-serif;height:100%;overflow-x:hidden;width:100%}.cb-lobby-main{backdrop-filter:blur(4px);background-color:color-mix(in oklab,#fff 5%,transparent);border-radius:50px;box-shadow:0 25px 50px -12px #00000040;padding:80px;position:relative;width:100%;z-index:1}.cb-lobby-bg{background-image:url(https://cobuy-dev.s3.af-south-1.amazonaws.com/public/sdk/cb-back-image.png);background-position:50%;background-repeat:no-repeat;background-size:cover;height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:-1}.cb-lobby-main-wp{align-items:center;display:flex;justify-content:center;padding:40px 80px}.lobby-indicator{align-items:center;backdrop-filter:blur(8px);background:rgba(16,185,129,.1);border:1px solid rgba(16,185,129,.3);border-radius:24px;display:flex;gap:12px;margin-bottom:16px;padding:6px 12px;width:fit-content}.lobby-indicator-logo{display:block;flex-shrink:0;height:34px;width:auto}.pulsing-dot{align-items:center;display:flex;height:8px;justify-content:center;position:relative;width:8px}.pulsing-dot:after{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.pulsing-dot:after,.pulsing-dot:before{background:#10b981;border-radius:50%;content:\"\";height:100%;position:absolute;width:100%}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.indicator-text{color:#047857;font-size:11px;font-weight:800;letter-spacing:.5px;line-height:1;text-transform:uppercase}.lobby-status-section{display:flex;flex-direction:column;margin-bottom:24px}.lobby-status{background:#fff;border-radius:4px;color:#000;font-size:10px;font-weight:600;line-height:1.2;padding:6px 10px;text-transform:uppercase}.lobby-status.active{background:#155dfc;color:#fff}.lobby-status.complete{background:#10b981;color:#fff}.lobby-status-wp{align-items:center;display:flex;flex-wrap:wrap;gap:10px}.lobby-number{backdrop-filter:blur(3px);background:hsla(0,0%,100%,.2);border-radius:4px;box-shadow:0 25px 50px -12px #00000040;font-size:12px;font-weight:700;letter-spacing:1px;line-height:1.2;padding:5px 8px;text-transform:uppercase}.title-wp{margin-top:25px}.title-wp h2{font-size:60px;font-weight:900;line-height:1;margin-bottom:15px}.sub-title{-webkit-text-fill-color:transparent;animation:gradient-flow 4s linear infinite;background-clip:text;-webkit-background-clip:text;background-image:linear-gradient(90deg,#1e293b,#2563eb 25%,#1e293b 50%);background-size:200% auto;color:#1e293b;font-size:16px;font-weight:700;line-height:1.5;margin-bottom:0}@keyframes gradient-flow{0%{background-position:200% 0}to{background-position:-200% 0}}.sub-title.completed{-webkit-text-fill-color:unset;background-clip:unset;-webkit-background-clip:unset;background-image:none;color:#1e293b}.connected-section{backdrop-filter:blur(4px);background:hsla(0,0%,100%,.05);border:1px solid hsla(0,0%,100%,.1);border-radius:24px;box-shadow:0 1px 2px 0 rgba(0,0,0,.05);display:flex;flex-direction:column;gap:20px;margin-top:24px;padding:24px;transition:all .3s ease}.connected-section:hover{background:hsla(0,0%,100%,.1);box-shadow:0 2px 4px 0 rgba(0,0,0,.08)}.lobby-contact-protection-banner{align-items:center;background:#fffbeb;border:1px solid #f59e0b;border-radius:12px;display:flex;gap:12px;justify-content:space-between;padding:12px 14px}.lobby-contact-protection-copy{color:#7c2d12;font-size:13px;font-weight:600;line-height:1.4}.lobby-contact-protection-btn{background:#1e293b;border:none;border-radius:8px;color:#fff;cursor:pointer;font-size:12px;font-weight:700;padding:8px 12px;white-space:nowrap}.lobby-contact-protection-btn:hover{background:#0f172a}.lobby-protected-leave-container{align-items:center;background:#f0fdf4;border:1px solid #86efac;border-radius:12px;display:none;gap:12px;justify-content:space-between;padding:12px 14px}.lobby-protected-leave-copy{color:#166534;font-size:13px;font-weight:600;line-height:1.4}.lobby-protected-leave-btn{background:#b91c1c;border:none;border-radius:8px;color:#fff;cursor:pointer;font-size:12px;font-weight:700;padding:8px 12px;white-space:nowrap}.lobby-protected-leave-btn:hover{background:#991b1b}.lobby-contact-overlay{align-items:center;background:rgba(2,6,23,.5);display:flex;inset:0;justify-content:center;padding:20px;position:fixed;z-index:10020}.lobby-contact-card{background:#fff;border-radius:16px;box-shadow:0 12px 32px rgba(15,23,42,.25);display:flex;flex-direction:column;gap:12px;padding:20px;width:min(460px,100%)}.lobby-contact-title{color:#0f172a;font-size:22px;font-weight:800;line-height:1.2}.lobby-contact-subtitle{color:#334155;font-size:14px;line-height:1.5}.lobby-contact-input{border:1px solid #cbd5e1;border-radius:10px;font-size:14px;outline:none;padding:11px 12px;width:100%}.lobby-contact-input:focus{border-color:#1d4ed8;box-shadow:0 0 0 3px rgba(59,130,246,.2)}.lobby-contact-actions{align-items:center;display:flex;flex-wrap:wrap;gap:10px}.lobby-contact-danger,.lobby-contact-primary,.lobby-contact-secondary{border:none;border-radius:10px;cursor:pointer;font-size:13px;font-weight:700;padding:10px 14px}.lobby-contact-danger:disabled,.lobby-contact-primary:disabled,.lobby-contact-secondary:disabled{cursor:not-allowed;opacity:.7}.lobby-contact-primary.is-loading{padding-right:34px;position:relative}.lobby-contact-primary.is-loading:after{animation:lobby-spin .8s linear infinite;border:2px solid hsla(0,0%,100%,.45);border-radius:50%;border-top-color:#fff;content:\"\";height:14px;margin-top:-7px;position:absolute;right:12px;top:50%;width:14px}@keyframes lobby-spin{to{transform:rotate(1turn)}}.lobby-contact-primary{background:#1d4ed8;color:#fff}.lobby-contact-primary:hover{background:#1e40af}.lobby-contact-secondary{background:#e2e8f0;color:#1e293b}.lobby-contact-secondary:hover{background:#cbd5e1}.lobby-contact-danger{background:#fee2e2;color:#991b1b}.lobby-contact-danger:hover{background:#fecaca}.lobby-contact-toast{background:#0f172a;border-radius:10px;bottom:30px;box-shadow:0 8px 20px rgba(0,0,0,.25);color:#fff;font-size:13px;font-weight:600;left:50%;padding:10px 14px;position:fixed;transform:translateX(-50%);z-index:10030}.lobby-blocking-loader-overlay{align-items:center;background:rgba(2,6,23,.45);display:flex;inset:0;justify-content:center;padding:20px;position:fixed;z-index:10035}.lobby-blocking-loader-card{align-items:center;background:#fff;border-radius:12px;box-shadow:0 12px 30px rgba(15,23,42,.24);display:flex;gap:10px;padding:16px 18px}.lobby-blocking-loader-spinner{animation:lobby-spin .8s linear infinite;border:2px solid #cbd5e1;border-radius:50%;border-top-color:#1d4ed8;height:16px;width:16px}.lobby-blocking-loader-text{color:#0f172a;font-size:14px;font-weight:600;margin:0}.link-share-container{display:flex;flex-direction:column}.link-share-wrapper{align-items:center;display:flex;gap:12px;width:100%}.lobby-link-box{align-items:center;backdrop-filter:blur(8px);background-color:hsla(0,0%,100%,.4);border:1px solid hsla(0,0%,100%,.3);border-radius:14px;box-shadow:0 1px 2px 0 rgba(0,0,0,.05);display:flex;flex:1;gap:16px;justify-content:space-between;min-height:50px;padding:16px 20px;transition:all .2s ease}.lobby-link-box:hover{background-color:hsla(0,0%,100%,.5);box-shadow:0 2px 4px 0 rgba(0,0,0,.08)}.lobby-link-text{color:#71717a;font-size:10px;font-weight:700;letter-spacing:.6px;line-height:1.2;margin-bottom:6px;text-transform:uppercase}.copy-link-btn{align-items:center;background:transparent;border:none;border-radius:6px;color:#64748b;cursor:pointer;display:flex;flex-shrink:0;font-size:13px;font-weight:600;gap:6px;justify-content:center;padding:0;transition:all .2s ease;white-space:nowrap}.copy-link-btn:hover{color:#1e293b}.copy-link-btn .copy-text{display:none}.copy-link-btn svg{flex-shrink:0;height:18px;width:18px}@media (min-width:640px){.copy-link-btn .copy-text{display:inline}}@media (max-width:640px){.lobby-contact-protection-banner{align-items:flex-start;flex-direction:column}.lobby-contact-protection-btn{width:100%}.lobby-protected-leave-container{align-items:flex-start;flex-direction:column}.lobby-protected-leave-btn{width:100%}}.lobby-link-url{color:#1e293b;font-size:15px;font-weight:700;line-height:1.4;word-break:break-all}.link-box-container{flex:1;min-width:0}.share-btn{align-items:center;background:#1e293b;border:none;border-radius:14px;box-shadow:0 2px 4px 0 rgba(0,0,0,.1);color:#fff;cursor:pointer;display:flex;flex-shrink:0;font-size:15px;font-weight:600;gap:8px;height:50px;justify-content:center;min-width:auto;padding:35px 24px;transition:all .2s ease}.share-btn .share-text{color:#fff;display:inline;font-size:18px;margin:0!important}.share-btn svg{color:#fff;flex-shrink:0;height:18px;width:18px}@media (max-width:640px){.share-btn{font-size:14px;height:50px;padding:0 18px}.share-btn .share-text{color:#fff;display:inline}}.share-btn:hover{background:#0f172a;box-shadow:0 4px 8px 0 rgba(0,0,0,.15);transform:translateY(-1px)}.share-btn:active{transform:scale(.95)}.lobby-offer-box{align-items:center;backdrop-filter:blur(8px);background-color:rgba(59,130,246,.063);border:1px solid rgba(59,130,246,.125);border-radius:1rem;display:flex;gap:30px;margin-top:30px;padding:15px 20px}.offer-box-icon{align-items:center;background:linear-gradient(135deg,#3b82f6,rgba(59,130,246,.867));border-radius:10px;box-shadow:0 10px 15px -3px rgba(59,130,246,.314);color:#fff;cursor:pointer;display:flex;height:56px;justify-content:center;padding:5px;width:56px}.offer-lock-status{align-items:center;display:flex;gap:6px;margin-bottom:2px}.offer-lock-status span{font-size:14px;font-weight:700;line-height:1}.offer-box-content h3{font-size:30px;font-weight:900;line-height:1.2}.cb-lobby-top{display:grid;gap:100px;grid-template-columns:7fr 5fr}.group-info{backdrop-filter:blur(8px);background-color:color-mix(in oklab,#fff 5%,transparent);border-radius:20px;box-shadow:0 10px 15px -3px #0000001a;padding:30px}.progress-header{align-items:center;display:flex;justify-content:space-between;margin-bottom:14px}.progress-header .title{align-items:center;color:#000;display:flex;font-size:14px;font-weight:900;justify-content:center;letter-spacing:.7px;position:relative}.progress-badge{background:#3b82f6;border-radius:999px;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;color:#fff;font-size:13px;font-weight:700;padding:6px 12px}.cb-lobby-modal-container .progress-bar{background:#fff;border-radius:999px;height:14px;overflow:hidden;width:100%}.cb-lobby-modal-container .progress-fill{animation:shimmer 2.7s linear infinite;background:#3b82f6;background-image:linear-gradient(120deg,hsla(0,0%,100%,.15) 25%,hsla(0,0%,100%,.45) 37%,hsla(0,0%,100%,.15) 63%);background-size:200% 100%;border-radius:999px;height:100%;position:relative;transition:width .6s ease;width:0}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.progress-labels{color:#474d56;display:flex;font-size:12px;justify-content:space-between;margin-top:8px}.team-card{margin-top:40px}.team-card .icon-box svg{width:16px}.team-card .team-header{align-items:center;display:flex;justify-content:space-between}.team-card .team-title{align-items:center;display:flex;font-size:14px;font-weight:600;gap:12px;letter-spacing:1px}.team-card .icon-box{align-items:center;background-color:#3b82f6;border-radius:.5rem;box-shadow:0 10px 15px -3px #3b82f64d;color:#fff;display:flex;font-size:18px;height:2rem;justify-content:center;width:2rem}.team-card .team-title span{color:#000;font-size:14px;font-weight:900;letter-spacing:.7px}.team-card .team-count{background-color:#3b82f6;border-radius:9999px;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;color:#fff;font-size:13px;font-weight:700;letter-spacing:.7px;padding:6px 12px}.team-card .team-members{align-items:center;display:flex;gap:14px;justify-content:center;margin-top:20px}.team-card .member{border:3px solid #fff;border-radius:50%;box-shadow:0 4px 12px #0000001a;font-size:22px;height:56px;position:relative;width:56px}.team-card .member,.team-card .member:after{align-items:center;color:#fff;display:flex;justify-content:center}.team-card .member:after{background:#3b82f6;background-image:url(https://cobuy-dev.s3.af-south-1.amazonaws.com/public/sdk/tick-mark-icon.svg);background-position:50%;background-repeat:no-repeat;background-size:75%;border:2px solid #fff;border-radius:50%;bottom:-4px;content:\"\";height:12px;padding:2px;position:absolute;right:-4px;width:12px}.team-card .member.blue{background:#2563eb}.team-card .member.purple{background:#9333ea}.team-card .member.pink{background:#ec4899}.team-card .member.orange{background:#f97316}.team-card .member.empty{background:#f8fafc;color:#9ca3af}.team-card .member.empty:after{display:none}.time-card{align-items:center;backdrop-filter:blur(8px);background:hsla(0,0%,100%,.2);border:1px solid hsla(0,0%,100%,.2);border-radius:20px;display:flex;gap:14px;margin-top:30px;padding:15px}.time-card .time-icon{align-items:center;background:#3b82f6;border-radius:14px;color:#fff;display:flex;flex-shrink:0;font-size:22px;height:48px;justify-content:center;width:48px}.time-card .time-content{display:flex;flex-direction:column}.time-card .time-label{color:#4b5563;font-size:12px;font-weight:600;letter-spacing:.6px}.time-card .time-value{color:#111827;font-size:22px;font-weight:900}.group-info-box{backdrop-filter:blur(8px);background:color-mix(in oklab,#fff 5%,transparent);border:1px solid color-mix(in oklab,#fff 20%,transparent);border-radius:1.5rem;box-shadow:0 10px 15px -3px #0000001a;padding:30px}.offer-lock-status svg{height:16px;width:16px}.cb-lobby-modal-container .offer-box-content .reward-text{background:unset;border:none;color:#000;font-size:14px;line-height:1;margin-top:4px}.progress-header .title:before{background:#3b82f6;border-radius:50%;content:\"\";display:inline-block;height:8px;margin-right:8px;position:relative;width:8px}.live-activity-wrapper{backdrop-filter:blur(8px);background-color:color-mix(in oklab,#fff 80%,transparent);border-radius:18px;box-shadow:0 30px 80px rgba(0,0,0,.15);padding:16px}.live-activity-header{display:flex;gap:10px;justify-content:space-between;margin-bottom:12px}.live-activity-header .title{align-items:center;color:#000;display:flex;font-size:14px;font-weight:900;gap:8px;letter-spacing:.7px}.live-activity-header .dot{background:#3b82f6;border-radius:50%;height:8px;position:relative;width:8px}.live-activity-header .dot:after{animation:livePulse 1.6s ease-out infinite;background:rgba(59,130,246,.6);border-radius:50%;content:\"\";inset:0;position:absolute}@keyframes livePulse{0%{opacity:.8;transform:scale(1)}70%{opacity:0;transform:scale(2.4)}to{opacity:0}}.activity-stats{align-items:center;display:flex;gap:8px}.activity-stats-badge{background-color:rgba(59,130,246,.082);border-radius:999px;color:#3b82f6;font-size:12px;font-weight:500;line-height:1;padding:4px 10px}.activity-stats-badge.light{background-color:#f9f3f4;color:#45556c}.activity-list{height:104px;overflow:hidden;position:relative}.activity-card{align-items:center;background:linear-gradient(90deg,#fff,#f2f6ff);border:1px solid #dbeafe;border-radius:14px;display:flex;gap:10px;height:58px;inset:0;padding:12px 10px;position:absolute;transition:transform .6s cubic-bezier(.22,.61,.36,1),opacity .6s}.activity-card .text{font-size:14px}.activity-card .avatar{align-items:center;border-radius:50%;color:#fff;display:flex;flex:0 0 30px;height:30px;justify-content:center;width:30px}.activity-card .pink{background:linear-gradient(135deg,#ec4899,#f472b6)}.activity-card .purple{background:linear-gradient(135deg,#8b5cf6,#a78bfa)}.activity-card .blue{background:linear-gradient(135deg,#3b82f6,#60a5fa)}.activity-card .green{background:linear-gradient(135deg,#10b981,#34d399)}.activity-card .orange{background:linear-gradient(135deg,#f97316,#fb923c)}.activity-card .text p{color:#6b7280;font-size:12px;margin:2px 0 0}.activity-card .time{color:#94a3b8;font-size:10px;margin-left:auto}.lobby-activity-wp{backdrop-filter:blur(12px);background-color:hsla(0,0%,100%,.4);border:1px solid hsla(0,0%,100%,.2);border-radius:25px;box-shadow:0 1px 3px 0 #0000001a;margin-top:50px;padding:8px}.lobby-close-icon{align-items:center;background-color:color-mix(in oklab,#000 80%,transparent);border-radius:50%;color:#fff;cursor:pointer;display:flex;height:34px;justify-content:center;position:fixed;right:30px;top:30px;width:34px;z-index:99}.lobby-close-icon svg{height:20px;width:20px}@media screen and (max-width:1600px){.cb-lobby-main{border-radius:30px;padding:50px}.title-wp h2{font-size:48px;margin-bottom:12px}.sub-title{font-size:16px}.title-wp{margin-top:20px}.lobby-link-section{margin-top:30px}.offer-box-content h3{font-size:26px}.lobby-offer-box{gap:24px;padding:15px}.cb-lobby-top{gap:80px}.group-info-box{border-radius:20px;padding:22px}.team-card{margin-top:30px}.team-card .team-members{margin-top:12px}.lobby-activity-wp{margin-top:40px}.lobby-close-icon{height:30px;top:20px;width:30px}}@media screen and (max-width:1280px){.cb-lobby-main,.cb-lobby-main-wp{padding:40px}.title-wp h2{font-size:42px}.cb-lobby-top{gap:60px}}@media screen and (max-width:1120px){.title-wp h2{font-size:38px}}@media screen and (max-width:991px){.cb-lobby-top{gap:30px;grid-template-columns:1fr}.cb-lobby-main{border-radius:15px;padding:30px}.cb-lobby-main-wp{padding:30px}.lobby-close-icon{right:20px}.lobby-link-box{border-radius:10px}.share-btn{border-radius:8px}.offer-box-content h3{font-size:24px}.lobby-activity-wp,.lobby-offer-box,.time-card{border-radius:15px;margin-top:20px}.live-activity-wrapper{border-radius:10px}.group-info-box{border-radius:15px}}@media screen and (max-width:767px){.link-share-wrapper{flex-direction:column!important}}@media screen and (max-width:575px){.cb-lobby-main,.cb-lobby-main-wp{padding:20px}.title-wp h2{font-size:30px}.lobby-link-text{font-size:11px;margin-bottom:4px}.lobby-link-url{font-size:14px}.lobby-link-box{min-height:48px;padding:12px 16px}.link-share-wrapper{gap:10px}.share-btn{border-radius:8px;font-size:13px;height:48px;min-width:auto;padding:0 14px}.lobby-offer-box{padding:10px}.offer-box-content h3{font-size:20px}.offer-box-content .reward-text{font-size:13px}.group-info-box{padding:13px}.progress-header .title,.team-card .team-title span{font-size:13px}.team-card .member{height:40px;width:40px}.team-card .member:after{height:8px;width:8px}.team-card .team-members{gap:10px;margin-top:12px}.time-card{padding:13px}.team-card{margin-top:22px}.activity-card .text{font-size:12px}.activity-card .text p{font-size:11px}.live-activity-header .title{font-size:12px}.time-card .time-value{font-size:20px}}@media screen and (max-width:480px){.cb-lobby-main-wp{padding:20px 12px}.cb-lobby-main{padding:15px 12px}.lobby-status{font-size:9px}.lobby-number{font-size:10px}.title-wp h2{font-size:28px;margin-bottom:7px}.sub-title{font-size:14px}.lobby-link-section{gap:10px;margin-top:20px}.lobby-offer-box{gap:12px}.offer-box-icon{height:45px;width:45px}.offer-box-content .reward-text,.offer-lock-status span{font-size:12px}.cb-lobby-top{gap:20px}.lobby-close-icon{right:10px;top:10px}.live-activity-header{flex-direction:column;gap:5px}.activity-card{border-radius:9px;padding:6px}}.share-overlay{align-items:center;background:rgba(0,0,0,.45);display:flex;inset:0;justify-content:center;opacity:0;position:fixed;transition:.3s ease;visibility:hidden;z-index:999}.share-overlay.active{opacity:1;visibility:visible}.share-popup{background:#fff;border-radius:18px;max-height:90%;overflow:auto;padding:24px;position:relative;transform:translateY(30px);transition:.35s ease;width:420px}.share-overlay.active .share-popup{transform:translateY(0)}.share-popup .close-btn{align-items:center;background:#f3f4f6;border:none;border-radius:50%;cursor:pointer;display:flex;height:32px;justify-content:center;position:absolute;right:14px;top:14px;transition:.3s;width:32px}.share-popup .close-btn:hover{background:#eeecec}.share-popup h2{font-size:22px;font-weight:600;line-height:1;margin-bottom:10px}.share-popup .subtitle{color:#4a5565;font-size:14px;margin:6px 0 30px}.share-popup .share-grid{display:grid;gap:14px;grid-template-columns:repeat(2,1fr)}.share-popup .share-card{align-items:center;background:#f2f6f9;border-radius:14px;cursor:pointer;display:flex;flex-direction:column;padding:16px;position:relative;text-align:center;transition:.25s ease}.share-popup .share-card:hover{transform:translateY(-3px)}.share-popup .share-card .icon{align-items:center;background:#1877f2;border-radius:50%;color:#fff;display:flex;font-size:30px;height:50px;justify-content:center;margin-bottom:8px;text-align:center;width:50px}.share-popup .share-card span{font-size:13px;font-weight:500}.share-popup .share-card.whatsapp{background:#ecfdf5;border:2px solid #22c55e;color:#22c55e}.share-popup .share-card.whatsapp .icon{background:#22c55e}.share-popup .share-card .badge{background:#2563eb;border-radius:12px;color:#fff;font-size:11px;padding:4px 10px;position:absolute;right:10px;top:-8px}.share-popup .link-box{background:#fbf9fa;border:1px solid #ebe6e7;border-radius:14px;margin-top:18px;padding:14px}.share-popup .link-box label{color:#64748b;font-size:13px}.share-popup .link-row{display:flex;gap:8px;margin-top:6px}.share-popup .link-row input{background:transparent;border:none;color:#334155;flex:1;font-size:13px;letter-spacing:.8px;line-height:1.2;outline:none}.share-popup .link-row button{align-items:center;background:#fff;border:1px solid #ebe6e7;border-radius:10px;cursor:pointer;display:flex;height:35px;justify-content:center;padding:6px 8px;width:35px}.share-popup .success{color:#2563eb;display:block;font-size:12px;margin-top:6px;opacity:0;transition:opacity .3s}.share-popup .success.show{opacity:1}.share-popup .footer-text{color:#64748b;font-size:12px;margin-top:14px;text-align:center}.share-popup .share-card.twitter .icon{background:#000}.share-popup .share-card.facebook .icon{background:#1877f2}.share-popup .share-card.sms .icon{background:#059669}.share-popup .share-card.copied .icon{background:#2563eb}@keyframes entrance-fade-in{0%{opacity:0}to{opacity:1}}@keyframes entrance-scale-in{0%{opacity:0;transform:scale(.9) translateY(20px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes entrance-text-fade-in{0%{opacity:0}to{opacity:1}}@keyframes exit-blur-scale{0%{filter:blur(0);opacity:1;transform:scale(1)}to{filter:blur(10px);opacity:0;transform:scale(1.1)}}@keyframes pulse-dot{0%,to{opacity:1}50%{opacity:.5}}.entrance-animation-overlay{align-items:center;animation:entrance-fade-in .8s ease-in-out forwards;background-color:#0f172a;color:#fff;display:flex;flex-direction:column;inset:0;justify-content:center;position:fixed;z-index:9999}.entrance-animation-overlay.exit{animation:exit-blur-scale .8s ease-in-out forwards}.entrance-content{align-items:center;animation:entrance-scale-in .5s ease-out .2s both;display:flex;flex-direction:column}.entrance-icon-box{align-items:center;background:#2563eb;border-radius:16px;box-shadow:0 10px 25px rgba(37,99,235,.2);display:flex;height:64px;justify-content:center;margin-bottom:24px;width:64px}.entrance-icon-box svg{color:#fff;height:32px;width:32px}.entrance-title{font-size:32px;font-weight:700;letter-spacing:-.5px;margin-bottom:12px}@media (min-width:768px){.entrance-title{font-size:48px}}.entrance-message{align-items:center;animation:entrance-text-fade-in .5s ease-out .5s both;color:#60a5fa;display:flex;font-size:18px;font-weight:500;gap:8px}.entrance-pulse-dot{animation:pulse-dot 1.5s ease-in-out infinite;background-color:#60a5fa;border-radius:50%;height:8px;width:8px}";
|
|
2163
2174
|
styleInject(css_248z);
|
|
2164
2175
|
|
|
2165
2176
|
/// <reference lib="dom" />
|
|
2177
|
+
const CONTACT_PROMPT_TITLE = "Protect your group spot";
|
|
2178
|
+
const CONTACT_PROMPT_SUBTITLE = "Add your email or phone so we can help you recover your group membership if you leave accidentally.";
|
|
2179
|
+
const CONTACT_PROMPT_PRIMARY_CTA = "Protect my spot";
|
|
2180
|
+
const CONTACT_PROMPT_REMIND_CTA = "Remind me later";
|
|
2181
|
+
const CONTACT_PROMPT_INVALID_INPUT = "Please enter a valid email or phone number.";
|
|
2182
|
+
const CONTACT_PROMPT_SAVE_ERROR = "Could not save contact right now. Please try again.";
|
|
2183
|
+
const CONTACT_PROTECTED_TOAST = "Spot protected. We can help recover your group membership.";
|
|
2184
|
+
const LEAVE_WARNING_TITLE = "Before you leave";
|
|
2185
|
+
const LEAVE_WARNING_SUBTITLE = "If you leave now without adding contact, you may lose this group spot and may not be able to rejoin the same group.";
|
|
2166
2186
|
/**
|
|
2167
2187
|
* LobbyModal - Renders and manages the group buying lobby modal
|
|
2168
2188
|
*/
|
|
2169
2189
|
class LobbyModal {
|
|
2170
|
-
constructor(data, callbacks, apiClient, socketManager = null, debug = false) {
|
|
2190
|
+
constructor(data, callbacks, apiClient, socketManager = null, analyticsClient = null, debug = false) {
|
|
2171
2191
|
this.modalElement = null;
|
|
2172
2192
|
this.timerInterval = null;
|
|
2173
2193
|
this.activityInterval = null;
|
|
@@ -2176,6 +2196,17 @@ var CoBuySDK = (function (exports) {
|
|
|
2176
2196
|
this.currentGroupId = null;
|
|
2177
2197
|
this.shareOverlay = null;
|
|
2178
2198
|
this.keyboardHandler = null;
|
|
2199
|
+
this.leaveFlowInProgress = false;
|
|
2200
|
+
this.leaveSignalSent = false;
|
|
2201
|
+
this.hasContactProtection = false;
|
|
2202
|
+
this.initialContactPromptTimer = null;
|
|
2203
|
+
this.contactPromptOverlay = null;
|
|
2204
|
+
this.leaveLoaderOverlay = null;
|
|
2205
|
+
this.beforeUnloadHandler = null;
|
|
2206
|
+
this.pageHideHandler = null;
|
|
2207
|
+
this.visibilityChangeHandler = null;
|
|
2208
|
+
this.CONTACT_PROTECTION_PREFIX = "cobuy_contact_protection";
|
|
2209
|
+
this.LAST_LEFT_GROUP_PREFIX = "cobuy_last_left_group";
|
|
2179
2210
|
/**
|
|
2180
2211
|
* Handle socket group update events
|
|
2181
2212
|
*/
|
|
@@ -2263,10 +2294,16 @@ var CoBuySDK = (function (exports) {
|
|
|
2263
2294
|
this.updateData(updatePayload);
|
|
2264
2295
|
// Also update team card to reflect new member count
|
|
2265
2296
|
this.updateTeamCard(participants, max);
|
|
2297
|
+
// If group just became complete and we need offline codes for THIS user, fetch them
|
|
2298
|
+
if (isComplete && !this.data.offlineRedemption) {
|
|
2299
|
+
this.logger.info("[Socket] Group fulfilled - fetching current user's offline codes");
|
|
2300
|
+
this.fetchCurrentUserOfflineCodesIfNeeded();
|
|
2301
|
+
}
|
|
2266
2302
|
};
|
|
2267
2303
|
this.logger = new Logger(debug);
|
|
2268
2304
|
this.apiClient = apiClient;
|
|
2269
2305
|
this.socketManager = socketManager;
|
|
2306
|
+
this.analyticsClient = analyticsClient;
|
|
2270
2307
|
// Log the group data being passed into the modal
|
|
2271
2308
|
this.logger.info("LobbyModal initialized with group data", {
|
|
2272
2309
|
groupId: data.groupId,
|
|
@@ -2278,14 +2315,385 @@ var CoBuySDK = (function (exports) {
|
|
|
2278
2315
|
timeLeft: data.timeLeft,
|
|
2279
2316
|
offlineRedemption: data.offlineRedemption,
|
|
2280
2317
|
});
|
|
2281
|
-
this.data = Object.assign({ groupNumber: "1000", status: "active", progress: 80, currentMembers: 4, totalMembers: 5, timeLeft: 1390, discount: "20% OFF", isLocked: true, activities: this.getDefaultActivities() }, data);
|
|
2318
|
+
this.data = Object.assign({ groupNumber: "1000", status: "active", progress: 80, currentMembers: 4, totalMembers: 5, timeLeft: 1390, discount: "20% OFF", isLocked: true, activities: this.getDefaultActivities(), redemptionMethod: "online" }, data);
|
|
2282
2319
|
// Normalize optional flags so undefined values don't override defaults
|
|
2283
2320
|
this.data.isLocked = this.computeIsLocked(this.data);
|
|
2284
2321
|
if (!this.data.activities || !Array.isArray(this.data.activities)) {
|
|
2285
2322
|
this.data.activities = this.getDefaultActivities();
|
|
2286
2323
|
}
|
|
2324
|
+
this.hasContactProtection = this.readContactProtectionState();
|
|
2287
2325
|
this.callbacks = callbacks;
|
|
2288
2326
|
}
|
|
2327
|
+
getSessionId() {
|
|
2328
|
+
var _a;
|
|
2329
|
+
return ((_a = this.apiClient) === null || _a === void 0 ? void 0 : _a.getSessionId()) || "anonymous";
|
|
2330
|
+
}
|
|
2331
|
+
getContactProtectionStorageKey() {
|
|
2332
|
+
return `${this.CONTACT_PROTECTION_PREFIX}:${this.getSessionId()}:${this.data.productId}`;
|
|
2333
|
+
}
|
|
2334
|
+
getLastLeftGroupStorageKey() {
|
|
2335
|
+
return `${this.LAST_LEFT_GROUP_PREFIX}:${this.getSessionId()}:${this.data.productId}`;
|
|
2336
|
+
}
|
|
2337
|
+
readContactProtectionState() {
|
|
2338
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
2339
|
+
return false;
|
|
2340
|
+
}
|
|
2341
|
+
try {
|
|
2342
|
+
return window.localStorage.getItem(this.getContactProtectionStorageKey()) === "1";
|
|
2343
|
+
}
|
|
2344
|
+
catch (_a) {
|
|
2345
|
+
return false;
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
persistContactProtectionState(value) {
|
|
2349
|
+
this.hasContactProtection = value;
|
|
2350
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
2351
|
+
return;
|
|
2352
|
+
}
|
|
2353
|
+
try {
|
|
2354
|
+
const key = this.getContactProtectionStorageKey();
|
|
2355
|
+
if (value) {
|
|
2356
|
+
window.localStorage.setItem(key, "1");
|
|
2357
|
+
}
|
|
2358
|
+
else {
|
|
2359
|
+
window.localStorage.removeItem(key);
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
catch (_a) {
|
|
2363
|
+
// Ignore storage errors (private mode, quota exceeded, etc.)
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
persistLastLeftGroup(groupId) {
|
|
2367
|
+
if (typeof window === "undefined" || !window.localStorage || !groupId) {
|
|
2368
|
+
return;
|
|
2369
|
+
}
|
|
2370
|
+
try {
|
|
2371
|
+
window.localStorage.setItem(this.getLastLeftGroupStorageKey(), JSON.stringify({
|
|
2372
|
+
groupId,
|
|
2373
|
+
productId: this.data.productId,
|
|
2374
|
+
leftAt: Date.now(),
|
|
2375
|
+
}));
|
|
2376
|
+
}
|
|
2377
|
+
catch (_a) {
|
|
2378
|
+
// Ignore storage errors
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
clearInitialContactPromptTimer() {
|
|
2382
|
+
if (this.initialContactPromptTimer !== null) {
|
|
2383
|
+
window.clearTimeout(this.initialContactPromptTimer);
|
|
2384
|
+
this.initialContactPromptTimer = null;
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
clearContactPromptOverlay() {
|
|
2388
|
+
if (this.contactPromptOverlay) {
|
|
2389
|
+
this.contactPromptOverlay.remove();
|
|
2390
|
+
this.contactPromptOverlay = null;
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
showBlockingLoader(message) {
|
|
2394
|
+
if (!this.modalElement) {
|
|
2395
|
+
return;
|
|
2396
|
+
}
|
|
2397
|
+
this.hideBlockingLoader();
|
|
2398
|
+
const overlay = document.createElement("div");
|
|
2399
|
+
overlay.className = "lobby-blocking-loader-overlay";
|
|
2400
|
+
const card = document.createElement("div");
|
|
2401
|
+
card.className = "lobby-blocking-loader-card";
|
|
2402
|
+
const spinner = document.createElement("div");
|
|
2403
|
+
spinner.className = "lobby-blocking-loader-spinner";
|
|
2404
|
+
const text = document.createElement("p");
|
|
2405
|
+
text.className = "lobby-blocking-loader-text";
|
|
2406
|
+
text.textContent = message;
|
|
2407
|
+
card.appendChild(spinner);
|
|
2408
|
+
card.appendChild(text);
|
|
2409
|
+
overlay.appendChild(card);
|
|
2410
|
+
this.modalElement.appendChild(overlay);
|
|
2411
|
+
this.leaveLoaderOverlay = overlay;
|
|
2412
|
+
}
|
|
2413
|
+
hideBlockingLoader() {
|
|
2414
|
+
if (this.leaveLoaderOverlay) {
|
|
2415
|
+
this.leaveLoaderOverlay.remove();
|
|
2416
|
+
this.leaveLoaderOverlay = null;
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
updateContactProtectionUI() {
|
|
2420
|
+
const banner = document.getElementById("lobbyContactProtectionBanner");
|
|
2421
|
+
const explicitLeaveContainer = document.getElementById("lobbyProtectedLeaveContainer");
|
|
2422
|
+
if (!banner)
|
|
2423
|
+
return;
|
|
2424
|
+
const canShowContactActions = this.data.status !== "complete" && Boolean(this.apiClient);
|
|
2425
|
+
if (!canShowContactActions) {
|
|
2426
|
+
banner.style.display = "none";
|
|
2427
|
+
if (explicitLeaveContainer) {
|
|
2428
|
+
explicitLeaveContainer.style.display = "none";
|
|
2429
|
+
}
|
|
2430
|
+
return;
|
|
2431
|
+
}
|
|
2432
|
+
if (this.hasContactProtection) {
|
|
2433
|
+
banner.style.display = "none";
|
|
2434
|
+
if (explicitLeaveContainer) {
|
|
2435
|
+
explicitLeaveContainer.style.display = "flex";
|
|
2436
|
+
}
|
|
2437
|
+
return;
|
|
2438
|
+
}
|
|
2439
|
+
banner.style.display = "flex";
|
|
2440
|
+
if (explicitLeaveContainer) {
|
|
2441
|
+
explicitLeaveContainer.style.display = "none";
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
showToastMessage(message) {
|
|
2445
|
+
if (!this.modalElement) {
|
|
2446
|
+
return;
|
|
2447
|
+
}
|
|
2448
|
+
const existing = this.modalElement.querySelector(".lobby-contact-toast");
|
|
2449
|
+
if (existing) {
|
|
2450
|
+
existing.remove();
|
|
2451
|
+
}
|
|
2452
|
+
const toast = document.createElement("div");
|
|
2453
|
+
toast.className = "lobby-contact-toast";
|
|
2454
|
+
toast.textContent = message;
|
|
2455
|
+
this.modalElement.appendChild(toast);
|
|
2456
|
+
window.setTimeout(() => {
|
|
2457
|
+
toast.remove();
|
|
2458
|
+
}, 2400);
|
|
2459
|
+
}
|
|
2460
|
+
trackAnalyticsEvent(eventName, metadata) {
|
|
2461
|
+
if (!this.analyticsClient) {
|
|
2462
|
+
return;
|
|
2463
|
+
}
|
|
2464
|
+
this.analyticsClient
|
|
2465
|
+
.trackEvent(eventName, this.data.productId, Object.assign({ groupId: this.currentGroupId || this.data.groupId }, metadata))
|
|
2466
|
+
.catch((error) => {
|
|
2467
|
+
this.logger.warn(`[Analytics] Failed to track ${eventName}`, error);
|
|
2468
|
+
});
|
|
2469
|
+
}
|
|
2470
|
+
async saveContactValue(contactValue, promptSource) {
|
|
2471
|
+
const trimmed = contactValue.trim();
|
|
2472
|
+
if (!trimmed || !this.apiClient) {
|
|
2473
|
+
return false;
|
|
2474
|
+
}
|
|
2475
|
+
const contactType = this.inferContactType(trimmed);
|
|
2476
|
+
if (!contactType) {
|
|
2477
|
+
window.alert(CONTACT_PROMPT_INVALID_INPUT);
|
|
2478
|
+
return false;
|
|
2479
|
+
}
|
|
2480
|
+
const payload = contactType === "phone"
|
|
2481
|
+
? { type: "phone", value: trimmed.replace(/\D/g, "") }
|
|
2482
|
+
: { type: "email", value: trimmed };
|
|
2483
|
+
const result = await this.apiClient.setContact(payload);
|
|
2484
|
+
if (!result.success) {
|
|
2485
|
+
this.logger.warn("Failed to save lobby contact", result.error);
|
|
2486
|
+
window.alert(CONTACT_PROMPT_SAVE_ERROR);
|
|
2487
|
+
return false;
|
|
2488
|
+
}
|
|
2489
|
+
this.persistContactProtectionState(true);
|
|
2490
|
+
this.trackAnalyticsEvent("CONTACT_INFO_SAVED", {
|
|
2491
|
+
promptSource,
|
|
2492
|
+
contactType,
|
|
2493
|
+
protectionState: "protected",
|
|
2494
|
+
});
|
|
2495
|
+
this.updateContactProtectionUI();
|
|
2496
|
+
this.showToastMessage(CONTACT_PROTECTED_TOAST);
|
|
2497
|
+
return true;
|
|
2498
|
+
}
|
|
2499
|
+
openContactProtectionPrompt(source) {
|
|
2500
|
+
if (typeof document === "undefined" || !this.modalElement) {
|
|
2501
|
+
return Promise.resolve("cancel");
|
|
2502
|
+
}
|
|
2503
|
+
this.clearContactPromptOverlay();
|
|
2504
|
+
this.trackAnalyticsEvent("CONTACT_PROMPT_SHOWN", {
|
|
2505
|
+
promptSource: source,
|
|
2506
|
+
protectionState: "unprotected",
|
|
2507
|
+
});
|
|
2508
|
+
return new Promise((resolve) => {
|
|
2509
|
+
var _a;
|
|
2510
|
+
const overlay = document.createElement("div");
|
|
2511
|
+
overlay.className = "lobby-contact-overlay";
|
|
2512
|
+
const card = document.createElement("div");
|
|
2513
|
+
card.className = "lobby-contact-card";
|
|
2514
|
+
const title = document.createElement("h3");
|
|
2515
|
+
title.className = "lobby-contact-title";
|
|
2516
|
+
title.textContent = CONTACT_PROMPT_TITLE;
|
|
2517
|
+
const subtitle = document.createElement("p");
|
|
2518
|
+
subtitle.className = "lobby-contact-subtitle";
|
|
2519
|
+
subtitle.textContent = CONTACT_PROMPT_SUBTITLE;
|
|
2520
|
+
const input = document.createElement("input");
|
|
2521
|
+
input.type = "text";
|
|
2522
|
+
input.className = "lobby-contact-input";
|
|
2523
|
+
input.placeholder = "Email or phone";
|
|
2524
|
+
input.autocomplete = "email";
|
|
2525
|
+
const actions = document.createElement("div");
|
|
2526
|
+
actions.className = "lobby-contact-actions";
|
|
2527
|
+
const primary = document.createElement("button");
|
|
2528
|
+
primary.type = "button";
|
|
2529
|
+
primary.className = "lobby-contact-primary";
|
|
2530
|
+
primary.textContent = CONTACT_PROMPT_PRIMARY_CTA;
|
|
2531
|
+
const secondary = document.createElement("button");
|
|
2532
|
+
secondary.type = "button";
|
|
2533
|
+
secondary.className = "lobby-contact-secondary";
|
|
2534
|
+
secondary.textContent = source === "leave-warning" ? "Cancel" : CONTACT_PROMPT_REMIND_CTA;
|
|
2535
|
+
const cleanupAndResolve = (result) => {
|
|
2536
|
+
this.clearContactPromptOverlay();
|
|
2537
|
+
resolve(result);
|
|
2538
|
+
};
|
|
2539
|
+
primary.addEventListener("click", async () => {
|
|
2540
|
+
const originalLabel = primary.textContent;
|
|
2541
|
+
primary.disabled = true;
|
|
2542
|
+
secondary.disabled = true;
|
|
2543
|
+
input.disabled = true;
|
|
2544
|
+
primary.classList.add("is-loading");
|
|
2545
|
+
primary.textContent = "Saving...";
|
|
2546
|
+
try {
|
|
2547
|
+
const saved = await this.saveContactValue(input.value, source);
|
|
2548
|
+
if (saved) {
|
|
2549
|
+
cleanupAndResolve("saved");
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
finally {
|
|
2553
|
+
if (this.contactPromptOverlay) {
|
|
2554
|
+
primary.disabled = false;
|
|
2555
|
+
secondary.disabled = false;
|
|
2556
|
+
input.disabled = false;
|
|
2557
|
+
primary.classList.remove("is-loading");
|
|
2558
|
+
primary.textContent = originalLabel || CONTACT_PROMPT_PRIMARY_CTA;
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
});
|
|
2562
|
+
secondary.addEventListener("click", () => {
|
|
2563
|
+
if (source !== "leave-warning") {
|
|
2564
|
+
this.trackAnalyticsEvent("CONTACT_PROMPT_DEFERRED", {
|
|
2565
|
+
promptSource: source,
|
|
2566
|
+
protectionState: "unprotected",
|
|
2567
|
+
});
|
|
2568
|
+
}
|
|
2569
|
+
cleanupAndResolve(source === "leave-warning" ? "cancel" : "later");
|
|
2570
|
+
});
|
|
2571
|
+
overlay.addEventListener("click", (event) => {
|
|
2572
|
+
if (event.target === overlay) {
|
|
2573
|
+
if (source !== "leave-warning") {
|
|
2574
|
+
this.trackAnalyticsEvent("CONTACT_PROMPT_DEFERRED", {
|
|
2575
|
+
promptSource: source,
|
|
2576
|
+
protectionState: "unprotected",
|
|
2577
|
+
});
|
|
2578
|
+
}
|
|
2579
|
+
cleanupAndResolve(source === "leave-warning" ? "cancel" : "later");
|
|
2580
|
+
}
|
|
2581
|
+
});
|
|
2582
|
+
actions.appendChild(primary);
|
|
2583
|
+
actions.appendChild(secondary);
|
|
2584
|
+
card.appendChild(title);
|
|
2585
|
+
card.appendChild(subtitle);
|
|
2586
|
+
card.appendChild(input);
|
|
2587
|
+
card.appendChild(actions);
|
|
2588
|
+
overlay.appendChild(card);
|
|
2589
|
+
(_a = this.modalElement) === null || _a === void 0 ? void 0 : _a.appendChild(overlay);
|
|
2590
|
+
this.contactPromptOverlay = overlay;
|
|
2591
|
+
window.setTimeout(() => input.focus(), 0);
|
|
2592
|
+
});
|
|
2593
|
+
}
|
|
2594
|
+
openUnprotectedLeaveWarning() {
|
|
2595
|
+
if (typeof document === "undefined" || !this.modalElement) {
|
|
2596
|
+
return Promise.resolve("cancel");
|
|
2597
|
+
}
|
|
2598
|
+
this.clearContactPromptOverlay();
|
|
2599
|
+
return new Promise((resolve) => {
|
|
2600
|
+
var _a;
|
|
2601
|
+
const overlay = document.createElement("div");
|
|
2602
|
+
overlay.className = "lobby-contact-overlay";
|
|
2603
|
+
const card = document.createElement("div");
|
|
2604
|
+
card.className = "lobby-contact-card";
|
|
2605
|
+
const title = document.createElement("h3");
|
|
2606
|
+
title.className = "lobby-contact-title";
|
|
2607
|
+
title.textContent = LEAVE_WARNING_TITLE;
|
|
2608
|
+
const subtitle = document.createElement("p");
|
|
2609
|
+
subtitle.className = "lobby-contact-subtitle";
|
|
2610
|
+
subtitle.textContent = LEAVE_WARNING_SUBTITLE;
|
|
2611
|
+
const actions = document.createElement("div");
|
|
2612
|
+
actions.className = "lobby-contact-actions";
|
|
2613
|
+
const addContactBtn = document.createElement("button");
|
|
2614
|
+
addContactBtn.type = "button";
|
|
2615
|
+
addContactBtn.className = "lobby-contact-primary";
|
|
2616
|
+
addContactBtn.textContent = "Add contact first";
|
|
2617
|
+
const leaveBtn = document.createElement("button");
|
|
2618
|
+
leaveBtn.type = "button";
|
|
2619
|
+
leaveBtn.className = "lobby-contact-danger";
|
|
2620
|
+
leaveBtn.textContent = "Leave anyway";
|
|
2621
|
+
const cancelBtn = document.createElement("button");
|
|
2622
|
+
cancelBtn.type = "button";
|
|
2623
|
+
cancelBtn.className = "lobby-contact-secondary";
|
|
2624
|
+
cancelBtn.textContent = "Cancel";
|
|
2625
|
+
const cleanup = (result) => {
|
|
2626
|
+
this.clearContactPromptOverlay();
|
|
2627
|
+
resolve(result);
|
|
2628
|
+
};
|
|
2629
|
+
addContactBtn.addEventListener("click", () => cleanup("add_contact"));
|
|
2630
|
+
leaveBtn.addEventListener("click", () => cleanup("leave"));
|
|
2631
|
+
cancelBtn.addEventListener("click", () => cleanup("cancel"));
|
|
2632
|
+
overlay.addEventListener("click", (event) => {
|
|
2633
|
+
if (event.target === overlay) {
|
|
2634
|
+
cleanup("cancel");
|
|
2635
|
+
}
|
|
2636
|
+
});
|
|
2637
|
+
actions.appendChild(addContactBtn);
|
|
2638
|
+
actions.appendChild(leaveBtn);
|
|
2639
|
+
actions.appendChild(cancelBtn);
|
|
2640
|
+
card.appendChild(title);
|
|
2641
|
+
card.appendChild(subtitle);
|
|
2642
|
+
card.appendChild(actions);
|
|
2643
|
+
overlay.appendChild(card);
|
|
2644
|
+
(_a = this.modalElement) === null || _a === void 0 ? void 0 : _a.appendChild(overlay);
|
|
2645
|
+
this.contactPromptOverlay = overlay;
|
|
2646
|
+
});
|
|
2647
|
+
}
|
|
2648
|
+
scheduleInitialContactPrompt() {
|
|
2649
|
+
if (!this.modalElement ||
|
|
2650
|
+
!this.shouldRunLeaveFlow() ||
|
|
2651
|
+
this.hasContactProtection ||
|
|
2652
|
+
!this.apiClient) {
|
|
2653
|
+
return;
|
|
2654
|
+
}
|
|
2655
|
+
this.clearInitialContactPromptTimer();
|
|
2656
|
+
this.initialContactPromptTimer = window.setTimeout(async () => {
|
|
2657
|
+
this.initialContactPromptTimer = null;
|
|
2658
|
+
if (!this.modalElement || this.hasContactProtection) {
|
|
2659
|
+
return;
|
|
2660
|
+
}
|
|
2661
|
+
await this.openContactProtectionPrompt("initial");
|
|
2662
|
+
this.updateContactProtectionUI();
|
|
2663
|
+
}, 2000);
|
|
2664
|
+
}
|
|
2665
|
+
async leaveGroupExplicitly() {
|
|
2666
|
+
var _a, _b;
|
|
2667
|
+
if (!this.apiClient || !this.currentGroupId || this.leaveFlowInProgress) {
|
|
2668
|
+
return;
|
|
2669
|
+
}
|
|
2670
|
+
this.leaveFlowInProgress = true;
|
|
2671
|
+
try {
|
|
2672
|
+
this.showBlockingLoader("Leaving group...");
|
|
2673
|
+
const leaveResult = await this.apiClient.leaveGroup(this.currentGroupId, {
|
|
2674
|
+
leave_source: "close_icon",
|
|
2675
|
+
leave_reason: "voluntary_with_contact",
|
|
2676
|
+
});
|
|
2677
|
+
if (!leaveResult.success) {
|
|
2678
|
+
this.logger.warn("Explicit leave group failed", leaveResult.error);
|
|
2679
|
+
this.hideBlockingLoader();
|
|
2680
|
+
window.alert("Could not leave the group right now. Please try again.");
|
|
2681
|
+
return;
|
|
2682
|
+
}
|
|
2683
|
+
this.leaveSignalSent = true;
|
|
2684
|
+
this.persistLastLeftGroup(((_b = (_a = leaveResult.data) === null || _a === void 0 ? void 0 : _a.group) === null || _b === void 0 ? void 0 : _b.id) || this.currentGroupId);
|
|
2685
|
+
this.trackAnalyticsEvent("PROTECTED_USER_EXPLICIT_LEAVE", {
|
|
2686
|
+
leaveSource: "close_icon",
|
|
2687
|
+
leaveReason: "voluntary_with_contact",
|
|
2688
|
+
protectionState: "protected",
|
|
2689
|
+
});
|
|
2690
|
+
this.close({ skipLeaveFlow: true });
|
|
2691
|
+
}
|
|
2692
|
+
finally {
|
|
2693
|
+
this.hideBlockingLoader();
|
|
2694
|
+
this.leaveFlowInProgress = false;
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2289
2697
|
/**
|
|
2290
2698
|
* Derive lock state from data so UI reflects completion
|
|
2291
2699
|
*/
|
|
@@ -2602,6 +3010,7 @@ var CoBuySDK = (function (exports) {
|
|
|
2602
3010
|
* Create connected section (subtitle + link + share)
|
|
2603
3011
|
*/
|
|
2604
3012
|
createConnectedSection() {
|
|
3013
|
+
var _a, _b;
|
|
2605
3014
|
const connectedSection = document.createElement("div");
|
|
2606
3015
|
connectedSection.className = "connected-section";
|
|
2607
3016
|
connectedSection.id = "lobbyConnectedSection";
|
|
@@ -2612,18 +3021,59 @@ var CoBuySDK = (function (exports) {
|
|
|
2612
3021
|
subtitle.id = "lobbySubtitleText";
|
|
2613
3022
|
subtitle.textContent = titleContent.subtitle;
|
|
2614
3023
|
connectedSection.appendChild(subtitle);
|
|
3024
|
+
const contactBanner = document.createElement("div");
|
|
3025
|
+
contactBanner.className = "lobby-contact-protection-banner";
|
|
3026
|
+
contactBanner.id = "lobbyContactProtectionBanner";
|
|
3027
|
+
const contactCopy = document.createElement("p");
|
|
3028
|
+
contactCopy.className = "lobby-contact-protection-copy";
|
|
3029
|
+
contactCopy.textContent =
|
|
3030
|
+
"Spot not protected. Add contact so we can help recover your group membership.";
|
|
3031
|
+
const contactAction = document.createElement("button");
|
|
3032
|
+
contactAction.type = "button";
|
|
3033
|
+
contactAction.className = "lobby-contact-protection-btn";
|
|
3034
|
+
contactAction.textContent = "Add contact";
|
|
3035
|
+
contactAction.addEventListener("click", async () => {
|
|
3036
|
+
await this.openContactProtectionPrompt("banner");
|
|
3037
|
+
this.updateContactProtectionUI();
|
|
3038
|
+
});
|
|
3039
|
+
contactBanner.appendChild(contactCopy);
|
|
3040
|
+
contactBanner.appendChild(contactAction);
|
|
3041
|
+
connectedSection.appendChild(contactBanner);
|
|
3042
|
+
const protectedLeaveContainer = document.createElement("div");
|
|
3043
|
+
protectedLeaveContainer.className = "lobby-protected-leave-container";
|
|
3044
|
+
protectedLeaveContainer.id = "lobbyProtectedLeaveContainer";
|
|
3045
|
+
const protectedLeaveText = document.createElement("p");
|
|
3046
|
+
protectedLeaveText.className = "lobby-protected-leave-copy";
|
|
3047
|
+
protectedLeaveText.textContent = "You are protected and still part of this group.";
|
|
3048
|
+
const protectedLeaveAction = document.createElement("button");
|
|
3049
|
+
protectedLeaveAction.type = "button";
|
|
3050
|
+
protectedLeaveAction.className = "lobby-protected-leave-btn";
|
|
3051
|
+
protectedLeaveAction.textContent = "Leave group";
|
|
3052
|
+
protectedLeaveAction.addEventListener("click", () => {
|
|
3053
|
+
void this.leaveGroupExplicitly();
|
|
3054
|
+
});
|
|
3055
|
+
protectedLeaveContainer.appendChild(protectedLeaveText);
|
|
3056
|
+
protectedLeaveContainer.appendChild(protectedLeaveAction);
|
|
3057
|
+
connectedSection.appendChild(protectedLeaveContainer);
|
|
2615
3058
|
// Check if group is fulfilled and has offline redemption
|
|
2616
3059
|
const isComplete = !this.computeIsLocked(this.data);
|
|
2617
3060
|
const hasOfflineRedemption = isComplete &&
|
|
2618
3061
|
this.data.offlineRedemption &&
|
|
2619
|
-
isValidOfflineRedemption(this.data.offlineRedemption)
|
|
3062
|
+
isValidOfflineRedemption(this.data.offlineRedemption) &&
|
|
3063
|
+
((_a = this.data.redemptionMethod) !== null && _a !== void 0 ? _a : "online") !== "online";
|
|
2620
3064
|
if (hasOfflineRedemption) {
|
|
2621
|
-
//
|
|
3065
|
+
// Complete + offline/both: show offline redemption section
|
|
2622
3066
|
const offlineSection = this.createOfflineRedemptionSection(this.data.offlineRedemption);
|
|
2623
3067
|
connectedSection.appendChild(offlineSection);
|
|
2624
3068
|
}
|
|
3069
|
+
else if (isComplete && ((_b = this.data.redemptionMethod) !== null && _b !== void 0 ? _b : "online") !== "offline") {
|
|
3070
|
+
// Complete + online/both (no offline redemption data): show only checkout CTA
|
|
3071
|
+
const checkoutBtn = this.createOnlineCheckoutButton();
|
|
3072
|
+
connectedSection.appendChild(checkoutBtn);
|
|
3073
|
+
this.injectOfflineRedemptionStyles();
|
|
3074
|
+
}
|
|
2625
3075
|
else {
|
|
2626
|
-
//
|
|
3076
|
+
// Group not yet complete: show link and share
|
|
2627
3077
|
const linkShareContainer = document.createElement("div");
|
|
2628
3078
|
linkShareContainer.className = "link-share-container";
|
|
2629
3079
|
const linkWp = this.createLinkSection();
|
|
@@ -2667,6 +3117,23 @@ var CoBuySDK = (function (exports) {
|
|
|
2667
3117
|
linkShareWrapper.appendChild(shareBtnInner);
|
|
2668
3118
|
return linkShareWrapper;
|
|
2669
3119
|
}
|
|
3120
|
+
/**
|
|
3121
|
+
* Create a standalone "Checkout Online" button for the link/share section
|
|
3122
|
+
*/
|
|
3123
|
+
createOnlineCheckoutButton() {
|
|
3124
|
+
const btn = document.createElement("button");
|
|
3125
|
+
btn.className = "offline-online-checkout-btn lobby-online-checkout-btn";
|
|
3126
|
+
btn.textContent = "Checkout Online";
|
|
3127
|
+
btn.style.marginTop = "10px";
|
|
3128
|
+
btn.style.width = "100%";
|
|
3129
|
+
btn.addEventListener("click", () => {
|
|
3130
|
+
this.close();
|
|
3131
|
+
window.setTimeout(() => {
|
|
3132
|
+
this.triggerContinueToCheckout();
|
|
3133
|
+
}, 0);
|
|
3134
|
+
});
|
|
3135
|
+
return btn;
|
|
3136
|
+
}
|
|
2670
3137
|
/**
|
|
2671
3138
|
* Create offline redemption section
|
|
2672
3139
|
*/
|
|
@@ -2731,14 +3198,16 @@ var CoBuySDK = (function (exports) {
|
|
|
2731
3198
|
onlineCheckoutBtn.className = "offline-online-checkout-btn";
|
|
2732
3199
|
onlineCheckoutBtn.textContent = "Checkout Online";
|
|
2733
3200
|
onlineCheckoutBtn.addEventListener("click", () => {
|
|
2734
|
-
this.close();
|
|
3201
|
+
this.close({ skipLeaveFlow: true });
|
|
2735
3202
|
// Ensure modal closes before triggering checkout intent
|
|
2736
3203
|
window.setTimeout(() => {
|
|
2737
3204
|
this.triggerContinueToCheckout();
|
|
2738
3205
|
}, 0);
|
|
2739
3206
|
});
|
|
2740
3207
|
actionsRow.appendChild(downloadQRBtn);
|
|
2741
|
-
|
|
3208
|
+
if (this.data.redemptionMethod !== "offline") {
|
|
3209
|
+
actionsRow.appendChild(onlineCheckoutBtn);
|
|
3210
|
+
}
|
|
2742
3211
|
section.appendChild(topRow);
|
|
2743
3212
|
section.appendChild(expiryInfo);
|
|
2744
3213
|
section.appendChild(actionsRow);
|
|
@@ -3644,6 +4113,10 @@ var CoBuySDK = (function (exports) {
|
|
|
3644
4113
|
this.logger.info(`Lobby modal opened for product: ${this.data.productId}`);
|
|
3645
4114
|
// Set up keyboard accessibility (focus trap, ESC to close)
|
|
3646
4115
|
this.setupKeyboardAccessibility();
|
|
4116
|
+
// Register lifecycle-based dropoff handling for browser/tab close.
|
|
4117
|
+
this.registerLifecycleLeaveHandlers();
|
|
4118
|
+
this.updateContactProtectionUI();
|
|
4119
|
+
this.scheduleInitialContactPrompt();
|
|
3647
4120
|
});
|
|
3648
4121
|
}
|
|
3649
4122
|
/**
|
|
@@ -3709,19 +4182,166 @@ var CoBuySDK = (function (exports) {
|
|
|
3709
4182
|
this.keyboardHandler = null;
|
|
3710
4183
|
}
|
|
3711
4184
|
}
|
|
4185
|
+
registerLifecycleLeaveHandlers() {
|
|
4186
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
4187
|
+
return;
|
|
4188
|
+
}
|
|
4189
|
+
if (!this.shouldRunLeaveFlow()) {
|
|
4190
|
+
return;
|
|
4191
|
+
}
|
|
4192
|
+
if (!this.beforeUnloadHandler) {
|
|
4193
|
+
this.beforeUnloadHandler = () => {
|
|
4194
|
+
this.triggerBestEffortLeave("browser_close");
|
|
4195
|
+
};
|
|
4196
|
+
window.addEventListener("beforeunload", this.beforeUnloadHandler);
|
|
4197
|
+
}
|
|
4198
|
+
if (!this.pageHideHandler) {
|
|
4199
|
+
this.pageHideHandler = () => {
|
|
4200
|
+
this.triggerBestEffortLeave("browser_close");
|
|
4201
|
+
};
|
|
4202
|
+
window.addEventListener("pagehide", this.pageHideHandler);
|
|
4203
|
+
}
|
|
4204
|
+
if (!this.visibilityChangeHandler) {
|
|
4205
|
+
this.visibilityChangeHandler = () => {
|
|
4206
|
+
if (document.visibilityState === "hidden") {
|
|
4207
|
+
this.triggerBestEffortLeave("browser_close");
|
|
4208
|
+
}
|
|
4209
|
+
};
|
|
4210
|
+
document.addEventListener("visibilitychange", this.visibilityChangeHandler);
|
|
4211
|
+
}
|
|
4212
|
+
}
|
|
4213
|
+
unregisterLifecycleLeaveHandlers() {
|
|
4214
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
4215
|
+
return;
|
|
4216
|
+
}
|
|
4217
|
+
if (this.beforeUnloadHandler) {
|
|
4218
|
+
window.removeEventListener("beforeunload", this.beforeUnloadHandler);
|
|
4219
|
+
this.beforeUnloadHandler = null;
|
|
4220
|
+
}
|
|
4221
|
+
if (this.pageHideHandler) {
|
|
4222
|
+
window.removeEventListener("pagehide", this.pageHideHandler);
|
|
4223
|
+
this.pageHideHandler = null;
|
|
4224
|
+
}
|
|
4225
|
+
if (this.visibilityChangeHandler) {
|
|
4226
|
+
document.removeEventListener("visibilitychange", this.visibilityChangeHandler);
|
|
4227
|
+
this.visibilityChangeHandler = null;
|
|
4228
|
+
}
|
|
4229
|
+
}
|
|
4230
|
+
triggerBestEffortLeave(source) {
|
|
4231
|
+
if (this.leaveSignalSent || this.leaveFlowInProgress) {
|
|
4232
|
+
return;
|
|
4233
|
+
}
|
|
4234
|
+
if (this.hasContactProtection) {
|
|
4235
|
+
return;
|
|
4236
|
+
}
|
|
4237
|
+
if (!this.apiClient || !this.currentGroupId || !this.shouldRunLeaveFlow()) {
|
|
4238
|
+
return;
|
|
4239
|
+
}
|
|
4240
|
+
this.leaveSignalSent = true;
|
|
4241
|
+
this.apiClient.leaveGroupBestEffort(this.currentGroupId, {
|
|
4242
|
+
leave_source: source,
|
|
4243
|
+
leave_reason: "voluntary_no_contact",
|
|
4244
|
+
});
|
|
4245
|
+
}
|
|
4246
|
+
shouldRunLeaveFlow() {
|
|
4247
|
+
if (!this.apiClient || !this.currentGroupId) {
|
|
4248
|
+
return false;
|
|
4249
|
+
}
|
|
4250
|
+
// Dropoff handling applies only while group is still active/forming.
|
|
4251
|
+
return this.data.status !== "complete";
|
|
4252
|
+
}
|
|
4253
|
+
inferContactType(value) {
|
|
4254
|
+
const trimmed = value.trim();
|
|
4255
|
+
if (!trimmed)
|
|
4256
|
+
return null;
|
|
4257
|
+
if (trimmed.includes("@")) {
|
|
4258
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
4259
|
+
return emailRegex.test(trimmed) ? "email" : null;
|
|
4260
|
+
}
|
|
4261
|
+
const digitsOnly = trimmed.replace(/\D/g, "");
|
|
4262
|
+
return digitsOnly.length >= 10 && digitsOnly.length <= 15 ? "phone" : null;
|
|
4263
|
+
}
|
|
4264
|
+
async runLeaveFlowAndClose() {
|
|
4265
|
+
var _a, _b;
|
|
4266
|
+
if (this.leaveFlowInProgress)
|
|
4267
|
+
return;
|
|
4268
|
+
this.leaveFlowInProgress = true;
|
|
4269
|
+
try {
|
|
4270
|
+
if (this.hasContactProtection) {
|
|
4271
|
+
this.trackAnalyticsEvent("LOBBY_CLOSED_PROTECTED", {
|
|
4272
|
+
leaveSource: "close_icon",
|
|
4273
|
+
leaveReason: "stay_in_group",
|
|
4274
|
+
protectionState: "protected",
|
|
4275
|
+
});
|
|
4276
|
+
this.close({ skipLeaveFlow: true });
|
|
4277
|
+
return;
|
|
4278
|
+
}
|
|
4279
|
+
let leaveReason = "voluntary_no_contact";
|
|
4280
|
+
const leaveDecision = await this.openUnprotectedLeaveWarning();
|
|
4281
|
+
if (leaveDecision === "cancel") {
|
|
4282
|
+
return;
|
|
4283
|
+
}
|
|
4284
|
+
if (leaveDecision === "add_contact") {
|
|
4285
|
+
const promptResult = await this.openContactProtectionPrompt("leave-warning");
|
|
4286
|
+
if (promptResult === "saved") {
|
|
4287
|
+
this.updateContactProtectionUI();
|
|
4288
|
+
return;
|
|
4289
|
+
}
|
|
4290
|
+
else {
|
|
4291
|
+
return;
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
if (this.apiClient && this.currentGroupId) {
|
|
4295
|
+
this.showBlockingLoader("Leaving group...");
|
|
4296
|
+
const leaveResult = await this.apiClient.leaveGroup(this.currentGroupId, {
|
|
4297
|
+
leave_source: "close_icon",
|
|
4298
|
+
leave_reason: leaveReason,
|
|
4299
|
+
});
|
|
4300
|
+
if (!leaveResult.success) {
|
|
4301
|
+
this.logger.warn("Leave group failed", leaveResult.error);
|
|
4302
|
+
this.hideBlockingLoader();
|
|
4303
|
+
window.alert("Could not leave the group right now. Please try again.");
|
|
4304
|
+
return;
|
|
4305
|
+
}
|
|
4306
|
+
this.leaveSignalSent = true;
|
|
4307
|
+
this.persistLastLeftGroup(((_b = (_a = leaveResult.data) === null || _a === void 0 ? void 0 : _a.group) === null || _b === void 0 ? void 0 : _b.id) || this.currentGroupId);
|
|
4308
|
+
this.trackAnalyticsEvent("LOBBY_LEFT_UNPROTECTED", {
|
|
4309
|
+
leaveSource: "close_icon",
|
|
4310
|
+
leaveReason,
|
|
4311
|
+
protectionState: "unprotected",
|
|
4312
|
+
});
|
|
4313
|
+
}
|
|
4314
|
+
this.close({ skipLeaveFlow: true });
|
|
4315
|
+
}
|
|
4316
|
+
finally {
|
|
4317
|
+
this.hideBlockingLoader();
|
|
4318
|
+
this.leaveFlowInProgress = false;
|
|
4319
|
+
}
|
|
4320
|
+
}
|
|
3712
4321
|
/**
|
|
3713
4322
|
* Close the modal
|
|
3714
4323
|
*/
|
|
3715
|
-
close() {
|
|
4324
|
+
close(options = {}) {
|
|
3716
4325
|
if (!this.modalElement) {
|
|
3717
4326
|
this.logger.warn("Modal not open");
|
|
3718
4327
|
return;
|
|
3719
4328
|
}
|
|
4329
|
+
if (!options.skipLeaveFlow && this.shouldRunLeaveFlow()) {
|
|
4330
|
+
void this.runLeaveFlowAndClose();
|
|
4331
|
+
return;
|
|
4332
|
+
}
|
|
3720
4333
|
// Stop timers and animations
|
|
3721
4334
|
this.stopTimer();
|
|
3722
4335
|
this.stopActivityAnimation();
|
|
3723
4336
|
// Remove keyboard event listeners
|
|
3724
4337
|
this.removeKeyboardAccessibility();
|
|
4338
|
+
// Remove lifecycle handlers for dropoff detection
|
|
4339
|
+
this.unregisterLifecycleLeaveHandlers();
|
|
4340
|
+
this.clearInitialContactPromptTimer();
|
|
4341
|
+
this.clearContactPromptOverlay();
|
|
4342
|
+
this.hideBlockingLoader();
|
|
4343
|
+
// Remove socket listeners to avoid duplicate handlers on reopen
|
|
4344
|
+
this.unsubscribeFromSocketEvents();
|
|
3725
4345
|
// Remove modal from DOM
|
|
3726
4346
|
document.body.removeChild(this.modalElement);
|
|
3727
4347
|
this.modalElement = null;
|
|
@@ -3834,29 +4454,47 @@ var CoBuySDK = (function (exports) {
|
|
|
3834
4454
|
* Update offline redemption visibility when group is fulfilled
|
|
3835
4455
|
*/
|
|
3836
4456
|
updateOfflineRedemptionVisibility() {
|
|
4457
|
+
var _a, _b;
|
|
3837
4458
|
const connectedSection = document.getElementById("lobbyConnectedSection");
|
|
3838
4459
|
if (!connectedSection)
|
|
3839
4460
|
return;
|
|
3840
4461
|
const isComplete = !this.computeIsLocked(this.data);
|
|
3841
|
-
const hasOfflineRedemption = this.data.offlineRedemption &&
|
|
4462
|
+
const hasOfflineRedemption = this.data.offlineRedemption &&
|
|
4463
|
+
isValidOfflineRedemption(this.data.offlineRedemption) &&
|
|
4464
|
+
((_a = this.data.redemptionMethod) !== null && _a !== void 0 ? _a : "online") !== "online";
|
|
3842
4465
|
// Get existing elements
|
|
3843
4466
|
const existingOffline = connectedSection.querySelector(".offline-redemption-section");
|
|
3844
4467
|
const existingLink = connectedSection.querySelector(".link-share-container");
|
|
4468
|
+
const existingCheckoutBtn = connectedSection.querySelector(".lobby-online-checkout-btn");
|
|
3845
4469
|
if (isComplete && hasOfflineRedemption) {
|
|
3846
|
-
//
|
|
3847
|
-
if (existingLink)
|
|
4470
|
+
// Complete + offline/both: show offline section, remove link/share and standalone checkout btn
|
|
4471
|
+
if (existingLink)
|
|
3848
4472
|
existingLink.remove();
|
|
3849
|
-
|
|
4473
|
+
if (existingCheckoutBtn)
|
|
4474
|
+
existingCheckoutBtn.remove();
|
|
3850
4475
|
if (!existingOffline) {
|
|
3851
4476
|
const offlineSection = this.createOfflineRedemptionSection(this.data.offlineRedemption);
|
|
3852
4477
|
connectedSection.appendChild(offlineSection);
|
|
3853
4478
|
}
|
|
3854
4479
|
}
|
|
3855
|
-
else {
|
|
3856
|
-
//
|
|
3857
|
-
if (
|
|
4480
|
+
else if (isComplete && ((_b = this.data.redemptionMethod) !== null && _b !== void 0 ? _b : "online") !== "offline") {
|
|
4481
|
+
// Complete + online/both (no offline data): show only checkout CTA, remove link/share
|
|
4482
|
+
if (existingLink)
|
|
4483
|
+
existingLink.remove();
|
|
4484
|
+
if (existingOffline)
|
|
3858
4485
|
existingOffline.remove();
|
|
4486
|
+
if (!existingCheckoutBtn) {
|
|
4487
|
+
const checkoutBtn = this.createOnlineCheckoutButton();
|
|
4488
|
+
connectedSection.appendChild(checkoutBtn);
|
|
4489
|
+
this.injectOfflineRedemptionStyles();
|
|
3859
4490
|
}
|
|
4491
|
+
}
|
|
4492
|
+
else {
|
|
4493
|
+
// Group not yet complete: show link/share, remove offline and checkout btn
|
|
4494
|
+
if (existingOffline)
|
|
4495
|
+
existingOffline.remove();
|
|
4496
|
+
if (existingCheckoutBtn)
|
|
4497
|
+
existingCheckoutBtn.remove();
|
|
3860
4498
|
if (!existingLink) {
|
|
3861
4499
|
const linkShareContainer = document.createElement("div");
|
|
3862
4500
|
linkShareContainer.className = "link-share-container";
|
|
@@ -3890,9 +4528,47 @@ var CoBuySDK = (function (exports) {
|
|
|
3890
4528
|
}
|
|
3891
4529
|
window.addEventListener("group:fulfilled", this.handleSocketGroupUpdate);
|
|
3892
4530
|
window.addEventListener("group:member:joined", this.handleSocketGroupUpdate);
|
|
4531
|
+
window.addEventListener("group:member:left", this.handleSocketGroupUpdate);
|
|
3893
4532
|
window.addEventListener("group:created", this.handleSocketGroupUpdate);
|
|
3894
4533
|
this.socketListenerRegistered = true;
|
|
3895
4534
|
}
|
|
4535
|
+
unsubscribeFromSocketEvents() {
|
|
4536
|
+
if (typeof window === "undefined" || !this.socketListenerRegistered) {
|
|
4537
|
+
return;
|
|
4538
|
+
}
|
|
4539
|
+
window.removeEventListener("group:fulfilled", this.handleSocketGroupUpdate);
|
|
4540
|
+
window.removeEventListener("group:member:joined", this.handleSocketGroupUpdate);
|
|
4541
|
+
window.removeEventListener("group:member:left", this.handleSocketGroupUpdate);
|
|
4542
|
+
window.removeEventListener("group:created", this.handleSocketGroupUpdate);
|
|
4543
|
+
this.socketListenerRegistered = false;
|
|
4544
|
+
}
|
|
4545
|
+
/**
|
|
4546
|
+
* Fetch current user's offline redemption codes when group becomes fulfilled
|
|
4547
|
+
* This ensures we show the current user's codes, not someone else's
|
|
4548
|
+
*/
|
|
4549
|
+
async fetchCurrentUserOfflineCodesIfNeeded() {
|
|
4550
|
+
var _a;
|
|
4551
|
+
if (!this.apiClient || !this.currentGroupId) {
|
|
4552
|
+
return;
|
|
4553
|
+
}
|
|
4554
|
+
try {
|
|
4555
|
+
this.logger.info("[Offline] Fetching current user's offline codes");
|
|
4556
|
+
const response = await this.apiClient.getGroupOfflineRedemption(this.currentGroupId);
|
|
4557
|
+
if (response.success && ((_a = response.data) === null || _a === void 0 ? void 0 : _a.offline_redemption)) {
|
|
4558
|
+
const offlineRedemption = response.data.offline_redemption;
|
|
4559
|
+
if (isValidOfflineRedemption(offlineRedemption)) {
|
|
4560
|
+
this.logger.info("[Offline] Successfully fetched current user's offline codes");
|
|
4561
|
+
this.updateData({ offlineRedemption });
|
|
4562
|
+
}
|
|
4563
|
+
}
|
|
4564
|
+
else {
|
|
4565
|
+
this.logger.warn("[Offline] Failed to fetch offline codes", response.error);
|
|
4566
|
+
}
|
|
4567
|
+
}
|
|
4568
|
+
catch (error) {
|
|
4569
|
+
this.logger.error("[Offline] Error fetching offline codes", error);
|
|
4570
|
+
}
|
|
4571
|
+
}
|
|
3896
4572
|
/**
|
|
3897
4573
|
* Create activity item from socket event data
|
|
3898
4574
|
*/
|
|
@@ -3923,6 +4599,10 @@ var CoBuySDK = (function (exports) {
|
|
|
3923
4599
|
emoji = "🛒";
|
|
3924
4600
|
action = "started a new group";
|
|
3925
4601
|
break;
|
|
4602
|
+
case "group:member:left":
|
|
4603
|
+
emoji = "🚪";
|
|
4604
|
+
action = "left the group";
|
|
4605
|
+
break;
|
|
3926
4606
|
default:
|
|
3927
4607
|
emoji = "⚡";
|
|
3928
4608
|
action = "activity in group";
|
|
@@ -4395,6 +5075,9 @@ var CoBuySDK = (function (exports) {
|
|
|
4395
5075
|
WidgetState["LOADED"] = "loaded";
|
|
4396
5076
|
WidgetState["ERROR"] = "error";
|
|
4397
5077
|
})(WidgetState || (WidgetState = {}));
|
|
5078
|
+
const RECOVERY_TOAST_REJOINED = "Rejoined your previous group.";
|
|
5079
|
+
const RECOVERY_TOAST_NEXT_AVAILABLE = "Your previous group was full, so we joined you to the next available group.";
|
|
5080
|
+
const RECOVERY_TOAST_CREATED_NEW = "Your previous group was no longer available, so we created a fresh group for you.";
|
|
4398
5081
|
/**
|
|
4399
5082
|
* WidgetRoot handles rendering of the CoBuy widget into a DOM container
|
|
4400
5083
|
*/
|
|
@@ -4426,6 +5109,7 @@ var CoBuySDK = (function (exports) {
|
|
|
4426
5109
|
this.groupExpiryRefreshTriggered = false; // Track if expiry refresh already triggered for current group
|
|
4427
5110
|
this.offlineRedemption = null;
|
|
4428
5111
|
this.offlineRedemptionModal = null;
|
|
5112
|
+
this.campaignRedemptionMethod = "online";
|
|
4429
5113
|
this.isRendering = false;
|
|
4430
5114
|
this.renderPromise = null;
|
|
4431
5115
|
this.liveRegionAnnouncer = null;
|
|
@@ -4435,6 +5119,7 @@ var CoBuySDK = (function (exports) {
|
|
|
4435
5119
|
this.renderDebounceTimer = null;
|
|
4436
5120
|
this.pendingRenderOptions = null;
|
|
4437
5121
|
this.RENDER_DEBOUNCE_MS = 300; // 300ms debounce for rapid re-renders
|
|
5122
|
+
this.LAST_LEFT_GROUP_PREFIX = "cobuy_last_left_group";
|
|
4438
5123
|
/** Handle backend fulfillment notifications */
|
|
4439
5124
|
this.handleGroupFulfilledEvent = (event) => {
|
|
4440
5125
|
const detail = event.detail;
|
|
@@ -4478,6 +5163,80 @@ var CoBuySDK = (function (exports) {
|
|
|
4478
5163
|
this.handleCTAClick(productId);
|
|
4479
5164
|
}, 300); // 300ms debounce to prevent double-clicks
|
|
4480
5165
|
}
|
|
5166
|
+
getRecoveryStorageKey(productId) {
|
|
5167
|
+
var _a;
|
|
5168
|
+
const sessionId = (_a = this.apiClient) === null || _a === void 0 ? void 0 : _a.getSessionId();
|
|
5169
|
+
if (!sessionId || !productId) {
|
|
5170
|
+
return null;
|
|
5171
|
+
}
|
|
5172
|
+
return `${this.LAST_LEFT_GROUP_PREFIX}:${sessionId}:${productId}`;
|
|
5173
|
+
}
|
|
5174
|
+
getStoredRecoveryGroupId(productId) {
|
|
5175
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
5176
|
+
return null;
|
|
5177
|
+
}
|
|
5178
|
+
const key = this.getRecoveryStorageKey(productId);
|
|
5179
|
+
if (!key) {
|
|
5180
|
+
return null;
|
|
5181
|
+
}
|
|
5182
|
+
try {
|
|
5183
|
+
const raw = window.localStorage.getItem(key);
|
|
5184
|
+
if (!raw) {
|
|
5185
|
+
return null;
|
|
5186
|
+
}
|
|
5187
|
+
const parsed = JSON.parse(raw);
|
|
5188
|
+
if (!(parsed === null || parsed === void 0 ? void 0 : parsed.groupId) || parsed.productId !== productId) {
|
|
5189
|
+
return null;
|
|
5190
|
+
}
|
|
5191
|
+
return parsed.groupId;
|
|
5192
|
+
}
|
|
5193
|
+
catch (_a) {
|
|
5194
|
+
return null;
|
|
5195
|
+
}
|
|
5196
|
+
}
|
|
5197
|
+
clearStoredRecoveryGroupId(productId) {
|
|
5198
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
5199
|
+
return;
|
|
5200
|
+
}
|
|
5201
|
+
const key = this.getRecoveryStorageKey(productId);
|
|
5202
|
+
if (!key) {
|
|
5203
|
+
return;
|
|
5204
|
+
}
|
|
5205
|
+
try {
|
|
5206
|
+
window.localStorage.removeItem(key);
|
|
5207
|
+
}
|
|
5208
|
+
catch (_a) {
|
|
5209
|
+
// Ignore storage issues
|
|
5210
|
+
}
|
|
5211
|
+
}
|
|
5212
|
+
showRecoveryToast(message) {
|
|
5213
|
+
if (typeof document === "undefined") {
|
|
5214
|
+
return;
|
|
5215
|
+
}
|
|
5216
|
+
const existing = document.getElementById("cobuy-recovery-toast");
|
|
5217
|
+
if (existing) {
|
|
5218
|
+
existing.remove();
|
|
5219
|
+
}
|
|
5220
|
+
const toast = document.createElement("div");
|
|
5221
|
+
toast.id = "cobuy-recovery-toast";
|
|
5222
|
+
toast.style.position = "fixed";
|
|
5223
|
+
toast.style.left = "50%";
|
|
5224
|
+
toast.style.bottom = "24px";
|
|
5225
|
+
toast.style.transform = "translateX(-50%)";
|
|
5226
|
+
toast.style.background = "#0f172a";
|
|
5227
|
+
toast.style.color = "#fff";
|
|
5228
|
+
toast.style.padding = "10px 14px";
|
|
5229
|
+
toast.style.borderRadius = "10px";
|
|
5230
|
+
toast.style.fontSize = "13px";
|
|
5231
|
+
toast.style.fontWeight = "600";
|
|
5232
|
+
toast.style.zIndex = "10040";
|
|
5233
|
+
toast.style.boxShadow = "0 8px 20px rgba(0,0,0,0.24)";
|
|
5234
|
+
toast.textContent = message;
|
|
5235
|
+
document.body.appendChild(toast);
|
|
5236
|
+
window.setTimeout(() => {
|
|
5237
|
+
toast.remove();
|
|
5238
|
+
}, 2800);
|
|
5239
|
+
}
|
|
4481
5240
|
/** Subscribe once to backend socket events routed through the host page */
|
|
4482
5241
|
subscribeToSocketEvents() {
|
|
4483
5242
|
if (typeof window === "undefined" || this.socketListenerRegistered) {
|
|
@@ -4485,11 +5244,13 @@ var CoBuySDK = (function (exports) {
|
|
|
4485
5244
|
}
|
|
4486
5245
|
window.addEventListener("group:fulfilled", this.handleGroupFulfilledEvent);
|
|
4487
5246
|
window.addEventListener("group:member:joined", this.handleGroupUpdatedEvent);
|
|
5247
|
+
window.addEventListener("group:member:left", this.handleGroupUpdatedEvent);
|
|
4488
5248
|
window.addEventListener("group:created", this.handleGroupUpdatedEvent);
|
|
4489
5249
|
this.socketListenerRegistered = true;
|
|
4490
5250
|
}
|
|
4491
5251
|
/** Fetch latest group data and re-render containers */
|
|
4492
5252
|
async refreshGroupDataFromRealtime() {
|
|
5253
|
+
var _a;
|
|
4493
5254
|
// Debounce rapid refreshes to prevent loops and reduce API load
|
|
4494
5255
|
const now = Date.now();
|
|
4495
5256
|
if (now - this.lastGroupDataRefreshTime < this.GROUP_REFRESH_DEBOUNCE) {
|
|
@@ -4506,21 +5267,29 @@ var CoBuySDK = (function (exports) {
|
|
|
4506
5267
|
const groupData = await this.fetchPrimaryGroup(this.currentProductId);
|
|
4507
5268
|
this.currentGroupData = groupData;
|
|
4508
5269
|
this.currentGroupId = (groupData === null || groupData === void 0 ? void 0 : groupData.id) || this.currentGroupId;
|
|
5270
|
+
// If backend returned member-scoped offline redemption, this session is a member.
|
|
5271
|
+
const hasMemberOfflineRedemption = !!(groupData === null || groupData === void 0 ? void 0 : groupData.offline_redemption) && isValidOfflineRedemption(groupData.offline_redemption);
|
|
5272
|
+
if (hasMemberOfflineRedemption && !this.currentSessionId) {
|
|
5273
|
+
this.currentSessionId = ((_a = this.apiClient) === null || _a === void 0 ? void 0 : _a.getSessionId()) || null;
|
|
5274
|
+
}
|
|
4509
5275
|
// If backend signals completion via counts, reflect it — but only for actual members.
|
|
4510
5276
|
// Observers may see a full group returned by fetchPrimaryGroup; they should NOT enter
|
|
4511
5277
|
// the checkout flow. currentSessionId is the membership signal (non-null = joined).
|
|
4512
5278
|
if (groupData) {
|
|
4513
5279
|
const participants = Number(groupData.participants_count || 0);
|
|
4514
5280
|
const max = Number(groupData.max_participants || 0);
|
|
4515
|
-
|
|
5281
|
+
const isMember = !!this.currentSessionId || hasMemberOfflineRedemption;
|
|
5282
|
+
if (max > 0 && participants >= max && isMember) {
|
|
4516
5283
|
this.groupFulfilled = true;
|
|
4517
5284
|
// The primary group API returns offline_redemption for members of fulfilled groups.
|
|
4518
5285
|
// Extract it here so renderFulfilledSummary shows the "Redeem In-store" link
|
|
4519
5286
|
// (matching the UI users see on a full page reload).
|
|
4520
|
-
if (
|
|
4521
|
-
isValidOfflineRedemption(groupData.offline_redemption)) {
|
|
5287
|
+
if (hasMemberOfflineRedemption) {
|
|
4522
5288
|
this.offlineRedemption = groupData.offline_redemption;
|
|
4523
5289
|
}
|
|
5290
|
+
if (groupData.campaign_redemption_method) {
|
|
5291
|
+
this.campaignRedemptionMethod = groupData.campaign_redemption_method;
|
|
5292
|
+
}
|
|
4524
5293
|
}
|
|
4525
5294
|
}
|
|
4526
5295
|
else {
|
|
@@ -4616,7 +5385,7 @@ var CoBuySDK = (function (exports) {
|
|
|
4616
5385
|
}
|
|
4617
5386
|
/** Persist fulfilled state, emit callback, and refresh UI */
|
|
4618
5387
|
processGroupFulfilled(eventData) {
|
|
4619
|
-
var _a, _b, _c, _d;
|
|
5388
|
+
var _a, _b, _c, _d, _e;
|
|
4620
5389
|
this.groupFulfilled = true;
|
|
4621
5390
|
// Extract reward from new event structure or fallback to legacy
|
|
4622
5391
|
const reward = ((_a = eventData.frozen_reward) === null || _a === void 0 ? void 0 : _a.reward) || eventData.reward || null;
|
|
@@ -4631,7 +5400,10 @@ var CoBuySDK = (function (exports) {
|
|
|
4631
5400
|
code: this.offlineRedemption.redemption_code,
|
|
4632
5401
|
});
|
|
4633
5402
|
}
|
|
4634
|
-
|
|
5403
|
+
if ((_c = eventData.group) === null || _c === void 0 ? void 0 : _c.campaign_redemption_method) {
|
|
5404
|
+
this.campaignRedemptionMethod = eventData.group.campaign_redemption_method;
|
|
5405
|
+
}
|
|
5406
|
+
(_e = (_d = this.events) === null || _d === void 0 ? void 0 : _d.onGroupFulfilled) === null || _e === void 0 ? void 0 : _e.call(_d, eventData);
|
|
4635
5407
|
this.renderFulfilledState();
|
|
4636
5408
|
}
|
|
4637
5409
|
/** Re-render widget and external containers to reflect fulfillment */
|
|
@@ -4771,8 +5543,10 @@ var CoBuySDK = (function (exports) {
|
|
|
4771
5543
|
footer.style.justifyContent = "flex-end";
|
|
4772
5544
|
footer.style.alignItems = "center";
|
|
4773
5545
|
footer.style.gap = "12px";
|
|
4774
|
-
// Add "Redeem In-store" CTA if offline redemption is available
|
|
4775
|
-
if (this.offlineRedemption &&
|
|
5546
|
+
// Add "Redeem In-store" CTA if offline redemption is available and campaign allows it
|
|
5547
|
+
if (this.offlineRedemption &&
|
|
5548
|
+
isValidOfflineRedemption(this.offlineRedemption) &&
|
|
5549
|
+
this.campaignRedemptionMethod !== "online") {
|
|
4776
5550
|
const redeemLink = document.createElement("button");
|
|
4777
5551
|
redeemLink.className = "cobuy-redeem-instore-link";
|
|
4778
5552
|
redeemLink.style.background = "none";
|
|
@@ -4870,7 +5644,7 @@ var CoBuySDK = (function (exports) {
|
|
|
4870
5644
|
}
|
|
4871
5645
|
// Set callback to open lobby when a group is successfully joined
|
|
4872
5646
|
this.groupListModal.setOnGroupJoined((joinData) => {
|
|
4873
|
-
var _a;
|
|
5647
|
+
var _a, _b;
|
|
4874
5648
|
// Update current group ID and session ID so the modal knows which group the user has joined
|
|
4875
5649
|
this.currentGroupId = joinData.group.id;
|
|
4876
5650
|
this.currentSessionId = ((_a = this.apiClient) === null || _a === void 0 ? void 0 : _a.getSessionId()) || null;
|
|
@@ -4884,8 +5658,9 @@ var CoBuySDK = (function (exports) {
|
|
|
4884
5658
|
currentMembers: joinData.group.participants_count,
|
|
4885
5659
|
totalMembers: joinData.group.max_participants,
|
|
4886
5660
|
timeLeft: joinData.group.timeLeftSeconds,
|
|
5661
|
+
redemptionMethod: (_b = joinData.group.campaign_redemption_method) !== null && _b !== void 0 ? _b : this.campaignRedemptionMethod,
|
|
4887
5662
|
};
|
|
4888
|
-
const lobbyModal = new LobbyModal(lobbyData, {},
|
|
5663
|
+
const lobbyModal = new LobbyModal(lobbyData, {}, this.apiClient, null, this.analyticsClient, this.config.debug);
|
|
4889
5664
|
lobbyModal.open(joinData.group.id);
|
|
4890
5665
|
});
|
|
4891
5666
|
// Set callback for viewing progress on already joined group
|
|
@@ -4901,8 +5676,9 @@ var CoBuySDK = (function (exports) {
|
|
|
4901
5676
|
progress: Math.round((groupData.joined / groupData.total) * 100),
|
|
4902
5677
|
currentMembers: groupData.joined,
|
|
4903
5678
|
totalMembers: groupData.total,
|
|
5679
|
+
redemptionMethod: this.campaignRedemptionMethod,
|
|
4904
5680
|
};
|
|
4905
|
-
const lobbyModal = new LobbyModal(lobbyData, {},
|
|
5681
|
+
const lobbyModal = new LobbyModal(lobbyData, {}, this.apiClient, null, this.analyticsClient, this.config.debug);
|
|
4906
5682
|
lobbyModal.open(groupId);
|
|
4907
5683
|
});
|
|
4908
5684
|
}
|
|
@@ -5086,6 +5862,9 @@ var CoBuySDK = (function (exports) {
|
|
|
5086
5862
|
isValidOfflineRedemption(groupData.offline_redemption)) {
|
|
5087
5863
|
this.offlineRedemption = groupData.offline_redemption;
|
|
5088
5864
|
}
|
|
5865
|
+
if (groupData === null || groupData === void 0 ? void 0 : groupData.campaign_redemption_method) {
|
|
5866
|
+
this.campaignRedemptionMethod = groupData.campaign_redemption_method;
|
|
5867
|
+
}
|
|
5089
5868
|
// Check if group is already fulfilled (full) on initial load
|
|
5090
5869
|
if (groupData) {
|
|
5091
5870
|
const participants = Number(groupData.participants_count || 0);
|
|
@@ -5807,10 +6586,99 @@ var CoBuySDK = (function (exports) {
|
|
|
5807
6586
|
to { transform: rotate(360deg); }
|
|
5808
6587
|
}
|
|
5809
6588
|
|
|
6589
|
+
.cobuy-prejoin-contact-overlay {
|
|
6590
|
+
position: fixed;
|
|
6591
|
+
inset: 0;
|
|
6592
|
+
z-index: 10045;
|
|
6593
|
+
background: rgba(2, 6, 23, 0.5);
|
|
6594
|
+
display: flex;
|
|
6595
|
+
align-items: center;
|
|
6596
|
+
justify-content: center;
|
|
6597
|
+
padding: 16px;
|
|
6598
|
+
}
|
|
6599
|
+
|
|
6600
|
+
.cobuy-prejoin-contact-card {
|
|
6601
|
+
width: min(440px, 100%);
|
|
6602
|
+
background: #fff;
|
|
6603
|
+
border-radius: 14px;
|
|
6604
|
+
padding: 18px;
|
|
6605
|
+
box-shadow: 0 14px 36px rgba(15, 23, 42, 0.22);
|
|
6606
|
+
display: flex;
|
|
6607
|
+
flex-direction: column;
|
|
6608
|
+
gap: 10px;
|
|
6609
|
+
}
|
|
6610
|
+
|
|
6611
|
+
.cobuy-prejoin-contact-title {
|
|
6612
|
+
font-size: 20px;
|
|
6613
|
+
font-weight: 800;
|
|
6614
|
+
color: #0f172a;
|
|
6615
|
+
margin: 0;
|
|
6616
|
+
}
|
|
6617
|
+
|
|
6618
|
+
.cobuy-prejoin-contact-subtitle {
|
|
6619
|
+
font-size: 14px;
|
|
6620
|
+
line-height: 1.5;
|
|
6621
|
+
color: #334155;
|
|
6622
|
+
margin: 0;
|
|
6623
|
+
}
|
|
6624
|
+
|
|
6625
|
+
.cobuy-prejoin-contact-input {
|
|
6626
|
+
width: 100%;
|
|
6627
|
+
border: 1px solid #cbd5e1;
|
|
6628
|
+
border-radius: 10px;
|
|
6629
|
+
padding: 11px 12px;
|
|
6630
|
+
font-size: 14px;
|
|
6631
|
+
outline: none;
|
|
6632
|
+
}
|
|
6633
|
+
|
|
6634
|
+
.cobuy-prejoin-contact-input:focus {
|
|
6635
|
+
border-color: #1d4ed8;
|
|
6636
|
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
|
|
6637
|
+
}
|
|
6638
|
+
|
|
6639
|
+
.cobuy-prejoin-contact-actions {
|
|
6640
|
+
display: flex;
|
|
6641
|
+
gap: 8px;
|
|
6642
|
+
flex-wrap: wrap;
|
|
6643
|
+
}
|
|
6644
|
+
|
|
6645
|
+
.cobuy-prejoin-contact-primary,
|
|
6646
|
+
.cobuy-prejoin-contact-secondary {
|
|
6647
|
+
border: none;
|
|
6648
|
+
border-radius: 10px;
|
|
6649
|
+
padding: 10px 12px;
|
|
6650
|
+
font-weight: 700;
|
|
6651
|
+
cursor: pointer;
|
|
6652
|
+
font-size: 13px;
|
|
6653
|
+
}
|
|
6654
|
+
|
|
6655
|
+
.cobuy-prejoin-contact-primary {
|
|
6656
|
+
background: #1d4ed8;
|
|
6657
|
+
color: #fff;
|
|
6658
|
+
}
|
|
6659
|
+
|
|
6660
|
+
.cobuy-prejoin-contact-primary:hover {
|
|
6661
|
+
background: #1e40af;
|
|
6662
|
+
}
|
|
6663
|
+
|
|
6664
|
+
.cobuy-prejoin-contact-secondary {
|
|
6665
|
+
background: #e2e8f0;
|
|
6666
|
+
color: #1e293b;
|
|
6667
|
+
}
|
|
6668
|
+
|
|
6669
|
+
.cobuy-prejoin-contact-secondary:hover {
|
|
6670
|
+
background: #cbd5e1;
|
|
6671
|
+
}
|
|
6672
|
+
|
|
5810
6673
|
@media (max-width: 640px) {
|
|
5811
6674
|
.cobuy-widget {
|
|
5812
6675
|
grid-template-columns: 1fr;
|
|
5813
6676
|
}
|
|
6677
|
+
|
|
6678
|
+
.cobuy-prejoin-contact-primary,
|
|
6679
|
+
.cobuy-prejoin-contact-secondary {
|
|
6680
|
+
width: 100%;
|
|
6681
|
+
}
|
|
5814
6682
|
}
|
|
5815
6683
|
`;
|
|
5816
6684
|
document.head.appendChild(style);
|
|
@@ -5819,7 +6687,7 @@ var CoBuySDK = (function (exports) {
|
|
|
5819
6687
|
* Handle CTA button click with analytics and modal opening
|
|
5820
6688
|
*/
|
|
5821
6689
|
async handleCTAClick(productId) {
|
|
5822
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
6690
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
5823
6691
|
this.logger.info(`CTA clicked for product: ${productId}`);
|
|
5824
6692
|
// Track analytics event asynchronously (fire-and-forget)
|
|
5825
6693
|
if (this.analyticsClient) {
|
|
@@ -5834,29 +6702,113 @@ var CoBuySDK = (function (exports) {
|
|
|
5834
6702
|
// Join group before opening modal
|
|
5835
6703
|
let groupJoinData = null;
|
|
5836
6704
|
let inviteData = null;
|
|
6705
|
+
let recoveryGroupId = null;
|
|
5837
6706
|
if (this.apiClient && this.currentGroupId) {
|
|
5838
6707
|
try {
|
|
5839
|
-
|
|
5840
|
-
if (this.analyticsClient) {
|
|
6708
|
+
recoveryGroupId = this.getStoredRecoveryGroupId(productId);
|
|
6709
|
+
if (this.analyticsClient && recoveryGroupId) {
|
|
5841
6710
|
this.analyticsClient
|
|
5842
|
-
.
|
|
6711
|
+
.trackEvent("RECOVERY_ATTEMPT", productId, {
|
|
6712
|
+
previousGroupId: recoveryGroupId,
|
|
6713
|
+
protectionState: "protected",
|
|
6714
|
+
})
|
|
5843
6715
|
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
5844
6716
|
}
|
|
5845
|
-
const
|
|
5846
|
-
if (
|
|
5847
|
-
|
|
6717
|
+
const recoverResponse = await this.apiClient.recoverOrJoinGroup(productId);
|
|
6718
|
+
if (recoverResponse.success && recoverResponse.data) {
|
|
6719
|
+
groupJoinData = recoverResponse.data;
|
|
6720
|
+
this.currentGroupId = groupJoinData.group.id;
|
|
6721
|
+
this.clearStoredRecoveryGroupId(productId);
|
|
6722
|
+
if (this.analyticsClient && groupJoinData.recovery.recovered) {
|
|
6723
|
+
this.analyticsClient
|
|
6724
|
+
.trackEvent("RECOVERY_SUCCESS", productId, {
|
|
6725
|
+
previousGroupId: groupJoinData.recovery.previous_group_id || recoveryGroupId,
|
|
6726
|
+
joinedGroupId: groupJoinData.recovery.joined_group_id,
|
|
6727
|
+
recoveryOutcome: groupJoinData.recovery.outcome,
|
|
6728
|
+
protectionState: groupJoinData.recovery.matched_by === "none" ? "unprotected" : "protected",
|
|
6729
|
+
})
|
|
6730
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
6731
|
+
}
|
|
6732
|
+
else if (this.analyticsClient && recoveryGroupId) {
|
|
6733
|
+
this.analyticsClient
|
|
6734
|
+
.trackEvent("RECOVERY_FAILED", productId, {
|
|
6735
|
+
previousGroupId: recoveryGroupId,
|
|
6736
|
+
errorCode: "NOT_RECOVERED",
|
|
6737
|
+
errorMessage: "Recovery did not restore the previous membership.",
|
|
6738
|
+
protectionState: "protected",
|
|
6739
|
+
})
|
|
6740
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
6741
|
+
}
|
|
6742
|
+
switch (groupJoinData.recovery.outcome) {
|
|
6743
|
+
case "rejoined_previous_group":
|
|
6744
|
+
this.showRecoveryToast(RECOVERY_TOAST_REJOINED);
|
|
6745
|
+
break;
|
|
6746
|
+
case "joined_next_available_group":
|
|
6747
|
+
if (groupJoinData.recovery.recovered) {
|
|
6748
|
+
this.showRecoveryToast(RECOVERY_TOAST_NEXT_AVAILABLE);
|
|
6749
|
+
}
|
|
6750
|
+
break;
|
|
6751
|
+
case "created_new_group":
|
|
6752
|
+
if (groupJoinData.recovery.recovered) {
|
|
6753
|
+
this.showRecoveryToast(RECOVERY_TOAST_CREATED_NEW);
|
|
6754
|
+
}
|
|
6755
|
+
break;
|
|
6756
|
+
}
|
|
6757
|
+
}
|
|
6758
|
+
else {
|
|
6759
|
+
if (this.analyticsClient && recoveryGroupId) {
|
|
6760
|
+
const errCode = (_c = recoverResponse.error) === null || _c === void 0 ? void 0 : _c.code;
|
|
6761
|
+
const errMsg = (_d = recoverResponse.error) === null || _d === void 0 ? void 0 : _d.message;
|
|
6762
|
+
this.analyticsClient
|
|
6763
|
+
.trackEvent("RECOVERY_FAILED", productId, {
|
|
6764
|
+
previousGroupId: recoveryGroupId,
|
|
6765
|
+
errorCode: errCode || "RECOVERY_UNAVAILABLE",
|
|
6766
|
+
errorMessage: errMsg || "Recovery response was not successful.",
|
|
6767
|
+
protectionState: "protected",
|
|
6768
|
+
})
|
|
6769
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
6770
|
+
}
|
|
6771
|
+
const joinTargetGroupId = recoveryGroupId || this.currentGroupId;
|
|
6772
|
+
this.logger.info(`Joining group: ${joinTargetGroupId}`);
|
|
5848
6773
|
if (this.analyticsClient) {
|
|
5849
|
-
const errCode = (_c = joinResponse.error) === null || _c === void 0 ? void 0 : _c.code;
|
|
5850
|
-
const errMsg = (_d = joinResponse.error) === null || _d === void 0 ? void 0 : _d.message;
|
|
5851
6774
|
this.analyticsClient
|
|
5852
|
-
.
|
|
6775
|
+
.trackJoinAttempt(productId, joinTargetGroupId)
|
|
5853
6776
|
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
5854
6777
|
}
|
|
5855
|
-
this.
|
|
5856
|
-
|
|
6778
|
+
let joinResponse = await this.apiClient.joinGroup(joinTargetGroupId);
|
|
6779
|
+
if (!joinResponse.success && recoveryGroupId && this.currentGroupId !== recoveryGroupId) {
|
|
6780
|
+
this.clearStoredRecoveryGroupId(productId);
|
|
6781
|
+
joinResponse = await this.apiClient.joinGroup(this.currentGroupId);
|
|
6782
|
+
}
|
|
6783
|
+
if (!joinResponse.success) {
|
|
6784
|
+
this.logger.error("Failed to join group, modal will not open", joinResponse.error);
|
|
6785
|
+
if (this.analyticsClient) {
|
|
6786
|
+
const errCode = (_e = joinResponse.error) === null || _e === void 0 ? void 0 : _e.code;
|
|
6787
|
+
const errMsg = (_f = joinResponse.error) === null || _f === void 0 ? void 0 : _f.message;
|
|
6788
|
+
this.analyticsClient
|
|
6789
|
+
.trackJoinFailure(productId, joinTargetGroupId, errCode, errMsg)
|
|
6790
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
6791
|
+
}
|
|
6792
|
+
this.setButtonLoadingState(false);
|
|
6793
|
+
return; // Don't open modal on error
|
|
6794
|
+
}
|
|
6795
|
+
groupJoinData = joinResponse.data;
|
|
6796
|
+
if ((_g = groupJoinData === null || groupJoinData === void 0 ? void 0 : groupJoinData.group) === null || _g === void 0 ? void 0 : _g.id) {
|
|
6797
|
+
this.currentGroupId = groupJoinData.group.id;
|
|
6798
|
+
}
|
|
6799
|
+
if (recoveryGroupId && ((_h = groupJoinData === null || groupJoinData === void 0 ? void 0 : groupJoinData.group) === null || _h === void 0 ? void 0 : _h.id)) {
|
|
6800
|
+
this.clearStoredRecoveryGroupId(productId);
|
|
6801
|
+
if (groupJoinData.group.id !== recoveryGroupId) {
|
|
6802
|
+
this.showRecoveryToast(RECOVERY_TOAST_NEXT_AVAILABLE);
|
|
6803
|
+
}
|
|
6804
|
+
else {
|
|
6805
|
+
this.showRecoveryToast(RECOVERY_TOAST_REJOINED);
|
|
6806
|
+
}
|
|
6807
|
+
}
|
|
5857
6808
|
}
|
|
5858
|
-
groupJoinData = joinResponse.data;
|
|
5859
6809
|
this.logger.info("Successfully joined group", groupJoinData);
|
|
6810
|
+
// Mark this widget session as an active member for subsequent socket fulfillment handling.
|
|
6811
|
+
this.currentSessionId = ((_j = this.apiClient) === null || _j === void 0 ? void 0 : _j.getSessionId()) || this.currentSessionId;
|
|
5860
6812
|
if (this.analyticsClient && groupJoinData) {
|
|
5861
6813
|
this.analyticsClient
|
|
5862
6814
|
.trackJoinSuccess(productId, groupJoinData.group.id)
|
|
@@ -5868,26 +6820,39 @@ var CoBuySDK = (function (exports) {
|
|
|
5868
6820
|
isValidOfflineRedemption(groupJoinData.offline_redemption)
|
|
5869
6821
|
? groupJoinData.offline_redemption
|
|
5870
6822
|
: null;
|
|
6823
|
+
const joinedGroupId = (_k = groupJoinData === null || groupJoinData === void 0 ? void 0 : groupJoinData.group) === null || _k === void 0 ? void 0 : _k.id;
|
|
5871
6824
|
// Trigger invite tracking before opening lobby (global for product)
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
6825
|
+
if (joinedGroupId) {
|
|
6826
|
+
try {
|
|
6827
|
+
const inviteResponse = await this.apiClient.inviteToGroup(joinedGroupId, "copy_link");
|
|
6828
|
+
if (inviteResponse.success && inviteResponse.data) {
|
|
6829
|
+
inviteData = inviteResponse.data;
|
|
6830
|
+
this.logger.info("Invite link generated", inviteData);
|
|
6831
|
+
}
|
|
6832
|
+
else {
|
|
6833
|
+
this.logger.warn("Invite link generation failed", inviteResponse.error);
|
|
6834
|
+
}
|
|
5877
6835
|
}
|
|
5878
|
-
|
|
5879
|
-
this.logger.warn("Invite
|
|
6836
|
+
catch (inviteError) {
|
|
6837
|
+
this.logger.warn("Invite request failed", inviteError);
|
|
5880
6838
|
}
|
|
5881
6839
|
}
|
|
5882
|
-
catch (inviteError) {
|
|
5883
|
-
this.logger.warn("Invite request failed", inviteError);
|
|
5884
|
-
}
|
|
5885
6840
|
}
|
|
5886
6841
|
catch (error) {
|
|
5887
6842
|
this.logger.error("Group join failed, modal will not open", error);
|
|
6843
|
+
if (this.analyticsClient && recoveryGroupId) {
|
|
6844
|
+
this.analyticsClient
|
|
6845
|
+
.trackEvent("RECOVERY_FAILED", productId, {
|
|
6846
|
+
previousGroupId: recoveryGroupId,
|
|
6847
|
+
errorCode: "EXCEPTION",
|
|
6848
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
6849
|
+
protectionState: "protected",
|
|
6850
|
+
})
|
|
6851
|
+
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
6852
|
+
}
|
|
5888
6853
|
if (this.analyticsClient) {
|
|
5889
6854
|
this.analyticsClient
|
|
5890
|
-
.trackJoinFailure(productId, (
|
|
6855
|
+
.trackJoinFailure(productId, (_l = this.currentGroupId) !== null && _l !== void 0 ? _l : undefined, "EXCEPTION", error instanceof Error ? error.message : String(error))
|
|
5891
6856
|
.catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
5892
6857
|
}
|
|
5893
6858
|
this.setButtonLoadingState(false);
|
|
@@ -5904,7 +6869,7 @@ var CoBuySDK = (function (exports) {
|
|
|
5904
6869
|
const progress = Math.round((groupJoinData.group.participants_count / groupJoinData.group.max_participants) * 100);
|
|
5905
6870
|
// Format discount based on reward type
|
|
5906
6871
|
let discountText = "";
|
|
5907
|
-
if ((
|
|
6872
|
+
if ((_o = (_m = this.currentRewardData) === null || _m === void 0 ? void 0 : _m.reward) === null || _o === void 0 ? void 0 : _o.value) {
|
|
5908
6873
|
const rewardType = this.currentRewardData.reward.type;
|
|
5909
6874
|
const rewardValue = this.currentRewardData.reward.value;
|
|
5910
6875
|
if (rewardType === "percentage" || rewardType === "cashback") {
|
|
@@ -5944,6 +6909,7 @@ var CoBuySDK = (function (exports) {
|
|
|
5944
6909
|
shareMessage: shareMessageFromInvite,
|
|
5945
6910
|
isLocked: !isGroupFulfilled,
|
|
5946
6911
|
offlineRedemption: offlineRedemptionFromJoin,
|
|
6912
|
+
redemptionMethod: (_p = groupJoinData.group.campaign_redemption_method) !== null && _p !== void 0 ? _p : this.campaignRedemptionMethod,
|
|
5947
6913
|
onShare: this.analyticsClient
|
|
5948
6914
|
? () => {
|
|
5949
6915
|
this.analyticsClient.trackShareClick(productId, groupJoinData.group.id, "other").catch((e) => this.logger.warn("Analytics tracking failed", e));
|
|
@@ -6114,11 +7080,14 @@ var CoBuySDK = (function (exports) {
|
|
|
6114
7080
|
PRODUCT_CONTEXT: "/v1/sdk/products/:productId/context",
|
|
6115
7081
|
PRODUCT_REWARD: "/v1/sdk/products/:productId/reward",
|
|
6116
7082
|
PRODUCT_PRIMARY_GROUP: "/v1/sdk/products/:productId/group/primary",
|
|
7083
|
+
PRODUCT_RECOVER_OR_JOIN_GROUP: "/v1/sdk/products/:productId/group/recover-or-join",
|
|
6117
7084
|
// Group endpoints
|
|
6118
7085
|
GROUP_JOIN: "/v1/sdk/groups/:groupId/join",
|
|
7086
|
+
GROUP_LEAVE: "/v1/sdk/groups/:groupId/leave",
|
|
6119
7087
|
GROUP_CREATE_AND_JOIN: "/v1/sdk/groups/new/join",
|
|
6120
7088
|
PRODUCT_ACTIVE_GROUPS: "/v1/sdk/products/:productId/groups/active",
|
|
6121
7089
|
GROUP_INVITE: "/v1/sdk/groups/:groupId/invite",
|
|
7090
|
+
GROUP_OFFLINE_REDEMPTION: "/v1/sdk/groups/:groupId/offline-redemption",
|
|
6122
7091
|
GROUP_CHECKOUT_PREPARE: "/v1/sdk/groups/:groupId/checkout/prepare",
|
|
6123
7092
|
GROUP_CHECKOUT_VALIDATE: "/v1/sdk/groups/:groupId/checkout/validate",
|
|
6124
7093
|
GROUP_CHECKOUT_CONFIRM: "/v1/sdk/groups/:groupId/checkout/confirm",
|
|
@@ -7206,6 +8175,98 @@ var CoBuySDK = (function (exports) {
|
|
|
7206
8175
|
},
|
|
7207
8176
|
};
|
|
7208
8177
|
}
|
|
8178
|
+
/**
|
|
8179
|
+
* Leave an active group for the current SDK session.
|
|
8180
|
+
*/
|
|
8181
|
+
async leaveGroup(groupId, params) {
|
|
8182
|
+
const endpoint = buildApiUrl("", API_ENDPOINTS.GROUP_LEAVE, { groupId });
|
|
8183
|
+
this.logger.info(`Leaving group: ${groupId}`);
|
|
8184
|
+
const response = await this.post(endpoint, {
|
|
8185
|
+
leave_source: (params === null || params === void 0 ? void 0 : params.leave_source) || "unknown",
|
|
8186
|
+
leave_reason: (params === null || params === void 0 ? void 0 : params.leave_reason) || "unknown",
|
|
8187
|
+
});
|
|
8188
|
+
if (response.success) {
|
|
8189
|
+
const payload = response.data;
|
|
8190
|
+
return {
|
|
8191
|
+
success: true,
|
|
8192
|
+
data: ((payload === null || payload === void 0 ? void 0 : payload.data) || response.data),
|
|
8193
|
+
};
|
|
8194
|
+
}
|
|
8195
|
+
return {
|
|
8196
|
+
success: false,
|
|
8197
|
+
error: response.error || {
|
|
8198
|
+
message: "Failed to leave group",
|
|
8199
|
+
code: "LEAVE_GROUP_ERROR",
|
|
8200
|
+
},
|
|
8201
|
+
};
|
|
8202
|
+
}
|
|
8203
|
+
/**
|
|
8204
|
+
* Fetch current session member's offline redemption codes for a group.
|
|
8205
|
+
*/
|
|
8206
|
+
async getGroupOfflineRedemption(groupId) {
|
|
8207
|
+
const endpoint = buildApiUrl("", API_ENDPOINTS.GROUP_OFFLINE_REDEMPTION, {
|
|
8208
|
+
groupId,
|
|
8209
|
+
});
|
|
8210
|
+
const response = await this.get(endpoint);
|
|
8211
|
+
if (response.success) {
|
|
8212
|
+
const payload = response.data;
|
|
8213
|
+
return {
|
|
8214
|
+
success: true,
|
|
8215
|
+
data: (payload === null || payload === void 0 ? void 0 : payload.data) || {},
|
|
8216
|
+
};
|
|
8217
|
+
}
|
|
8218
|
+
return {
|
|
8219
|
+
success: false,
|
|
8220
|
+
error: response.error || {
|
|
8221
|
+
message: "Failed to fetch offline redemption",
|
|
8222
|
+
code: "OFFLINE_REDEMPTION_FETCH_ERROR",
|
|
8223
|
+
},
|
|
8224
|
+
};
|
|
8225
|
+
}
|
|
8226
|
+
async recoverOrJoinGroup(productId, contact) {
|
|
8227
|
+
var _a;
|
|
8228
|
+
const endpoint = buildApiUrl("", API_ENDPOINTS.PRODUCT_RECOVER_OR_JOIN_GROUP, {
|
|
8229
|
+
productId,
|
|
8230
|
+
});
|
|
8231
|
+
this.logger.info(`Recovering or joining group for product: ${productId}`);
|
|
8232
|
+
const response = await this.post(endpoint, contact ? { contact } : {});
|
|
8233
|
+
if (response.success && ((_a = response.data) === null || _a === void 0 ? void 0 : _a.data)) {
|
|
8234
|
+
return {
|
|
8235
|
+
success: true,
|
|
8236
|
+
data: response.data.data,
|
|
8237
|
+
};
|
|
8238
|
+
}
|
|
8239
|
+
return {
|
|
8240
|
+
success: false,
|
|
8241
|
+
error: response.error || {
|
|
8242
|
+
message: "Failed to recover or join group",
|
|
8243
|
+
code: "RECOVER_OR_JOIN_GROUP_ERROR",
|
|
8244
|
+
},
|
|
8245
|
+
};
|
|
8246
|
+
}
|
|
8247
|
+
/**
|
|
8248
|
+
* Best-effort leave signal for unload/pagehide scenarios.
|
|
8249
|
+
* Uses fetch keepalive so it can run while page is closing.
|
|
8250
|
+
*/
|
|
8251
|
+
leaveGroupBestEffort(groupId, params) {
|
|
8252
|
+
if (!groupId)
|
|
8253
|
+
return;
|
|
8254
|
+
const endpoint = buildApiUrl("", API_ENDPOINTS.GROUP_LEAVE, { groupId });
|
|
8255
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
8256
|
+
const payload = JSON.stringify({
|
|
8257
|
+
leave_source: (params === null || params === void 0 ? void 0 : params.leave_source) || "browser_close",
|
|
8258
|
+
leave_reason: (params === null || params === void 0 ? void 0 : params.leave_reason) || "voluntary_no_contact",
|
|
8259
|
+
});
|
|
8260
|
+
const authHeaders = this.authStrategy.getHeaders();
|
|
8261
|
+
void fetch(url, {
|
|
8262
|
+
method: "POST",
|
|
8263
|
+
headers: Object.assign({ "Content-Type": "application/json", "X-CoBuy-SDK-Version": "1.0.0", "X-CoBuy-Session": this.sessionId }, authHeaders),
|
|
8264
|
+
body: payload,
|
|
8265
|
+
keepalive: true,
|
|
8266
|
+
}).catch((error) => {
|
|
8267
|
+
this.logger.debug("Best-effort leave request failed", error);
|
|
8268
|
+
});
|
|
8269
|
+
}
|
|
7209
8270
|
/**
|
|
7210
8271
|
* Prepare checkout for a group
|
|
7211
8272
|
*
|
|
@@ -12035,15 +13096,16 @@ var CoBuySDK = (function (exports) {
|
|
|
12035
13096
|
return;
|
|
12036
13097
|
const bind = (eventName, handler) => {
|
|
12037
13098
|
var _a;
|
|
12038
|
-
if (!handler)
|
|
12039
|
-
return;
|
|
12040
13099
|
(_a = this.socket) === null || _a === void 0 ? void 0 : _a.on(eventName, (payload) => {
|
|
12041
13100
|
this.logger.info(`[Socket] ${eventName} payload: ${this.formatPayload(payload)}`);
|
|
12042
13101
|
this.dispatchWindowEvent(eventName, payload);
|
|
12043
|
-
handler
|
|
13102
|
+
if (handler) {
|
|
13103
|
+
handler(payload);
|
|
13104
|
+
}
|
|
12044
13105
|
});
|
|
12045
13106
|
};
|
|
12046
13107
|
bind("group:member:joined", handlers.onGroupMemberJoined);
|
|
13108
|
+
bind("group:member:left", handlers.onGroupMemberLeft);
|
|
12047
13109
|
bind("group:created", handlers.onGroupCreated);
|
|
12048
13110
|
bind("group:fulfilled", handlers.onGroupFulfilled);
|
|
12049
13111
|
}
|
|
@@ -12054,6 +13116,7 @@ var CoBuySDK = (function (exports) {
|
|
|
12054
13116
|
if (!this.socket)
|
|
12055
13117
|
return;
|
|
12056
13118
|
this.socket.off("group:member:joined");
|
|
13119
|
+
this.socket.off("group:member:left");
|
|
12057
13120
|
this.socket.off("group:created");
|
|
12058
13121
|
this.socket.off("group:fulfilled");
|
|
12059
13122
|
}
|
|
@@ -13427,6 +14490,7 @@ var CoBuySDK = (function (exports) {
|
|
|
13427
14490
|
activities: options.activities,
|
|
13428
14491
|
isLocked: options.isLocked,
|
|
13429
14492
|
offlineRedemption: options.offlineRedemption,
|
|
14493
|
+
redemptionMethod: options.redemptionMethod,
|
|
13430
14494
|
};
|
|
13431
14495
|
// Create modal instance
|
|
13432
14496
|
const modal = new LobbyModal(modalData, {
|
|
@@ -13438,10 +14502,22 @@ var CoBuySDK = (function (exports) {
|
|
|
13438
14502
|
if ((_a = config.events) === null || _a === void 0 ? void 0 : _a.onModalClose) {
|
|
13439
14503
|
config.events.onModalClose(options.productId);
|
|
13440
14504
|
}
|
|
14505
|
+
// Sync widget state after lobby closes so fulfilled CTAs/group view
|
|
14506
|
+
// reflect socket-driven changes without requiring a page reload.
|
|
14507
|
+
const refreshPromises = [];
|
|
14508
|
+
this.widgets.forEach((w) => {
|
|
14509
|
+
const pid = typeof w.getProductId === "function"
|
|
14510
|
+
? w.getProductId()
|
|
14511
|
+
: null;
|
|
14512
|
+
if (pid === options.productId && typeof w.requestRefresh === "function") {
|
|
14513
|
+
refreshPromises.push(w.requestRefresh());
|
|
14514
|
+
}
|
|
14515
|
+
});
|
|
14516
|
+
void Promise.all(refreshPromises.map((p) => p.then(() => undefined).catch(() => undefined)));
|
|
13441
14517
|
},
|
|
13442
14518
|
onCopyLink: options.onCopyLink,
|
|
13443
14519
|
onShare: options.onShare,
|
|
13444
|
-
}, this.apiClient, this.socketManager, config.debug);
|
|
14520
|
+
}, this.apiClient, this.socketManager, this.analyticsClient, config.debug);
|
|
13445
14521
|
// Store in map for persistence
|
|
13446
14522
|
this.modals.set(modalKey, modal);
|
|
13447
14523
|
// Maintain backward compatibility
|