shogun-button-react 1.10.2 → 1.11.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.
@@ -0,0 +1,501 @@
1
+ import React from 'react';
2
+ import { BasePlugin } from "shogun-core";
3
+ export class GunAdvancedPlugin extends BasePlugin {
4
+ constructor(core, config = {}) {
5
+ super();
6
+ this.name = "gun-advanced";
7
+ this.connectionMonitors = new Map();
8
+ this.collectionCache = new Map();
9
+ this.core = core;
10
+ this.config = {
11
+ enableDebug: true,
12
+ enableConnectionMonitoring: true,
13
+ defaultPageSize: 20,
14
+ connectionTimeout: 10000,
15
+ debounceInterval: 100,
16
+ ...config
17
+ };
18
+ this.debugEnabled = this.config.enableDebug;
19
+ }
20
+ setDebugEnabled(enabled) {
21
+ this.debugEnabled = enabled;
22
+ this.log('Debug mode:', enabled ? 'enabled' : 'disabled');
23
+ }
24
+ log(...args) {
25
+ if (this.debugEnabled) {
26
+ console.log('[GunAdvancedPlugin]', ...args);
27
+ }
28
+ }
29
+ createHooks() {
30
+ return {
31
+ useGunState: this.useGunState.bind(this),
32
+ useGunCollection: this.useGunCollection.bind(this),
33
+ useGunConnection: this.useGunConnection.bind(this),
34
+ useGunDebug: this.useGunDebug.bind(this),
35
+ useGunRealtime: this.useGunRealtime.bind(this),
36
+ };
37
+ }
38
+ useGunState(path, defaultValue) {
39
+ const [data, setData] = React.useState(defaultValue || null);
40
+ const [isLoading, setIsLoading] = React.useState(true);
41
+ const [error, setError] = React.useState(null);
42
+ const isMounted = React.useRef(true);
43
+ React.useEffect(() => {
44
+ var _a;
45
+ if (!((_a = this.core) === null || _a === void 0 ? void 0 : _a.gun) || !this.core.isLoggedIn())
46
+ return;
47
+ setIsLoading(true);
48
+ setError(null);
49
+ const chain = this.core.gun.get(path);
50
+ const handler = (item) => {
51
+ if (!isMounted.current)
52
+ return;
53
+ if (item) {
54
+ setData(item);
55
+ setIsLoading(false);
56
+ setError(null);
57
+ this.log(`State updated for ${path}:`, item);
58
+ }
59
+ };
60
+ chain.on(handler);
61
+ const timeoutId = setTimeout(() => {
62
+ if (isLoading) {
63
+ setError('Connection timeout - no data received');
64
+ setIsLoading(false);
65
+ }
66
+ }, this.config.connectionTimeout);
67
+ return () => {
68
+ isMounted.current = false;
69
+ chain.off();
70
+ clearTimeout(timeoutId);
71
+ };
72
+ }, [path]);
73
+ const update = async (updates) => {
74
+ var _a;
75
+ if (!((_a = this.core) === null || _a === void 0 ? void 0 : _a.gun) || !this.core.isLoggedIn()) {
76
+ throw new Error('SDK not initialized or user not logged in');
77
+ }
78
+ try {
79
+ setError(null);
80
+ const newData = { ...data, ...updates };
81
+ await new Promise((resolve, reject) => {
82
+ this.core.gun.get(path).put(newData, (ack) => {
83
+ if (ack.err) {
84
+ reject(new Error(ack.err));
85
+ }
86
+ else {
87
+ resolve();
88
+ }
89
+ });
90
+ });
91
+ this.log(`State updated for ${path}:`, newData);
92
+ }
93
+ catch (err) {
94
+ const errorMsg = err.message || 'Failed to update state';
95
+ setError(errorMsg);
96
+ this.log(`Error updating state for ${path}:`, errorMsg);
97
+ throw err;
98
+ }
99
+ };
100
+ const set = async (newData) => {
101
+ var _a;
102
+ if (!((_a = this.core) === null || _a === void 0 ? void 0 : _a.gun) || !this.core.isLoggedIn()) {
103
+ throw new Error('SDK not initialized or user not logged in');
104
+ }
105
+ try {
106
+ setError(null);
107
+ await new Promise((resolve, reject) => {
108
+ this.core.gun.get(path).put(newData, (ack) => {
109
+ if (ack.err) {
110
+ reject(new Error(ack.err));
111
+ }
112
+ else {
113
+ resolve();
114
+ }
115
+ });
116
+ });
117
+ this.log(`State set for ${path}:`, newData);
118
+ }
119
+ catch (err) {
120
+ const errorMsg = err.message || 'Failed to set state';
121
+ setError(errorMsg);
122
+ this.log(`Error setting state for ${path}:`, errorMsg);
123
+ throw err;
124
+ }
125
+ };
126
+ const remove = async () => {
127
+ var _a;
128
+ if (!((_a = this.core) === null || _a === void 0 ? void 0 : _a.gun) || !this.core.isLoggedIn()) {
129
+ throw new Error('SDK not initialized or user not logged in');
130
+ }
131
+ try {
132
+ setError(null);
133
+ await new Promise((resolve, reject) => {
134
+ this.core.gun.get(path).put(null, (ack) => {
135
+ if (ack.err) {
136
+ reject(new Error(ack.err));
137
+ }
138
+ else {
139
+ resolve();
140
+ }
141
+ });
142
+ });
143
+ setData(null);
144
+ this.log(`State removed for ${path}`);
145
+ }
146
+ catch (err) {
147
+ const errorMsg = err.message || 'Failed to remove state';
148
+ setError(errorMsg);
149
+ this.log(`Error removing state for ${path}:`, errorMsg);
150
+ throw err;
151
+ }
152
+ };
153
+ const refresh = () => {
154
+ var _a;
155
+ setIsLoading(true);
156
+ setError(null);
157
+ if ((_a = this.core) === null || _a === void 0 ? void 0 : _a.gun) {
158
+ this.core.gun.get(path).once(() => { });
159
+ }
160
+ };
161
+ return {
162
+ data,
163
+ isLoading,
164
+ error,
165
+ update,
166
+ set,
167
+ remove,
168
+ refresh
169
+ };
170
+ }
171
+ useGunCollection(path, options = {}) {
172
+ const { pageSize = this.config.defaultPageSize, sortBy, sortOrder = 'desc', filter, enableRealtime = true } = options;
173
+ const [items, setItems] = React.useState([]);
174
+ const [currentPage, setCurrentPage] = React.useState(0);
175
+ const [isLoading, setIsLoading] = React.useState(true);
176
+ const [error, setError] = React.useState(null);
177
+ const [allItems, setAllItems] = React.useState([]);
178
+ const isMounted = React.useRef(true);
179
+ const cacheKey = `${path}-${JSON.stringify(options)}`;
180
+ React.useEffect(() => {
181
+ var _a;
182
+ if (!((_a = this.core) === null || _a === void 0 ? void 0 : _a.gun) || !this.core.isLoggedIn())
183
+ return;
184
+ setIsLoading(true);
185
+ setError(null);
186
+ const chain = this.core.gun.get(path);
187
+ const tempItems = [];
188
+ const processItems = (items) => {
189
+ if (!isMounted.current)
190
+ return;
191
+ let processedItems = filter ? items.filter(filter) : items;
192
+ if (sortBy) {
193
+ processedItems = [...processedItems].sort((a, b) => {
194
+ if (typeof sortBy === 'function') {
195
+ return sortOrder === 'asc' ? sortBy(a, b) : sortBy(b, a);
196
+ }
197
+ const aVal = a[sortBy];
198
+ const bVal = b[sortBy];
199
+ let comparison = 0;
200
+ if (aVal < bVal)
201
+ comparison = -1;
202
+ else if (aVal > bVal)
203
+ comparison = 1;
204
+ return sortOrder === 'asc' ? comparison : -comparison;
205
+ });
206
+ }
207
+ const startIndex = currentPage * pageSize;
208
+ const endIndex = startIndex + pageSize;
209
+ const pageItems = processedItems.slice(startIndex, endIndex);
210
+ setAllItems(processedItems);
211
+ setItems(pageItems);
212
+ setIsLoading(false);
213
+ setError(null);
214
+ this.collectionCache.set(cacheKey, {
215
+ items: processedItems,
216
+ timestamp: Date.now()
217
+ });
218
+ };
219
+ const mapHandler = (item, key) => {
220
+ if (item) {
221
+ const itemWithKey = { ...item, _key: key };
222
+ tempItems.push(itemWithKey);
223
+ if (enableRealtime) {
224
+ processItems(tempItems);
225
+ }
226
+ }
227
+ };
228
+ chain.map().on(mapHandler);
229
+ const timeoutId = setTimeout(() => {
230
+ if (isLoading) {
231
+ setError('Connection timeout - no data received');
232
+ setIsLoading(false);
233
+ }
234
+ }, this.config.connectionTimeout);
235
+ return () => {
236
+ isMounted.current = false;
237
+ chain.off();
238
+ clearTimeout(timeoutId);
239
+ };
240
+ }, [path, currentPage, pageSize, sortBy, sortOrder, filter, enableRealtime]);
241
+ const totalPages = Math.ceil(allItems.length / pageSize);
242
+ const hasNextPage = currentPage < totalPages - 1;
243
+ const hasPrevPage = currentPage > 0;
244
+ const nextPage = () => {
245
+ if (hasNextPage)
246
+ setCurrentPage(prev => prev + 1);
247
+ };
248
+ const prevPage = () => {
249
+ if (hasPrevPage)
250
+ setCurrentPage(prev => prev - 1);
251
+ };
252
+ const goToPage = (page) => {
253
+ if (page >= 0 && page < totalPages)
254
+ setCurrentPage(page);
255
+ };
256
+ const refresh = () => {
257
+ var _a;
258
+ setIsLoading(true);
259
+ setError(null);
260
+ this.collectionCache.delete(cacheKey);
261
+ if ((_a = this.core) === null || _a === void 0 ? void 0 : _a.gun) {
262
+ this.core.gun.get(path).once(() => { });
263
+ }
264
+ };
265
+ const addItem = async (item) => {
266
+ var _a;
267
+ if (!((_a = this.core) === null || _a === void 0 ? void 0 : _a.gun) || !this.core.isLoggedIn()) {
268
+ throw new Error('SDK not initialized or user not logged in');
269
+ }
270
+ try {
271
+ setError(null);
272
+ await new Promise((resolve, reject) => {
273
+ this.core.gun.get(path).set(item, (ack) => {
274
+ if (ack.err) {
275
+ reject(new Error(ack.err));
276
+ }
277
+ else {
278
+ resolve();
279
+ }
280
+ });
281
+ });
282
+ this.log(`Item added to collection ${path}:`, item);
283
+ refresh();
284
+ }
285
+ catch (err) {
286
+ const errorMsg = err.message || 'Failed to add item';
287
+ setError(errorMsg);
288
+ this.log(`Error adding item to collection ${path}:`, errorMsg);
289
+ throw err;
290
+ }
291
+ };
292
+ const updateItem = async (id, updates) => {
293
+ var _a;
294
+ if (!((_a = this.core) === null || _a === void 0 ? void 0 : _a.gun) || !this.core.isLoggedIn()) {
295
+ throw new Error('SDK not initialized or user not logged in');
296
+ }
297
+ try {
298
+ setError(null);
299
+ await new Promise((resolve, reject) => {
300
+ this.core.gun.get(path).get(id).put(updates, (ack) => {
301
+ if (ack.err) {
302
+ reject(new Error(ack.err));
303
+ }
304
+ else {
305
+ resolve();
306
+ }
307
+ });
308
+ });
309
+ this.log(`Item updated in collection ${path}:`, { id, updates });
310
+ refresh();
311
+ }
312
+ catch (err) {
313
+ const errorMsg = err.message || 'Failed to update item';
314
+ setError(errorMsg);
315
+ this.log(`Error updating item in collection ${path}:`, errorMsg);
316
+ throw err;
317
+ }
318
+ };
319
+ const removeItem = async (id) => {
320
+ var _a;
321
+ if (!((_a = this.core) === null || _a === void 0 ? void 0 : _a.gun) || !this.core.isLoggedIn()) {
322
+ throw new Error('SDK not initialized or user not logged in');
323
+ }
324
+ try {
325
+ setError(null);
326
+ await new Promise((resolve, reject) => {
327
+ this.core.gun.get(path).get(id).put(null, (ack) => {
328
+ if (ack.err) {
329
+ reject(new Error(ack.err));
330
+ }
331
+ else {
332
+ resolve();
333
+ }
334
+ });
335
+ });
336
+ this.log(`Item removed from collection ${path}:`, id);
337
+ refresh();
338
+ }
339
+ catch (err) {
340
+ const errorMsg = err.message || 'Failed to remove item';
341
+ setError(errorMsg);
342
+ this.log(`Error removing item from collection ${path}:`, errorMsg);
343
+ throw err;
344
+ }
345
+ };
346
+ return {
347
+ items,
348
+ currentPage,
349
+ totalPages,
350
+ hasNextPage,
351
+ hasPrevPage,
352
+ nextPage,
353
+ prevPage,
354
+ goToPage,
355
+ isLoading,
356
+ error,
357
+ refresh,
358
+ addItem,
359
+ updateItem,
360
+ removeItem
361
+ };
362
+ }
363
+ useGunConnection(path) {
364
+ const [isConnected, setIsConnected] = React.useState(false);
365
+ const [lastSeen, setLastSeen] = React.useState(null);
366
+ const [error, setError] = React.useState(null);
367
+ React.useEffect(() => {
368
+ var _a;
369
+ if (!this.config.enableConnectionMonitoring || !((_a = this.core) === null || _a === void 0 ? void 0 : _a.gun) || !this.core.isLoggedIn())
370
+ return;
371
+ let timeoutId;
372
+ const chain = this.core.gun.get(path);
373
+ const resetTimeout = () => {
374
+ clearTimeout(timeoutId);
375
+ timeoutId = window.setTimeout(() => {
376
+ setIsConnected(false);
377
+ setError('Connection timeout');
378
+ this.log(`Connection timeout for ${path}`);
379
+ }, this.config.connectionTimeout);
380
+ };
381
+ const handler = () => {
382
+ setIsConnected(true);
383
+ setLastSeen(new Date());
384
+ setError(null);
385
+ resetTimeout();
386
+ this.log(`Connection active for ${path}`);
387
+ };
388
+ chain.on(handler);
389
+ resetTimeout();
390
+ this.connectionMonitors.set(path, { off: () => chain.off(), timeoutId });
391
+ return () => {
392
+ clearTimeout(timeoutId);
393
+ chain.off();
394
+ this.connectionMonitors.delete(path);
395
+ };
396
+ }, [path]);
397
+ return { isConnected, lastSeen, error };
398
+ }
399
+ useGunDebug(path, enabled = true) {
400
+ React.useEffect(() => {
401
+ var _a;
402
+ if (!enabled || !this.debugEnabled || !((_a = this.core) === null || _a === void 0 ? void 0 : _a.gun) || !this.core.isLoggedIn())
403
+ return;
404
+ this.log(`Debug mode enabled for ${path}`);
405
+ const chain = this.core.gun.get(path);
406
+ const handler = (data, key) => {
407
+ this.log(`[${path}] Update:`, {
408
+ key,
409
+ data,
410
+ timestamp: new Date().toISOString(),
411
+ });
412
+ };
413
+ chain.on(handler);
414
+ return () => {
415
+ chain.off();
416
+ this.log(`Debug mode disabled for ${path}`);
417
+ };
418
+ }, [path, enabled]);
419
+ }
420
+ useGunRealtime(path, callback) {
421
+ const [data, setData] = React.useState(null);
422
+ const [key, setKey] = React.useState(null);
423
+ React.useEffect(() => {
424
+ var _a;
425
+ if (!((_a = this.core) === null || _a === void 0 ? void 0 : _a.gun) || !this.core.isLoggedIn())
426
+ return;
427
+ const chain = this.core.gun.get(path);
428
+ const handler = (item, itemKey) => {
429
+ if (item) {
430
+ setData(item);
431
+ setKey(itemKey);
432
+ if (callback) {
433
+ callback(item, itemKey);
434
+ }
435
+ this.log(`Realtime update for ${path}:`, { data: item, key: itemKey });
436
+ }
437
+ };
438
+ chain.on(handler);
439
+ return () => {
440
+ chain.off();
441
+ };
442
+ }, [path, callback]);
443
+ return { data, key };
444
+ }
445
+ async put(path, data) {
446
+ var _a;
447
+ if (!((_a = this.core) === null || _a === void 0 ? void 0 : _a.gun) || !this.core.isLoggedIn()) {
448
+ throw new Error('SDK not initialized or user not logged in');
449
+ }
450
+ return new Promise((resolve, reject) => {
451
+ this.core.gun.get(path).put(data, (ack) => {
452
+ if (ack.err) {
453
+ reject(new Error(ack.err));
454
+ }
455
+ else {
456
+ resolve();
457
+ }
458
+ });
459
+ });
460
+ }
461
+ get(path) {
462
+ var _a;
463
+ if (!((_a = this.core) === null || _a === void 0 ? void 0 : _a.gun) || !this.core.isLoggedIn())
464
+ return null;
465
+ return this.core.gun.get(path);
466
+ }
467
+ async remove(path) {
468
+ var _a;
469
+ if (!((_a = this.core) === null || _a === void 0 ? void 0 : _a.gun) || !this.core.isLoggedIn()) {
470
+ throw new Error('SDK not initialized or user not logged in');
471
+ }
472
+ return new Promise((resolve, reject) => {
473
+ this.core.gun.get(path).put(null, (ack) => {
474
+ if (ack.err) {
475
+ reject(new Error(ack.err));
476
+ }
477
+ else {
478
+ resolve();
479
+ }
480
+ });
481
+ });
482
+ }
483
+ cleanup() {
484
+ this.connectionMonitors.forEach(({ off, timeoutId }) => {
485
+ clearTimeout(timeoutId);
486
+ if (typeof off === 'function')
487
+ off();
488
+ });
489
+ this.connectionMonitors.clear();
490
+ this.collectionCache.clear();
491
+ this.log('Plugin cleanup completed');
492
+ }
493
+ getStats() {
494
+ return {
495
+ activeConnections: this.connectionMonitors.size,
496
+ cachedCollections: this.collectionCache.size,
497
+ debugEnabled: this.debugEnabled,
498
+ config: this.config
499
+ };
500
+ }
501
+ }
@@ -445,36 +445,10 @@
445
445
  border-radius: 6px;
