@followgate/js 0.10.1 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -10,8 +10,10 @@ type SocialAction = 'follow' | 'repost' | 'like';
10
10
  * Twitter/X configuration
11
11
  */
12
12
  interface TwitterConfig {
13
- handle: string;
13
+ handle?: string;
14
+ overrideHandle?: string;
14
15
  tweetId?: string;
16
+ username?: string;
15
17
  }
16
18
  /**
17
19
  * SDK Configuration
@@ -111,6 +113,13 @@ declare class FollowGateClient {
111
113
  private injectStyles;
112
114
  private createModal;
113
115
  private getContentElement;
116
+ /**
117
+ * Get target handle with priority:
118
+ * 1. config.twitter.overrideHandle (explicit override)
119
+ * 2. serverConfig.targetHandle (from Dashboard)
120
+ * 3. config.twitter.handle (legacy fallback, deprecated)
121
+ */
122
+ private getTargetHandle;
114
123
  private renderUsernameStep;
115
124
  private handleUsernameSubmit;
116
125
  private renderFollowStep;
package/dist/index.d.ts CHANGED
@@ -10,8 +10,10 @@ type SocialAction = 'follow' | 'repost' | 'like';
10
10
  * Twitter/X configuration
11
11
  */
12
12
  interface TwitterConfig {
13
- handle: string;
13
+ handle?: string;
14
+ overrideHandle?: string;
14
15
  tweetId?: string;
16
+ username?: string;
15
17
  }
16
18
  /**
17
19
  * SDK Configuration
@@ -111,6 +113,13 @@ declare class FollowGateClient {
111
113
  private injectStyles;
112
114
  private createModal;
113
115
  private getContentElement;
116
+ /**
117
+ * Get target handle with priority:
118
+ * 1. config.twitter.overrideHandle (explicit override)
119
+ * 2. serverConfig.targetHandle (from Dashboard)
120
+ * 3. config.twitter.handle (legacy fallback, deprecated)
121
+ */
122
+ private getTargetHandle;
114
123
  private renderUsernameStep;
115
124
  private handleUsernameSubmit;
116
125
  private renderFollowStep;
package/dist/index.js CHANGED
@@ -446,6 +446,35 @@ var MODAL_STYLES = `
446
446
  color: #94a3b8;
447
447
  }
448
448
 
449
+ .fg-close-btn {
450
+ position: absolute;
451
+ top: 16px;
452
+ right: 16px;
453
+ width: 32px;
454
+ height: 32px;
455
+ border-radius: 8px;
456
+ background: transparent;
457
+ border: 1px solid #334155;
458
+ color: #64748b;
459
+ cursor: pointer;
460
+ display: flex;
461
+ align-items: center;
462
+ justify-content: center;
463
+ transition: all 0.2s;
464
+ padding: 0;
465
+ }
466
+
467
+ .fg-close-btn:hover {
468
+ background: #1e293b;
469
+ color: #94a3b8;
470
+ border-color: #475569;
471
+ }
472
+
473
+ .fg-close-btn svg {
474
+ width: 16px;
475
+ height: 16px;
476
+ }
477
+
449
478
  .fg-btn-row {
450
479
  display: flex;
451
480
  gap: 8px;
@@ -462,7 +491,8 @@ var ICONS = {
462
491
  x: '<svg viewBox="0 0 24 24"><path fill="currentColor" d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>',
463
492
  check: '<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>',
464
493
  repost: '<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg>',
465
- warning: '<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/></svg>'
494
+ warning: '<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/></svg>',
495
+ close: '<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg>'
466
496
  };
467
497
  var FollowGateClient = class {
468
498
  config = null;
@@ -582,6 +612,15 @@ var FollowGateClient = class {
582
612
  return;
583
613
  }
584
614
  await this.fetchServerConfig();
615
+ if (this.config.twitter?.username && !this.hasUsername()) {
616
+ this.setUsername(this.config.twitter.username, "twitter");
617
+ if (this.config.debug) {
618
+ console.log(
619
+ "[FollowGate] Pre-filled username:",
620
+ this.config.twitter.username
621
+ );
622
+ }
623
+ }
585
624
  this.injectStyles();
586
625
  this.createModal();
587
626
  }
@@ -626,6 +665,9 @@ var FollowGateClient = class {
626
665
  backdrop.className = "fg-modal-backdrop";
627
666
  backdrop.innerHTML = `
