@followgate/js 0.14.0 → 0.15.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/README.md +35 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +67 -20
- package/dist/index.mjs +67 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,6 +108,41 @@ FollowGate.on('unlocked', (data) => {
|
|
|
108
108
|
});
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
+
## Username Input (2 Methods)
|
|
112
|
+
|
|
113
|
+
The SDK needs the user's social media username to work. There are exactly 2 ways to provide it:
|
|
114
|
+
|
|
115
|
+
**Method 1: User enters username manually (default)**
|
|
116
|
+
If no username is passed, the modal shows a Welcome step where the user types their username.
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
FollowGate.init({
|
|
120
|
+
appId: 'your-app-id',
|
|
121
|
+
apiKey: 'fg_live_xxx',
|
|
122
|
+
twitter: { handle: 'your_handle' },
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
FollowGate.show(); // → Modal starts with username input step
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Method 2: App passes username via code (skips Welcome step)**
|
|
129
|
+
If your app already knows the user's username (e.g. from your own auth system), pass it directly. The Welcome step is skipped entirely.
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
FollowGate.init({
|
|
133
|
+
appId: 'your-app-id',
|
|
134
|
+
apiKey: 'fg_live_xxx',
|
|
135
|
+
twitter: {
|
|
136
|
+
handle: 'your_handle',
|
|
137
|
+
username: 'their_x_username', // ← skips Welcome step
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
FollowGate.show(); // → Modal starts directly with Follow step
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
> **Note:** No Twitter API calls are made in either case. The SDK uses intent URLs only.
|
|
145
|
+
|
|
111
146
|
## Configuration Options
|
|
112
147
|
|
|
113
148
|
```typescript
|
package/dist/index.d.mts
CHANGED
|
@@ -29,6 +29,7 @@ interface FollowGateConfig {
|
|
|
29
29
|
onComplete?: () => void;
|
|
30
30
|
theme?: 'dark' | 'light';
|
|
31
31
|
accentColor?: string;
|
|
32
|
+
forceShow?: boolean;
|
|
32
33
|
}
|
|
33
34
|
/**
|
|
34
35
|
* SDK Error class with helpful messages
|
|
@@ -93,6 +94,7 @@ declare class FollowGateClient {
|
|
|
93
94
|
private completedActions;
|
|
94
95
|
private modalElement;
|
|
95
96
|
private stylesInjected;
|
|
97
|
+
private currentStep;
|
|
96
98
|
/**
|
|
97
99
|
* Initialize the SDK
|
|
98
100
|
*/
|
|
@@ -103,7 +105,7 @@ declare class FollowGateClient {
|
|
|
103
105
|
private fetchServerConfig;
|
|
104
106
|
/**
|
|
105
107
|
* Show the FollowGate modal
|
|
106
|
-
* If user is already unlocked, calls onComplete immediately
|
|
108
|
+
* If user is already unlocked, calls onComplete immediately (unless forceShow is true)
|
|
107
109
|
*/
|
|
108
110
|
show(): Promise<void>;
|
|
109
111
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ interface FollowGateConfig {
|
|
|
29
29
|
onComplete?: () => void;
|
|
30
30
|
theme?: 'dark' | 'light';
|
|
31
31
|
accentColor?: string;
|
|
32
|
+
forceShow?: boolean;
|
|
32
33
|
}
|
|
33
34
|
/**
|
|
34
35
|
* SDK Error class with helpful messages
|
|
@@ -93,6 +94,7 @@ declare class FollowGateClient {
|
|
|
93
94
|
private completedActions;
|
|
94
95
|
private modalElement;
|
|
95
96
|
private stylesInjected;
|
|
97
|
+
private currentStep;
|
|
96
98
|
/**
|
|
97
99
|
* Initialize the SDK
|
|
98
100
|
*/
|
|
@@ -103,7 +105,7 @@ declare class FollowGateClient {
|
|
|
103
105
|
private fetchServerConfig;
|
|
104
106
|
/**
|
|
105
107
|
* Show the FollowGate modal
|
|
106
|
-
* If user is already unlocked, calls onComplete immediately
|
|
108
|
+
* If user is already unlocked, calls onComplete immediately (unless forceShow is true)
|
|
107
109
|
*/
|
|
108
110
|
show(): Promise<void>;
|
|
109
111
|
/**
|
package/dist/index.js
CHANGED
|
@@ -503,6 +503,7 @@ var FollowGateClient = class {
|
|
|
503
503
|
completedActions = [];
|
|
504
504
|
modalElement = null;
|
|
505
505
|
stylesInjected = false;
|
|
506
|
+
currentStep = "welcome";
|
|
506
507
|
/**
|
|
507
508
|
* Initialize the SDK
|
|
508
509
|
*/
|
|
@@ -598,22 +599,29 @@ var FollowGateClient = class {
|
|
|
598
599
|
}
|
|
599
600
|
/**
|
|
600
601
|
* Show the FollowGate modal
|
|
601
|
-
* If user is already unlocked, calls onComplete immediately
|
|
602
|
+
* If user is already unlocked, calls onComplete immediately (unless forceShow is true)
|
|
602
603
|
*/
|
|
603
604
|
async show() {
|
|
604
605
|
if (!this.config) {
|
|
605
606
|
throw new Error("[FollowGate] SDK not initialized. Call init() first.");
|
|
606
607
|
}
|
|
607
608
|
if (this.isUnlocked()) {
|
|
608
|
-
if (this.config.
|
|
609
|
-
|
|
609
|
+
if (this.config.forceShow) {
|
|
610
|
+
this.clearUnlockStatus();
|
|
611
|
+
if (this.config.debug) {
|
|
612
|
+
console.log(
|
|
613
|
+
"[FollowGate] forceShow enabled - cleared unlock status, showing modal"
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
} else {
|
|
617
|
+
console.warn(
|
|
610
618
|
"[FollowGate] Modal skipped - user already unlocked.",
|
|
611
|
-
"
|
|
619
|
+
"Use forceShow: true in init() to always show modal, or call FollowGate.reset() to clear.",
|
|
612
620
|
this.config.userId ? `(userId: ${this.config.userId})` : "(no userId set)"
|
|
613
621
|
);
|
|
622
|
+
this.config.onComplete?.();
|
|
623
|
+
return;
|
|
614
624
|
}
|
|
615
|
-
this.config.onComplete?.();
|
|
616
|
-
return;
|
|
617
625
|
}
|
|
618
626
|
await this.fetchServerConfig();
|
|
619
627
|
if (this.config.twitter?.username && !this.hasUsername()) {
|
|
@@ -625,6 +633,7 @@ var FollowGateClient = class {
|
|
|
625
633
|
);
|
|
626
634
|
}
|
|
627
635
|
}
|
|
636
|
+
this.trackEvent("modal_opened");
|
|
628
637
|
this.injectStyles();
|
|
629
638
|
this.createModal();
|
|
630
639
|
}
|
|
@@ -681,6 +690,7 @@ var FollowGateClient = class {
|
|
|
681
690
|
document.body.appendChild(backdrop);
|
|
682
691
|
this.modalElement = backdrop;
|
|
683
692
|
document.getElementById("fg-close-btn")?.addEventListener("click", () => {
|
|
693
|
+
this.trackEvent("modal_closed", { step: this.currentStep });
|
|
684
694
|
this.hide(true);
|
|
685
695
|
});
|
|
686
696
|
requestAnimationFrame(() => {
|
|
@@ -773,14 +783,16 @@ var FollowGateClient = class {
|
|
|
773
783
|
renderUsernameStep() {
|
|
774
784
|
const content = this.getContentElement();
|
|
775
785
|
if (!content) return;
|
|
786
|
+
this.currentStep = "welcome";
|
|
787
|
+
this.trackEvent("step_viewed", { step: "welcome" });
|
|
776
788
|
const handle = this.getTargetHandle();
|
|
777
789
|
const hasRepost = this.shouldShowRepostStep();
|
|
778
790
|
const allowSkip = this.serverConfig?.allowSkip ?? false;
|
|
779
791
|
const welcomeTitle = this.serverConfig?.welcomeTitle || "Unlock Free Access";
|
|
780
792
|
const welcomeMessage = this.serverConfig?.welcomeMessage || "Enter your X username to get started";
|
|
781
|
-
let explanationText = handle ? `
|
|
793
|
+
let explanationText = handle ? `Follow @${handle} on X${hasRepost ? " and repost a post" : ""} to unlock.` : `Complete a quick action on X to unlock.`;
|
|
782
794
|
if (allowSkip) {
|
|
783
|
-
explanationText += ` You can skip
|
|
795
|
+
explanationText += ` You can skip single steps, but it really helps the developer.`;
|
|
784
796
|
}
|
|
785
797
|
content.innerHTML = `
|
|
786
798
|
<div class="fg-icon-box">
|
|
@@ -824,11 +836,14 @@ var FollowGateClient = class {
|
|
|
824
836
|
handleUsernameSubmit(username) {
|
|
825
837
|
const normalized = username.replace(/^@/, "");
|
|
826
838
|
this.setUsername(normalized);
|
|
839
|
+
this.trackEvent("username_submitted", { username: normalized });
|
|
827
840
|
this.renderFollowStep();
|
|
828
841
|
}
|
|
829
842
|
renderFollowStep() {
|
|
830
843
|
const content = this.getContentElement();
|
|
831
844
|
if (!content) return;
|
|
845
|
+
this.currentStep = "follow";
|
|
846
|
+
this.trackEvent("step_viewed", { step: "follow" });
|
|
832
847
|
const handle = this.getTargetHandle();
|
|
833
848
|
if (!handle) {
|
|
834
849
|
console.error(
|
|
@@ -871,6 +886,7 @@ var FollowGateClient = class {
|
|
|
871
886
|
});
|
|
872
887
|
}
|
|
873
888
|
handleSkipFollow() {
|
|
889
|
+
this.trackEvent("step_skipped", { step: "follow" });
|
|
874
890
|
if (this.shouldShowRepostStep()) {
|
|
875
891
|
this.renderRepostStep();
|
|
876
892
|
} else {
|
|
@@ -952,6 +968,8 @@ var FollowGateClient = class {
|
|
|
952
968
|
const content = this.getContentElement();
|
|
953
969
|
const postId = this.getTargetPostUrl();
|
|
954
970
|
if (!content || !postId) return;
|
|
971
|
+
this.currentStep = "repost";
|
|
972
|
+
this.trackEvent("step_viewed", { step: "repost" });
|
|
955
973
|
content.innerHTML = `
|
|
956
974
|
${this.renderStepIndicator(2)}
|
|
957
975
|
<div class="fg-icon-box fg-success">
|
|
@@ -978,6 +996,7 @@ var FollowGateClient = class {
|
|
|
978
996
|
this.handleRepostClick();
|
|
979
997
|
});
|
|
980
998
|
document.getElementById("fg-skip-repost")?.addEventListener("click", () => {
|
|
999
|
+
this.trackEvent("step_skipped", { step: "repost" });
|
|
981
1000
|
this.renderConfirmStep();
|
|
982
1001
|
});
|
|
983
1002
|
}
|
|
@@ -1049,6 +1068,8 @@ var FollowGateClient = class {
|
|
|
1049
1068
|
renderConfirmStep() {
|
|
1050
1069
|
const content = this.getContentElement();
|
|
1051
1070
|
if (!content) return;
|
|
1071
|
+
this.currentStep = "confirm";
|
|
1072
|
+
this.trackEvent("step_viewed", { step: "confirm" });
|
|
1052
1073
|
const username = this.currentUser?.username;
|
|
1053
1074
|
const targetHandle = this.getTargetHandle();
|
|
1054
1075
|
const postId = this.getTargetPostUrl();
|
|
@@ -1065,7 +1086,10 @@ var FollowGateClient = class {
|
|
|
1065
1086
|
verifyText = "Verifying repost";
|
|
1066
1087
|
}
|
|
1067
1088
|
content.innerHTML = `
|
|
1068
|
-
<
|
|
1089
|
+
<div style="display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 4px;">
|
|
1090
|
+
<span style="color: #4ade80; width: 28px; height: 28px;">${ICONS.check}</span>
|
|
1091
|
+
<h2 class="fg-title" style="margin: 0;">${this.escapeHtml(successTitle)}</h2>
|
|
1092
|
+
</div>
|
|
1069
1093
|
${successMessage ? `<p class="fg-subtitle" style="margin-bottom: 16px;">${this.escapeHtml(successMessage)}</p>` : ""}
|
|
1070
1094
|
<div class="fg-verify-box">
|
|
1071
1095
|
<div class="fg-verify-box-left">
|
|
@@ -1080,15 +1104,13 @@ var FollowGateClient = class {
|
|
|
1080
1104
|
` : ""}
|
|
1081
1105
|
</div>
|
|
1082
1106
|
<p class="fg-verify-hint">Verification may take some time</p>
|
|
1083
|
-
<
|
|
1084
|
-
${ICONS.warning}
|
|
1085
|
-
<p><strong>Note:</strong> Access may be revoked if actions are not completed.</p>
|
|
1086
|
-
</div>
|
|
1107
|
+
<p style="color: rgba(255,255,255,0.4); font-size: 11px; margin: 8px 0 16px; text-align: center;">Access may be revoked if actions are not completed.</p>
|
|
1087
1108
|
<button class="fg-btn fg-btn-green" id="fg-finish-btn">
|
|
1088
1109
|
${ICONS.check}
|
|
1089
|
-
Got it
|
|
1110
|
+
Got it \u2014 Continue
|
|
1090
1111
|
</button>
|
|
1091
1112
|
${hasFollow || hasRepost ? `
|
|
1113
|
+
<p style="color: rgba(255,255,255,0.4); font-size: 11px; margin: 12px 0 6px; text-align: center;">Missed something? Open again:</p>
|
|
1092
1114
|
<div class="fg-btn-row">
|
|
1093
1115
|
${hasFollow ? `
|
|
1094
1116
|
<button class="fg-btn fg-btn-secondary" id="fg-redo-follow">
|
|
@@ -1543,8 +1565,37 @@ var FollowGateClient = class {
|
|
|
1543
1565
|
throw new Error(`[FollowGate] Unsupported LinkedIn action: ${action}`);
|
|
1544
1566
|
}
|
|
1545
1567
|
}
|
|
1546
|
-
async trackEvent(event, data) {
|
|
1568
|
+
async trackEvent(event, data = {}) {
|
|
1547
1569
|
if (!this.config) return;
|
|
1570
|
+
const payload = {
|
|
1571
|
+
event
|
|
1572
|
+
};
|
|
1573
|
+
if (data.platform) {
|
|
1574
|
+
payload.platform = data.platform.toUpperCase();
|
|
1575
|
+
}
|
|
1576
|
+
if (data.action) {
|
|
1577
|
+
payload.action = data.action.toUpperCase();
|
|
1578
|
+
}
|
|
1579
|
+
if (data.target) {
|
|
1580
|
+
payload.target = data.target;
|
|
1581
|
+
}
|
|
1582
|
+
if (data.username || this.currentUser?.username) {
|
|
1583
|
+
payload.username = data.username || this.currentUser?.username;
|
|
1584
|
+
}
|
|
1585
|
+
if (data.externalUserId) {
|
|
1586
|
+
payload.externalUserId = data.externalUserId;
|
|
1587
|
+
}
|
|
1588
|
+
const {
|
|
1589
|
+
platform: _p,
|
|
1590
|
+
action: _a,
|
|
1591
|
+
target: _t,
|
|
1592
|
+
username: _u,
|
|
1593
|
+
externalUserId: _e,
|
|
1594
|
+
...rest
|
|
1595
|
+
} = data;
|
|
1596
|
+
if (Object.keys(rest).length > 0) {
|
|
1597
|
+
payload.metadata = rest;
|
|
1598
|
+
}
|
|
1548
1599
|
try {
|
|
1549
1600
|
await fetch(`${this.config.apiUrl}/api/v1/events`, {
|
|
1550
1601
|
method: "POST",
|
|
@@ -1552,11 +1603,7 @@ var FollowGateClient = class {
|
|
|
1552
1603
|
"Content-Type": "application/json",
|
|
1553
1604
|
"X-API-Key": this.config.apiKey
|
|
1554
1605
|
},
|
|
1555
|
-
body: JSON.stringify(
|
|
1556
|
-
event,
|
|
1557
|
-
appId: this.config.appId,
|
|
1558
|
-
...data
|
|
1559
|
-
})
|
|
1606
|
+
body: JSON.stringify(payload)
|
|
1560
1607
|
});
|
|
1561
1608
|
} catch (error) {
|
|
1562
1609
|
if (this.config.debug) {
|
package/dist/index.mjs
CHANGED
|
@@ -477,6 +477,7 @@ var FollowGateClient = class {
|
|
|
477
477
|
completedActions = [];
|
|
478
478
|
modalElement = null;
|
|
479
479
|
stylesInjected = false;
|
|
480
|
+
currentStep = "welcome";
|
|
480
481
|
/**
|
|
481
482
|
* Initialize the SDK
|
|
482
483
|
*/
|
|
@@ -572,22 +573,29 @@ var FollowGateClient = class {
|
|
|
572
573
|
}
|
|
573
574
|
/**
|
|
574
575
|
* Show the FollowGate modal
|
|
575
|
-
* If user is already unlocked, calls onComplete immediately
|
|
576
|
+
* If user is already unlocked, calls onComplete immediately (unless forceShow is true)
|
|
576
577
|
*/
|
|
577
578
|
async show() {
|
|
578
579
|
if (!this.config) {
|
|
579
580
|
throw new Error("[FollowGate] SDK not initialized. Call init() first.");
|
|
580
581
|
}
|
|
581
582
|
if (this.isUnlocked()) {
|
|
582
|
-
if (this.config.
|
|
583
|
-
|
|
583
|
+
if (this.config.forceShow) {
|
|
584
|
+
this.clearUnlockStatus();
|
|
585
|
+
if (this.config.debug) {
|
|
586
|
+
console.log(
|
|
587
|
+
"[FollowGate] forceShow enabled - cleared unlock status, showing modal"
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
} else {
|
|
591
|
+
console.warn(
|
|
584
592
|
"[FollowGate] Modal skipped - user already unlocked.",
|
|
585
|
-
"
|
|
593
|
+
"Use forceShow: true in init() to always show modal, or call FollowGate.reset() to clear.",
|
|
586
594
|
this.config.userId ? `(userId: ${this.config.userId})` : "(no userId set)"
|
|
587
595
|
);
|
|
596
|
+
this.config.onComplete?.();
|
|
597
|
+
return;
|
|
588
598
|
}
|
|
589
|
-
this.config.onComplete?.();
|
|
590
|
-
return;
|
|
591
599
|
}
|
|
592
600
|
await this.fetchServerConfig();
|
|
593
601
|
if (this.config.twitter?.username && !this.hasUsername()) {
|
|
@@ -599,6 +607,7 @@ var FollowGateClient = class {
|
|
|
599
607
|
);
|
|
600
608
|
}
|
|
601
609
|
}
|
|
610
|
+
this.trackEvent("modal_opened");
|
|
602
611
|
this.injectStyles();
|
|
603
612
|
this.createModal();
|
|
604
613
|
}
|
|
@@ -655,6 +664,7 @@ var FollowGateClient = class {
|
|
|
655
664
|
document.body.appendChild(backdrop);
|
|
656
665
|
this.modalElement = backdrop;
|
|
657
666
|
document.getElementById("fg-close-btn")?.addEventListener("click", () => {
|
|
667
|
+
this.trackEvent("modal_closed", { step: this.currentStep });
|
|
658
668
|
this.hide(true);
|
|
659
669
|
});
|
|
660
670
|
requestAnimationFrame(() => {
|
|
@@ -747,14 +757,16 @@ var FollowGateClient = class {
|
|
|
747
757
|
renderUsernameStep() {
|
|
748
758
|
const content = this.getContentElement();
|
|
749
759
|
if (!content) return;
|
|
760
|
+
this.currentStep = "welcome";
|
|
761
|
+
this.trackEvent("step_viewed", { step: "welcome" });
|
|
750
762
|
const handle = this.getTargetHandle();
|
|
751
763
|
const hasRepost = this.shouldShowRepostStep();
|
|
752
764
|
const allowSkip = this.serverConfig?.allowSkip ?? false;
|
|
753
765
|
const welcomeTitle = this.serverConfig?.welcomeTitle || "Unlock Free Access";
|
|
754
766
|
const welcomeMessage = this.serverConfig?.welcomeMessage || "Enter your X username to get started";
|
|
755
|
-
let explanationText = handle ? `
|
|
767
|
+
let explanationText = handle ? `Follow @${handle} on X${hasRepost ? " and repost a post" : ""} to unlock.` : `Complete a quick action on X to unlock.`;
|
|
756
768
|
if (allowSkip) {
|
|
757
|
-
explanationText += ` You can skip
|
|
769
|
+
explanationText += ` You can skip single steps, but it really helps the developer.`;
|
|
758
770
|
}
|
|
759
771
|
content.innerHTML = `
|
|
760
772
|
<div class="fg-icon-box">
|
|
@@ -798,11 +810,14 @@ var FollowGateClient = class {
|
|
|
798
810
|
handleUsernameSubmit(username) {
|
|
799
811
|
const normalized = username.replace(/^@/, "");
|
|
800
812
|
this.setUsername(normalized);
|
|
813
|
+
this.trackEvent("username_submitted", { username: normalized });
|
|
801
814
|
this.renderFollowStep();
|
|
802
815
|
}
|
|
803
816
|
renderFollowStep() {
|
|
804
817
|
const content = this.getContentElement();
|
|
805
818
|
if (!content) return;
|
|
819
|
+
this.currentStep = "follow";
|
|
820
|
+
this.trackEvent("step_viewed", { step: "follow" });
|
|
806
821
|
const handle = this.getTargetHandle();
|
|
807
822
|
if (!handle) {
|
|
808
823
|
console.error(
|
|
@@ -845,6 +860,7 @@ var FollowGateClient = class {
|
|
|
845
860
|
});
|
|
846
861
|
}
|
|
847
862
|
handleSkipFollow() {
|
|
863
|
+
this.trackEvent("step_skipped", { step: "follow" });
|
|
848
864
|
if (this.shouldShowRepostStep()) {
|
|
849
865
|
this.renderRepostStep();
|
|
850
866
|
} else {
|
|
@@ -926,6 +942,8 @@ var FollowGateClient = class {
|
|
|
926
942
|
const content = this.getContentElement();
|
|
927
943
|
const postId = this.getTargetPostUrl();
|
|
928
944
|
if (!content || !postId) return;
|
|
945
|
+
this.currentStep = "repost";
|
|
946
|
+
this.trackEvent("step_viewed", { step: "repost" });
|
|
929
947
|
content.innerHTML = `
|
|
930
948
|
${this.renderStepIndicator(2)}
|
|
931
949
|
<div class="fg-icon-box fg-success">
|
|
@@ -952,6 +970,7 @@ var FollowGateClient = class {
|
|
|
952
970
|
this.handleRepostClick();
|
|
953
971
|
});
|
|
954
972
|
document.getElementById("fg-skip-repost")?.addEventListener("click", () => {
|
|
973
|
+
this.trackEvent("step_skipped", { step: "repost" });
|
|
955
974
|
this.renderConfirmStep();
|
|
956
975
|
});
|
|
957
976
|
}
|
|
@@ -1023,6 +1042,8 @@ var FollowGateClient = class {
|
|
|
1023
1042
|
renderConfirmStep() {
|
|
1024
1043
|
const content = this.getContentElement();
|
|
1025
1044
|
if (!content) return;
|
|
1045
|
+
this.currentStep = "confirm";
|
|
1046
|
+
this.trackEvent("step_viewed", { step: "confirm" });
|
|
1026
1047
|
const username = this.currentUser?.username;
|
|
1027
1048
|
const targetHandle = this.getTargetHandle();
|
|
1028
1049
|
const postId = this.getTargetPostUrl();
|
|
@@ -1039,7 +1060,10 @@ var FollowGateClient = class {
|
|
|
1039
1060
|
verifyText = "Verifying repost";
|
|
1040
1061
|
}
|
|
1041
1062
|
content.innerHTML = `
|
|
1042
|
-
<
|
|
1063
|
+
<div style="display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 4px;">
|
|
1064
|
+
<span style="color: #4ade80; width: 28px; height: 28px;">${ICONS.check}</span>
|
|
1065
|
+
<h2 class="fg-title" style="margin: 0;">${this.escapeHtml(successTitle)}</h2>
|
|
1066
|
+
</div>
|
|
1043
1067
|
${successMessage ? `<p class="fg-subtitle" style="margin-bottom: 16px;">${this.escapeHtml(successMessage)}</p>` : ""}
|
|
1044
1068
|
<div class="fg-verify-box">
|
|
1045
1069
|
<div class="fg-verify-box-left">
|
|
@@ -1054,15 +1078,13 @@ var FollowGateClient = class {
|
|
|
1054
1078
|
` : ""}
|
|
1055
1079
|
</div>
|
|
1056
1080
|
<p class="fg-verify-hint">Verification may take some time</p>
|
|
1057
|
-
<
|
|
1058
|
-
${ICONS.warning}
|
|
1059
|
-
<p><strong>Note:</strong> Access may be revoked if actions are not completed.</p>
|
|
1060
|
-
</div>
|
|
1081
|
+
<p style="color: rgba(255,255,255,0.4); font-size: 11px; margin: 8px 0 16px; text-align: center;">Access may be revoked if actions are not completed.</p>
|
|
1061
1082
|
<button class="fg-btn fg-btn-green" id="fg-finish-btn">
|
|
1062
1083
|
${ICONS.check}
|
|
1063
|
-
Got it
|
|
1084
|
+
Got it \u2014 Continue
|
|
1064
1085
|
</button>
|
|
1065
1086
|
${hasFollow || hasRepost ? `
|
|
1087
|
+
<p style="color: rgba(255,255,255,0.4); font-size: 11px; margin: 12px 0 6px; text-align: center;">Missed something? Open again:</p>
|
|
1066
1088
|
<div class="fg-btn-row">
|
|
1067
1089
|
${hasFollow ? `
|
|
1068
1090
|
<button class="fg-btn fg-btn-secondary" id="fg-redo-follow">
|
|
@@ -1517,8 +1539,37 @@ var FollowGateClient = class {
|
|
|
1517
1539
|
throw new Error(`[FollowGate] Unsupported LinkedIn action: ${action}`);
|
|
1518
1540
|
}
|
|
1519
1541
|
}
|
|
1520
|
-
async trackEvent(event, data) {
|
|
1542
|
+
async trackEvent(event, data = {}) {
|
|
1521
1543
|
if (!this.config) return;
|
|
1544
|
+
const payload = {
|
|
1545
|
+
event
|
|
1546
|
+
};
|
|
1547
|
+
if (data.platform) {
|
|
1548
|
+
payload.platform = data.platform.toUpperCase();
|
|
1549
|
+
}
|
|
1550
|
+
if (data.action) {
|
|
1551
|
+
payload.action = data.action.toUpperCase();
|
|
1552
|
+
}
|
|
1553
|
+
if (data.target) {
|
|
1554
|
+
payload.target = data.target;
|
|
1555
|
+
}
|
|
1556
|
+
if (data.username || this.currentUser?.username) {
|
|
1557
|
+
payload.username = data.username || this.currentUser?.username;
|
|
1558
|
+
}
|
|
1559
|
+
if (data.externalUserId) {
|
|
1560
|
+
payload.externalUserId = data.externalUserId;
|
|
1561
|
+
}
|
|
1562
|
+
const {
|
|
1563
|
+
platform: _p,
|
|
1564
|
+
action: _a,
|
|
1565
|
+
target: _t,
|
|
1566
|
+
username: _u,
|
|
1567
|
+
externalUserId: _e,
|
|
1568
|
+
...rest
|
|
1569
|
+
} = data;
|
|
1570
|
+
if (Object.keys(rest).length > 0) {
|
|
1571
|
+
payload.metadata = rest;
|
|
1572
|
+
}
|
|
1522
1573
|
try {
|
|
1523
1574
|
await fetch(`${this.config.apiUrl}/api/v1/events`, {
|
|
1524
1575
|
method: "POST",
|
|
@@ -1526,11 +1577,7 @@ var FollowGateClient = class {
|
|
|
1526
1577
|
"Content-Type": "application/json",
|
|
1527
1578
|
"X-API-Key": this.config.apiKey
|
|
1528
1579
|
},
|
|
1529
|
-
body: JSON.stringify(
|
|
1530
|
-
event,
|
|
1531
|
-
appId: this.config.appId,
|
|
1532
|
-
...data
|
|
1533
|
-
})
|
|
1580
|
+
body: JSON.stringify(payload)
|
|
1534
1581
|
});
|
|
1535
1582
|
} catch (error) {
|
|
1536
1583
|
if (this.config.debug) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@followgate/js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "FollowGate SDK - Grow your audience with every download. Require social actions (follow, repost) before users can access your app.",
|
|
5
5
|
"author": "FollowGate <hello@followgate.app>",
|
|
6
6
|
"homepage": "https://followgate.app",
|