446
446
  background-color: transparent;
447
447
  transition: all 0.2s ease;
448
- cursor: pointer;
449
- display: inline-block;
450
- text-decoration: none;
451
- font-size: 14px;
452
- line-height: 1.4;
453
- min-height: 44px;
454
- display: flex;
455
- align-items: center;
456
- justify-content: center;
457
448
  }
458
449
 
459
450
  .shogun-prominent-toggle:hover {
460
451
  text-decoration: underline;
461
- background-color: rgba(59, 130, 246, 0.1);
462
- transform: translateY(-1px);
463
- }
464
-
465
- .shogun-prominent-toggle:active {
466
- transform: translateY(0);
467
- }
468
-
469
- .shogun-prominent-toggle:disabled {
470
- opacity: 0.5;
471
- cursor: not-allowed;
472
- pointer-events: none;
473
- }
474
-
475
- .shogun-prominent-toggle:focus {
476
- outline: 2px solid var(--shogun-primary);
477
- outline-offset: 2px;
478
452
  }
479
453
 
480
454
  /* Redundant dark theme styles removed */
@@ -1,4 +1,5 @@
1
1
  import { ShogunCore, IGunInstance } from "shogun-core";
2
+ import { GunAdvancedPlugin } from "../plugins/GunAdvancedPlugin";
2
3
  export interface ShogunConnectorOptions {
3
4
  appName: string;
4
5
  appDescription?: string;
@@ -9,13 +10,9 @@ export interface ShogunConnectorOptions {
9
10
  showNostr?: boolean;
10
11
  showOauth?: boolean;
11
12
  darkMode?: boolean;
12
- websocketSecure?: boolean;
13
- providerUrl?: string | null;
14
13
  peers?: string[];
15
14
  authToken?: string;
16
15
  gunInstance?: IGunInstance<any>;
17
- localStorage?: boolean;
18
- radisk?: boolean;
19
16
  timeouts?: {
20
17
  login?: number;
21
18
  signup?: number;
@@ -28,10 +25,29 @@ export interface ShogunConnectorOptions {
28
25
  redirectUri?: string;
29
26
  }>;
30
27
  };
28
+ webauthn?: {
29
+ enabled?: boolean;
30
+ };
31
+ nostr?: {
32
+ enabled?: boolean;
33
+ };
34
+ web3?: {
35
+ enabled?: boolean;
36
+ };
37
+ localStorage?: boolean;
38
+ radisk?: boolean;
39
+ enableGunDebug?: boolean;
40
+ enableConnectionMonitoring?: boolean;
41
+ defaultPageSize?: number;
42
+ connectionTimeout?: number;
43
+ debounceInterval?: number;
31
44
  }
32
45
  export interface ShogunConnectorResult {
33
46
  core: ShogunCore;
34
47
  options: ShogunConnectorOptions;
48
+ setProvider: (provider: any) => boolean;
49
+ getCurrentProviderUrl: () => string | null;
35
50
  registerPlugin: (plugin: any) => boolean;
36
51
  hasPlugin: (name: string) => boolean;
52
+ gunPlugin: GunAdvancedPlugin;
37
53
  }
@@ -1,2 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "shogun-button-react",
3
3
  "description": "Shogun connector button",
4
- "version": "1.10.2",
4
+ "version": "1.11.0",
5
5
  "files": [
6
6
  "dist",
7
7
  "src/styles/index.css"
@@ -17,6 +17,7 @@
17
17
  ],
18
18
  "author": "Scobru",
19
19
  "license": "MIT",
20
+ "type": "module",
20
21
  "exports": {
21
22
  ".": "./dist/index.js",
22
23
  "./styles.css": "./dist/index.css"
@@ -24,7 +25,7 @@
24
25
  "main": "./dist/index.js",
25
26
  "types": "./dist/index.d.ts",
26
27
  "scripts": {
27
- "build": "tsc && copyfiles -u 1 \"src/styles/index.css\" dist && copyfiles dist/styles/index.css dist",
28
+ "build": "tsc && copyfiles -u 1 \"src/styles/index.css\" dist",
28
29
  "dev": "tsc --watch",
29
30
  "test": "echo \"Error: no test specified\" && exit 1",
30
31
  "prepare": "npm run build"
@@ -33,7 +34,7 @@
33
34
  "ethers": "^6.13.5",
34
35
  "prettier": "^3.5.3",
35
36
  "rxjs": "^7.8.1",
36
- "shogun-core": "^1.10.2"
37
+ "shogun-core": "^1.11.0"
37
38
  },
38
39
  "peerDependencies": {
39
40
  "react": "^18.0.0",
@@ -445,36 +445,10 @@
445
445
  border-radius: 6px;
446
446
  background-color: transparent;
447
447
  transition: all 0.2s ease;
448
- cursor: pointer;
449
- display: inline-block;
450
- text-decoration: none;
451
- font-size: 14px;
452
- line-height: 1.4;
453
- min-height: 44px;
454
- display: flex;
455
- align-items: center;
456
- justify-content: center;
457
448
  }
458
449
 
459
450
  .shogun-prominent-toggle:hover {
460
451
  text-decoration: underline;
461
- background-color: rgba(59, 130, 246, 0.1);
462
- transform: translateY(-1px);
463
- }
464
-
465
- .shogun-prominent-toggle:active {
466
- transform: translateY(0);
467
- }
468
-
469
- .shogun-prominent-toggle:disabled {
470
- opacity: 0.5;
471
- cursor: not-allowed;
472
- pointer-events: none;
473
- }
474
-
475
- .shogun-prominent-toggle:focus {
476
- outline: 2px solid var(--shogun-primary);
477
- outline-offset: 2px;
478
452
  }
479
453
 
480
454
  /* Redundant dark theme styles removed */