628
667
  <div class="fg-modal">
668
+ <button class="fg-close-btn" id="fg-close-btn" aria-label="Close">
669
+ ${ICONS.close}
670
+ </button>
629
671
  <div id="fg-content"></div>
630
672
  <div class="fg-footer">
631
673
  <p>Powered by <a href="https://followgate.app" target="_blank" rel="noopener">FollowGate</a></p>
@@ -634,6 +676,9 @@ var FollowGateClient = class {
634
676
  `;
635
677
  document.body.appendChild(backdrop);
636
678
  this.modalElement = backdrop;
679
+ document.getElementById("fg-close-btn")?.addEventListener("click", () => {
680
+ this.hide(true);
681
+ });
637
682
  requestAnimationFrame(() => {
638
683
  backdrop.classList.add("fg-visible");
639
684
  });
@@ -646,10 +691,28 @@ var FollowGateClient = class {
646
691
  getContentElement() {
647
692
  return document.getElementById("fg-content");
648
693
  }
694
+ /**
695
+ * Get target handle with priority:
696
+ * 1. config.twitter.overrideHandle (explicit override)
697
+ * 2. serverConfig.targetHandle (from Dashboard)
698
+ * 3. config.twitter.handle (legacy fallback, deprecated)
699
+ */
700
+ getTargetHandle() {
701
+ if (this.config?.twitter?.overrideHandle) {
702
+ return this.config.twitter.overrideHandle;
703
+ }
704
+ if (this.serverConfig?.targetHandle) {
705
+ return this.serverConfig.targetHandle;
706
+ }
707
+ if (this.config?.twitter?.handle) {
708
+ return this.config.twitter.handle;
709
+ }
710
+ return null;
711
+ }
649
712
  renderUsernameStep() {
650
713
  const content = this.getContentElement();
651
714
  if (!content) return;
652
- const handle = this.serverConfig?.targetHandle || this.config?.twitter?.handle;
715
+ const handle = this.getTargetHandle();
653
716
  const hasRepost = !!this.config?.twitter?.tweetId;
654
717
  const allowSkip = this.serverConfig?.allowSkip ?? false;
655
718
  const welcomeTitle = this.serverConfig?.welcomeTitle || "Unlock Free Access";
@@ -704,9 +767,13 @@ var FollowGateClient = class {
704
767
  }
705
768
  renderFollowStep() {
706
769
  const content = this.getContentElement();
707
- if (!content || !this.config?.twitter) return;
708
- const handle = this.config.twitter.handle;
709
- const hasRepost = !!this.config.twitter.tweetId;
770
+ if (!content) return;
771
+ const handle = this.getTargetHandle();
772
+ if (!handle) {
773
+ console.error("[FollowGate] No target handle configured. Set it in Dashboard or use overrideHandle.");
774
+ return;
775
+ }
776
+ const hasRepost = !!this.config?.twitter?.tweetId;
710
777
  content.innerHTML = `
711
778
  ${hasRepost ? this.renderStepIndicator(1) : ""}
712
779
  <div class="fg-icon-box">
@@ -745,8 +812,8 @@ var FollowGateClient = class {
745
812
  }
746
813
  }
