@followgate/js 0.12.1 → 0.14.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
@@ -12,6 +12,7 @@ type SocialAction = 'follow' | 'repost' | 'like';
12
12
  interface TwitterConfig {
13
13
  handle?: string;
14
14
  overrideHandle?: string;
15
+ overridePostUrl?: string;
15
16
  tweetId?: string;
16
17
  username?: string;
17
18
  }
@@ -120,6 +121,24 @@ declare class FollowGateClient {
120
121
  * 3. config.twitter.handle (legacy fallback, deprecated)
121
122
  */
122
123
  private getTargetHandle;
124
+ /**
125
+ * Get target post URL/ID with priority:
126
+ * 1. config.twitter.overridePostUrl (explicit override)
127
+ * 2. serverConfig.targetPostUrl (from Dashboard)
128
+ * 3. config.twitter.tweetId (legacy fallback, deprecated)
129
+ */
130
+ private getTargetPostUrl;
131
+ /**
132
+ * Extract post ID from URL or return as-is if already an ID
133
+ * Supports: Tweet ID, Twitter/X URLs
134
+ */
135
+ private extractPostId;
136
+ /**
137
+ * Check if repost step should be shown based on:
138
+ * 1. serverConfig.actions includes 'repost'
139
+ * 2. A valid postUrl is available
140
+ */
141
+ private shouldShowRepostStep;
123
142
  private renderUsernameStep;
124
143
  private handleUsernameSubmit;
125
144
  private renderFollowStep;
@@ -147,9 +166,14 @@ declare class FollowGateClient {
147
166
  */
148
167
  hasUsername(): boolean;
149
168
  /**
150
- * Clear stored session
169
+ * Clear stored session (user, actions, and unlock status)
151
170
  */
152
171
  reset(): void;
172
+ /**
173
+ * Clear only the unlock status (keeps user and actions)
174
+ * Useful for allowing users to go through the flow again
175
+ */
176
+ clearUnlockStatus(): void;
153
177
  getFollowUrl(platform: Platform, target: string): string;
154
178
  getRepostUrl(platform: Platform, target: string): string;
155
179
  getLikeUrl(platform: Platform, target: string): string;
package/dist/index.d.ts CHANGED
@@ -12,6 +12,7 @@ type SocialAction = 'follow' | 'repost' | 'like';
12
12
  interface TwitterConfig {
13
13
  handle?: string;
14
14
  overrideHandle?: string;
15
+ overridePostUrl?: string;
15
16
  tweetId?: string;
16
17
  username?: string;
17
18
  }
@@ -120,6 +121,24 @@ declare class FollowGateClient {
120
121
  * 3. config.twitter.handle (legacy fallback, deprecated)
121
122
  */
122
123
  private getTargetHandle;
124
+ /**
125
+ * Get target post URL/ID with priority:
126
+ * 1. config.twitter.overridePostUrl (explicit override)
127
+ * 2. serverConfig.targetPostUrl (from Dashboard)
128
+ * 3. config.twitter.tweetId (legacy fallback, deprecated)
129
+ */
130
+ private getTargetPostUrl;
131
+ /**
132
+ * Extract post ID from URL or return as-is if already an ID
133
+ * Supports: Tweet ID, Twitter/X URLs
134
+ */
135
+ private extractPostId;
136
+ /**
137
+ * Check if repost step should be shown based on:
138
+ * 1. serverConfig.actions includes 'repost'
139
+ * 2. A valid postUrl is available
140
+ */
141
+ private shouldShowRepostStep;
123
142
  private renderUsernameStep;
124
143
  private handleUsernameSubmit;
125
144
  private renderFollowStep;
@@ -147,9 +166,14 @@ declare class FollowGateClient {
147
166
  */
148
167
  hasUsername(): boolean;
149
168
  /**
150
- * Clear stored session
169
+ * Clear stored session (user, actions, and unlock status)
151
170
  */
152
171
  reset(): void;
172
+ /**
173
+ * Clear only the unlock status (keeps user and actions)
174
+ * Useful for allowing users to go through the flow again
175
+ */
176
+ clearUnlockStatus(): void;
153
177
  getFollowUrl(platform: Platform, target: string): string;
154
178
  getRepostUrl(platform: Platform, target: string): string;
155
179
  getLikeUrl(platform: Platform, target: string): string;
package/dist/index.js CHANGED
@@ -606,7 +606,11 @@ var FollowGateClient = class {
606
606
  }
607
607
  if (this.isUnlocked()) {
608
608
  if (this.config.debug) {
609
- console.log("[FollowGate] Already unlocked, skipping modal");
609
+ console.log(
610
+ "[FollowGate] Modal skipped - user already unlocked.",
611
+ "Call FollowGate.reset() to clear unlock status.",
612
+ this.config.userId ? `(userId: ${this.config.userId})` : "(no userId set)"
613
+ );
610
614
  }
611
615
  this.config.onComplete?.();
612
616
  return;
@@ -709,11 +713,68 @@ var FollowGateClient = class {
709
713
  }
710
714
  return null;
711
715
  }
716
+ /**
717
+ * Get target post URL/ID with priority:
718
+ * 1. config.twitter.overridePostUrl (explicit override)
719
+ * 2. serverConfig.targetPostUrl (from Dashboard)
720
+ * 3. config.twitter.tweetId (legacy fallback, deprecated)
721
+ */
722
+ getTargetPostUrl() {
723
+ if (this.config?.twitter?.overridePostUrl) {
724
+ return this.extractPostId(this.config.twitter.overridePostUrl);
725
+ }
726
+ if (this.serverConfig?.targetPostUrl) {
727
+ return this.extractPostId(this.serverConfig.targetPostUrl);
728
+ }
729
+ if (this.config?.twitter?.tweetId) {
730
+ return this.config.twitter.tweetId;
731
+ }
732
+ return null;
733
+ }
734
+ /**
735
+ * Extract post ID from URL or return as-is if already an ID
736
+ * Supports: Tweet ID, Twitter/X URLs
737
+ */
738
+ extractPostId(input) {
739
+ if (!input) return null;
740
+ const trimmed = input.trim();
741
+ if (/^\d+$/.test(trimmed)) {
742
+ return trimmed;
743
+ }
744
+ const twitterMatch = trimmed.match(
745
+ /(?:twitter|x)\.com\/\w+\/status\/(\d+)/
746
+ );
747
+ if (twitterMatch) {
748
+ return twitterMatch[1];
749
+ }
750
+ return trimmed;
751
+ }
752
+ /**
753
+ * Check if repost step should be shown based on:
754
+ * 1. serverConfig.actions includes 'repost'
755
+ * 2. A valid postUrl is available
756
+ */
757
+ shouldShowRepostStep() {
758
+ const postUrl = this.getTargetPostUrl();
759
+ const actionsIncludeRepost = this.serverConfig?.actions?.includes("repost");
760
+ if (actionsIncludeRepost && !postUrl) {
761
+ console.warn(
762
+ "[FollowGate] REPOST action configured but no targetPostUrl available.",
763
+ "Set targetPostUrl in Dashboard or use overridePostUrl in config.",
764
+ "Skipping repost step."
765
+ );
766
+ return false;
767
+ }
768
+ if (this.config?.twitter?.tweetId) {
769
+ return true;
770
+ }
771
+ return !!actionsIncludeRepost && !!postUrl;
772
+ }
712
773
  renderUsernameStep() {
713
774
  const content = this.getContentElement();
714
775
  if (!content) return;
715
776
  const handle = this.getTargetHandle();
716
- const hasRepost = !!this.config?.twitter?.tweetId;
777
+ const hasRepost = this.shouldShowRepostStep();
717
778
  const allowSkip = this.serverConfig?.allowSkip ?? false;
718
779
  const welcomeTitle = this.serverConfig?.welcomeTitle || "Unlock Free Access";
719
780
  const welcomeMessage = this.serverConfig?.welcomeMessage || "Enter your X username to get started";
@@ -775,7 +836,7 @@ var FollowGateClient = class {
775
836
  );
776
837
  return;
777
838
  }
778
- const hasRepost = !!this.config?.twitter?.tweetId;
839
+ const hasRepost = this.shouldShowRepostStep();
779
840
  const defaultTitle = hasRepost ? "Step 1: Follow" : "Follow to Continue";
780
841
  const defaultMessage = `Follow @${handle} on X`;
781
842
  const title = this.serverConfig?.welcomeTitle || defaultTitle;
@@ -810,8 +871,7 @@ var FollowGateClient = class {
810
871
  });
811
872
  }
812
873
  handleSkipFollow() {
813
- if (!this.config?.twitter) return;
814
- if (this.config.twitter.tweetId) {
874
+ if (this.shouldShowRepostStep()) {
815
875
  this.renderRepostStep();
816
876
  } else {
817
877
  this.renderConfirmStep();
@@ -882,7 +942,7 @@ var FollowGateClient = class {
882
942
  action: "follow",
883
943
  target: handle
884
944
  });
885
- if (this.config?.twitter?.tweetId) {
945
+ if (this.shouldShowRepostStep()) {
886
946
  this.renderRepostStep();
887
947
  } else {
888
948
  this.renderConfirmStep();
@@ -890,7 +950,8 @@ var FollowGateClient = class {
890
950
  }
891
951
  renderRepostStep() {
892
952
  const content = this.getContentElement();
893
- if (!content || !this.config?.twitter?.tweetId) return;
953
+ const postId = this.getTargetPostUrl();
954
+ if (!content || !postId) return;
894
955
  content.innerHTML = `
895
956
  ${this.renderStepIndicator(2)}
896
957
  <div class="fg-icon-box fg-success">
@@ -921,12 +982,12 @@ var FollowGateClient = class {
921
982
  });
922
983
  }
923
984
  handleRepostClick() {
924
- if (!this.config?.twitter?.tweetId) return;
925
- const tweetId = this.config.twitter.tweetId;
985
+ const postId = this.getTargetPostUrl();
986
+ if (!postId) return;
926
987
  this.openIntent({
927
988
  platform: "twitter",
928
989
  action: "repost",
929
- target: tweetId
990
+ target: postId
930
991
  });
931
992
  this.showRepostConfirmation();
932
993
  }
@@ -976,11 +1037,12 @@ var FollowGateClient = class {
976
1037
  }
977
1038
  }
978
1039
  async handleRepostConfirm() {
979
- if (!this.config?.twitter?.tweetId) return;
1040
+ const postId = this.getTargetPostUrl();
1041
+ if (!postId) return;
980
1042
  await this.complete({
981
1043
  platform: "twitter",
982
1044
  action: "repost",
983
- target: this.config.twitter.tweetId
1045
+ target: postId
984
1046
  });
985
1047
  this.renderConfirmStep();
986
1048
  }
@@ -988,15 +1050,27 @@ var FollowGateClient = class {
988
1050
  const content = this.getContentElement();
989
1051
  if (!content) return;
990
1052
  const username = this.currentUser?.username;
1053
+ const targetHandle = this.getTargetHandle();
1054
+ const postId = this.getTargetPostUrl();
991
1055
  const successTitle = this.serverConfig?.successTitle || "Almost done!";
992
1056
  const successMessage = this.serverConfig?.successMessage || null;
1057
+ const hasFollow = targetHandle !== null;
1058
+ const hasRepost = postId !== null;
1059
+ let verifyText = "Verifying";
1060
+ if (hasFollow && hasRepost) {
1061
+ verifyText = "Verifying follow & repost";
1062
+ } else if (hasFollow) {
1063
+ verifyText = "Verifying follow";
1064
+ } else if (hasRepost) {
1065
+ verifyText = "Verifying repost";
1066
+ }
993
1067
  content.innerHTML = `
994
1068
  <h2 class="fg-title">${this.escapeHtml(successTitle)}</h2>
995
1069
  ${successMessage ? `<p class="fg-subtitle" style="margin-bottom: 16px;">${this.escapeHtml(successMessage)}</p>` : ""}
996
1070
  <div class="fg-verify-box">
997
1071
  <div class="fg-verify-box-left">
998
1072
  <div class="fg-verify-spinner"></div>
999
- <span class="fg-verify-text">Verifying follow & repost</span>
1073
+ <span class="fg-verify-text">${verifyText}</span>
1000
1074
  </div>
1001
1075
  ${username ? `
1002
1076
  <div class="fg-user-badge" style="margin: 0; padding: 4px 10px; gap: 6px;">
@@ -1014,13 +1088,15 @@ var FollowGateClient = class {
1014
1088
  ${ICONS.check}
1015
1089
  Got it
1016
1090
  </button>
1017
- ${this.config?.twitter ? `
1091
+ ${hasFollow || hasRepost ? `
1018
1092
  <div class="fg-btn-row">
1019
- <button class="fg-btn fg-btn-secondary" id="fg-redo-follow">
1020
- ${ICONS.x}
1021
- Open follow
1022
- </button>
1023
- ${this.config.twitter.tweetId ? `
1093
+ ${hasFollow ? `
1094
+ <button class="fg-btn fg-btn-secondary" id="fg-redo-follow">
1095
+ ${ICONS.x}
1096
+ Open follow
1097
+ </button>
1098
+ ` : ""}
1099
+ ${hasRepost ? `
1024
1100
  <button class="fg-btn fg-btn-secondary" id="fg-redo-repost">
1025
1101
  ${ICONS.repost}
1026
1102
  Open repost
@@ -1043,11 +1119,12 @@ var FollowGateClient = class {
1043
1119
  }
1044
1120
  });
1045
1121
  document.getElementById("fg-redo-repost")?.addEventListener("click", () => {
1046
- if (this.config?.twitter?.tweetId) {
1122
+ const repostId = this.getTargetPostUrl();
1123
+ if (repostId) {
1047
1124
  this.openIntent({
1048
1125
  platform: "twitter",
1049
1126
  action: "repost",
1050
- target: this.config.twitter.tweetId
1127
+ target: repostId
1051
1128
  });
1052
1129
  }
1053
1130
  });
@@ -1111,7 +1188,7 @@ var FollowGateClient = class {
1111
1188
  return this.currentUser !== null;
1112
1189
  }
1113
1190
  /**
1114
- * Clear stored session
1191
+ * Clear stored session (user, actions, and unlock status)
1115
1192
  */
1116
1193
  reset() {
1117
1194
  this.currentUser = null;
@@ -1125,6 +1202,18 @@ var FollowGateClient = class {
1125
1202
  console.log("[FollowGate] Session reset");
1126
1203
  }
1127
1204
  }
1205
+ /**
1206
+ * Clear only the unlock status (keeps user and actions)
1207
+ * Useful for allowing users to go through the flow again
1208
+ */
1209
+ clearUnlockStatus() {
1210
+ if (typeof localStorage !== "undefined") {
1211
+ localStorage.removeItem(this.getStorageKey("followgate_unlocked"));
1212
+ }
1213
+ if (this.config?.debug) {
1214
+ console.log("[FollowGate] Unlock status cleared");
1215
+ }
1216
+ }
1128
1217
  // ============================================
1129
1218
  // Intent URL Methods
1130
1219
  // ============================================
package/dist/index.mjs CHANGED
@@ -580,7 +580,11 @@ var FollowGateClient = class {
580
580
  }
581
581
  if (this.isUnlocked()) {
582
582
  if (this.config.debug) {
583
- console.log("[FollowGate] Already unlocked, skipping modal");
583
+ console.log(
584
+ "[FollowGate] Modal skipped - user already unlocked.",
585
+ "Call FollowGate.reset() to clear unlock status.",
586
+ this.config.userId ? `(userId: ${this.config.userId})` : "(no userId set)"
587
+ );
584
588
  }
585
589
  this.config.onComplete?.();
586
590
  return;
@@ -683,11 +687,68 @@ var FollowGateClient = class {
683
687
  }
684
688
  return null;
685
689
  }
690
+ /**
691
+ * Get target post URL/ID with priority:
692
+ * 1. config.twitter.overridePostUrl (explicit override)
693
+ * 2. serverConfig.targetPostUrl (from Dashboard)
694
+ * 3. config.twitter.tweetId (legacy fallback, deprecated)
695
+ */
696
+ getTargetPostUrl() {
697
+ if (this.config?.twitter?.overridePostUrl) {
698
+ return this.extractPostId(this.config.twitter.overridePostUrl);
699
+ }
700
+ if (this.serverConfig?.targetPostUrl) {
701
+ return this.extractPostId(this.serverConfig.targetPostUrl);
702
+ }
703
+ if (this.config?.twitter?.tweetId) {
704
+ return this.config.twitter.tweetId;
705
+ }
706
+ return null;
707
+ }
708
+ /**
709
+ * Extract post ID from URL or return as-is if already an ID
710
+ * Supports: Tweet ID, Twitter/X URLs
711
+ */
712
+ extractPostId(input) {
713
+ if (!input) return null;
714
+ const trimmed = input.trim();
715
+ if (/^\d+$/.test(trimmed)) {
716
+ return trimmed;
717
+ }
718
+ const twitterMatch = trimmed.match(
719
+ /(?:twitter|x)\.com\/\w+\/status\/(\d+)/
720
+ );
721
+ if (twitterMatch) {
722
+ return twitterMatch[1];
723
+ }
724
+ return trimmed;
725
+ }
726
+ /**
727
+ * Check if repost step should be shown based on:
728
+ * 1. serverConfig.actions includes 'repost'
729
+ * 2. A valid postUrl is available
730
+ */
731
+ shouldShowRepostStep() {
732
+ const postUrl = this.getTargetPostUrl();
733
+ const actionsIncludeRepost = this.serverConfig?.actions?.includes("repost");
734
+ if (actionsIncludeRepost && !postUrl) {
735
+ console.warn(
736
+ "[FollowGate] REPOST action configured but no targetPostUrl available.",
737
+ "Set targetPostUrl in Dashboard or use overridePostUrl in config.",
738
+ "Skipping repost step."
739
+ );
740
+ return false;
741
+ }
742
+ if (this.config?.twitter?.tweetId) {
743
+ return true;
744
+ }
745
+ return !!actionsIncludeRepost && !!postUrl;
746
+ }
686
747
  renderUsernameStep() {
687
748
  const content = this.getContentElement();
688
749
  if (!content) return;
689
750
  const handle = this.getTargetHandle();
690
- const hasRepost = !!this.config?.twitter?.tweetId;
751
+ const hasRepost = this.shouldShowRepostStep();
691
752
  const allowSkip = this.serverConfig?.allowSkip ?? false;
692
753
  const welcomeTitle = this.serverConfig?.welcomeTitle || "Unlock Free Access";
693
754
  const welcomeMessage = this.serverConfig?.welcomeMessage || "Enter your X username to get started";
@@ -749,7 +810,7 @@ var FollowGateClient = class {
749
810
  );
750
811
  return;
751
812
  }
752
- const hasRepost = !!this.config?.twitter?.tweetId;
813
+ const hasRepost = this.shouldShowRepostStep();
753
814
  const defaultTitle = hasRepost ? "Step 1: Follow" : "Follow to Continue";
754
815
  const defaultMessage = `Follow @${handle} on X`;
755
816
  const title = this.serverConfig?.welcomeTitle || defaultTitle;
@@ -784,8 +845,7 @@ var FollowGateClient = class {
784
845
  });
785
846
  }
786
847
  handleSkipFollow() {
787
- if (!this.config?.twitter) return;
788
- if (this.config.twitter.tweetId) {
848
+ if (this.shouldShowRepostStep()) {
789
849
  this.renderRepostStep();
790
850
  } else {
791
851
  this.renderConfirmStep();
@@ -856,7 +916,7 @@ var FollowGateClient = class {
856
916
  action: "follow",
857
917
  target: handle
858
918
  });
859
- if (this.config?.twitter?.tweetId) {
919
+ if (this.shouldShowRepostStep()) {
860
920
  this.renderRepostStep();
861
921
  } else {
862
922
  this.renderConfirmStep();
@@ -864,7 +924,8 @@ var FollowGateClient = class {
864
924
  }
865
925
  renderRepostStep() {
866
926
  const content = this.getContentElement();
867
- if (!content || !this.config?.twitter?.tweetId) return;
927
+ const postId = this.getTargetPostUrl();
928
+ if (!content || !postId) return;
868
929
  content.innerHTML = `
869
930
  ${this.renderStepIndicator(2)}
870
931
  <div class="fg-icon-box fg-success">
@@ -895,12 +956,12 @@ var FollowGateClient = class {
895
956
  });
896
957
  }
897
958
  handleRepostClick() {
898
- if (!this.config?.twitter?.tweetId) return;
899
- const tweetId = this.config.twitter.tweetId;
959
+ const postId = this.getTargetPostUrl();
960
+ if (!postId) return;
900
961
  this.openIntent({
901
962
  platform: "twitter",
902
963
  action: "repost",
903
- target: tweetId
964
+ target: postId
904
965
  });
905
966
  this.showRepostConfirmation();
906
967
  }
@@ -950,11 +1011,12 @@ var FollowGateClient = class {
950
1011
  }
951
1012
  }
952
1013
  async handleRepostConfirm() {
953
- if (!this.config?.twitter?.tweetId) return;
1014
+ const postId = this.getTargetPostUrl();
1015
+ if (!postId) return;
954
1016
  await this.complete({
955
1017
  platform: "twitter",
956
1018
  action: "repost",
957
- target: this.config.twitter.tweetId
1019
+ target: postId
958
1020
  });
959
1021
  this.renderConfirmStep();
960
1022
  }
@@ -962,15 +1024,27 @@ var FollowGateClient = class {
962
1024
  const content = this.getContentElement();
963
1025
  if (!content) return;
964
1026
  const username = this.currentUser?.username;
1027
+ const targetHandle = this.getTargetHandle();
1028
+ const postId = this.getTargetPostUrl();
965
1029
  const successTitle = this.serverConfig?.successTitle || "Almost done!";
966
1030
  const successMessage = this.serverConfig?.successMessage || null;
1031
+ const hasFollow = targetHandle !== null;
1032
+ const hasRepost = postId !== null;
1033
+ let verifyText = "Verifying";
1034
+ if (hasFollow && hasRepost) {
1035
+ verifyText = "Verifying follow & repost";
1036
+ } else if (hasFollow) {
1037
+ verifyText = "Verifying follow";
1038
+ } else if (hasRepost) {
1039
+ verifyText = "Verifying repost";
1040
+ }
967
1041
  content.innerHTML = `
968
1042
  <h2 class="fg-title">${this.escapeHtml(successTitle)}</h2>
969
1043
  ${successMessage ? `<p class="fg-subtitle" style="margin-bottom: 16px;">${this.escapeHtml(successMessage)}</p>` : ""}
970
1044
  <div class="fg-verify-box">
971
1045
  <div class="fg-verify-box-left">
972
1046
  <div class="fg-verify-spinner"></div>
973
- <span class="fg-verify-text">Verifying follow & repost</span>
1047
+ <span class="fg-verify-text">${verifyText}</span>
974
1048
  </div>
975
1049
  ${username ? `
976
1050
  <div class="fg-user-badge" style="margin: 0; padding: 4px 10px; gap: 6px;">
@@ -988,13 +1062,15 @@ var FollowGateClient = class {
988
1062
  ${ICONS.check}
989
1063
  Got it
990
1064
  </button>
991
- ${this.config?.twitter ? `
1065
+ ${hasFollow || hasRepost ? `
992
1066
  <div class="fg-btn-row">
993
- <button class="fg-btn fg-btn-secondary" id="fg-redo-follow">
994
- ${ICONS.x}
995
- Open follow
996
- </button>
997
- ${this.config.twitter.tweetId ? `
1067
+ ${hasFollow ? `
1068
+ <button class="fg-btn fg-btn-secondary" id="fg-redo-follow">
1069
+ ${ICONS.x}
1070
+ Open follow
1071
+ </button>
1072
+ ` : ""}
1073
+ ${hasRepost ? `
998
1074
  <button class="fg-btn fg-btn-secondary" id="fg-redo-repost">
999
1075
  ${ICONS.repost}
1000
1076
  Open repost
@@ -1017,11 +1093,12 @@ var FollowGateClient = class {
1017
1093
  }
1018
1094
  });
1019
1095
  document.getElementById("fg-redo-repost")?.addEventListener("click", () => {
1020
- if (this.config?.twitter?.tweetId) {
1096
+ const repostId = this.getTargetPostUrl();
1097
+ if (repostId) {
1021
1098
  this.openIntent({
1022
1099
  platform: "twitter",
1023
1100
  action: "repost",
1024
- target: this.config.twitter.tweetId
1101
+ target: repostId
1025
1102
  });
1026
1103
  }
1027
1104
  });
@@ -1085,7 +1162,7 @@ var FollowGateClient = class {
1085
1162
  return this.currentUser !== null;
1086
1163
  }
1087
1164
  /**
1088
- * Clear stored session
1165
+ * Clear stored session (user, actions, and unlock status)
1089
1166
  */
1090
1167
  reset() {
1091
1168
  this.currentUser = null;
@@ -1099,6 +1176,18 @@ var FollowGateClient = class {
1099
1176
  console.log("[FollowGate] Session reset");
1100
1177
  }
1101
1178
  }
1179
+ /**
1180
+ * Clear only the unlock status (keeps user and actions)
1181
+ * Useful for allowing users to go through the flow again
1182
+ */
1183
+ clearUnlockStatus() {
1184
+ if (typeof localStorage !== "undefined") {
1185
+ localStorage.removeItem(this.getStorageKey("followgate_unlocked"));
1186
+ }
1187
+ if (this.config?.debug) {
1188
+ console.log("[FollowGate] Unlock status cleared");
1189
+ }
1190
+ }
1102
1191
  // ============================================
1103
1192
  // Intent URL Methods
1104
1193
  // ============================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@followgate/js",
3
- "version": "0.12.1",
3
+ "version": "0.14.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",