@followgate/js 0.7.0 → 0.8.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/README.md +42 -0
- package/dist/index.d.mts +30 -1
- package/dist/index.d.ts +30 -1
- package/dist/index.js +191 -3
- package/dist/index.mjs +191 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -225,6 +225,47 @@ FollowGate.getUnlockStatus();
|
|
|
225
225
|
// }
|
|
226
226
|
```
|
|
227
227
|
|
|
228
|
+
### Server-Side Verification
|
|
229
|
+
|
|
230
|
+
When `userId` is set, completions are automatically saved to the server. You can verify users from your backend:
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
// Check if user is verified (server-side)
|
|
234
|
+
const isVerified = await FollowGate.isVerified();
|
|
235
|
+
// Returns: true | false
|
|
236
|
+
|
|
237
|
+
// Get full verification status
|
|
238
|
+
const status = await FollowGate.getVerificationStatus();
|
|
239
|
+
// Returns: {
|
|
240
|
+
// verified: boolean,
|
|
241
|
+
// userId: string,
|
|
242
|
+
// completedAt?: string, // ISO date string
|
|
243
|
+
// actions?: [{ action: 'follow', target: 'lukasvanuden' }, ...]
|
|
244
|
+
// }
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Use Case: Conditional Features**
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
// In your app (e.g., Chrome extension, Electron app)
|
|
251
|
+
FollowGate.init({
|
|
252
|
+
appId: 'your-app-id',
|
|
253
|
+
apiKey: 'fg_live_xxx',
|
|
254
|
+
userId: clerkUserId,
|
|
255
|
+
twitter: { handle: 'lukasvanuden' },
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
const isVerified = await FollowGate.isVerified();
|
|
259
|
+
|
|
260
|
+
if (isVerified) {
|
|
261
|
+
// User completed FollowGate → Full access
|
|
262
|
+
enableAllFeatures();
|
|
263
|
+
} else {
|
|
264
|
+
// User hasn't completed → Limited access
|
|
265
|
+
enableTrialMode();
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
228
269
|
### Event Listeners
|
|
229
270
|
|
|
230
271
|
```typescript
|
|
@@ -339,6 +380,7 @@ import type {
|
|
|
339
380
|
CompleteOptions,
|
|
340
381
|
UserInfo,
|
|
341
382
|
UnlockStatus,
|
|
383
|
+
VerificationStatus,
|
|
342
384
|
} from '@followgate/js';
|
|
343
385
|
```
|
|
344
386
|
|
package/dist/index.d.mts
CHANGED
|
@@ -66,6 +66,18 @@ interface UnlockStatus {
|
|
|
66
66
|
username?: string;
|
|
67
67
|
completedActions?: CompleteOptions[];
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Server-side verification status
|
|
71
|
+
*/
|
|
72
|
+
interface VerificationStatus {
|
|
73
|
+
verified: boolean;
|
|
74
|
+
userId: string;
|
|
75
|
+
completedAt?: string;
|
|
76
|
+
actions?: Array<{
|
|
77
|
+
action: string;
|
|
78
|
+
target: string;
|
|
79
|
+
}>;
|
|
80
|
+
}
|
|
69
81
|
/**
|
|
70
82
|
* FollowGate SDK Client
|
|
71
83
|
*/
|
|
@@ -130,6 +142,18 @@ declare class FollowGateClient {
|
|
|
130
142
|
isUnlocked(): boolean;
|
|
131
143
|
getUnlockStatus(): UnlockStatus;
|
|
132
144
|
getCompletedActions(): CompleteOptions[];
|
|
145
|
+
/**
|
|
146
|
+
* Check if user is verified server-side
|
|
147
|
+
* Requires userId to be set in config
|
|
148
|
+
* @returns Promise<boolean> - true if verified, false otherwise
|
|
149
|
+
*/
|
|
150
|
+
isVerified(): Promise<boolean>;
|
|
151
|
+
/**
|
|
152
|
+
* Get full verification status from server
|
|
153
|
+
* Requires userId to be set in config
|
|
154
|
+
* @returns Promise<VerificationStatus> - full verification details
|
|
155
|
+
*/
|
|
156
|
+
getVerificationStatus(): Promise<VerificationStatus>;
|
|
133
157
|
on(event: EventType, callback: EventCallback): void;
|
|
134
158
|
off(event: EventType, callback: EventCallback): void;
|
|
135
159
|
/**
|
|
@@ -139,6 +163,11 @@ declare class FollowGateClient {
|
|
|
139
163
|
private getStorageKey;
|
|
140
164
|
private restoreSession;
|
|
141
165
|
private saveCompletedActions;
|
|
166
|
+
/**
|
|
167
|
+
* Save completion to server for verification
|
|
168
|
+
* Only works if userId is set in config
|
|
169
|
+
*/
|
|
170
|
+
private saveCompletion;
|
|
142
171
|
private buildIntentUrl;
|
|
143
172
|
private buildTwitterUrl;
|
|
144
173
|
private buildBlueskyUrl;
|
|
@@ -148,4 +177,4 @@ declare class FollowGateClient {
|
|
|
148
177
|
}
|
|
149
178
|
declare const FollowGate: FollowGateClient;
|
|
150
179
|
|
|
151
|
-
export { type CompleteOptions, type EventCallback, type EventType, FollowGate, FollowGateClient, type FollowGateConfig, FollowGateError, type Platform, type SocialAction, type TwitterConfig, type UnlockStatus, type UserInfo };
|
|
180
|
+
export { type CompleteOptions, type EventCallback, type EventType, FollowGate, FollowGateClient, type FollowGateConfig, FollowGateError, type Platform, type SocialAction, type TwitterConfig, type UnlockStatus, type UserInfo, type VerificationStatus };
|
package/dist/index.d.ts
CHANGED
|
@@ -66,6 +66,18 @@ interface UnlockStatus {
|
|
|
66
66
|
username?: string;
|
|
67
67
|
completedActions?: CompleteOptions[];
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Server-side verification status
|
|
71
|
+
*/
|
|
72
|
+
interface VerificationStatus {
|
|
73
|
+
verified: boolean;
|
|
74
|
+
userId: string;
|
|
75
|
+
completedAt?: string;
|
|
76
|
+
actions?: Array<{
|
|
77
|
+
action: string;
|
|
78
|
+
target: string;
|
|
79
|
+
}>;
|
|
80
|
+
}
|
|
69
81
|
/**
|
|
70
82
|
* FollowGate SDK Client
|
|
71
83
|
*/
|
|
@@ -130,6 +142,18 @@ declare class FollowGateClient {
|
|
|
130
142
|
isUnlocked(): boolean;
|
|
131
143
|
getUnlockStatus(): UnlockStatus;
|
|
132
144
|
getCompletedActions(): CompleteOptions[];
|
|
145
|
+
/**
|
|
146
|
+
* Check if user is verified server-side
|
|
147
|
+
* Requires userId to be set in config
|
|
148
|
+
* @returns Promise<boolean> - true if verified, false otherwise
|
|
149
|
+
*/
|
|
150
|
+
isVerified(): Promise<boolean>;
|
|
151
|
+
/**
|
|
152
|
+
* Get full verification status from server
|
|
153
|
+
* Requires userId to be set in config
|
|
154
|
+
* @returns Promise<VerificationStatus> - full verification details
|
|
155
|
+
*/
|
|
156
|
+
getVerificationStatus(): Promise<VerificationStatus>;
|
|
133
157
|
on(event: EventType, callback: EventCallback): void;
|
|
134
158
|
off(event: EventType, callback: EventCallback): void;
|
|
135
159
|
/**
|
|
@@ -139,6 +163,11 @@ declare class FollowGateClient {
|
|
|
139
163
|
private getStorageKey;
|
|
140
164
|
private restoreSession;
|
|
141
165
|
private saveCompletedActions;
|
|
166
|
+
/**
|
|
167
|
+
* Save completion to server for verification
|
|
168
|
+
* Only works if userId is set in config
|
|
169
|
+
*/
|
|
170
|
+
private saveCompletion;
|
|
142
171
|
private buildIntentUrl;
|
|
143
172
|
private buildTwitterUrl;
|
|
144
173
|
private buildBlueskyUrl;
|
|
@@ -148,4 +177,4 @@ declare class FollowGateClient {
|
|
|
148
177
|
}
|
|
149
178
|
declare const FollowGate: FollowGateClient;
|
|
150
179
|
|
|
151
|
-
export { type CompleteOptions, type EventCallback, type EventType, FollowGate, FollowGateClient, type FollowGateConfig, FollowGateError, type Platform, type SocialAction, type TwitterConfig, type UnlockStatus, type UserInfo };
|
|
180
|
+
export { type CompleteOptions, type EventCallback, type EventType, FollowGate, FollowGateClient, type FollowGateConfig, FollowGateError, type Platform, type SocialAction, type TwitterConfig, type UnlockStatus, type UserInfo, type VerificationStatus };
|
package/dist/index.js
CHANGED
|
@@ -307,6 +307,45 @@ var MODAL_STYLES = `
|
|
|
307
307
|
height: 20px;
|
|
308
308
|
color: #fbbf24;
|
|
309
309
|
flex-shrink: 0;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.fg-verify-box {
|
|
313
|
+
background: rgba(59, 130, 246, 0.05);
|
|
314
|
+
border: 1px solid rgba(96, 165, 250, 0.5);
|
|
315
|
+
border-radius: 12px;
|
|
316
|
+
padding: 12px 16px;
|
|
317
|
+
margin-bottom: 8px;
|
|
318
|
+
display: flex;
|
|
319
|
+
align-items: center;
|
|
320
|
+
justify-content: space-between;
|
|
321
|
+
gap: 12px;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.fg-verify-box-left {
|
|
325
|
+
display: flex;
|
|
326
|
+
align-items: center;
|
|
327
|
+
gap: 8px;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.fg-verify-spinner {
|
|
331
|
+
width: 20px;
|
|
332
|
+
height: 20px;
|
|
333
|
+
border: 2px solid rgba(96, 165, 250, 0.3);
|
|
334
|
+
border-top-color: #60a5fa;
|
|
335
|
+
border-radius: 50%;
|
|
336
|
+
animation: fg-spin 1s linear infinite;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.fg-verify-text {
|
|
340
|
+
font-size: 14px;
|
|
341
|
+
color: #93c5fd;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.fg-verify-hint {
|
|
345
|
+
font-size: 12px;
|
|
346
|
+
color: #64748b;
|
|
347
|
+
text-align: center;
|
|
348
|
+
margin-bottom: 16px;
|
|
310
349
|
margin-top: 2px;
|
|
311
350
|
}
|
|
312
351
|
|
|
@@ -784,8 +823,22 @@ var FollowGateClient = class {
|
|
|
784
823
|
renderConfirmStep() {
|
|
785
824
|
const content = this.getContentElement();
|
|
786
825
|
if (!content) return;
|
|
826
|
+
const username = this.currentUser?.username;
|
|
787
827
|
content.innerHTML = `
|
|
788
828
|
<h2 class="fg-title">Almost done!</h2>
|
|
829
|
+
<div class="fg-verify-box">
|
|
830
|
+
<div class="fg-verify-box-left">
|
|
831
|
+
<div class="fg-verify-spinner"></div>
|
|
832
|
+
<span class="fg-verify-text">Verifying follow & repost</span>
|
|
833
|
+
</div>
|
|
834
|
+
${username ? `
|
|
835
|
+
<div class="fg-user-badge" style="margin: 0;">
|
|
836
|
+
<div class="fg-user-badge-dot" style="background: #facc15;"></div>
|
|
837
|
+
<span class="fg-user-badge-text">@${username}</span>
|
|
838
|
+
</div>
|
|
839
|
+
` : ""}
|
|
840
|
+
</div>
|
|
841
|
+
<p class="fg-verify-hint">Verification may take some time</p>
|
|
789
842
|
<div class="fg-warning-box">
|
|
790
843
|
${ICONS.warning}
|
|
791
844
|
<p><strong>Note:</strong> Access may be revoked if actions are not completed.</p>
|
|
@@ -865,7 +918,10 @@ var FollowGateClient = class {
|
|
|
865
918
|
platform
|
|
866
919
|
};
|
|
867
920
|
if (typeof localStorage !== "undefined") {
|
|
868
|
-
localStorage.setItem(
|
|
921
|
+
localStorage.setItem(
|
|
922
|
+
this.getStorageKey("followgate_user"),
|
|
923
|
+
JSON.stringify(this.currentUser)
|
|
924
|
+
);
|
|
869
925
|
}
|
|
870
926
|
if (this.config.debug) {
|
|
871
927
|
console.log("[FollowGate] Username set:", normalizedUsername);
|
|
@@ -959,6 +1015,7 @@ var FollowGateClient = class {
|
|
|
959
1015
|
if (typeof localStorage !== "undefined") {
|
|
960
1016
|
localStorage.setItem(this.getStorageKey("followgate_unlocked"), "true");
|
|
961
1017
|
}
|
|
1018
|
+
await this.saveCompletion();
|
|
962
1019
|
await this.trackEvent("gate_unlocked", {
|
|
963
1020
|
username: this.currentUser?.username,
|
|
964
1021
|
actions: this.completedActions
|
|
@@ -986,6 +1043,93 @@ var FollowGateClient = class {
|
|
|
986
1043
|
return [...this.completedActions];
|
|
987
1044
|
}
|
|
988
1045
|
// ============================================
|
|
1046
|
+
// Server-Side Verification
|
|
1047
|
+
// ============================================
|
|
1048
|
+
/**
|
|
1049
|
+
* Check if user is verified server-side
|
|
1050
|
+
* Requires userId to be set in config
|
|
1051
|
+
* @returns Promise<boolean> - true if verified, false otherwise
|
|
1052
|
+
*/
|
|
1053
|
+
async isVerified() {
|
|
1054
|
+
if (!this.config) {
|
|
1055
|
+
throw new Error("[FollowGate] SDK not initialized. Call init() first.");
|
|
1056
|
+
}
|
|
1057
|
+
if (!this.config.userId) {
|
|
1058
|
+
if (this.config.debug) {
|
|
1059
|
+
console.warn("[FollowGate] isVerified() requires userId to be set");
|
|
1060
|
+
}
|
|
1061
|
+
return false;
|
|
1062
|
+
}
|
|
1063
|
+
try {
|
|
1064
|
+
const response = await fetch(
|
|
1065
|
+
`${this.config.apiUrl}/api/v1/completions/verify/${encodeURIComponent(this.config.userId)}`,
|
|
1066
|
+
{
|
|
1067
|
+
headers: {
|
|
1068
|
+
Authorization: `Bearer ${this.config.apiKey}`
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
);
|
|
1072
|
+
if (!response.ok) {
|
|
1073
|
+
if (this.config.debug) {
|
|
1074
|
+
console.warn(
|
|
1075
|
+
"[FollowGate] Verification check failed:",
|
|
1076
|
+
response.status
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1079
|
+
return false;
|
|
1080
|
+
}
|
|
1081
|
+
const data = await response.json();
|
|
1082
|
+
return data.data?.verified === true;
|
|
1083
|
+
} catch (error) {
|
|
1084
|
+
if (this.config.debug) {
|
|
1085
|
+
console.warn("[FollowGate] Verification check error:", error);
|
|
1086
|
+
}
|
|
1087
|
+
return false;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Get full verification status from server
|
|
1092
|
+
* Requires userId to be set in config
|
|
1093
|
+
* @returns Promise<VerificationStatus> - full verification details
|
|
1094
|
+
*/
|
|
1095
|
+
async getVerificationStatus() {
|
|
1096
|
+
if (!this.config) {
|
|
1097
|
+
throw new Error("[FollowGate] SDK not initialized. Call init() first.");
|
|
1098
|
+
}
|
|
1099
|
+
if (!this.config.userId) {
|
|
1100
|
+
return {
|
|
1101
|
+
verified: false,
|
|
1102
|
+
userId: ""
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
try {
|
|
1106
|
+
const response = await fetch(
|
|
1107
|
+
`${this.config.apiUrl}/api/v1/completions/verify/${encodeURIComponent(this.config.userId)}`,
|
|
1108
|
+
{
|
|
1109
|
+
headers: {
|
|
1110
|
+
Authorization: `Bearer ${this.config.apiKey}`
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
);
|
|
1114
|
+
if (!response.ok) {
|
|
1115
|
+
return {
|
|
1116
|
+
verified: false,
|
|
1117
|
+
userId: this.config.userId
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
const data = await response.json();
|
|
1121
|
+
return data.data;
|
|
1122
|
+
} catch (error) {
|
|
1123
|
+
if (this.config.debug) {
|
|
1124
|
+
console.warn("[FollowGate] getVerificationStatus error:", error);
|
|
1125
|
+
}
|
|
1126
|
+
return {
|
|
1127
|
+
verified: false,
|
|
1128
|
+
userId: this.config.userId
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
// ============================================
|
|
989
1133
|
// Event System
|
|
990
1134
|
// ============================================
|
|
991
1135
|
on(event, callback) {
|
|
@@ -1012,7 +1156,9 @@ var FollowGateClient = class {
|
|
|
1012
1156
|
}
|
|
1013
1157
|
restoreSession() {
|
|
1014
1158
|
if (typeof localStorage === "undefined") return;
|
|
1015
|
-
const userJson = localStorage.getItem(
|
|
1159
|
+
const userJson = localStorage.getItem(
|
|
1160
|
+
this.getStorageKey("followgate_user")
|
|
1161
|
+
);
|
|
1016
1162
|
if (userJson) {
|
|
1017
1163
|
try {
|
|
1018
1164
|
this.currentUser = JSON.parse(userJson);
|
|
@@ -1020,7 +1166,9 @@ var FollowGateClient = class {
|
|
|
1020
1166
|
localStorage.removeItem(this.getStorageKey("followgate_user"));
|
|
1021
1167
|
}
|
|
1022
1168
|
}
|
|
1023
|
-
const actionsJson = localStorage.getItem(
|
|
1169
|
+
const actionsJson = localStorage.getItem(
|
|
1170
|
+
this.getStorageKey("followgate_actions")
|
|
1171
|
+
);
|
|
1024
1172
|
if (actionsJson) {
|
|
1025
1173
|
try {
|
|
1026
1174
|
this.completedActions = JSON.parse(actionsJson);
|
|
@@ -1037,6 +1185,46 @@ var FollowGateClient = class {
|
|
|
1037
1185
|
);
|
|
1038
1186
|
}
|
|
1039
1187
|
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Save completion to server for verification
|
|
1190
|
+
* Only works if userId is set in config
|
|
1191
|
+
*/
|
|
1192
|
+
async saveCompletion() {
|
|
1193
|
+
if (!this.config?.userId) {
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
try {
|
|
1197
|
+
const response = await fetch(`${this.config.apiUrl}/api/v1/completions`, {
|
|
1198
|
+
method: "POST",
|
|
1199
|
+
headers: {
|
|
1200
|
+
"Content-Type": "application/json",
|
|
1201
|
+
Authorization: `Bearer ${this.config.apiKey}`
|
|
1202
|
+
},
|
|
1203
|
+
body: JSON.stringify({
|
|
1204
|
+
userId: this.config.userId,
|
|
1205
|
+
platform: this.currentUser?.platform || "twitter",
|
|
1206
|
+
actions: this.completedActions.map((a) => ({
|
|
1207
|
+
action: a.action,
|
|
1208
|
+
target: a.target
|
|
1209
|
+
}))
|
|
1210
|
+
})
|
|
1211
|
+
});
|
|
1212
|
+
if (this.config.debug) {
|
|
1213
|
+
if (response.ok) {
|
|
1214
|
+
console.log("[FollowGate] Completion saved to server");
|
|
1215
|
+
} else {
|
|
1216
|
+
console.warn(
|
|
1217
|
+
"[FollowGate] Failed to save completion:",
|
|
1218
|
+
response.status
|
|
1219
|
+
);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
} catch (error) {
|
|
1223
|
+
if (this.config.debug) {
|
|
1224
|
+
console.warn("[FollowGate] Failed to save completion:", error);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1040
1228
|
buildIntentUrl(options) {
|
|
1041
1229
|
const { platform, action, target } = options;
|
|
1042
1230
|
switch (platform) {
|
package/dist/index.mjs
CHANGED
|
@@ -281,6 +281,45 @@ var MODAL_STYLES = `
|
|
|
281
281
|
height: 20px;
|
|
282
282
|
color: #fbbf24;
|
|
283
283
|
flex-shrink: 0;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.fg-verify-box {
|
|
287
|
+
background: rgba(59, 130, 246, 0.05);
|
|
288
|
+
border: 1px solid rgba(96, 165, 250, 0.5);
|
|
289
|
+
border-radius: 12px;
|
|
290
|
+
padding: 12px 16px;
|
|
291
|
+
margin-bottom: 8px;
|
|
292
|
+
display: flex;
|
|
293
|
+
align-items: center;
|
|
294
|
+
justify-content: space-between;
|
|
295
|
+
gap: 12px;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.fg-verify-box-left {
|
|
299
|
+
display: flex;
|
|
300
|
+
align-items: center;
|
|
301
|
+
gap: 8px;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.fg-verify-spinner {
|
|
305
|
+
width: 20px;
|
|
306
|
+
height: 20px;
|
|
307
|
+
border: 2px solid rgba(96, 165, 250, 0.3);
|
|
308
|
+
border-top-color: #60a5fa;
|
|
309
|
+
border-radius: 50%;
|
|
310
|
+
animation: fg-spin 1s linear infinite;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.fg-verify-text {
|
|
314
|
+
font-size: 14px;
|
|
315
|
+
color: #93c5fd;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.fg-verify-hint {
|
|
319
|
+
font-size: 12px;
|
|
320
|
+
color: #64748b;
|
|
321
|
+
text-align: center;
|
|
322
|
+
margin-bottom: 16px;
|
|
284
323
|
margin-top: 2px;
|
|
285
324
|
}
|
|
286
325
|
|
|
@@ -758,8 +797,22 @@ var FollowGateClient = class {
|
|
|
758
797
|
renderConfirmStep() {
|
|
759
798
|
const content = this.getContentElement();
|
|
760
799
|
if (!content) return;
|
|
800
|
+
const username = this.currentUser?.username;
|
|
761
801
|
content.innerHTML = `
|
|
762
802
|
<h2 class="fg-title">Almost done!</h2>
|
|
803
|
+
<div class="fg-verify-box">
|
|
804
|
+
<div class="fg-verify-box-left">
|
|
805
|
+
<div class="fg-verify-spinner"></div>
|
|
806
|
+
<span class="fg-verify-text">Verifying follow & repost</span>
|
|
807
|
+
</div>
|
|
808
|
+
${username ? `
|
|
809
|
+
<div class="fg-user-badge" style="margin: 0;">
|
|
810
|
+
<div class="fg-user-badge-dot" style="background: #facc15;"></div>
|
|
811
|
+
<span class="fg-user-badge-text">@${username}</span>
|
|
812
|
+
</div>
|
|
813
|
+
` : ""}
|
|
814
|
+
</div>
|
|
815
|
+
<p class="fg-verify-hint">Verification may take some time</p>
|
|
763
816
|
<div class="fg-warning-box">
|
|
764
817
|
${ICONS.warning}
|
|
765
818
|
<p><strong>Note:</strong> Access may be revoked if actions are not completed.</p>
|
|
@@ -839,7 +892,10 @@ var FollowGateClient = class {
|
|
|
839
892
|
platform
|
|
840
893
|
};
|
|
841
894
|
if (typeof localStorage !== "undefined") {
|
|
842
|
-
localStorage.setItem(
|
|
895
|
+
localStorage.setItem(
|
|
896
|
+
this.getStorageKey("followgate_user"),
|
|
897
|
+
JSON.stringify(this.currentUser)
|
|
898
|
+
);
|
|
843
899
|
}
|
|
844
900
|
if (this.config.debug) {
|
|
845
901
|
console.log("[FollowGate] Username set:", normalizedUsername);
|
|
@@ -933,6 +989,7 @@ var FollowGateClient = class {
|
|
|
933
989
|
if (typeof localStorage !== "undefined") {
|
|
934
990
|
localStorage.setItem(this.getStorageKey("followgate_unlocked"), "true");
|
|
935
991
|
}
|
|
992
|
+
await this.saveCompletion();
|
|
936
993
|
await this.trackEvent("gate_unlocked", {
|
|
937
994
|
username: this.currentUser?.username,
|
|
938
995
|
actions: this.completedActions
|
|
@@ -960,6 +1017,93 @@ var FollowGateClient = class {
|
|
|
960
1017
|
return [...this.completedActions];
|
|
961
1018
|
}
|
|
962
1019
|
// ============================================
|
|
1020
|
+
// Server-Side Verification
|
|
1021
|
+
// ============================================
|
|
1022
|
+
/**
|
|
1023
|
+
* Check if user is verified server-side
|
|
1024
|
+
* Requires userId to be set in config
|
|
1025
|
+
* @returns Promise<boolean> - true if verified, false otherwise
|
|
1026
|
+
*/
|
|
1027
|
+
async isVerified() {
|
|
1028
|
+
if (!this.config) {
|
|
1029
|
+
throw new Error("[FollowGate] SDK not initialized. Call init() first.");
|
|
1030
|
+
}
|
|
1031
|
+
if (!this.config.userId) {
|
|
1032
|
+
if (this.config.debug) {
|
|
1033
|
+
console.warn("[FollowGate] isVerified() requires userId to be set");
|
|
1034
|
+
}
|
|
1035
|
+
return false;
|
|
1036
|
+
}
|
|
1037
|
+
try {
|
|
1038
|
+
const response = await fetch(
|
|
1039
|
+
`${this.config.apiUrl}/api/v1/completions/verify/${encodeURIComponent(this.config.userId)}`,
|
|
1040
|
+
{
|
|
1041
|
+
headers: {
|
|
1042
|
+
Authorization: `Bearer ${this.config.apiKey}`
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
);
|
|
1046
|
+
if (!response.ok) {
|
|
1047
|
+
if (this.config.debug) {
|
|
1048
|
+
console.warn(
|
|
1049
|
+
"[FollowGate] Verification check failed:",
|
|
1050
|
+
response.status
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
return false;
|
|
1054
|
+
}
|
|
1055
|
+
const data = await response.json();
|
|
1056
|
+
return data.data?.verified === true;
|
|
1057
|
+
} catch (error) {
|
|
1058
|
+
if (this.config.debug) {
|
|
1059
|
+
console.warn("[FollowGate] Verification check error:", error);
|
|
1060
|
+
}
|
|
1061
|
+
return false;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* Get full verification status from server
|
|
1066
|
+
* Requires userId to be set in config
|
|
1067
|
+
* @returns Promise<VerificationStatus> - full verification details
|
|
1068
|
+
*/
|
|
1069
|
+
async getVerificationStatus() {
|
|
1070
|
+
if (!this.config) {
|
|
1071
|
+
throw new Error("[FollowGate] SDK not initialized. Call init() first.");
|
|
1072
|
+
}
|
|
1073
|
+
if (!this.config.userId) {
|
|
1074
|
+
return {
|
|
1075
|
+
verified: false,
|
|
1076
|
+
userId: ""
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
try {
|
|
1080
|
+
const response = await fetch(
|
|
1081
|
+
`${this.config.apiUrl}/api/v1/completions/verify/${encodeURIComponent(this.config.userId)}`,
|
|
1082
|
+
{
|
|
1083
|
+
headers: {
|
|
1084
|
+
Authorization: `Bearer ${this.config.apiKey}`
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
);
|
|
1088
|
+
if (!response.ok) {
|
|
1089
|
+
return {
|
|
1090
|
+
verified: false,
|
|
1091
|
+
userId: this.config.userId
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
const data = await response.json();
|
|
1095
|
+
return data.data;
|
|
1096
|
+
} catch (error) {
|
|
1097
|
+
if (this.config.debug) {
|
|
1098
|
+
console.warn("[FollowGate] getVerificationStatus error:", error);
|
|
1099
|
+
}
|
|
1100
|
+
return {
|
|
1101
|
+
verified: false,
|
|
1102
|
+
userId: this.config.userId
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
// ============================================
|
|
963
1107
|
// Event System
|
|
964
1108
|
// ============================================
|
|
965
1109
|
on(event, callback) {
|
|
@@ -986,7 +1130,9 @@ var FollowGateClient = class {
|
|
|
986
1130
|
}
|
|
987
1131
|
restoreSession() {
|
|
988
1132
|
if (typeof localStorage === "undefined") return;
|
|
989
|
-
const userJson = localStorage.getItem(
|
|
1133
|
+
const userJson = localStorage.getItem(
|
|
1134
|
+
this.getStorageKey("followgate_user")
|
|
1135
|
+
);
|
|
990
1136
|
if (userJson) {
|
|
991
1137
|
try {
|
|
992
1138
|
this.currentUser = JSON.parse(userJson);
|
|
@@ -994,7 +1140,9 @@ var FollowGateClient = class {
|
|
|
994
1140
|
localStorage.removeItem(this.getStorageKey("followgate_user"));
|
|
995
1141
|
}
|
|
996
1142
|
}
|
|
997
|
-
const actionsJson = localStorage.getItem(
|
|
1143
|
+
const actionsJson = localStorage.getItem(
|
|
1144
|
+
this.getStorageKey("followgate_actions")
|
|
1145
|
+
);
|
|
998
1146
|
if (actionsJson) {
|
|
999
1147
|
try {
|
|
1000
1148
|
this.completedActions = JSON.parse(actionsJson);
|
|
@@ -1011,6 +1159,46 @@ var FollowGateClient = class {
|
|
|
1011
1159
|
);
|
|
1012
1160
|
}
|
|
1013
1161
|
}
|
|
1162
|
+
/**
|
|
1163
|
+
* Save completion to server for verification
|
|
1164
|
+
* Only works if userId is set in config
|
|
1165
|
+
*/
|
|
1166
|
+
async saveCompletion() {
|
|
1167
|
+
if (!this.config?.userId) {
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
try {
|
|
1171
|
+
const response = await fetch(`${this.config.apiUrl}/api/v1/completions`, {
|
|
1172
|
+
method: "POST",
|
|
1173
|
+
headers: {
|
|
1174
|
+
"Content-Type": "application/json",
|
|
1175
|
+
Authorization: `Bearer ${this.config.apiKey}`
|
|
1176
|
+
},
|
|
1177
|
+
body: JSON.stringify({
|
|
1178
|
+
userId: this.config.userId,
|
|
1179
|
+
platform: this.currentUser?.platform || "twitter",
|
|
1180
|
+
actions: this.completedActions.map((a) => ({
|
|
1181
|
+
action: a.action,
|
|
1182
|
+
target: a.target
|
|
1183
|
+
}))
|
|
1184
|
+
})
|
|
1185
|
+
});
|
|
1186
|
+
if (this.config.debug) {
|
|
1187
|
+
if (response.ok) {
|
|
1188
|
+
console.log("[FollowGate] Completion saved to server");
|
|
1189
|
+
} else {
|
|
1190
|
+
console.warn(
|
|
1191
|
+
"[FollowGate] Failed to save completion:",
|
|
1192
|
+
response.status
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
} catch (error) {
|
|
1197
|
+
if (this.config.debug) {
|
|
1198
|
+
console.warn("[FollowGate] Failed to save completion:", error);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1014
1202
|
buildIntentUrl(options) {
|
|
1015
1203
|
const { platform, action, target } = options;
|
|
1016
1204
|
switch (platform) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@followgate/js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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",
|