@followgate/js 0.14.0 → 0.14.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/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.debug) {
609
- console.log(
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
- "Call FollowGate.reset() to clear unlock status.",
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,6 +783,8 @@ 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;
@@ -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();
@@ -1543,8 +1564,37 @@ var FollowGateClient = class {
1543
1564
  throw new Error(`[FollowGate] Unsupported LinkedIn action: ${action}`);
1544
1565
  }
1545
1566
  }
1546
- async trackEvent(event, data) {
1567
+ async trackEvent(event, data = {}) {
1547
1568
  if (!this.config) return;
1569
+ const payload = {
1570
+ event
1571
+ };
1572
+ if (data.platform) {
1573
+ payload.platform = data.platform.toUpperCase();
1574
+ }
1575
+ if (data.action) {
1576
+ payload.action = data.action.toUpperCase();
1577
+ }
1578
+ if (data.target) {
1579
+ payload.target = data.target;
1580
+ }
1581
+ if (data.username || this.currentUser?.username) {
1582
+ payload.username = data.username || this.currentUser?.username;
1583
+ }
1584
+ if (data.externalUserId) {
1585
+ payload.externalUserId = data.externalUserId;
1586
+ }
1587
+ const {
1588
+ platform: _p,
1589
+ action: _a,
1590
+ target: _t,
1591
+ username: _u,
1592
+ externalUserId: _e,
1593
+ ...rest
1594
+ } = data;
1595
+ if (Object.keys(rest).length > 0) {
1596
+ payload.metadata = rest;
1597
+ }
1548
1598
  try {
1549
1599
  await fetch(`${this.config.apiUrl}/api/v1/events`, {
1550
1600
  method: "POST",
@@ -1552,11 +1602,7 @@ var FollowGateClient = class {
1552
1602
  "Content-Type": "application/json",
1553
1603
  "X-API-Key": this.config.apiKey
1554
1604
  },
1555
- body: JSON.stringify({
1556
- event,
1557
- appId: this.config.appId,
1558
- ...data
1559
- })
1605
+ body: JSON.stringify(payload)
1560
1606
  });
1561
1607
  } catch (error) {
1562
1608
  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.debug) {
583
- console.log(
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
- "Call FollowGate.reset() to clear unlock status.",
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,6 +757,8 @@ 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;
@@ -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();
@@ -1517,8 +1538,37 @@ var FollowGateClient = class {
1517
1538
  throw new Error(`[FollowGate] Unsupported LinkedIn action: ${action}`);
1518
1539
  }
1519
1540
  }
1520
- async trackEvent(event, data) {
1541
+ async trackEvent(event, data = {}) {
1521
1542
  if (!this.config) return;
1543
+ const payload = {
1544
+ event
1545
+ };
1546
+ if (data.platform) {
1547
+ payload.platform = data.platform.toUpperCase();
1548
+ }
1549
+ if (data.action) {
1550
+ payload.action = data.action.toUpperCase();
1551
+ }
1552
+ if (data.target) {
1553
+ payload.target = data.target;
1554
+ }
1555
+ if (data.username || this.currentUser?.username) {
1556
+ payload.username = data.username || this.currentUser?.username;
1557
+ }
1558
+ if (data.externalUserId) {
1559
+ payload.externalUserId = data.externalUserId;
1560
+ }
1561
+ const {
1562
+ platform: _p,
1563
+ action: _a,
1564
+ target: _t,
1565
+ username: _u,
1566
+ externalUserId: _e,
1567
+ ...rest
1568
+ } = data;
1569
+ if (Object.keys(rest).length > 0) {
1570
+ payload.metadata = rest;
1571
+ }
1522
1572
  try {
1523
1573
  await fetch(`${this.config.apiUrl}/api/v1/events`, {
1524
1574
  method: "POST",
@@ -1526,11 +1576,7 @@ var FollowGateClient = class {
1526
1576
  "Content-Type": "application/json",
1527
1577
  "X-API-Key": this.config.apiKey
1528
1578
  },
1529
- body: JSON.stringify({
1530
- event,
1531
- appId: this.config.appId,
1532
- ...data
1533
- })
1579
+ body: JSON.stringify(payload)
1534
1580
  });
1535
1581
  } catch (error) {
1536
1582
  if (this.config.debug) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@followgate/js",
3
- "version": "0.14.0",
3
+ "version": "0.14.1",
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",