@youidian/sdk 3.3.7 → 3.4.1
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/client.cjs +400 -36
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +41 -5
- package/dist/client.d.ts +41 -5
- package/dist/client.js +399 -36
- package/dist/client.js.map +1 -1
- package/dist/feedback.cjs +682 -0
- package/dist/feedback.cjs.map +1 -0
- package/dist/feedback.d.cts +38 -0
- package/dist/feedback.d.ts +38 -0
- package/dist/feedback.js +656 -0
- package/dist/feedback.js.map +1 -0
- package/dist/index.cjs +1164 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1161 -36
- package/dist/index.js.map +1 -1
- package/dist/{login-DerOcXcH.d.cts → login-Dqemys65.d.cts} +8 -4
- package/dist/{login-DerOcXcH.d.ts → login-Dqemys65.d.ts} +8 -4
- package/dist/login.cjs +320 -30
- package/dist/login.cjs.map +1 -1
- package/dist/login.d.cts +1 -1
- package/dist/login.d.ts +1 -1
- package/dist/login.js +320 -30
- package/dist/login.js.map +1 -1
- package/dist/server.cjs +108 -0
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +137 -1
- package/dist/server.d.ts +137 -1
- package/dist/server.js +108 -0
- package/dist/server.js.map +1 -1
- package/package.json +6 -1
package/dist/index.js
CHANGED
|
@@ -204,6 +204,91 @@ var HostedFrameModal = class {
|
|
|
204
204
|
}
|
|
205
205
|
};
|
|
206
206
|
|
|
207
|
+
// src/environment.ts
|
|
208
|
+
function normalizeText(value) {
|
|
209
|
+
return value || "";
|
|
210
|
+
}
|
|
211
|
+
function getRuntimeInput() {
|
|
212
|
+
if (typeof navigator === "undefined") {
|
|
213
|
+
return {};
|
|
214
|
+
}
|
|
215
|
+
const nav = navigator;
|
|
216
|
+
const coarsePointer = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(pointer: coarse)").matches : void 0;
|
|
217
|
+
return {
|
|
218
|
+
coarsePointer,
|
|
219
|
+
maxTouchPoints: nav.maxTouchPoints || 0,
|
|
220
|
+
platform: nav.platform || "",
|
|
221
|
+
standalone: nav.standalone,
|
|
222
|
+
userAgent: nav.userAgent || "",
|
|
223
|
+
userAgentDataMobile: nav.userAgentData?.mobile
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function includesAny(value, patterns) {
|
|
227
|
+
return patterns.some((pattern) => pattern.test(value));
|
|
228
|
+
}
|
|
229
|
+
function detectLoginEnvironment(input = getRuntimeInput()) {
|
|
230
|
+
const userAgent = normalizeText(input.userAgent);
|
|
231
|
+
const platform = normalizeText(input.platform);
|
|
232
|
+
const rawMaxTouchPoints = input.maxTouchPoints ?? 0;
|
|
233
|
+
const maxTouchPoints = Number.isFinite(rawMaxTouchPoints) ? Math.max(0, rawMaxTouchPoints) : 0;
|
|
234
|
+
const isCoarsePointer = input.coarsePointer === true;
|
|
235
|
+
const isTouch = maxTouchPoints > 0 || isCoarsePointer;
|
|
236
|
+
const isWeChat = /micromessenger/i.test(userAgent);
|
|
237
|
+
const isAndroid = /\bandroid\b/i.test(userAgent);
|
|
238
|
+
const hasExplicitIOSDevice = /\b(iPad|iPhone|iPod)\b/i.test(userAgent);
|
|
239
|
+
const hasMacintoshUserAgent = /\bMacintosh\b/i.test(userAgent);
|
|
240
|
+
const hasMacPlatform = /^Mac/i.test(platform);
|
|
241
|
+
const isIPadOS = !hasExplicitIOSDevice && hasMacintoshUserAgent && hasMacPlatform && typeof input.standalone !== "undefined" && maxTouchPoints > 2;
|
|
242
|
+
const isIOS = hasExplicitIOSDevice || isIPadOS;
|
|
243
|
+
const isTabletByUa = includesAny(userAgent, [
|
|
244
|
+
/\biPad\b/i,
|
|
245
|
+
/\btablet\b/i,
|
|
246
|
+
/\bplaybook\b/i,
|
|
247
|
+
/\bsilk\b(?!.*\bmobile\b)/i,
|
|
248
|
+
/\bkindle\b/i,
|
|
249
|
+
/\bnexus 7\b/i,
|
|
250
|
+
/\bnexus 9\b/i,
|
|
251
|
+
/\bxoom\b/i,
|
|
252
|
+
/\bsm-t\d+/i,
|
|
253
|
+
/\bgt-p\d+/i,
|
|
254
|
+
/\bmi pad\b/i
|
|
255
|
+
]);
|
|
256
|
+
const isMobileByUa = includesAny(userAgent, [
|
|
257
|
+
/\bMobile\b/i,
|
|
258
|
+
/\biPhone\b/i,
|
|
259
|
+
/\biPod\b/i,
|
|
260
|
+
/\bWindows Phone\b/i,
|
|
261
|
+
/\bIEMobile\b/i,
|
|
262
|
+
/\bBlackBerry\b/i,
|
|
263
|
+
/\bBB10\b/i,
|
|
264
|
+
/\bOpera Mini\b/i,
|
|
265
|
+
/\bOpera Mobi\b/i,
|
|
266
|
+
/\bwebOS\b/i
|
|
267
|
+
]);
|
|
268
|
+
const isAndroidTablet = isAndroid && !/\bMobile\b/i.test(userAgent);
|
|
269
|
+
const isTablet = isIPadOS || isTabletByUa || isAndroidTablet;
|
|
270
|
+
const isMobile = input.userAgentDataMobile === true || isMobileByUa || isAndroid && !isTablet || isIOS && !isTablet;
|
|
271
|
+
const deviceType = isTablet ? "tablet" : isMobile ? "mobile" : userAgent || platform || isTouch ? "desktop" : "unknown";
|
|
272
|
+
const shouldUseRedirectLogin = deviceType === "mobile" || deviceType === "tablet" || isWeChat;
|
|
273
|
+
return {
|
|
274
|
+
deviceType,
|
|
275
|
+
isAndroid,
|
|
276
|
+
isCoarsePointer,
|
|
277
|
+
isDesktop: deviceType === "desktop",
|
|
278
|
+
isIOS,
|
|
279
|
+
isIPadOS,
|
|
280
|
+
isMobile,
|
|
281
|
+
isStandalone: input.standalone === true,
|
|
282
|
+
isTablet,
|
|
283
|
+
isTouch,
|
|
284
|
+
isWeChat,
|
|
285
|
+
maxTouchPoints,
|
|
286
|
+
platform,
|
|
287
|
+
shouldUseRedirectLogin,
|
|
288
|
+
userAgent
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
207
292
|
// src/login.ts
|
|
208
293
|
function getOrigin(value) {
|
|
209
294
|
if (!value) return null;
|
|
@@ -213,6 +298,15 @@ function getOrigin(value) {
|
|
|
213
298
|
return null;
|
|
214
299
|
}
|
|
215
300
|
}
|
|
301
|
+
function isValidLoginRedirectUrl(value) {
|
|
302
|
+
if (!value) return false;
|
|
303
|
+
try {
|
|
304
|
+
const url = new URL(value);
|
|
305
|
+
return url.protocol === "https:" || url.protocol === "http:";
|
|
306
|
+
} catch {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
216
310
|
function createLoginCallbackState() {
|
|
217
311
|
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
218
312
|
return crypto.randomUUID().replace(/-/g, "");
|
|
@@ -559,6 +653,24 @@ function applyLoginPanelStyle(panel) {
|
|
|
559
653
|
backdropFilter: "blur(16px)"
|
|
560
654
|
});
|
|
561
655
|
}
|
|
656
|
+
function applyLoginLoadingPanelStyle(panel) {
|
|
657
|
+
Object.assign(panel.style, {
|
|
658
|
+
position: "absolute",
|
|
659
|
+
inset: "0",
|
|
660
|
+
display: "flex",
|
|
661
|
+
alignItems: "center",
|
|
662
|
+
justifyContent: "center",
|
|
663
|
+
border: "0",
|
|
664
|
+
borderRadius: "28px",
|
|
665
|
+
background: "rgba(255,255,255,0.94)",
|
|
666
|
+
color: "#0f172a",
|
|
667
|
+
padding: "24px",
|
|
668
|
+
textAlign: "center",
|
|
669
|
+
zIndex: "1",
|
|
670
|
+
boxShadow: "none",
|
|
671
|
+
backdropFilter: "blur(16px)"
|
|
672
|
+
});
|
|
673
|
+
}
|
|
562
674
|
function createLoginPanelContent() {
|
|
563
675
|
const content = document.createElement("div");
|
|
564
676
|
Object.assign(content.style, {
|
|
@@ -583,6 +695,71 @@ function createLoginPanelContent() {
|
|
|
583
695
|
content.appendChild(badge);
|
|
584
696
|
return content;
|
|
585
697
|
}
|
|
698
|
+
function createBrandLoadingMark() {
|
|
699
|
+
const logo = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
700
|
+
logo.setAttribute("viewBox", "0 0 1024 1024");
|
|
701
|
+
logo.setAttribute("fill", "none");
|
|
702
|
+
logo.setAttribute("aria-hidden", "true");
|
|
703
|
+
Object.assign(logo.style, {
|
|
704
|
+
width: "112px",
|
|
705
|
+
height: "112px",
|
|
706
|
+
color: "#22c55e",
|
|
707
|
+
display: "block",
|
|
708
|
+
overflow: "visible"
|
|
709
|
+
});
|
|
710
|
+
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
711
|
+
group.setAttribute("fill", "currentColor");
|
|
712
|
+
const ring = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
713
|
+
ring.setAttribute(
|
|
714
|
+
"d",
|
|
715
|
+
"M704.298 679.54A264 264 0 1 1 704.298 309.46A49 49 0 0 1 634.4 378.149A166 166 0 1 0 634.4 610.851A49 49 0 0 1 704.298 679.54Z"
|
|
716
|
+
);
|
|
717
|
+
ring.setAttribute("class", "youidian-sdk-brand-loading__ring");
|
|
718
|
+
const dot = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
719
|
+
dot.setAttribute("class", "youidian-sdk-brand-loading__dot");
|
|
720
|
+
dot.setAttribute("cx", "714.5");
|
|
721
|
+
dot.setAttribute("cy", "490.5");
|
|
722
|
+
dot.setAttribute("r", "68");
|
|
723
|
+
group.appendChild(ring);
|
|
724
|
+
group.appendChild(dot);
|
|
725
|
+
logo.appendChild(group);
|
|
726
|
+
return logo;
|
|
727
|
+
}
|
|
728
|
+
function createBrandLoadingCopy() {
|
|
729
|
+
const brand = document.createElement("div");
|
|
730
|
+
brand.className = "youidian-sdk-brand-loading__copy";
|
|
731
|
+
Object.assign(brand.style, {
|
|
732
|
+
display: "flex",
|
|
733
|
+
flexDirection: "column",
|
|
734
|
+
alignItems: "flex-start",
|
|
735
|
+
justifyContent: "center",
|
|
736
|
+
lineHeight: "1"
|
|
737
|
+
});
|
|
738
|
+
const name = document.createElement("div");
|
|
739
|
+
name.className = "youidian-sdk-brand-loading__name";
|
|
740
|
+
name.textContent = "\u4F18\u6613\u70B9";
|
|
741
|
+
Object.assign(name.style, {
|
|
742
|
+
color: "#0f172a",
|
|
743
|
+
fontSize: "34px",
|
|
744
|
+
fontWeight: "900",
|
|
745
|
+
letterSpacing: "0",
|
|
746
|
+
lineHeight: "1"
|
|
747
|
+
});
|
|
748
|
+
const tagline = document.createElement("div");
|
|
749
|
+
tagline.className = "youidian-sdk-brand-loading__tagline";
|
|
750
|
+
tagline.textContent = "\u5F00\u53D1\u8005\u670D\u52A1\u4E2D\u53F0";
|
|
751
|
+
Object.assign(tagline.style, {
|
|
752
|
+
color: "#64748b",
|
|
753
|
+
fontSize: "16px",
|
|
754
|
+
fontWeight: "700",
|
|
755
|
+
letterSpacing: "0.24em",
|
|
756
|
+
lineHeight: "1",
|
|
757
|
+
marginTop: "6px"
|
|
758
|
+
});
|
|
759
|
+
brand.appendChild(name);
|
|
760
|
+
brand.appendChild(tagline);
|
|
761
|
+
return brand;
|
|
762
|
+
}
|
|
586
763
|
function createLoginPanelTitle(value) {
|
|
587
764
|
const title = document.createElement("div");
|
|
588
765
|
title.textContent = value;
|
|
@@ -612,32 +789,111 @@ function ensureLoginLoadingStyles() {
|
|
|
612
789
|
}
|
|
613
790
|
const style = document.createElement("style");
|
|
614
791
|
style.id = "youidian-login-loading-style";
|
|
615
|
-
style.textContent =
|
|
792
|
+
style.textContent = `
|
|
793
|
+
.youidian-sdk-brand-loading__ring,
|
|
794
|
+
.youidian-sdk-brand-loading__dot {
|
|
795
|
+
transform-box: fill-box;
|
|
796
|
+
transform-origin: center;
|
|
797
|
+
will-change: opacity, transform;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
.youidian-sdk-brand-loading__ring {
|
|
801
|
+
animation: youidian-sdk-brand-loading-ring 1400ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
.youidian-sdk-brand-loading__dot {
|
|
805
|
+
animation: youidian-sdk-brand-loading-dot 1400ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
.youidian-sdk-brand-loading__copy {
|
|
809
|
+
will-change: opacity, transform;
|
|
810
|
+
animation: youidian-sdk-brand-loading-copy 1600ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
.youidian-sdk-brand-loading__tagline {
|
|
814
|
+
will-change: opacity;
|
|
815
|
+
animation: youidian-sdk-brand-loading-tagline 1600ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
@keyframes youidian-sdk-brand-loading-ring {
|
|
819
|
+
0%,
|
|
820
|
+
100% {
|
|
821
|
+
opacity: 0.72;
|
|
822
|
+
transform: rotate(0deg) scale(0.98);
|
|
823
|
+
}
|
|
824
|
+
50% {
|
|
825
|
+
opacity: 1;
|
|
826
|
+
transform: rotate(8deg) scale(1.02);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
@keyframes youidian-sdk-brand-loading-dot {
|
|
831
|
+
0%,
|
|
832
|
+
100% {
|
|
833
|
+
opacity: 0.55;
|
|
834
|
+
transform: scale(0.9);
|
|
835
|
+
}
|
|
836
|
+
50% {
|
|
837
|
+
opacity: 1;
|
|
838
|
+
transform: scale(1.08);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
@keyframes youidian-sdk-brand-loading-copy {
|
|
843
|
+
0%,
|
|
844
|
+
100% {
|
|
845
|
+
opacity: 0.86;
|
|
846
|
+
transform: translateY(0);
|
|
847
|
+
}
|
|
848
|
+
50% {
|
|
849
|
+
opacity: 1;
|
|
850
|
+
transform: translateY(-1px);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
@keyframes youidian-sdk-brand-loading-tagline {
|
|
855
|
+
0%,
|
|
856
|
+
100% {
|
|
857
|
+
opacity: 0.64;
|
|
858
|
+
}
|
|
859
|
+
50% {
|
|
860
|
+
opacity: 0.92;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
@media (prefers-reduced-motion: reduce) {
|
|
865
|
+
.youidian-sdk-brand-loading__ring,
|
|
866
|
+
.youidian-sdk-brand-loading__dot,
|
|
867
|
+
.youidian-sdk-brand-loading__copy,
|
|
868
|
+
.youidian-sdk-brand-loading__tagline {
|
|
869
|
+
animation: none;
|
|
870
|
+
opacity: 1;
|
|
871
|
+
transform: none;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
`;
|
|
616
875
|
document.head.appendChild(style);
|
|
617
876
|
}
|
|
618
|
-
function createLoginLoadingPanel(
|
|
877
|
+
function createLoginLoadingPanel() {
|
|
619
878
|
ensureLoginLoadingStyles();
|
|
620
879
|
const panel = document.createElement("div");
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
margin: "0 auto 18px",
|
|
631
|
-
animation: "youidian-login-spin 780ms linear infinite"
|
|
880
|
+
applyLoginLoadingPanelStyle(panel);
|
|
881
|
+
panel.setAttribute("role", "status");
|
|
882
|
+
panel.setAttribute("aria-label", "\u4F18\u6613\u70B9\u5F00\u53D1\u8005\u670D\u52A1\u4E2D\u53F0");
|
|
883
|
+
const content = document.createElement("div");
|
|
884
|
+
Object.assign(content.style, {
|
|
885
|
+
display: "flex",
|
|
886
|
+
alignItems: "center",
|
|
887
|
+
justifyContent: "center",
|
|
888
|
+
gap: "12px"
|
|
632
889
|
});
|
|
633
|
-
content.appendChild(
|
|
634
|
-
content.appendChild(
|
|
635
|
-
content.appendChild(createLoginPanelDescription(options.description));
|
|
890
|
+
content.appendChild(createBrandLoadingMark());
|
|
891
|
+
content.appendChild(createBrandLoadingCopy());
|
|
636
892
|
panel.appendChild(content);
|
|
637
893
|
return panel;
|
|
638
894
|
}
|
|
639
|
-
function createLoginRedirectingPanel(
|
|
640
|
-
return createLoginLoadingPanel(
|
|
895
|
+
function createLoginRedirectingPanel(_options) {
|
|
896
|
+
return createLoginLoadingPanel();
|
|
641
897
|
}
|
|
642
898
|
function createLoginFallbackPanel(options) {
|
|
643
899
|
const panel = document.createElement("div");
|
|
@@ -737,14 +993,11 @@ var LoginUI = class extends HostedFrameModal {
|
|
|
737
993
|
this.iframeFallbackPanel = null;
|
|
738
994
|
this.iframeLoadingPanel = null;
|
|
739
995
|
}
|
|
740
|
-
showIframeLoading(container
|
|
996
|
+
showIframeLoading(container) {
|
|
741
997
|
if (this.iframeLoadingPanel || this.iframeReady) {
|
|
742
998
|
return;
|
|
743
999
|
}
|
|
744
|
-
const panel = createLoginLoadingPanel(
|
|
745
|
-
description: options?.loadingDescription || "Please wait while the secure login page opens.",
|
|
746
|
-
title: options?.loadingTitle || "Opening login page"
|
|
747
|
-
});
|
|
1000
|
+
const panel = createLoginLoadingPanel();
|
|
748
1001
|
this.iframeLoadingPanel = panel;
|
|
749
1002
|
container.appendChild(panel);
|
|
750
1003
|
}
|
|
@@ -832,6 +1085,20 @@ var LoginUI = class extends HostedFrameModal {
|
|
|
832
1085
|
break;
|
|
833
1086
|
case "LOGIN_RESIZE":
|
|
834
1087
|
break;
|
|
1088
|
+
case "LOGIN_REDIRECT_REQUIRED":
|
|
1089
|
+
if (!isValidLoginRedirectUrl(data.authUrl)) {
|
|
1090
|
+
logLoginWarn("Login redirect requested with invalid authUrl");
|
|
1091
|
+
options?.onError?.("Login redirect URL is invalid", data);
|
|
1092
|
+
break;
|
|
1093
|
+
}
|
|
1094
|
+
logLoginInfo("Login redirect requested by hosted page", {
|
|
1095
|
+
attemptId: data.attemptId || null,
|
|
1096
|
+
channel: data.channel || null,
|
|
1097
|
+
authUrl: data.authUrl
|
|
1098
|
+
});
|
|
1099
|
+
this.close();
|
|
1100
|
+
window.location.assign(data.authUrl);
|
|
1101
|
+
break;
|
|
835
1102
|
case "LOGIN_ERROR":
|
|
836
1103
|
if (this.completed) {
|
|
837
1104
|
break;
|
|
@@ -855,6 +1122,25 @@ var LoginUI = class extends HostedFrameModal {
|
|
|
855
1122
|
break;
|
|
856
1123
|
}
|
|
857
1124
|
}
|
|
1125
|
+
openLoginRedirect(params, options) {
|
|
1126
|
+
if (typeof window === "undefined") return;
|
|
1127
|
+
const { callbackState, finalUrl, launchPayload } = this.buildOpenContext(
|
|
1128
|
+
params,
|
|
1129
|
+
options
|
|
1130
|
+
);
|
|
1131
|
+
logLoginInfo("Redirecting to hosted login page", {
|
|
1132
|
+
appId: params.appId,
|
|
1133
|
+
callbackState,
|
|
1134
|
+
callbackUrl: launchPayload.callbackUrl || null,
|
|
1135
|
+
hasCallbackUrl: Boolean(launchPayload.callbackUrl),
|
|
1136
|
+
loginUrl: finalUrl,
|
|
1137
|
+
autoClose: options?.autoClose ?? true,
|
|
1138
|
+
displayMode: "redirect",
|
|
1139
|
+
pageUrl: window.location.href,
|
|
1140
|
+
parentOrigin: launchPayload.origin || null
|
|
1141
|
+
});
|
|
1142
|
+
window.location.assign(finalUrl);
|
|
1143
|
+
}
|
|
858
1144
|
listenForLoginCallback(callbackState, options) {
|
|
859
1145
|
try {
|
|
860
1146
|
this.callbackChannel = new BroadcastChannel(
|
|
@@ -931,21 +1217,17 @@ var LoginUI = class extends HostedFrameModal {
|
|
|
931
1217
|
options?.onCancel?.();
|
|
932
1218
|
options?.onClose?.();
|
|
933
1219
|
},
|
|
934
|
-
onMessage: (data,
|
|
1220
|
+
onMessage: (data, _container, event) => {
|
|
935
1221
|
const isIframeEvent = event.source === this.iframe?.contentWindow;
|
|
936
1222
|
if (isIframeEvent && (data.type === "LOGIN_READY" || data.type === "LOGIN_RESIZE") || data.type === "LOGIN_SUCCESS" || data.type === "LOGIN_ERROR") {
|
|
937
1223
|
this.markIframeReady();
|
|
938
1224
|
}
|
|
939
|
-
if (data.type === "LOGIN_RESIZE" && data.height) {
|
|
940
|
-
const maxHeight = window.innerHeight * 0.94;
|
|
941
|
-
container.style.height = `${Math.min(data.height, maxHeight)}px`;
|
|
942
|
-
}
|
|
943
1225
|
this.handleLoginEvent(data, options);
|
|
944
1226
|
},
|
|
945
1227
|
width: "min(520px, 100%)"
|
|
946
1228
|
});
|
|
947
1229
|
if (hostedFrame) {
|
|
948
|
-
this.showIframeLoading(hostedFrame.container
|
|
1230
|
+
this.showIframeLoading(hostedFrame.container);
|
|
949
1231
|
this.startIframeWatchdog({
|
|
950
1232
|
container: hostedFrame.container,
|
|
951
1233
|
finalUrl,
|
|
@@ -1049,11 +1331,19 @@ var LoginUI = class extends HostedFrameModal {
|
|
|
1049
1331
|
}
|
|
1050
1332
|
openLogin(params, options) {
|
|
1051
1333
|
if (typeof window === "undefined") return;
|
|
1052
|
-
const displayMode = options?.displayMode ?? "
|
|
1334
|
+
const displayMode = options?.displayMode ?? "auto";
|
|
1335
|
+
if (displayMode === "redirect") {
|
|
1336
|
+
this.openLoginRedirect(params, options);
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1053
1339
|
if (displayMode === "popup") {
|
|
1054
1340
|
this.openLoginPopup(params, options);
|
|
1055
1341
|
return;
|
|
1056
1342
|
}
|
|
1343
|
+
if (displayMode === "auto" && detectLoginEnvironment().shouldUseRedirectLogin) {
|
|
1344
|
+
this.openLoginRedirect(params, options);
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1057
1347
|
this.openLoginModal(params, options);
|
|
1058
1348
|
}
|
|
1059
1349
|
};
|
|
@@ -1064,6 +1354,43 @@ handleLoginCallbackIfPresent({ autoClose: false });
|
|
|
1064
1354
|
|
|
1065
1355
|
// src/client.ts
|
|
1066
1356
|
var PaymentUI = class extends HostedFrameModal {
|
|
1357
|
+
constructor() {
|
|
1358
|
+
super(...arguments);
|
|
1359
|
+
__publicField(this, "activeCheckoutAppId", null);
|
|
1360
|
+
__publicField(this, "activeCheckoutBaseUrl", null);
|
|
1361
|
+
__publicField(this, "activeOrderId", null);
|
|
1362
|
+
__publicField(this, "paymentCompleted", false);
|
|
1363
|
+
__publicField(this, "cancelRequestedOrderId", null);
|
|
1364
|
+
}
|
|
1365
|
+
cancelHostedOrder(reason = "user_cancel", orderId) {
|
|
1366
|
+
const targetOrderId = orderId || this.activeOrderId;
|
|
1367
|
+
if (!targetOrderId || !this.activeCheckoutAppId || !this.activeCheckoutBaseUrl || this.paymentCompleted || this.cancelRequestedOrderId === targetOrderId) {
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
this.cancelRequestedOrderId = targetOrderId;
|
|
1371
|
+
const url = `${this.activeCheckoutBaseUrl}/api/internal/checkout/cancel-order`;
|
|
1372
|
+
const payload = JSON.stringify({
|
|
1373
|
+
appId: this.activeCheckoutAppId,
|
|
1374
|
+
orderId: targetOrderId,
|
|
1375
|
+
reason
|
|
1376
|
+
});
|
|
1377
|
+
if (typeof navigator !== "undefined" && navigator.sendBeacon) {
|
|
1378
|
+
const sent = navigator.sendBeacon(
|
|
1379
|
+
url,
|
|
1380
|
+
new Blob([payload], { type: "application/json" })
|
|
1381
|
+
);
|
|
1382
|
+
if (sent) return;
|
|
1383
|
+
}
|
|
1384
|
+
void fetch(url, {
|
|
1385
|
+
method: "POST",
|
|
1386
|
+
body: payload,
|
|
1387
|
+
headers: { "Content-Type": "text/plain;charset=UTF-8" },
|
|
1388
|
+
keepalive: true,
|
|
1389
|
+
mode: "no-cors"
|
|
1390
|
+
}).catch(() => {
|
|
1391
|
+
this.cancelRequestedOrderId = null;
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1067
1394
|
/**
|
|
1068
1395
|
* Opens the payment checkout page in an iframe modal.
|
|
1069
1396
|
* @param urlOrParams - The checkout page URL or payment parameters
|
|
@@ -1073,11 +1400,25 @@ var PaymentUI = class extends HostedFrameModal {
|
|
|
1073
1400
|
if (typeof document === "undefined") return;
|
|
1074
1401
|
if (this.modal) return;
|
|
1075
1402
|
let checkoutUrl;
|
|
1403
|
+
this.activeOrderId = null;
|
|
1404
|
+
this.paymentCompleted = false;
|
|
1405
|
+
this.cancelRequestedOrderId = null;
|
|
1076
1406
|
if (typeof urlOrParams === "string") {
|
|
1077
1407
|
checkoutUrl = urlOrParams;
|
|
1408
|
+
try {
|
|
1409
|
+
const parsedUrl = new URL(checkoutUrl);
|
|
1410
|
+
this.activeCheckoutBaseUrl = parsedUrl.origin;
|
|
1411
|
+
const parts = parsedUrl.pathname.split("/").filter(Boolean);
|
|
1412
|
+
const checkoutIndex = parts.indexOf("checkout");
|
|
1413
|
+
this.activeCheckoutAppId = checkoutIndex >= 0 ? parts[checkoutIndex + 1] || null : null;
|
|
1414
|
+
} catch {
|
|
1415
|
+
this.activeCheckoutAppId = null;
|
|
1416
|
+
this.activeCheckoutBaseUrl = null;
|
|
1417
|
+
}
|
|
1078
1418
|
} else {
|
|
1079
1419
|
const {
|
|
1080
1420
|
appId,
|
|
1421
|
+
orderId,
|
|
1081
1422
|
productId,
|
|
1082
1423
|
priceId,
|
|
1083
1424
|
productCode,
|
|
@@ -1087,9 +1428,10 @@ var PaymentUI = class extends HostedFrameModal {
|
|
|
1087
1428
|
baseUrl = "https://pay.imgto.link"
|
|
1088
1429
|
} = urlOrParams;
|
|
1089
1430
|
const base = (checkoutUrlParam || baseUrl).replace(/\/$/, "");
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1431
|
+
this.activeCheckoutAppId = appId;
|
|
1432
|
+
this.activeCheckoutBaseUrl = base;
|
|
1433
|
+
const query = new URLSearchParams();
|
|
1434
|
+
if (userId) query.set("userId", userId);
|
|
1093
1435
|
if (customAmount) {
|
|
1094
1436
|
const amount = Number(customAmount.amount);
|
|
1095
1437
|
const currency = customAmount.currency.trim().toUpperCase();
|
|
@@ -1106,13 +1448,22 @@ var PaymentUI = class extends HostedFrameModal {
|
|
|
1106
1448
|
query.set("customAmount", String(amount));
|
|
1107
1449
|
query.set("customCurrency", currency);
|
|
1108
1450
|
}
|
|
1109
|
-
if (
|
|
1451
|
+
if (orderId) {
|
|
1452
|
+
const cleanOrderId = orderId.trim();
|
|
1453
|
+
if (!cleanOrderId) {
|
|
1454
|
+
throw new Error("orderId is required");
|
|
1455
|
+
}
|
|
1456
|
+
this.activeOrderId = cleanOrderId;
|
|
1457
|
+
checkoutUrl = `${base}/checkout/${appId}/orders/${cleanOrderId}`;
|
|
1458
|
+
const queryString = query.toString();
|
|
1459
|
+
if (queryString) checkoutUrl += `?${queryString}`;
|
|
1460
|
+
} else if (productCode) {
|
|
1110
1461
|
checkoutUrl = `${base}/checkout/${appId}/code/${productCode}?${query.toString()}`;
|
|
1111
1462
|
} else if (productId && priceId) {
|
|
1112
1463
|
checkoutUrl = `${base}/checkout/${appId}/${productId}/${priceId}?${query.toString()}`;
|
|
1113
1464
|
} else {
|
|
1114
1465
|
throw new Error(
|
|
1115
|
-
"Either productCode or both productId and priceId are required"
|
|
1466
|
+
"Either orderId, productCode, or both productId and priceId are required"
|
|
1116
1467
|
);
|
|
1117
1468
|
}
|
|
1118
1469
|
}
|
|
@@ -1122,13 +1473,23 @@ var PaymentUI = class extends HostedFrameModal {
|
|
|
1122
1473
|
);
|
|
1123
1474
|
this.openHostedFrame(finalUrl, {
|
|
1124
1475
|
allowedOrigin: options?.allowedOrigin,
|
|
1125
|
-
onCloseButton: () =>
|
|
1476
|
+
onCloseButton: () => {
|
|
1477
|
+
this.cancelHostedOrder("modal_close");
|
|
1478
|
+
options?.onCancel?.(this.activeOrderId || void 0);
|
|
1479
|
+
},
|
|
1126
1480
|
onMessage: (data, container) => {
|
|
1127
1481
|
switch (data.type) {
|
|
1482
|
+
case "PAYMENT_STARTED":
|
|
1483
|
+
if (data.orderId) {
|
|
1484
|
+
this.activeOrderId = data.orderId;
|
|
1485
|
+
}
|
|
1486
|
+
break;
|
|
1128
1487
|
case "PAYMENT_SUCCESS":
|
|
1488
|
+
this.paymentCompleted = true;
|
|
1129
1489
|
options?.onSuccess?.(data.orderId);
|
|
1130
1490
|
break;
|
|
1131
1491
|
case "PAYMENT_CANCELLED":
|
|
1492
|
+
this.cancelHostedOrder("payment_cancelled", data.orderId);
|
|
1132
1493
|
options?.onCancel?.(data.orderId);
|
|
1133
1494
|
break;
|
|
1134
1495
|
case "PAYMENT_RESIZE":
|
|
@@ -1139,6 +1500,7 @@ var PaymentUI = class extends HostedFrameModal {
|
|
|
1139
1500
|
}
|
|
1140
1501
|
break;
|
|
1141
1502
|
case "PAYMENT_CLOSE":
|
|
1503
|
+
this.cancelHostedOrder("payment_close", data.orderId);
|
|
1142
1504
|
this.close();
|
|
1143
1505
|
options?.onClose?.();
|
|
1144
1506
|
break;
|
|
@@ -1194,6 +1556,658 @@ function createPaymentUI() {
|
|
|
1194
1556
|
return new PaymentUI();
|
|
1195
1557
|
}
|
|
1196
1558
|
|
|
1559
|
+
// src/feedback.ts
|
|
1560
|
+
var DEFAULT_CATEGORIES = [
|
|
1561
|
+
{ value: "BUG", label: "\u95EE\u9898\u53CD\u9988" },
|
|
1562
|
+
{ value: "PAYMENT", label: "\u652F\u4ED8\u95EE\u9898" },
|
|
1563
|
+
{ value: "LOGIN", label: "\u767B\u5F55\u95EE\u9898" },
|
|
1564
|
+
{ value: "SUGGESTION", label: "\u529F\u80FD\u5EFA\u8BAE" },
|
|
1565
|
+
{ value: "OTHER", label: "\u5176\u4ED6" }
|
|
1566
|
+
];
|
|
1567
|
+
var STYLE_ID = "youidian-feedback-widget-style";
|
|
1568
|
+
var MAX_IMAGES_PER_MESSAGE = 4;
|
|
1569
|
+
var DEFAULT_PANEL_TITLE = "\u53CD\u9988";
|
|
1570
|
+
var DEFAULT_PANEL_SUBTITLE = "\u63D0\u4EA4\u95EE\u9898\u3001\u5EFA\u8BAE\u6216\u4F7F\u7528\u53CD\u9988\u3002";
|
|
1571
|
+
var DEFAULT_ATTACHMENTS_LABEL = "\u652F\u6301\u7C98\u8D34\u3001\u62D6\u5165\u6216\u4E0A\u4F20\u622A\u56FE\uFF0C\u6700\u591A 4 \u5F20";
|
|
1572
|
+
var FEEDBACK_ICON = `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M21 11.5a8.4 8.4 0 0 1-.9 3.8 8.6 8.6 0 0 1-7.7 4.7 8.5 8.5 0 0 1-3.8-.9L3 21l1.9-5.2a8.5 8.5 0 0 1-1-4.1 8.6 8.6 0 0 1 8.6-8.6 8.5 8.5 0 0 1 8.5 8.4Z" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"/><path d="M8.4 10.2h7.2M8.4 13.5h4.7" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"/></svg>`;
|
|
1573
|
+
var BUG_ICON = `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M8 8.5a4 4 0 0 1 8 0v6a4 4 0 0 1-8 0v-6Z" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M5 9h3m8 0h3M5.5 15H8m8 0h2.5M7 5.5 5.5 4M17 5.5 18.5 4M12 4.5V2.8M12 20.5v-12" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>`;
|
|
1574
|
+
var NOTE_ICON = `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 3.5h7l3.5 3.5v13.5H7A2.5 2.5 0 0 1 4.5 18V6A2.5 2.5 0 0 1 7 3.5Z" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M14 3.8V7h3.2M8 11h8M8 15h6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>`;
|
|
1575
|
+
var MAIL_ICON = `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4.5 6.5h15v11h-15v-11Z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="m5 7 7 6 7-6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
|
1576
|
+
var IMAGE_ICON = `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 5h14v14H5V5Z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="m7.5 16 3.2-3.2 2.4 2.4 1.7-1.7L18 16.8" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M15.8 8.2h.01" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round"/></svg>`;
|
|
1577
|
+
var CHEVRON_ICON = `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m7 10 5 5 5-5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
|
1578
|
+
function ensureStyle() {
|
|
1579
|
+
if (typeof document === "undefined") return;
|
|
1580
|
+
if (document.getElementById(STYLE_ID)) return;
|
|
1581
|
+
const style = document.createElement("style");
|
|
1582
|
+
style.id = STYLE_ID;
|
|
1583
|
+
style.textContent = `
|
|
1584
|
+
.yd-feedback-root,.yd-feedback-root *{box-sizing:border-box}
|
|
1585
|
+
.yd-feedback-root{--yd-feedback-bg:hsl(var(--background,0 0% 100%));--yd-feedback-fg:hsl(var(--foreground,240 10% 3.9%));--yd-feedback-card:hsl(var(--card,0 0% 100%));--yd-feedback-card-fg:hsl(var(--card-foreground,240 10% 3.9%));--yd-feedback-muted:hsl(var(--muted,240 4.8% 95.9%));--yd-feedback-muted-fg:hsl(var(--muted-foreground,240 3.8% 46.1%));--yd-feedback-border:hsl(var(--border,240 5.9% 90%));--yd-feedback-primary:hsl(var(--primary,142 76% 36%));--yd-feedback-primary-fg:hsl(var(--primary-foreground,0 0% 100%));--yd-feedback-ring:hsl(var(--ring,142 76% 36%));font:14px/1.5 Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;color:var(--yd-feedback-fg)}
|
|
1586
|
+
.yd-feedback-root button,.yd-feedback-root input,.yd-feedback-root textarea{font:inherit}
|
|
1587
|
+
.yd-feedback-launcher{position:fixed;right:22px;bottom:22px;z-index:2147483000;width:54px;height:54px;display:grid;place-items:center;border:1px solid color-mix(in srgb,var(--yd-feedback-primary) 24%,var(--yd-feedback-border));border-radius:999px;background:var(--yd-feedback-primary);color:var(--yd-feedback-primary-fg);box-shadow:0 18px 45px color-mix(in srgb,var(--yd-feedback-primary) 22%,transparent);cursor:pointer;transition:transform .18s ease,box-shadow .18s ease,background .18s ease}
|
|
1588
|
+
.yd-feedback-launcher:hover{transform:translateY(-2px);box-shadow:0 22px 56px color-mix(in srgb,var(--yd-feedback-primary) 30%,transparent)}
|
|
1589
|
+
.yd-feedback-launcher:focus-visible{outline:3px solid color-mix(in srgb,var(--yd-feedback-ring) 32%,transparent);outline-offset:3px}
|
|
1590
|
+
.yd-feedback-launcher svg{width:27px;height:27px}
|
|
1591
|
+
.yd-feedback-panel{position:fixed;inset:auto 22px 86px auto;z-index:2147483000;width:min(760px,calc(100vw - 44px));height:min(560px,calc(100vh - 108px));display:none;grid-template-rows:auto minmax(0,1fr);overflow:hidden;border:1px solid var(--yd-feedback-border);border-radius:18px;background:var(--yd-feedback-card);color:var(--yd-feedback-card-fg);box-shadow:0 24px 70px rgba(15,23,42,.18)}
|
|
1592
|
+
.yd-feedback-panel[data-open="true"]{display:grid}
|
|
1593
|
+
.yd-feedback-header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding:18px 20px 16px;border-bottom:1px solid var(--yd-feedback-border);background:var(--yd-feedback-card)}
|
|
1594
|
+
.yd-feedback-title{margin:0;font-size:22px;line-height:1.15;font-weight:760;letter-spacing:0;color:var(--yd-feedback-fg)}
|
|
1595
|
+
.yd-feedback-subtitle{margin:8px 0 0;color:var(--yd-feedback-muted-fg);font-size:13px;line-height:1.45}
|
|
1596
|
+
.yd-feedback-close{display:grid;place-items:center;width:34px;height:34px;border:0;border-radius:10px;background:transparent;color:var(--yd-feedback-muted-fg);cursor:pointer;transition:background .16s ease,color .16s ease}
|
|
1597
|
+
.yd-feedback-close:hover{background:var(--yd-feedback-muted);color:var(--yd-feedback-fg)}
|
|
1598
|
+
.yd-feedback-close svg{width:24px;height:24px}
|
|
1599
|
+
.yd-feedback-shell{display:grid;min-height:0;grid-template-columns:230px minmax(0,1fr)}
|
|
1600
|
+
.yd-feedback-sidebar{min-width:0;border-right:1px solid var(--yd-feedback-border);background:var(--yd-feedback-muted)}
|
|
1601
|
+
.yd-feedback-sidebar-toolbar{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:14px 14px;border-bottom:1px solid var(--yd-feedback-border)}
|
|
1602
|
+
.yd-feedback-sidebar-title{display:flex;align-items:center;gap:9px;color:var(--yd-feedback-fg);font-size:15px;font-weight:720}
|
|
1603
|
+
.yd-feedback-sidebar-title svg{width:20px;height:20px;color:var(--yd-feedback-primary)}
|
|
1604
|
+
.yd-feedback-icon-button{display:grid;place-items:center;width:34px;height:34px;border:1px solid var(--yd-feedback-border);border-radius:999px;background:var(--yd-feedback-card);color:var(--yd-feedback-fg);cursor:pointer;transition:background .16s ease,border-color .16s ease}
|
|
1605
|
+
.yd-feedback-icon-button:hover{background:color-mix(in srgb,var(--yd-feedback-primary) 8%,var(--yd-feedback-card));border-color:color-mix(in srgb,var(--yd-feedback-primary) 38%,var(--yd-feedback-border))}
|
|
1606
|
+
.yd-feedback-icon-button svg{width:18px;height:18px}
|
|
1607
|
+
.yd-feedback-thread-list{padding:12px}
|
|
1608
|
+
.yd-feedback-thread-card{display:none;border:1px solid var(--yd-feedback-border);border-radius:14px;background:var(--yd-feedback-card);padding:12px}
|
|
1609
|
+
.yd-feedback-thread-card[data-visible="true"]{display:block}
|
|
1610
|
+
.yd-feedback-thread-badges{display:flex;flex-wrap:wrap;gap:7px;margin-bottom:10px}
|
|
1611
|
+
.yd-feedback-badge{display:inline-flex;align-items:center;border:1px solid var(--yd-feedback-border);border-radius:999px;background:var(--yd-feedback-muted);color:var(--yd-feedback-fg);padding:4px 9px;font-size:11px;font-weight:700;line-height:1}
|
|
1612
|
+
.yd-feedback-badge[data-status="true"]{border-color:color-mix(in srgb,var(--yd-feedback-primary) 45%,var(--yd-feedback-border));background:color-mix(in srgb,var(--yd-feedback-primary) 12%,var(--yd-feedback-card));color:var(--yd-feedback-primary)}
|
|
1613
|
+
.yd-feedback-thread-preview{margin:0;color:var(--yd-feedback-fg);font-size:13px;line-height:1.45;word-break:break-word}
|
|
1614
|
+
.yd-feedback-thread-meta{display:flex;justify-content:space-between;gap:10px;margin-top:10px;color:var(--yd-feedback-muted-fg);font-size:11px}
|
|
1615
|
+
.yd-feedback-empty-thread{border:1px dashed var(--yd-feedback-border);border-radius:14px;padding:14px;color:var(--yd-feedback-muted-fg);font-size:12px}
|
|
1616
|
+
.yd-feedback-main{min-width:0;min-height:0;background:var(--yd-feedback-card);overflow:hidden}
|
|
1617
|
+
.yd-feedback-form{height:100%;display:grid;grid-template-rows:auto auto auto minmax(0,1fr) auto auto;gap:14px;padding:16px 18px 0}
|
|
1618
|
+
.yd-feedback-row{display:grid;gap:14px;grid-template-columns:180px minmax(0,1fr)}
|
|
1619
|
+
.yd-feedback-field{display:flex;min-width:0;flex-direction:column;gap:8px}
|
|
1620
|
+
.yd-feedback-label{display:flex;align-items:center;gap:8px;color:var(--yd-feedback-fg);font-size:13px;font-weight:720}
|
|
1621
|
+
.yd-feedback-label svg{width:17px;height:17px;color:var(--yd-feedback-fg)}
|
|
1622
|
+
.yd-feedback-input,.yd-feedback-textarea{width:100%;border:1px solid var(--yd-feedback-border);border-radius:13px;background:var(--yd-feedback-card);color:var(--yd-feedback-fg);outline:none;transition:border-color .16s ease,box-shadow .16s ease,background .16s ease}
|
|
1623
|
+
.yd-feedback-input{height:40px;padding:0 13px;font-size:14px}
|
|
1624
|
+
.yd-feedback-textarea{min-height:118px;padding:13px 14px;font-size:14px;resize:none}
|
|
1625
|
+
.yd-feedback-input::placeholder,.yd-feedback-textarea::placeholder{color:var(--yd-feedback-muted-fg)}
|
|
1626
|
+
.yd-feedback-input:focus,.yd-feedback-textarea:focus{border-color:var(--yd-feedback-ring);box-shadow:0 0 0 3px color-mix(in srgb,var(--yd-feedback-ring) 18%,transparent);background:var(--yd-feedback-card)}
|
|
1627
|
+
.yd-feedback-compose-box{overflow:hidden;border:1px solid var(--yd-feedback-border);border-radius:15px;background:var(--yd-feedback-card)}
|
|
1628
|
+
.yd-feedback-compose-box .yd-feedback-textarea{border:0;border-radius:0;box-shadow:none;background:transparent}
|
|
1629
|
+
.yd-feedback-attachment-row{display:flex;align-items:center;justify-content:space-between;gap:12px;border-top:1px solid var(--yd-feedback-border);padding:9px 12px;color:var(--yd-feedback-muted-fg)}
|
|
1630
|
+
.yd-feedback-file-trigger{display:inline-flex;align-items:center;gap:8px;border:0;background:transparent;color:var(--yd-feedback-fg);cursor:pointer;padding:7px 8px;border-radius:10px}
|
|
1631
|
+
.yd-feedback-file-trigger:hover{background:var(--yd-feedback-muted)}
|
|
1632
|
+
.yd-feedback-file-trigger svg{width:18px;height:18px}
|
|
1633
|
+
.yd-feedback-file-input{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;clip-path:inset(50%)}
|
|
1634
|
+
.yd-feedback-files{min-width:0;text-align:right;color:var(--yd-feedback-muted-fg);font-size:12px}
|
|
1635
|
+
.yd-feedback-select{position:relative;width:142px}
|
|
1636
|
+
.yd-feedback-select-button{width:100%;height:40px;display:flex;align-items:center;justify-content:space-between;gap:10px;border:1px solid var(--yd-feedback-border);border-radius:13px;background:var(--yd-feedback-card);color:var(--yd-feedback-fg);padding:0 12px;cursor:pointer}
|
|
1637
|
+
.yd-feedback-select-value{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:700}
|
|
1638
|
+
.yd-feedback-select-value svg{width:18px;height:18px;color:var(--yd-feedback-muted-fg)}
|
|
1639
|
+
.yd-feedback-select-chevron{width:17px;height:17px;color:var(--yd-feedback-muted-fg)}
|
|
1640
|
+
.yd-feedback-select-menu{position:absolute;left:0;top:calc(100% + 7px);z-index:2;display:none;min-width:176px;max-height:220px;overflow:auto;border:1px solid var(--yd-feedback-border);border-radius:13px;background:var(--yd-feedback-card);box-shadow:0 18px 45px rgba(15,23,42,.16);padding:5px}
|
|
1641
|
+
.yd-feedback-select[data-open="true"] .yd-feedback-select-menu{display:block}
|
|
1642
|
+
.yd-feedback-select-option{width:100%;min-height:38px;display:flex;align-items:center;gap:8px;border:0;border-radius:10px;background:transparent;color:var(--yd-feedback-fg);text-align:left;padding:9px 10px;cursor:pointer}
|
|
1643
|
+
.yd-feedback-select-option svg{width:18px;height:18px;flex:0 0 18px;color:var(--yd-feedback-muted-fg)}
|
|
1644
|
+
.yd-feedback-select-option span{line-height:1.2;white-space:nowrap}
|
|
1645
|
+
.yd-feedback-select-option:hover,.yd-feedback-select-option[data-selected="true"]{background:color-mix(in srgb,var(--yd-feedback-primary) 10%,var(--yd-feedback-card));color:var(--yd-feedback-fg)}
|
|
1646
|
+
.yd-feedback-messages{display:flex;min-height:0;flex-direction:column;gap:8px;overflow:auto}
|
|
1647
|
+
.yd-feedback-message{border:1px solid var(--yd-feedback-border);border-radius:12px;padding:9px 10px;background:var(--yd-feedback-muted)}
|
|
1648
|
+
.yd-feedback-message[data-admin="true"]{background:color-mix(in srgb,var(--yd-feedback-primary) 10%,var(--yd-feedback-card));border-color:color-mix(in srgb,var(--yd-feedback-primary) 24%,var(--yd-feedback-border))}
|
|
1649
|
+
.yd-feedback-message-meta{margin-bottom:4px;color:var(--yd-feedback-muted-fg);font-size:11px}
|
|
1650
|
+
.yd-feedback-message-content{color:var(--yd-feedback-fg);font-size:12px;line-height:1.45;word-break:break-word}
|
|
1651
|
+
.yd-feedback-actions{position:sticky;bottom:0;display:flex;align-items:center;justify-content:flex-end;gap:12px;margin:0 -18px;padding:12px 18px 16px;border-top:1px solid var(--yd-feedback-border);background:var(--yd-feedback-card)}
|
|
1652
|
+
.yd-feedback-submit{min-width:104px;height:44px;border:0;border-radius:13px;background:var(--yd-feedback-primary);color:var(--yd-feedback-primary-fg);padding:0 18px;font-size:14px;font-weight:800;cursor:pointer;box-shadow:0 12px 24px color-mix(in srgb,var(--yd-feedback-primary) 18%,transparent);transition:background .16s ease,transform .16s ease}
|
|
1653
|
+
.yd-feedback-submit:hover{transform:translateY(-1px)}
|
|
1654
|
+
.yd-feedback-submit:disabled{opacity:.55;cursor:not-allowed;transform:none}
|
|
1655
|
+
.yd-feedback-secondary{height:38px;border:0;border-radius:12px;background:transparent;color:var(--yd-feedback-fg);padding:0 12px;font-size:14px;font-weight:760;cursor:pointer}
|
|
1656
|
+
.yd-feedback-secondary:hover{background:var(--yd-feedback-muted)}
|
|
1657
|
+
.yd-feedback-error{min-height:18px;color:#fb7185;font-size:13px}
|
|
1658
|
+
@media (max-width:760px){
|
|
1659
|
+
.yd-feedback-panel{inset:auto 14px 78px 14px;width:auto;height:min(560px,calc(100vh - 96px));border-radius:16px}
|
|
1660
|
+
.yd-feedback-header{padding:16px 16px 14px}
|
|
1661
|
+
.yd-feedback-title{font-size:20px}
|
|
1662
|
+
.yd-feedback-shell{grid-template-columns:1fr}
|
|
1663
|
+
.yd-feedback-sidebar{display:none}
|
|
1664
|
+
.yd-feedback-form{padding:14px 14px 0;gap:12px}
|
|
1665
|
+
.yd-feedback-row{grid-template-columns:1fr}
|
|
1666
|
+
.yd-feedback-actions{justify-content:space-between}
|
|
1667
|
+
}
|
|
1668
|
+
`;
|
|
1669
|
+
document.head.appendChild(style);
|
|
1670
|
+
}
|
|
1671
|
+
function getBaseUrl(options) {
|
|
1672
|
+
return (options.apiBaseUrl || "https://pay.imgto.link").replace(/\/$/, "");
|
|
1673
|
+
}
|
|
1674
|
+
function safeJson(value) {
|
|
1675
|
+
try {
|
|
1676
|
+
return JSON.stringify(value);
|
|
1677
|
+
} catch {
|
|
1678
|
+
return "{}";
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
function escapeHtml(value) {
|
|
1682
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1683
|
+
}
|
|
1684
|
+
function getInitialContact(actor) {
|
|
1685
|
+
return actor?.contactEmail || actor?.contactPhone || "";
|
|
1686
|
+
}
|
|
1687
|
+
function formatCategoryLabel(value, categories = DEFAULT_CATEGORIES) {
|
|
1688
|
+
const matched = categories.find((item) => item.value === value);
|
|
1689
|
+
return matched?.label || value || DEFAULT_CATEGORIES[0]?.label || "\u53CD\u9988";
|
|
1690
|
+
}
|
|
1691
|
+
function formatThreadStatus(value) {
|
|
1692
|
+
const statusMap = {
|
|
1693
|
+
OPEN: "\u5DF2\u63D0\u4EA4",
|
|
1694
|
+
WAITING_ADMIN: "\u7B49\u5F85\u56DE\u590D",
|
|
1695
|
+
WAITING_USER: "\u5F85\u8865\u5145",
|
|
1696
|
+
RESOLVED: "\u5DF2\u5904\u7406",
|
|
1697
|
+
CLOSED: "\u5DF2\u5173\u95ED"
|
|
1698
|
+
};
|
|
1699
|
+
return statusMap[value || ""] || value || "\u5DF2\u63D0\u4EA4";
|
|
1700
|
+
}
|
|
1701
|
+
function formatSenderType(value) {
|
|
1702
|
+
if (value === "END_USER") return "\u7528\u6237";
|
|
1703
|
+
if (value === "ADMIN") return "\u7BA1\u7406\u5458";
|
|
1704
|
+
return "\u7CFB\u7EDF";
|
|
1705
|
+
}
|
|
1706
|
+
function formatThreadTime(value) {
|
|
1707
|
+
if (!value) return "";
|
|
1708
|
+
return new Date(value).toLocaleString();
|
|
1709
|
+
}
|
|
1710
|
+
async function requestJson(url, init) {
|
|
1711
|
+
const response = await fetch(url, {
|
|
1712
|
+
...init,
|
|
1713
|
+
headers: {
|
|
1714
|
+
Accept: "application/json",
|
|
1715
|
+
...init?.body ? { "Content-Type": "application/json" } : {},
|
|
1716
|
+
...init?.headers || {}
|
|
1717
|
+
}
|
|
1718
|
+
});
|
|
1719
|
+
const payload = await response.json().catch(() => null);
|
|
1720
|
+
if (!response.ok || payload?.code !== 200) {
|
|
1721
|
+
throw new Error(payload?.message || response.statusText || "Request failed");
|
|
1722
|
+
}
|
|
1723
|
+
return payload.data;
|
|
1724
|
+
}
|
|
1725
|
+
function collectMetadata(options) {
|
|
1726
|
+
const page = typeof window === "undefined" ? {} : {
|
|
1727
|
+
pageUrl: window.location.href,
|
|
1728
|
+
pageTitle: document.title,
|
|
1729
|
+
userAgent: navigator.userAgent
|
|
1730
|
+
};
|
|
1731
|
+
return { ...page, ...options.metadata || {} };
|
|
1732
|
+
}
|
|
1733
|
+
async function compressImage(file) {
|
|
1734
|
+
if (typeof document === "undefined" || !file.type.startsWith("image/")) {
|
|
1735
|
+
return file;
|
|
1736
|
+
}
|
|
1737
|
+
const bitmap = await createImageBitmap(file);
|
|
1738
|
+
const maxSize = 1600;
|
|
1739
|
+
const scale = Math.min(1, maxSize / Math.max(bitmap.width, bitmap.height));
|
|
1740
|
+
const canvas = document.createElement("canvas");
|
|
1741
|
+
canvas.width = Math.max(1, Math.round(bitmap.width * scale));
|
|
1742
|
+
canvas.height = Math.max(1, Math.round(bitmap.height * scale));
|
|
1743
|
+
const ctx = canvas.getContext("2d");
|
|
1744
|
+
if (!ctx) return file;
|
|
1745
|
+
ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
|
|
1746
|
+
const blob = await new Promise((resolve) => {
|
|
1747
|
+
canvas.toBlob(resolve, "image/webp", 0.82);
|
|
1748
|
+
});
|
|
1749
|
+
if (!blob) return file;
|
|
1750
|
+
return new File([blob], file.name.replace(/\.[^.]+$/, ".webp"), {
|
|
1751
|
+
type: "image/webp",
|
|
1752
|
+
lastModified: Date.now()
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
1755
|
+
function createFeedbackWidget(container, options) {
|
|
1756
|
+
if (typeof document === "undefined") {
|
|
1757
|
+
return { open() {
|
|
1758
|
+
}, close() {
|
|
1759
|
+
}, destroy() {
|
|
1760
|
+
} };
|
|
1761
|
+
}
|
|
1762
|
+
if (!options.appId) throw new Error("appId is required");
|
|
1763
|
+
ensureStyle();
|
|
1764
|
+
let threadState = null;
|
|
1765
|
+
let pollTimer = null;
|
|
1766
|
+
let panelReady = false;
|
|
1767
|
+
let pendingFiles = [];
|
|
1768
|
+
let renderedMessages = [];
|
|
1769
|
+
let removeLoadListener = null;
|
|
1770
|
+
const root = document.createElement("div");
|
|
1771
|
+
root.className = "yd-feedback-root";
|
|
1772
|
+
const launcher = document.createElement("button");
|
|
1773
|
+
launcher.type = "button";
|
|
1774
|
+
launcher.className = "yd-feedback-launcher";
|
|
1775
|
+
launcher.setAttribute("aria-label", options.launcherText || "\u53CD\u9988");
|
|
1776
|
+
launcher.innerHTML = FEEDBACK_ICON;
|
|
1777
|
+
root.appendChild(launcher);
|
|
1778
|
+
container.appendChild(root);
|
|
1779
|
+
const panel = document.createElement("section");
|
|
1780
|
+
panel.className = "yd-feedback-panel";
|
|
1781
|
+
panel.setAttribute("aria-label", options.panelTitle || DEFAULT_PANEL_TITLE);
|
|
1782
|
+
panel.innerHTML = `
|
|
1783
|
+
<div class="yd-feedback-header">
|
|
1784
|
+
<div>
|
|
1785
|
+
<h2 class="yd-feedback-title">${escapeHtml(options.panelTitle || DEFAULT_PANEL_TITLE)}</h2>
|
|
1786
|
+
<p class="yd-feedback-subtitle">${escapeHtml(options.panelSubtitle || DEFAULT_PANEL_SUBTITLE)}</p>
|
|
1787
|
+
</div>
|
|
1788
|
+
<button type="button" class="yd-feedback-close" aria-label="\u5173\u95ED">
|
|
1789
|
+
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6 6 18" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/></svg>
|
|
1790
|
+
</button>
|
|
1791
|
+
</div>
|
|
1792
|
+
<div class="yd-feedback-shell">
|
|
1793
|
+
<aside class="yd-feedback-sidebar">
|
|
1794
|
+
<div class="yd-feedback-sidebar-toolbar">
|
|
1795
|
+
<div class="yd-feedback-sidebar-title">${FEEDBACK_ICON}<span>\u53CD\u9988\u8BB0\u5F55</span></div>
|
|
1796
|
+
<button type="button" class="yd-feedback-icon-button" data-role="clear" aria-label="\u65B0\u5EFA\u53CD\u9988">
|
|
1797
|
+
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14M5 12h14" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/></svg>
|
|
1798
|
+
</button>
|
|
1799
|
+
</div>
|
|
1800
|
+
<div class="yd-feedback-thread-list">
|
|
1801
|
+
<div class="yd-feedback-thread-card" data-role="thread-card">
|
|
1802
|
+
<div class="yd-feedback-thread-badges">
|
|
1803
|
+
<span class="yd-feedback-badge" data-role="thread-category">\u95EE\u9898\u53CD\u9988</span>
|
|
1804
|
+
<span class="yd-feedback-badge" data-status="true" data-role="thread-status">\u5DF2\u63D0\u4EA4</span>
|
|
1805
|
+
</div>
|
|
1806
|
+
<p class="yd-feedback-thread-preview" data-role="thread-preview"></p>
|
|
1807
|
+
<div class="yd-feedback-thread-meta">
|
|
1808
|
+
<span data-role="thread-time"></span>
|
|
1809
|
+
<span>\u6211\u7684\u53CD\u9988</span>
|
|
1810
|
+
</div>
|
|
1811
|
+
</div>
|
|
1812
|
+
<div class="yd-feedback-empty-thread" data-role="thread-empty">\u63D0\u4EA4\u53CD\u9988\u540E\uFF0C\u8BB0\u5F55\u4F1A\u663E\u793A\u5728\u8FD9\u91CC\u3002</div>
|
|
1813
|
+
</div>
|
|
1814
|
+
</aside>
|
|
1815
|
+
<div class="yd-feedback-main">
|
|
1816
|
+
<div class="yd-feedback-form">
|
|
1817
|
+
<div class="yd-feedback-row">
|
|
1818
|
+
<div class="yd-feedback-field">
|
|
1819
|
+
<label class="yd-feedback-label">\u53CD\u9988\u7C7B\u578B</label>
|
|
1820
|
+
<div class="yd-feedback-select" data-role="category-select">
|
|
1821
|
+
<button type="button" class="yd-feedback-select-button" data-role="category-trigger">
|
|
1822
|
+
<span class="yd-feedback-select-value">${BUG_ICON}<span data-role="category-value">\u95EE\u9898\u53CD\u9988</span></span>
|
|
1823
|
+
<span class="yd-feedback-select-chevron">${CHEVRON_ICON}</span>
|
|
1824
|
+
</button>
|
|
1825
|
+
<div class="yd-feedback-select-menu" data-role="category-menu"></div>
|
|
1826
|
+
</div>
|
|
1827
|
+
</div>
|
|
1828
|
+
</div>
|
|
1829
|
+
<div class="yd-feedback-field">
|
|
1830
|
+
<label class="yd-feedback-label">${NOTE_ICON}<span>\u53CD\u9988\u5185\u5BB9</span></label>
|
|
1831
|
+
<div class="yd-feedback-compose-box">
|
|
1832
|
+
<textarea class="yd-feedback-textarea" data-role="content" placeholder="${escapeHtml(options.placeholder || "\u8BF7\u63CF\u8FF0\u95EE\u9898\u73B0\u8C61\u3001\u64CD\u4F5C\u6B65\u9AA4\u6216\u5EFA\u8BAE\u5185\u5BB9\u3002")}"></textarea>
|
|
1833
|
+
<div class="yd-feedback-attachment-row">
|
|
1834
|
+
<button type="button" class="yd-feedback-file-trigger" data-role="files-trigger">${IMAGE_ICON}<span>\u4E0A\u4F20\u56FE\u7247</span></button>
|
|
1835
|
+
<input class="yd-feedback-file-input" type="file" accept="image/png,image/jpeg,image/webp" multiple data-role="files" />
|
|
1836
|
+
<div class="yd-feedback-files" data-role="files-label">${DEFAULT_ATTACHMENTS_LABEL}</div>
|
|
1837
|
+
</div>
|
|
1838
|
+
</div>
|
|
1839
|
+
</div>
|
|
1840
|
+
<div class="yd-feedback-field">
|
|
1841
|
+
<label class="yd-feedback-label">${MAIL_ICON}<span>\u8054\u7CFB\u65B9\u5F0F\uFF08\u9009\u586B\uFF09</span></label>
|
|
1842
|
+
<input class="yd-feedback-input" data-role="contact" value="${escapeHtml(getInitialContact(options.actor))}" placeholder="${escapeHtml(options.contactPlaceholder || "\u90AE\u7BB1\u6216\u7535\u8BDD\u53F7\u7801\uFF0C\u4EC5\u7528\u4E8E\u53CD\u9988\u8DDF\u8FDB")}" />
|
|
1843
|
+
</div>
|
|
1844
|
+
<div class="yd-feedback-messages" data-role="messages"></div>
|
|
1845
|
+
<div class="yd-feedback-error" data-role="error"></div>
|
|
1846
|
+
<div class="yd-feedback-actions">
|
|
1847
|
+
<button type="button" class="yd-feedback-secondary" data-role="clear-secondary">\u5173\u95ED</button>
|
|
1848
|
+
<button type="button" class="yd-feedback-submit" data-role="submit">\u63D0\u4EA4\u53CD\u9988</button>
|
|
1849
|
+
</div>
|
|
1850
|
+
</div>
|
|
1851
|
+
</div>
|
|
1852
|
+
</div>
|
|
1853
|
+
`;
|
|
1854
|
+
function initPanel() {
|
|
1855
|
+
if (panelReady) return;
|
|
1856
|
+
panelReady = true;
|
|
1857
|
+
root.appendChild(panel);
|
|
1858
|
+
const categorySelect = panel.querySelector(
|
|
1859
|
+
'[data-role="category-select"]'
|
|
1860
|
+
);
|
|
1861
|
+
const categoryMenu = panel.querySelector(
|
|
1862
|
+
'[data-role="category-menu"]'
|
|
1863
|
+
);
|
|
1864
|
+
const categoryValue = panel.querySelector(
|
|
1865
|
+
'[data-role="category-value"]'
|
|
1866
|
+
);
|
|
1867
|
+
if (categoryMenu && categoryValue) {
|
|
1868
|
+
const categories = options.categories || DEFAULT_CATEGORIES;
|
|
1869
|
+
categoryValue.textContent = categories[0]?.label || DEFAULT_CATEGORIES[0]?.label || "\u53CD\u9988";
|
|
1870
|
+
for (const item of categories) {
|
|
1871
|
+
const option = document.createElement("button");
|
|
1872
|
+
option.type = "button";
|
|
1873
|
+
option.className = "yd-feedback-select-option";
|
|
1874
|
+
option.dataset.value = item.value;
|
|
1875
|
+
option.dataset.selected = String(item.value === categories[0]?.value);
|
|
1876
|
+
option.innerHTML = `${BUG_ICON}<span>${escapeHtml(item.label)}</span>`;
|
|
1877
|
+
option.addEventListener("click", () => {
|
|
1878
|
+
for (const node of categoryMenu.querySelectorAll(
|
|
1879
|
+
".yd-feedback-select-option"
|
|
1880
|
+
)) {
|
|
1881
|
+
node.dataset.selected = String(node === option);
|
|
1882
|
+
}
|
|
1883
|
+
categoryValue.textContent = item.label;
|
|
1884
|
+
if (categorySelect) {
|
|
1885
|
+
categorySelect.dataset.value = item.value;
|
|
1886
|
+
categorySelect.dataset.open = "false";
|
|
1887
|
+
}
|
|
1888
|
+
});
|
|
1889
|
+
categoryMenu.appendChild(option);
|
|
1890
|
+
}
|
|
1891
|
+
categorySelect?.setAttribute("data-value", categories[0]?.value || "BUG");
|
|
1892
|
+
}
|
|
1893
|
+
panel.querySelector('[data-role="category-trigger"]')?.addEventListener("click", () => {
|
|
1894
|
+
if (!categorySelect) return;
|
|
1895
|
+
categorySelect.dataset.open = categorySelect.dataset.open === "true" ? "false" : "true";
|
|
1896
|
+
});
|
|
1897
|
+
panel.querySelector(".yd-feedback-close")?.addEventListener("click", close);
|
|
1898
|
+
panel.querySelector('[data-role="clear"]')?.addEventListener("click", resetThread);
|
|
1899
|
+
panel.querySelector('[data-role="clear-secondary"]')?.addEventListener("click", close);
|
|
1900
|
+
panel.querySelector('[data-role="files-trigger"]')?.addEventListener("click", () => {
|
|
1901
|
+
panel.querySelector('[data-role="files"]')?.click();
|
|
1902
|
+
});
|
|
1903
|
+
panel.querySelector('[data-role="files"]')?.addEventListener("change", (event) => {
|
|
1904
|
+
setPendingFiles(event.target.files);
|
|
1905
|
+
});
|
|
1906
|
+
const composeBox = panel.querySelector(
|
|
1907
|
+
".yd-feedback-compose-box"
|
|
1908
|
+
);
|
|
1909
|
+
composeBox?.addEventListener("dragover", (event) => {
|
|
1910
|
+
event.preventDefault();
|
|
1911
|
+
});
|
|
1912
|
+
composeBox?.addEventListener("drop", (event) => {
|
|
1913
|
+
event.preventDefault();
|
|
1914
|
+
setPendingFiles(event.dataTransfer?.files || null);
|
|
1915
|
+
});
|
|
1916
|
+
composeBox?.addEventListener("paste", (event) => {
|
|
1917
|
+
const files = Array.from(event.clipboardData?.files || []).filter(
|
|
1918
|
+
(file) => file.type.startsWith("image/")
|
|
1919
|
+
);
|
|
1920
|
+
if (files.length > 0) {
|
|
1921
|
+
setPendingFiles(files);
|
|
1922
|
+
}
|
|
1923
|
+
});
|
|
1924
|
+
panel.querySelector('[data-role="submit"]')?.addEventListener("click", () => void submit());
|
|
1925
|
+
}
|
|
1926
|
+
function setPendingFiles(files) {
|
|
1927
|
+
pendingFiles = Array.from(files || []).filter((file) => file.type.startsWith("image/")).slice(0, MAX_IMAGES_PER_MESSAGE);
|
|
1928
|
+
const label = panel.querySelector('[data-role="files-label"]');
|
|
1929
|
+
if (label) {
|
|
1930
|
+
label.textContent = pendingFiles.length > 0 ? `\u5DF2\u9009\u62E9 ${pendingFiles.length} \u5F20\u56FE\u7247` : DEFAULT_ATTACHMENTS_LABEL;
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
function resetThread() {
|
|
1934
|
+
threadState = null;
|
|
1935
|
+
pendingFiles = [];
|
|
1936
|
+
renderedMessages = [];
|
|
1937
|
+
renderMessages([]);
|
|
1938
|
+
updateThreadCard();
|
|
1939
|
+
setError("");
|
|
1940
|
+
const textarea = panel.querySelector(
|
|
1941
|
+
'[data-role="content"]'
|
|
1942
|
+
);
|
|
1943
|
+
if (textarea) textarea.value = "";
|
|
1944
|
+
const fileInput = panel.querySelector(
|
|
1945
|
+
'[data-role="files"]'
|
|
1946
|
+
);
|
|
1947
|
+
if (fileInput) fileInput.value = "";
|
|
1948
|
+
const filesLabel = panel.querySelector(
|
|
1949
|
+
'[data-role="files-label"]'
|
|
1950
|
+
);
|
|
1951
|
+
if (filesLabel) filesLabel.textContent = DEFAULT_ATTACHMENTS_LABEL;
|
|
1952
|
+
}
|
|
1953
|
+
function updateThreadCard() {
|
|
1954
|
+
const card = panel.querySelector('[data-role="thread-card"]');
|
|
1955
|
+
const empty = panel.querySelector('[data-role="thread-empty"]');
|
|
1956
|
+
if (!card || !empty) return;
|
|
1957
|
+
if (!threadState) {
|
|
1958
|
+
card.dataset.visible = "false";
|
|
1959
|
+
empty.style.display = "block";
|
|
1960
|
+
return;
|
|
1961
|
+
}
|
|
1962
|
+
card.dataset.visible = "true";
|
|
1963
|
+
empty.style.display = "none";
|
|
1964
|
+
const category = card.querySelector(
|
|
1965
|
+
'[data-role="thread-category"]'
|
|
1966
|
+
);
|
|
1967
|
+
const status = card.querySelector(
|
|
1968
|
+
'[data-role="thread-status"]'
|
|
1969
|
+
);
|
|
1970
|
+
const preview = card.querySelector(
|
|
1971
|
+
'[data-role="thread-preview"]'
|
|
1972
|
+
);
|
|
1973
|
+
const time = card.querySelector('[data-role="thread-time"]');
|
|
1974
|
+
if (category) {
|
|
1975
|
+
category.textContent = formatCategoryLabel(
|
|
1976
|
+
threadState.category || DEFAULT_CATEGORIES[0]?.value,
|
|
1977
|
+
options.categories || DEFAULT_CATEGORIES
|
|
1978
|
+
);
|
|
1979
|
+
}
|
|
1980
|
+
if (status) status.textContent = formatThreadStatus(threadState.status);
|
|
1981
|
+
if (preview) preview.textContent = threadState.contentPreview || "";
|
|
1982
|
+
if (time) time.textContent = formatThreadTime(threadState.createdAt);
|
|
1983
|
+
}
|
|
1984
|
+
function setError(message) {
|
|
1985
|
+
const node = panel.querySelector('[data-role="error"]');
|
|
1986
|
+
if (node) node.textContent = message;
|
|
1987
|
+
if (message) options.onError?.(new Error(message));
|
|
1988
|
+
}
|
|
1989
|
+
function setSubmitting(value) {
|
|
1990
|
+
const button = panel.querySelector(
|
|
1991
|
+
'[data-role="submit"]'
|
|
1992
|
+
);
|
|
1993
|
+
if (button) button.disabled = value;
|
|
1994
|
+
}
|
|
1995
|
+
function renderMessages(messages) {
|
|
1996
|
+
const node = panel.querySelector('[data-role="messages"]');
|
|
1997
|
+
if (!node) return;
|
|
1998
|
+
renderedMessages = messages;
|
|
1999
|
+
node.innerHTML = "";
|
|
2000
|
+
for (const message of messages) {
|
|
2001
|
+
const item = document.createElement("div");
|
|
2002
|
+
item.className = "yd-feedback-message";
|
|
2003
|
+
item.dataset.admin = String(message.senderType !== "END_USER");
|
|
2004
|
+
item.innerHTML = `<div class="yd-feedback-message-meta">${formatSenderType(message.senderType)} \xB7 ${new Date(message.createdAt).toLocaleString()}</div><div class="yd-feedback-message-content"></div>`;
|
|
2005
|
+
const content = item.querySelector(".yd-feedback-message-content");
|
|
2006
|
+
if (content) {
|
|
2007
|
+
content.textContent = message.senderType === "END_USER" ? message.content : message.translatedContent || message.content;
|
|
2008
|
+
}
|
|
2009
|
+
node.appendChild(item);
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
function appendMessages(messages) {
|
|
2013
|
+
const byId = /* @__PURE__ */ new Map();
|
|
2014
|
+
for (const message of renderedMessages) byId.set(message.id, message);
|
|
2015
|
+
for (const message of messages) byId.set(message.id, message);
|
|
2016
|
+
renderMessages(Array.from(byId.values()));
|
|
2017
|
+
}
|
|
2018
|
+
async function uploadImages(files) {
|
|
2019
|
+
const selected = files.slice(0, MAX_IMAGES_PER_MESSAGE);
|
|
2020
|
+
const ids = [];
|
|
2021
|
+
for (const file of selected) {
|
|
2022
|
+
const compressed = await compressImage(file);
|
|
2023
|
+
const body = new FormData();
|
|
2024
|
+
body.append("file", compressed);
|
|
2025
|
+
if (threadState) body.append("threadId", threadState.threadId);
|
|
2026
|
+
const headers = {};
|
|
2027
|
+
if (threadState) headers["x-feedback-token"] = threadState.clientToken;
|
|
2028
|
+
const response = await fetch(
|
|
2029
|
+
`${getBaseUrl(options)}/api/v1/feedback/${encodeURIComponent(options.appId)}/resources/images`,
|
|
2030
|
+
{ method: "POST", body, headers }
|
|
2031
|
+
);
|
|
2032
|
+
const payload = await response.json();
|
|
2033
|
+
if (!response.ok || payload?.code !== 200) {
|
|
2034
|
+
throw new Error(payload?.message || "Image upload failed");
|
|
2035
|
+
}
|
|
2036
|
+
ids.push(payload.data.resource.id);
|
|
2037
|
+
}
|
|
2038
|
+
return ids;
|
|
2039
|
+
}
|
|
2040
|
+
async function submit() {
|
|
2041
|
+
const content = panel.querySelector('[data-role="content"]')?.value.trim();
|
|
2042
|
+
if (!content) {
|
|
2043
|
+
setError("\u8BF7\u8F93\u5165\u53CD\u9988\u5185\u5BB9\u3002");
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
2046
|
+
setSubmitting(true);
|
|
2047
|
+
setError("");
|
|
2048
|
+
try {
|
|
2049
|
+
const category = panel.querySelector('[data-role="category-select"]')?.dataset.value || "OTHER";
|
|
2050
|
+
const contact = panel.querySelector('[data-role="contact"]')?.value || "";
|
|
2051
|
+
const fileInput = panel.querySelector(
|
|
2052
|
+
'[data-role="files"]'
|
|
2053
|
+
);
|
|
2054
|
+
const attachments = await uploadImages(pendingFiles);
|
|
2055
|
+
if (!threadState) {
|
|
2056
|
+
const result = await requestJson(
|
|
2057
|
+
`${getBaseUrl(options)}/api/v1/feedback/${encodeURIComponent(options.appId)}/threads`,
|
|
2058
|
+
{
|
|
2059
|
+
method: "POST",
|
|
2060
|
+
body: safeJson({
|
|
2061
|
+
category,
|
|
2062
|
+
content,
|
|
2063
|
+
attachments,
|
|
2064
|
+
actor: {
|
|
2065
|
+
...options.actor || {},
|
|
2066
|
+
contactEmail: contact.includes("@") ? contact : options.actor?.contactEmail,
|
|
2067
|
+
contactPhone: contact && !contact.includes("@") ? contact : options.actor?.contactPhone
|
|
2068
|
+
},
|
|
2069
|
+
metadata: collectMetadata(options)
|
|
2070
|
+
})
|
|
2071
|
+
}
|
|
2072
|
+
);
|
|
2073
|
+
threadState = {
|
|
2074
|
+
threadId: result.thread.id,
|
|
2075
|
+
clientToken: result.clientToken,
|
|
2076
|
+
lastMessageId: result.message?.id,
|
|
2077
|
+
status: result.thread.status,
|
|
2078
|
+
category,
|
|
2079
|
+
contentPreview: content,
|
|
2080
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2081
|
+
};
|
|
2082
|
+
updateThreadCard();
|
|
2083
|
+
options.onThreadCreated?.(result.thread.id);
|
|
2084
|
+
options.onMessageSent?.(result.message.id);
|
|
2085
|
+
} else {
|
|
2086
|
+
const result = await requestJson(
|
|
2087
|
+
`${getBaseUrl(options)}/api/v1/feedback/${encodeURIComponent(options.appId)}/threads/${encodeURIComponent(threadState.threadId)}/messages`,
|
|
2088
|
+
{
|
|
2089
|
+
method: "POST",
|
|
2090
|
+
headers: { "x-feedback-token": threadState.clientToken },
|
|
2091
|
+
body: safeJson({
|
|
2092
|
+
content,
|
|
2093
|
+
attachments,
|
|
2094
|
+
metadata: collectMetadata(options)
|
|
2095
|
+
})
|
|
2096
|
+
}
|
|
2097
|
+
);
|
|
2098
|
+
threadState.lastMessageId = result.message.id;
|
|
2099
|
+
threadState.contentPreview = content;
|
|
2100
|
+
options.onMessageSent?.(result.message.id);
|
|
2101
|
+
}
|
|
2102
|
+
const textarea = panel.querySelector(
|
|
2103
|
+
'[data-role="content"]'
|
|
2104
|
+
);
|
|
2105
|
+
if (textarea) textarea.value = "";
|
|
2106
|
+
if (fileInput) fileInput.value = "";
|
|
2107
|
+
pendingFiles = [];
|
|
2108
|
+
const filesLabel = panel.querySelector(
|
|
2109
|
+
'[data-role="files-label"]'
|
|
2110
|
+
);
|
|
2111
|
+
if (filesLabel) filesLabel.textContent = DEFAULT_ATTACHMENTS_LABEL;
|
|
2112
|
+
await pollOnce();
|
|
2113
|
+
startPolling();
|
|
2114
|
+
} catch (error) {
|
|
2115
|
+
setError(error instanceof Error ? error.message : "\u63D0\u4EA4\u5931\u8D25");
|
|
2116
|
+
} finally {
|
|
2117
|
+
setSubmitting(false);
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
async function pollOnce() {
|
|
2121
|
+
if (!threadState) return;
|
|
2122
|
+
const currentThread = threadState;
|
|
2123
|
+
const params = new URLSearchParams();
|
|
2124
|
+
if (currentThread.lastMessageId) {
|
|
2125
|
+
params.set("afterMessageId", currentThread.lastMessageId);
|
|
2126
|
+
}
|
|
2127
|
+
params.set("feedbackToken", currentThread.clientToken);
|
|
2128
|
+
const result = await requestJson(
|
|
2129
|
+
`${getBaseUrl(options)}/api/v1/feedback/${encodeURIComponent(options.appId)}/threads/${encodeURIComponent(currentThread.threadId)}/messages?${params.toString()}`
|
|
2130
|
+
);
|
|
2131
|
+
currentThread.status = result.threadStatus;
|
|
2132
|
+
updateThreadCard();
|
|
2133
|
+
if (result.messages.length > 0) {
|
|
2134
|
+
const latestMessage = result.messages.at(-1);
|
|
2135
|
+
if (latestMessage) currentThread.lastMessageId = latestMessage.id;
|
|
2136
|
+
appendMessages(result.messages);
|
|
2137
|
+
}
|
|
2138
|
+
if (result.threadStatus !== "WAITING_ADMIN") stopPolling();
|
|
2139
|
+
else schedulePoll(result.nextPollAfterMs);
|
|
2140
|
+
}
|
|
2141
|
+
function schedulePoll(delay) {
|
|
2142
|
+
stopPolling();
|
|
2143
|
+
if (!delay || delay < 1 || document.hidden || !threadState) return;
|
|
2144
|
+
pollTimer = window.setTimeout(() => void pollOnce(), delay);
|
|
2145
|
+
}
|
|
2146
|
+
function startPolling() {
|
|
2147
|
+
if (!threadState || threadState.status !== "WAITING_ADMIN") return;
|
|
2148
|
+
schedulePoll(1e4);
|
|
2149
|
+
}
|
|
2150
|
+
function stopPolling() {
|
|
2151
|
+
if (pollTimer) window.clearTimeout(pollTimer);
|
|
2152
|
+
pollTimer = null;
|
|
2153
|
+
}
|
|
2154
|
+
function open() {
|
|
2155
|
+
initPanel();
|
|
2156
|
+
panel.dataset.open = "true";
|
|
2157
|
+
window.setTimeout(() => {
|
|
2158
|
+
panel.querySelector('[data-role="content"]')?.focus();
|
|
2159
|
+
}, 0);
|
|
2160
|
+
}
|
|
2161
|
+
function close() {
|
|
2162
|
+
panel.dataset.open = "false";
|
|
2163
|
+
stopPolling();
|
|
2164
|
+
}
|
|
2165
|
+
launcher.addEventListener("click", open);
|
|
2166
|
+
const handleVisibilityChange = () => {
|
|
2167
|
+
if (document.hidden) stopPolling();
|
|
2168
|
+
else startPolling();
|
|
2169
|
+
};
|
|
2170
|
+
const handleDocumentClick = (event) => {
|
|
2171
|
+
const categorySelect = panel.querySelector(
|
|
2172
|
+
'[data-role="category-select"]'
|
|
2173
|
+
);
|
|
2174
|
+
if (categorySelect && event.target instanceof Node && !categorySelect.contains(event.target)) {
|
|
2175
|
+
categorySelect.dataset.open = "false";
|
|
2176
|
+
}
|
|
2177
|
+
};
|
|
2178
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
2179
|
+
document.addEventListener("click", handleDocumentClick);
|
|
2180
|
+
if (typeof window !== "undefined") {
|
|
2181
|
+
const preload = () => {
|
|
2182
|
+
if ("requestIdleCallback" in window) {
|
|
2183
|
+
;
|
|
2184
|
+
window.requestIdleCallback(() => initPanel());
|
|
2185
|
+
} else {
|
|
2186
|
+
globalThis.setTimeout(() => initPanel(), 1200);
|
|
2187
|
+
}
|
|
2188
|
+
};
|
|
2189
|
+
if (document.readyState === "complete") preload();
|
|
2190
|
+
else {
|
|
2191
|
+
window.addEventListener("load", preload, { once: true });
|
|
2192
|
+
removeLoadListener = () => window.removeEventListener("load", preload);
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
return {
|
|
2196
|
+
open,
|
|
2197
|
+
close,
|
|
2198
|
+
destroy() {
|
|
2199
|
+
stopPolling();
|
|
2200
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
2201
|
+
document.removeEventListener("click", handleDocumentClick);
|
|
2202
|
+
removeLoadListener?.();
|
|
2203
|
+
root.remove();
|
|
2204
|
+
}
|
|
2205
|
+
};
|
|
2206
|
+
}
|
|
2207
|
+
function mountFeedbackWidget(container, options) {
|
|
2208
|
+
return createFeedbackWidget(container, options);
|
|
2209
|
+
}
|
|
2210
|
+
|
|
1197
2211
|
// src/server.ts
|
|
1198
2212
|
import crypto2 from "crypto";
|
|
1199
2213
|
function getCustomAmountRechargeRule(product, currency) {
|
|
@@ -1355,6 +2369,48 @@ var PaymentClient = class {
|
|
|
1355
2369
|
const path = params.toString() ? `/products?${params.toString()}` : "/products";
|
|
1356
2370
|
return this.request("GET", path);
|
|
1357
2371
|
}
|
|
2372
|
+
/**
|
|
2373
|
+
* Fetch the realtime stock snapshot for a single product.
|
|
2374
|
+
*/
|
|
2375
|
+
async getProductStock(productIdOrCode, options) {
|
|
2376
|
+
const value = productIdOrCode?.trim();
|
|
2377
|
+
if (!value) {
|
|
2378
|
+
throw new Error("productIdOrCode is required");
|
|
2379
|
+
}
|
|
2380
|
+
const params = new URLSearchParams();
|
|
2381
|
+
params.set("productIdOrCode", value);
|
|
2382
|
+
if (options?.lookupBy) params.set("lookupBy", options.lookupBy);
|
|
2383
|
+
if (options?.locale) params.set("locale", options.locale);
|
|
2384
|
+
if (options?.currency) params.set("currency", options.currency);
|
|
2385
|
+
return this.request(
|
|
2386
|
+
"GET",
|
|
2387
|
+
`/products/${encodeURIComponent(value)}/stock?${params.toString()}`
|
|
2388
|
+
);
|
|
2389
|
+
}
|
|
2390
|
+
async getProductStocks(params, options) {
|
|
2391
|
+
const productIds = [
|
|
2392
|
+
...new Set(
|
|
2393
|
+
(params.productIds || []).map((item) => item.trim()).filter(Boolean)
|
|
2394
|
+
)
|
|
2395
|
+
];
|
|
2396
|
+
const productCodes = [
|
|
2397
|
+
...new Set(
|
|
2398
|
+
(params.productCodes || []).map((item) => item.trim()).filter(Boolean)
|
|
2399
|
+
)
|
|
2400
|
+
];
|
|
2401
|
+
if (productIds.length === 0 && productCodes.length === 0) {
|
|
2402
|
+
throw new Error("productIds or productCodes is required");
|
|
2403
|
+
}
|
|
2404
|
+
const queryOptions = options || params;
|
|
2405
|
+
const query = new URLSearchParams();
|
|
2406
|
+
if (productIds.length > 0) query.set("productIds", productIds.join(","));
|
|
2407
|
+
if (productCodes.length > 0)
|
|
2408
|
+
query.set("productCodes", productCodes.join(","));
|
|
2409
|
+
if (queryOptions.lookupBy) query.set("lookupBy", queryOptions.lookupBy);
|
|
2410
|
+
if (queryOptions.locale) query.set("locale", queryOptions.locale);
|
|
2411
|
+
if (queryOptions.currency) query.set("currency", queryOptions.currency);
|
|
2412
|
+
return this.request("GET", `/products/stocks?${query.toString()}`);
|
|
2413
|
+
}
|
|
1358
2414
|
/**
|
|
1359
2415
|
* Create a new order
|
|
1360
2416
|
* @param params - Order creation parameters
|
|
@@ -1363,6 +2419,17 @@ var PaymentClient = class {
|
|
|
1363
2419
|
async createOrder(params) {
|
|
1364
2420
|
return this.request("POST", "/orders", params);
|
|
1365
2421
|
}
|
|
2422
|
+
/**
|
|
2423
|
+
* Create a paid manual bank transfer order.
|
|
2424
|
+
* @param params - Order creation parameters without channel
|
|
2425
|
+
* @returns Paid order details
|
|
2426
|
+
*/
|
|
2427
|
+
async createBankTransferOrder(params) {
|
|
2428
|
+
return this.request("POST", "/orders", {
|
|
2429
|
+
...params,
|
|
2430
|
+
channel: "BANK_TRANSFER"
|
|
2431
|
+
});
|
|
2432
|
+
}
|
|
1366
2433
|
/**
|
|
1367
2434
|
* Create a WeChat Mini Program order (channel fixed to WECHAT_MINI)
|
|
1368
2435
|
* @param params - Mini program order parameters
|
|
@@ -1385,6 +2452,36 @@ var PaymentClient = class {
|
|
|
1385
2452
|
async payOrder(orderId, params) {
|
|
1386
2453
|
return this.request("POST", `/orders/${orderId}/pay`, params);
|
|
1387
2454
|
}
|
|
2455
|
+
/**
|
|
2456
|
+
* Cancel a pending order and release its inventory reservation.
|
|
2457
|
+
* Paid/refunded/failed orders are returned unchanged by the gateway.
|
|
2458
|
+
*/
|
|
2459
|
+
async cancelOrder(orderId, params) {
|
|
2460
|
+
const value = orderId?.trim();
|
|
2461
|
+
if (!value) {
|
|
2462
|
+
throw new Error("orderId is required");
|
|
2463
|
+
}
|
|
2464
|
+
return this.request(
|
|
2465
|
+
"POST",
|
|
2466
|
+
`/orders/${encodeURIComponent(value)}/cancel`,
|
|
2467
|
+
params || {}
|
|
2468
|
+
);
|
|
2469
|
+
}
|
|
2470
|
+
/**
|
|
2471
|
+
* Complete a zero-amount FREE order.
|
|
2472
|
+
* This marks the pending order as paid and triggers normal paid-order side effects.
|
|
2473
|
+
*/
|
|
2474
|
+
async completeFreeOrder(orderId) {
|
|
2475
|
+
const value = orderId?.trim();
|
|
2476
|
+
if (!value) {
|
|
2477
|
+
throw new Error("orderId is required");
|
|
2478
|
+
}
|
|
2479
|
+
return this.request(
|
|
2480
|
+
"POST",
|
|
2481
|
+
`/orders/${encodeURIComponent(value)}/free/complete`,
|
|
2482
|
+
{}
|
|
2483
|
+
);
|
|
2484
|
+
}
|
|
1388
2485
|
/**
|
|
1389
2486
|
* Query order status
|
|
1390
2487
|
* @param orderId - The order ID to query
|
|
@@ -1463,6 +2560,31 @@ var PaymentClient = class {
|
|
|
1463
2560
|
...options
|
|
1464
2561
|
});
|
|
1465
2562
|
}
|
|
2563
|
+
/**
|
|
2564
|
+
* Consume numeric entitlements from an ordered key pool.
|
|
2565
|
+
* Useful when subscription credits and perpetual credits use separate keys.
|
|
2566
|
+
*/
|
|
2567
|
+
async consumeEntitlementPool(userId, keys, amount, options) {
|
|
2568
|
+
return this.request(
|
|
2569
|
+
"POST",
|
|
2570
|
+
`/users/${userId}/entitlements/consume-pool`,
|
|
2571
|
+
{
|
|
2572
|
+
keys,
|
|
2573
|
+
amount,
|
|
2574
|
+
...options
|
|
2575
|
+
}
|
|
2576
|
+
);
|
|
2577
|
+
}
|
|
2578
|
+
/**
|
|
2579
|
+
* Get active numeric entitlement buckets ordered by expiration.
|
|
2580
|
+
*/
|
|
2581
|
+
async getEntitlementCreditBuckets(userId, keys) {
|
|
2582
|
+
const query = keys && keys.length > 0 ? `?keys=${encodeURIComponent(keys.join(","))}` : "";
|
|
2583
|
+
return this.request(
|
|
2584
|
+
"GET",
|
|
2585
|
+
`/users/${userId}/entitlements/credit-buckets${query}`
|
|
2586
|
+
);
|
|
2587
|
+
}
|
|
1466
2588
|
/**
|
|
1467
2589
|
* Add numeric entitlement (e.g. refund)
|
|
1468
2590
|
* @param userId - User ID
|
|
@@ -1548,10 +2670,13 @@ export {
|
|
|
1548
2670
|
LoginUI,
|
|
1549
2671
|
PaymentClient,
|
|
1550
2672
|
PaymentUI,
|
|
2673
|
+
createFeedbackWidget,
|
|
1551
2674
|
createLoginUI,
|
|
1552
2675
|
createPaymentUI,
|
|
2676
|
+
detectLoginEnvironment,
|
|
1553
2677
|
getCustomAmountRechargeRule,
|
|
1554
2678
|
handleLoginCallbackIfPresent,
|
|
2679
|
+
mountFeedbackWidget,
|
|
1555
2680
|
validateCustomAmountRecharge
|
|
1556
2681
|
};
|
|
1557
2682
|
//# sourceMappingURL=index.js.map
|