747
814
  handleFollowClick() {
748
- if (!this.config?.twitter) return;
749
- const handle = this.config.twitter.handle;
815
+ const handle = this.getTargetHandle();
816
+ if (!handle) return;
750
817
  this.openIntent({
751
818
  platform: "twitter",
752
819
  action: "follow",
@@ -755,8 +822,8 @@ var FollowGateClient = class {
755
822
  this.showFollowConfirmation();
756
823
  }
757
824
  showFollowConfirmation() {
758
- if (!this.config?.twitter) return;
759
- const handle = this.config.twitter.handle;
825
+ const handle = this.getTargetHandle();
826
+ if (!handle) return;
760
827
  const subtitle = document.getElementById("fg-follow-subtitle");
761
828
  const actions = document.getElementById("fg-follow-actions");
762
829
  if (subtitle) {
@@ -802,14 +869,14 @@ var FollowGateClient = class {
802
869
  }
803
870
  }
804
871
  async handleFollowConfirm() {
805
- if (!this.config?.twitter) return;
806
- const handle = this.config.twitter.handle;
872
+ const handle = this.getTargetHandle();
873
+ if (!handle) return;
807
874
  await this.complete({
808
875
  platform: "twitter",
809
876
  action: "follow",
810
877
  target: handle
811
878
  });
812
- if (this.config.twitter.tweetId) {
879
+ if (this.config?.twitter?.tweetId) {
813
880
  this.renderRepostStep();
814
881
  } else {
815
882
  this.renderConfirmStep();
@@ -960,11 +1027,12 @@ var FollowGateClient = class {
960
1027
  this.handleFinish();
961
1028
  });
962
1029
  document.getElementById("fg-redo-follow")?.addEventListener("click", () => {
963
- if (this.config?.twitter) {
1030
+ const handle = this.getTargetHandle();
1031
+ if (handle) {
964
1032
  this.openIntent({
965
1033
  platform: "twitter",
966
1034
  action: "follow",
967
- target: this.config.twitter.handle
1035
+ target: handle
968
1036
  });
969
1037
  }
970
1038
  });
package/dist/index.mjs CHANGED
@@ -420,6 +420,35 @@ var MODAL_STYLES = `
420
420
  color: #94a3b8;
421
421
  }
422
422
 
423
+ .fg-close-btn {
424
+ position: absolute;
425
+ top: 16px;
426
+ right: 16px;
427
+ width: 32px;
428
+ height: 32px;
429
+ border-radius: 8px;
430
+ background: transparent;
431
+ border: 1px solid #334155;
432
+ color: #64748b;
433
+ cursor: pointer;
434
+ display: flex;
435
+ align-items: center;
436
+ justify-content: center;
437
+ transition: all 0.2s;
438
+ padding: 0;
439
+ }
440
+
441
+ .fg-close-btn:hover {
442
+ background: #1e293b;
443
+ color: #94a3b8;
444
+ border-color: #475569;
445
+ }
446
+
447
+ .fg-close-btn svg {
448
+ width: 16px;
449
+ height: 16px;
450
+ }
451
+
423
452
  .fg-btn-row {
424
453
  display: flex;
425
454
  gap: 8px;
@@ -436,7 +465,8 @@ var ICONS = {
436
465
  x: '<svg viewBox="0 0 24 24"><path fill="currentColor" d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>',
437
466
  check: '<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>',
438
467
  repost: '<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg>',
439
- warning: '<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/></svg>'
468
+ warning: '<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/></svg>',
469
+ close: '<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg>'
440
470
  };
441
471
  var FollowGateClient = class {
442
472
  config = null;
@@ -556,6 +586,15 @@ var FollowGateClient = class {
556
586
  return;
557
587
  }
558
588
  await this.fetchServerConfig();
589
+ if (this.config.twitter?.username && !this.hasUsername()) {
590
+ this.setUsername(this.config.twitter.username, "twitter");
591
+ if (this.config.debug) {
592
+ console.log(
593
+ "[FollowGate] Pre-filled username:",
594
+ this.config.twitter.username
595
+ );
596
+ }
597
+ }
559
598
  this.injectStyles();
560
599
  this.createModal();
561
600
  }
@@ -600,6 +639,9 @@ var FollowGateClient = class {
600
639
  backdrop.className = "fg-modal-backdrop";
601
640
  backdrop.innerHTML = `
602
641
  <div class="fg-modal">
642
+ <button class="fg-close-btn" id="fg-close-btn" aria-label="Close">
643
+ ${ICONS.close}
644
+ </button>
603
645
  <div id="fg-content"></div>
604
646
  <div class="fg-footer">
605
647
  <p>Powered by <a href="https://followgate.app" target="_blank" rel="noopener">FollowGate</a></p>
@@ -608,6 +650,9 @@ var FollowGateClient = class {
608
650
  `;
609
651
  document.body.appendChild(backdrop);
610
652
  this.modalElement = backdrop;
653
+ document.getElementById("fg-close-btn")?.addEventListener("click", () => {
654
+ this.hide(true);
655
+ });
611
656
  requestAnimationFrame(() => {
612
657
  backdrop.classList.add("fg-visible");
613
658
  });
@@ -620,10 +665,28 @@ var FollowGateClient = class {
620
665
  getContentElement() {
621
666
  return document.getElementById("fg-content");
622
667
  }
668
+ /**
669
+ * Get target handle with priority:
670
+ * 1. config.twitter.overrideHandle (explicit override)
671
+ * 2. serverConfig.targetHandle (from Dashboard)
672
+ * 3. config.twitter.handle (legacy fallback, deprecated)
673
+ */
674
+ getTargetHandle() {
675
+ if (this.config?.twitter?.overrideHandle) {
676
+ return this.config.twitter.overrideHandle;
677
+ }
678
+ if (this.serverConfig?.targetHandle) {
679
+ return this.serverConfig.targetHandle;
680
+ }
681
+ if (this.config?.twitter?.handle) {
682
+ return this.config.twitter.handle;
683
+ }
684
+ return null;
685
+ }
623
686
  renderUsernameStep() {
624
687
  const content = this.getContentElement();
625
688
  if (!content) return;
626
- const handle = this.serverConfig?.targetHandle || this.config?.twitter?.handle;
689
+ const handle = this.getTargetHandle();
627
690
  const hasRepost = !!this.config?.twitter?.tweetId;
628
691
  const allowSkip = this.serverConfig?.allowSkip ?? false;
629
692
  const welcomeTitle = this.serverConfig?.welcomeTitle || "Unlock Free Access";
@@ -678,9 +741,13 @@ var FollowGateClient = class {
678
741
  }
679
742
  renderFollowStep() {
680
743
  const content = this.getContentElement();
681
- if (!content || !this.config?.twitter) return;
682
- const handle = this.config.twitter.handle;
683
- const hasRepost = !!this.config.twitter.tweetId;
744
+ if (!content) return;
745
+ const handle = this.getTargetHandle();
746
+ if (!handle) {
747
+ console.error("[FollowGate] No target handle configured. Set it in Dashboard or use overrideHandle.");
748
+ return;
749
+ }
750
+ const hasRepost = !!this.config?.twitter?.tweetId;
684
751
  content.innerHTML = `
685
752
  ${hasRepost ? this.renderStepIndicator(1) : ""}
686
753
  <div class="fg-icon-box">
@@ -719,8 +786,8 @@ var FollowGateClient = class {
719
786
  }
720
787
  }
721
788
  handleFollowClick() {
722
- if (!this.config?.twitter) return;
723
- const handle = this.config.twitter.handle;
789
+ const handle = this.getTargetHandle();
790
+ if (!handle) return;
724
791
  this.openIntent({
725
792
  platform: "twitter",
726
793
  action: "follow",
@@ -729,8 +796,8 @@ var FollowGateClient = class {
729
796
  this.showFollowConfirmation();
730
797
  }
731
798
  showFollowConfirmation() {
732
- if (!this.config?.twitter) return;
733
- const handle = this.config.twitter.handle;
799
+ const handle = this.getTargetHandle();
800
+ if (!handle) return;
734
801
  const subtitle = document.getElementById("fg-follow-subtitle");
735
802
  const actions = document.getElementById("fg-follow-actions");
736
803
  if (subtitle) {
@@ -776,14 +843,14 @@ var FollowGateClient = class {
776
843
  }
777
844
  }
778
845
  async handleFollowConfirm() {
779
- if (!this.config?.twitter) return;
780
- const handle = this.config.twitter.handle;
846
+ const handle = this.getTargetHandle();
847
+ if (!handle) return;
781
848
  await this.complete({
782
849
  platform: "twitter",
783
850
  action: "follow",
784
851
  target: handle
785
852
  });
786
- if (this.config.twitter.tweetId) {
853
+ if (this.config?.twitter?.tweetId) {
787
854
  this.renderRepostStep();
788
855
  } else {
789
856
  this.renderConfirmStep();
@@ -934,11 +1001,12 @@ var FollowGateClient = class {
934
1001
  this.handleFinish();
935
1002
  });
936
1003
  document.getElementById("fg-redo-follow")?.addEventListener("click", () => {
937
- if (this.config?.twitter) {
1004
+ const handle = this.getTargetHandle();
1005
+ if (handle) {
938
1006
  this.openIntent({
939
1007
  platform: "twitter",
940
1008
  action: "follow",
941
- target: this.config.twitter.handle
1009
+ target: handle
942
1010
  });
943
1011
  }
944
1012
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@followgate/js",
3
- "version": "0.10.1",
3
+ "version": "0.12.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",