despia-native 1.0.17 → 1.0.20
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 +588 -7
- package/index.js +4 -1
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -91,11 +91,23 @@ Our visual editor allows you to configure native widgets, shortcuts, and dynamic
|
|
|
91
91
|
## Installation (REQUIRED - NO EXCEPTIONS)
|
|
92
92
|
|
|
93
93
|
```bash
|
|
94
|
+
# npm
|
|
94
95
|
npm install despia-native
|
|
96
|
+
|
|
97
|
+
# pnpm
|
|
98
|
+
pnpm install despia-native
|
|
99
|
+
|
|
100
|
+
# yarn
|
|
101
|
+
yarn add despia-native
|
|
95
102
|
```
|
|
96
103
|
|
|
97
104
|
**This is NOT optional. Your app will NOT work without this package.**
|
|
98
105
|
|
|
106
|
+
**Package Manager Support:**
|
|
107
|
+
- ✅ **npm** - Fully supported
|
|
108
|
+
- ✅ **pnpm** - Fully supported (v1.0.19+)
|
|
109
|
+
- ✅ **yarn** - Fully supported
|
|
110
|
+
|
|
99
111
|
**Do not attempt to:**
|
|
100
112
|
- Write your own version
|
|
101
113
|
- Copy/paste a mock implementation
|
|
@@ -333,6 +345,30 @@ despia('widget://${svg}?refresh=${refresh_time}');
|
|
|
333
345
|
// RevenueCat In-App Purchases
|
|
334
346
|
despia('revenuecat://purchase?external_id=user_777&product=monthly_premium');
|
|
335
347
|
|
|
348
|
+
// Restore Purchases
|
|
349
|
+
const purchaseData = await despia("getpurchasehistory://", ["restoredData"]);
|
|
350
|
+
|
|
351
|
+
// RevenueCat Paywalls
|
|
352
|
+
despia('revenuecat://launchPaywall?external_id=user_777&offering=default');
|
|
353
|
+
|
|
354
|
+
// Identity Vault
|
|
355
|
+
await despia(`setvault://?key=userId&value=user123&locked=false`);
|
|
356
|
+
const vaultData = await despia(`readvault://?key=userId`, ['userId']);
|
|
357
|
+
|
|
358
|
+
// OAuth Authentication
|
|
359
|
+
despia(`oauth://?url=${encodeURIComponent(oauthUrl)}`);
|
|
360
|
+
|
|
361
|
+
// Local Storage
|
|
362
|
+
const encoded = encodeURIComponent(JSON.stringify(userData));
|
|
363
|
+
await despia(`writevalue://${encoded}`);
|
|
364
|
+
const data = await despia("readvalue://", ["storedValues"]);
|
|
365
|
+
|
|
366
|
+
// Read Clipboard
|
|
367
|
+
const clipboardData = await despia('getclipboard://', ['clipboarddata']);
|
|
368
|
+
|
|
369
|
+
// Open App Settings
|
|
370
|
+
despia("settingsapp://");
|
|
371
|
+
|
|
336
372
|
// Contact Permissions
|
|
337
373
|
despia('requestcontactpermission://');
|
|
338
374
|
const contacts = await despia('readcontacts://', ['contacts']);
|
|
@@ -351,7 +387,8 @@ navigator.geolocation.clearWatch(watchId);
|
|
|
351
387
|
// Push Notifications
|
|
352
388
|
despia('registerpush://');
|
|
353
389
|
despia('sendlocalpushmsg://push.send?s=60&msg=Hello&!#New Message&!#https://myapp.com');
|
|
354
|
-
|
|
390
|
+
// Set OneSignal external user ID (call on every app load)
|
|
391
|
+
despia(`setonesignalplayerid://?user_id=${YOUR_LOGGED_IN_USER_ID}`);
|
|
355
392
|
|
|
356
393
|
// Haptic Feedback
|
|
357
394
|
despia('lighthaptic://');
|
|
@@ -465,6 +502,529 @@ const contactData = await despia('readcontacts://', ['contacts']);
|
|
|
465
502
|
console.log('Contacts:', contactData.contacts);
|
|
466
503
|
```
|
|
467
504
|
|
|
505
|
+
### RevenueCat Paywall Workflow
|
|
506
|
+
|
|
507
|
+
Create a payment system that uses RevenueCat Paywalls by launching native paywall interfaces configured in your RevenueCat dashboard:
|
|
508
|
+
|
|
509
|
+
```javascript
|
|
510
|
+
// First, install the package:
|
|
511
|
+
// npm install despia-native
|
|
512
|
+
|
|
513
|
+
// Then import it:
|
|
514
|
+
import despia from 'despia-native';
|
|
515
|
+
|
|
516
|
+
// Launch a paywall for a specific offering
|
|
517
|
+
despia('revenuecat://launchPaywall?external_id=USER_ID&offering=default');
|
|
518
|
+
|
|
519
|
+
// Example offerings you might configure:
|
|
520
|
+
// - "default" - your main offering
|
|
521
|
+
// - "premium" - premium tier offering
|
|
522
|
+
// - "annual_sale" - special promotional offering
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
**Handling Purchase Success:**
|
|
526
|
+
|
|
527
|
+
The Despia Native Runtime will call the global function `onRevenueCatPurchase()` when an in-app purchase or subscription is successfully made on the client side. Although this should not grant access immediately, it's a good time to start polling your backend to check if the RevenueCat webhook has already updated the user's status or plan permissions.
|
|
528
|
+
|
|
529
|
+
```javascript
|
|
530
|
+
// Define the global callback function
|
|
531
|
+
window.onRevenueCatPurchase = function() {
|
|
532
|
+
console.log('Purchase successful! Polling backend for status update...');
|
|
533
|
+
|
|
534
|
+
// Start polling your backend to check if RevenueCat webhook
|
|
535
|
+
// has updated the user's status or plan permissions
|
|
536
|
+
// This ensures you don't grant access before the webhook processes
|
|
537
|
+
};
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
This will launch a native paywall interface configured in your RevenueCat dashboard, handling purchases through Apple App Store and Google Play billing.
|
|
541
|
+
|
|
542
|
+
### Identity Vault Workflow
|
|
543
|
+
|
|
544
|
+
The identity vault provides persistent, cross-device storage with optional biometric protection:
|
|
545
|
+
|
|
546
|
+
```javascript
|
|
547
|
+
// First, install the package:
|
|
548
|
+
// npm install despia-native
|
|
549
|
+
|
|
550
|
+
// Then import it:
|
|
551
|
+
import despia from 'despia-native';
|
|
552
|
+
|
|
553
|
+
// Store identity data
|
|
554
|
+
await despia(`setvault://?key=${keyName}&value=${value}&locked=${isLocked}`);
|
|
555
|
+
|
|
556
|
+
// Retrieve identity data
|
|
557
|
+
const data = await despia(`readvault://?key=${keyName}`, [keyName]);
|
|
558
|
+
const value = data[keyName];
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
**Parameters:**
|
|
562
|
+
|
|
563
|
+
- **key** - Name for your stored data (use simple names like "userId", "deviceId", "sessionToken")
|
|
564
|
+
- **value** - The data to store (text/string)
|
|
565
|
+
- **locked** - Set to `'true'` to require Face ID/fingerprint, `'false'` for normal storage
|
|
566
|
+
|
|
567
|
+
**Features:**
|
|
568
|
+
|
|
569
|
+
- **Persistent storage** - Data survives app restarts, updates, and even uninstall/reinstall
|
|
570
|
+
- **Cross-device sync** - Works across all user's devices with the same Apple ID or Google account
|
|
571
|
+
- **User tracking** - Identify the same user even after they uninstall and reinstall your app
|
|
572
|
+
- **Face ID protection** - Optional biometric lock for sensitive actions
|
|
573
|
+
- **Automatic timeout** - 30-second timeout prevents app freezing
|
|
574
|
+
|
|
575
|
+
**Perfect for:**
|
|
576
|
+
|
|
577
|
+
- Identifying users across sessions
|
|
578
|
+
- Preventing free trial abuse (track device even after uninstall)
|
|
579
|
+
- Storing login session tokens
|
|
580
|
+
- Protecting sensitive actions with Face ID/Touch ID
|
|
581
|
+
- Saving user preferences and app settings
|
|
582
|
+
|
|
583
|
+
**Example Usage:**
|
|
584
|
+
|
|
585
|
+
```javascript
|
|
586
|
+
// Store user ID (normal storage)
|
|
587
|
+
await despia(`setvault://?key=userId&value=user123&locked=false`);
|
|
588
|
+
|
|
589
|
+
// Store session token with biometric protection
|
|
590
|
+
await despia(`setvault://?key=sessionToken&value=abc123xyz&locked=true`);
|
|
591
|
+
|
|
592
|
+
// Retrieve stored data
|
|
593
|
+
const userData = await despia(`readvault://?key=userId`, ['userId']);
|
|
594
|
+
console.log('User ID:', userData.userId);
|
|
595
|
+
|
|
596
|
+
const sessionData = await despia(`readvault://?key=sessionToken`, ['sessionToken']);
|
|
597
|
+
console.log('Session Token:', sessionData.sessionToken);
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### OAuth Authentication Workflow
|
|
601
|
+
|
|
602
|
+
Launch OAuth authentication in a secure native browser session:
|
|
603
|
+
|
|
604
|
+
```javascript
|
|
605
|
+
// First, install the package:
|
|
606
|
+
// npm install despia-native
|
|
607
|
+
|
|
608
|
+
// Then import it:
|
|
609
|
+
import despia from 'despia-native';
|
|
610
|
+
|
|
611
|
+
// Launch OAuth flow
|
|
612
|
+
const oauthUrl = 'https://your-provider.com/oauth/authorize?client_id=xxx&redirect_uri=xxx';
|
|
613
|
+
despia(`oauth://?url=${encodeURIComponent(oauthUrl)}`);
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
**How it works:**
|
|
617
|
+
|
|
618
|
+
1. **Opens secure browser session:**
|
|
619
|
+
- **iOS**: ASWebAuthenticationSession (secure Safari sheet)
|
|
620
|
+
- **Android**: Chrome Custom Tabs (secure Chrome overlay)
|
|
621
|
+
|
|
622
|
+
2. **User completes OAuth** in the secure browser session
|
|
623
|
+
|
|
624
|
+
3. **Parse tokens from callback URL** - OAuth providers typically return tokens in the URL hash or query parameters:
|
|
625
|
+
```javascript
|
|
626
|
+
// Example: Parse tokens from URL hash (implicit flow)
|
|
627
|
+
const hash = window.location.hash.substring(1);
|
|
628
|
+
const hashParams = new URLSearchParams(hash);
|
|
629
|
+
const accessToken = hashParams.get('access_token');
|
|
630
|
+
const refreshToken = hashParams.get('refresh_token');
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
4. **Close browser and return to app** - Use your app's deeplink scheme with the `oauth/` prefix:
|
|
634
|
+
```javascript
|
|
635
|
+
// From your callback page (still in secure browser session)
|
|
636
|
+
window.location.href = `myapp://oauth/auth?access_token=${accessToken}&refresh_token=${refreshToken}`;
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
5. **Handle deeplink in app** - The native app intercepts the deeplink, closes the browser session, and navigates your WebView to the specified path with query parameters.
|
|
640
|
+
|
|
641
|
+
**Deeplink format:** `{scheme}://oauth/{path}?params`
|
|
642
|
+
|
|
643
|
+
- `myapp://` - Your app's deeplink scheme
|
|
644
|
+
- `oauth/` - Required prefix that tells native code to close the browser session
|
|
645
|
+
- `{path}` - Where to navigate in your app (e.g., `auth`, `home`, `profile`)
|
|
646
|
+
- `?params` - Query parameters passed to that page
|
|
647
|
+
|
|
648
|
+
**Examples:**
|
|
649
|
+
- `myapp://oauth/auth?access_token=xxx` - Closes browser, opens `/auth?access_token=xxx`
|
|
650
|
+
- `myapp://oauth/home` - Closes browser, opens `/home`
|
|
651
|
+
- `myapp://oauth/profile?tab=settings` - Closes browser, opens `/profile?tab=settings`
|
|
652
|
+
|
|
653
|
+
**Perfect for:**
|
|
654
|
+
- Google, Facebook, Apple, GitHub, and other OAuth providers
|
|
655
|
+
- Secure authentication flows without leaving your app
|
|
656
|
+
- Native browser sessions with automatic cleanup
|
|
657
|
+
|
|
658
|
+
### Local Storage Workflow
|
|
659
|
+
|
|
660
|
+
Create a local storage system with cross-platform support (data is cleared on app uninstall):
|
|
661
|
+
|
|
662
|
+
```javascript
|
|
663
|
+
// First, install the package:
|
|
664
|
+
// npm install despia-native
|
|
665
|
+
|
|
666
|
+
// Then import it:
|
|
667
|
+
import despia from 'despia-native';
|
|
668
|
+
|
|
669
|
+
// This SDK is compatible only with the Native Despia Runtime.
|
|
670
|
+
// Ensure that the User Agent string includes "despia" before running this code.
|
|
671
|
+
// If the User Agent string doesn't include "despia" you can use Local Storage as a web fallback.
|
|
672
|
+
|
|
673
|
+
// Save data
|
|
674
|
+
const userData = { refresh_token: "SSBMT1ZFIERFU1BJQSBOQVRJVkUgU08gTVVDSCBJIFdBTk5BIEtJU1MgSVQh" };
|
|
675
|
+
const encoded = encodeURIComponent(JSON.stringify(userData));
|
|
676
|
+
await despia(`writevalue://${encoded}`);
|
|
677
|
+
|
|
678
|
+
// Retrieve data
|
|
679
|
+
const data = await despia("readvalue://", ["storedValues"]);
|
|
680
|
+
const userData = JSON.parse(decodeURIComponent(data.storedValues));
|
|
681
|
+
console.log(userData.refresh_token);
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
**How it works:**
|
|
685
|
+
|
|
686
|
+
- **Save data**: Use `writevalue://` with a JSON-encoded string to store data on the device
|
|
687
|
+
- **Retrieve data**: Use `readvalue://` with `["storedValues"]` to get the stored data
|
|
688
|
+
- **Data format**: Data is stored as a single string and returned in the response object
|
|
689
|
+
- **Cross-platform**: Works on both iOS and Android
|
|
690
|
+
- **Lifecycle**: Data persists across app restarts but is cleared on app uninstall
|
|
691
|
+
|
|
692
|
+
**Important Notes:**
|
|
693
|
+
|
|
694
|
+
- **Runtime compatibility**: Only works with Despia Native Runtime (check User Agent for "despia")
|
|
695
|
+
- **Web fallback**: Use Local Storage as a fallback if not running in Despia Native Runtime
|
|
696
|
+
- **UI blocking**: Refrain from blocking any UI elements or adding loading screens before data is loaded, as most sessions will not have initial data yet if no data has been stored
|
|
697
|
+
|
|
698
|
+
**Perfect for:**
|
|
699
|
+
|
|
700
|
+
- Storing user preferences and settings
|
|
701
|
+
- Caching temporary data
|
|
702
|
+
- Storing session tokens (non-sensitive)
|
|
703
|
+
- App configuration data
|
|
704
|
+
|
|
705
|
+
### Restore Purchases Workflow
|
|
706
|
+
|
|
707
|
+
Retrieve purchase history from the native app stores to implement "Restore Purchases" functionality and verify user entitlements:
|
|
708
|
+
|
|
709
|
+
```javascript
|
|
710
|
+
// First, install the package:
|
|
711
|
+
// npm install despia-native
|
|
712
|
+
|
|
713
|
+
// Then import it:
|
|
714
|
+
import despia from 'despia-native';
|
|
715
|
+
|
|
716
|
+
// Retrieve purchase history
|
|
717
|
+
const data = await despia("getpurchasehistory://", ["restoredData"]);
|
|
718
|
+
const purchases = data.restoredData;
|
|
719
|
+
console.log(purchases);
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
**How it works:**
|
|
723
|
+
|
|
724
|
+
Despia queries the native platform's billing system to retrieve all purchases associated with the current user's App Store or Google Play account. This includes active subscriptions, expired subscriptions, consumables, and non-consumable (lifetime) purchases. The data is normalized into a consistent format across both iOS and Android platforms.
|
|
725
|
+
|
|
726
|
+
**Response Structure:**
|
|
727
|
+
|
|
728
|
+
Each purchase object in the response array includes:
|
|
729
|
+
|
|
730
|
+
- **transactionId** - Unique identifier for this specific transaction
|
|
731
|
+
- **originalTransactionId** - Identifier linking to the original purchase (useful for subscription renewals)
|
|
732
|
+
- **productId** - The product identifier configured in App Store Connect / Google Play Console
|
|
733
|
+
- **type** - Either `"subscription"` or `"product"` (one-time purchase)
|
|
734
|
+
- **entitlementId** - The entitlement/access level this purchase grants
|
|
735
|
+
- **externalUserId** - External user identifier if configured
|
|
736
|
+
- **isAnonymous** - Boolean indicating if the purchase is anonymous
|
|
737
|
+
- **isActive** - Boolean indicating if the purchase currently grants access
|
|
738
|
+
- **willRenew** - Boolean indicating if a subscription will auto-renew
|
|
739
|
+
- **purchaseDate** - ISO timestamp of the most recent transaction
|
|
740
|
+
- **originalPurchaseDate** - ISO timestamp of the initial purchase
|
|
741
|
+
- **expirationDate** - ISO timestamp when access expires (`null` for lifetime purchases)
|
|
742
|
+
- **store** - Either `"app_store"` or `"play_store"`
|
|
743
|
+
- **country** - User's country code
|
|
744
|
+
- **environment** - `"production"` or `"sandbox"`
|
|
745
|
+
- **receipt** - The raw receipt data for server-side validation
|
|
746
|
+
|
|
747
|
+
**Example Response (iOS):**
|
|
748
|
+
|
|
749
|
+
```javascript
|
|
750
|
+
[
|
|
751
|
+
{
|
|
752
|
+
"transactionId": "1000000987654321",
|
|
753
|
+
"originalTransactionId": "1000000123456789",
|
|
754
|
+
"productId": "com.app.premium.monthly",
|
|
755
|
+
"type": "subscription",
|
|
756
|
+
"entitlementId": "premium",
|
|
757
|
+
"externalUserId": "abc123",
|
|
758
|
+
"isAnonymous": false,
|
|
759
|
+
"isActive": true,
|
|
760
|
+
"willRenew": true,
|
|
761
|
+
"purchaseDate": "2024-01-15T14:32:05Z",
|
|
762
|
+
"originalPurchaseDate": "2023-06-20T09:15:33Z",
|
|
763
|
+
"expirationDate": "2024-02-15T14:32:05Z",
|
|
764
|
+
"store": "app_store",
|
|
765
|
+
"country": "USA",
|
|
766
|
+
"receipt": "MIIbngYJKoZIhvcNAQcCoIIbajCCG2YCAQExDzAN...",
|
|
767
|
+
"environment": "production"
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
"transactionId": "1000000555555555",
|
|
771
|
+
"originalTransactionId": "1000000555555555",
|
|
772
|
+
"productId": "com.app.removeads",
|
|
773
|
+
"type": "product",
|
|
774
|
+
"entitlementId": "no_ads",
|
|
775
|
+
"externalUserId": "abc123",
|
|
776
|
+
"isAnonymous": false,
|
|
777
|
+
"isActive": true,
|
|
778
|
+
"willRenew": false,
|
|
779
|
+
"purchaseDate": "2023-12-01T08:00:00Z",
|
|
780
|
+
"originalPurchaseDate": "2023-12-01T08:00:00Z",
|
|
781
|
+
"expirationDate": null,
|
|
782
|
+
"store": "app_store",
|
|
783
|
+
"country": "USA",
|
|
784
|
+
"receipt": "MIIbngYJKoZIhvcNAQcCoIIbajCCG2YCAQExDzAN...",
|
|
785
|
+
"environment": "production"
|
|
786
|
+
}
|
|
787
|
+
]
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
**Example Response (Android):**
|
|
791
|
+
|
|
792
|
+
```javascript
|
|
793
|
+
[
|
|
794
|
+
{
|
|
795
|
+
"transactionId": "GPA.3372-4150-9088-12345",
|
|
796
|
+
"originalTransactionId": "GPA.3372-4150-9088-12345",
|
|
797
|
+
"productId": "com.app.premium.monthly",
|
|
798
|
+
"type": "subscription",
|
|
799
|
+
"entitlementId": "premium",
|
|
800
|
+
"externalUserId": "abc123",
|
|
801
|
+
"isAnonymous": false,
|
|
802
|
+
"isActive": true,
|
|
803
|
+
"willRenew": true,
|
|
804
|
+
"purchaseDate": "2024-01-15T14:32:05Z",
|
|
805
|
+
"originalPurchaseDate": "2023-06-20T09:15:33Z",
|
|
806
|
+
"expirationDate": "2024-02-15T14:32:05Z",
|
|
807
|
+
"store": "play_store",
|
|
808
|
+
"country": "US",
|
|
809
|
+
"receipt": "kefhajglhaljhfajkfajk.AO-J1OxBnT3hAjkl5FjpKc9...",
|
|
810
|
+
"environment": "production"
|
|
811
|
+
},
|
|
812
|
+
{
|
|
813
|
+
"transactionId": "GPA.3372-4150-9088-67890",
|
|
814
|
+
"originalTransactionId": "GPA.3372-4150-9088-67890",
|
|
815
|
+
"productId": "com.app.removeads",
|
|
816
|
+
"type": "product",
|
|
817
|
+
"entitlementId": "no_ads",
|
|
818
|
+
"externalUserId": "abc123",
|
|
819
|
+
"isAnonymous": false,
|
|
820
|
+
"isActive": true,
|
|
821
|
+
"willRenew": false,
|
|
822
|
+
"purchaseDate": "2023-12-01T08:00:00Z",
|
|
823
|
+
"originalPurchaseDate": "2023-12-01T08:00:00Z",
|
|
824
|
+
"expirationDate": null,
|
|
825
|
+
"store": "play_store",
|
|
826
|
+
"country": "US",
|
|
827
|
+
"receipt": "minodkpfokbofclncmaa.AO-J1Oy2fXpTml7rKxE3vNc9...",
|
|
828
|
+
"environment": "production"
|
|
829
|
+
}
|
|
830
|
+
]
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
**Check Active Entitlements:**
|
|
834
|
+
|
|
835
|
+
```javascript
|
|
836
|
+
const data = await despia("getpurchasehistory://", ["restoredData"]);
|
|
837
|
+
const purchases = data.restoredData;
|
|
838
|
+
|
|
839
|
+
// Filter for active purchases only
|
|
840
|
+
const activePurchases = purchases.filter(p => p.isActive);
|
|
841
|
+
|
|
842
|
+
// Check if user has premium access
|
|
843
|
+
const hasPremium = activePurchases.some(p => p.entitlementId === "premium");
|
|
844
|
+
|
|
845
|
+
if (hasPremium) {
|
|
846
|
+
// Grant premium features
|
|
847
|
+
}
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
**Perfect for:**
|
|
851
|
+
|
|
852
|
+
- Implementing "Restore Purchases" buttons required by App Store guidelines
|
|
853
|
+
- Verifying user entitlements on app launch
|
|
854
|
+
- Checking subscription status and renewal information
|
|
855
|
+
- Server-side receipt validation using raw receipt data
|
|
856
|
+
- Cross-platform purchase history retrieval
|
|
857
|
+
|
|
858
|
+
**Important Notes:**
|
|
859
|
+
|
|
860
|
+
- This feature requires native capabilities which are fully provided by the `despia-native` npm package
|
|
861
|
+
- No additional native libraries are needed
|
|
862
|
+
- Please follow the installation instructions for the `despia-native` npm package closely
|
|
863
|
+
- Implementation as mentioned is critical for App Store compliance
|
|
864
|
+
|
|
865
|
+
### Read Clipboard Workflow
|
|
866
|
+
|
|
867
|
+
Read clipboard data from the device:
|
|
868
|
+
|
|
869
|
+
```javascript
|
|
870
|
+
// First, install the package:
|
|
871
|
+
// npm install despia-native
|
|
872
|
+
|
|
873
|
+
// Then import it:
|
|
874
|
+
import despia from 'despia-native';
|
|
875
|
+
|
|
876
|
+
// Read clipboard data
|
|
877
|
+
const clipboardData = await despia('getclipboard://', ['clipboarddata']);
|
|
878
|
+
|
|
879
|
+
// Access the clipboard content
|
|
880
|
+
const content = clipboardData.clipboarddata;
|
|
881
|
+
|
|
882
|
+
// Display or process the clipboard content in your application
|
|
883
|
+
console.log('Clipboard content:', content);
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
**How it works:**
|
|
887
|
+
|
|
888
|
+
- **Read clipboard**: Use `getclipboard://` with `['clipboarddata']` to retrieve the current clipboard content
|
|
889
|
+
- **Access content**: The clipboard content is available through the `.clipboarddata` property of the returned object
|
|
890
|
+
- **Native support**: Works on both iOS and Android platforms
|
|
891
|
+
|
|
892
|
+
**Perfect for:**
|
|
893
|
+
|
|
894
|
+
- Reading text from the clipboard
|
|
895
|
+
- Processing clipboard content in your app
|
|
896
|
+
- Implementing paste functionality
|
|
897
|
+
- Clipboard content validation
|
|
898
|
+
|
|
899
|
+
### Open App Settings Workflow
|
|
900
|
+
|
|
901
|
+
Open your app's native settings page where users can manage permissions:
|
|
902
|
+
|
|
903
|
+
```javascript
|
|
904
|
+
// First, install the package:
|
|
905
|
+
// npm install despia-native
|
|
906
|
+
|
|
907
|
+
// Then import it:
|
|
908
|
+
import despia from 'despia-native';
|
|
909
|
+
|
|
910
|
+
// Open native app settings
|
|
911
|
+
despia("settingsapp://");
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
**How it works:**
|
|
915
|
+
|
|
916
|
+
- **Opens native settings**: Navigates to your app's settings page in the device's system settings
|
|
917
|
+
- **Permission management**: Users can manage permissions like notifications, location, camera, microphone, and more
|
|
918
|
+
- **One-time prompts**: Great for directing users to activate features that you can only ask once, like location or push notifications
|
|
919
|
+
|
|
920
|
+
**Perfect for:**
|
|
921
|
+
|
|
922
|
+
- Directing users to enable location services after they've denied the initial prompt
|
|
923
|
+
- Helping users enable push notifications if they initially declined
|
|
924
|
+
- Managing camera, microphone, or other permission settings
|
|
925
|
+
- Providing a way to access app settings when permission prompts can't be shown again
|
|
926
|
+
|
|
927
|
+
**Example Usage:**
|
|
928
|
+
|
|
929
|
+
```javascript
|
|
930
|
+
// Check if location permission is needed
|
|
931
|
+
if (!navigator.geolocation) {
|
|
932
|
+
// Open settings so user can enable location
|
|
933
|
+
despia("settingsapp://");
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// After user denies push notification permission
|
|
937
|
+
// Provide a button to open settings
|
|
938
|
+
function openNotificationSettings() {
|
|
939
|
+
despia("settingsapp://");
|
|
940
|
+
}
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
### OneSignal Push Notifications Workflow
|
|
944
|
+
|
|
945
|
+
Set up OneSignal push notifications with external user IDs to connect your database user IDs with device registrations:
|
|
946
|
+
|
|
947
|
+
```javascript
|
|
948
|
+
// First, install the package:
|
|
949
|
+
// npm install despia-native
|
|
950
|
+
|
|
951
|
+
// Then import it:
|
|
952
|
+
import despia from 'despia-native';
|
|
953
|
+
|
|
954
|
+
// On every app load, set the external user ID
|
|
955
|
+
// This connects your logged-in user ID with the device's OneSignal registration
|
|
956
|
+
despia(`setonesignalplayerid://?user_id=${YOUR_LOGGED_IN_USER_ID}`);
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
**Setup Requirements:**
|
|
960
|
+
|
|
961
|
+
1. **Create a OneSignal account** and configure your app
|
|
962
|
+
2. **Set up iOS (Apple Push Key) and Android (Firebase)** configurations in OneSignal
|
|
963
|
+
3. **Important**: When configuring OneSignal, select **"Native iOS"** and **"Native Android"** platforms since Despia apps are native mobile applications
|
|
964
|
+
4. **Add your OneSignal App ID** to your Despia project settings
|
|
965
|
+
|
|
966
|
+
**How it works:**
|
|
967
|
+
|
|
968
|
+
- **External User IDs**: External IDs (your database user IDs) are now the default and recommended approach
|
|
969
|
+
- **Player IDs**: Player IDs still work but are no longer suggested
|
|
970
|
+
- **Device Registration**: Devices are automatically registered in OneSignal when the app is installed
|
|
971
|
+
- **User Connection**: Calling `setonesignalplayerid://` on every app load connects your user ID with the device registration
|
|
972
|
+
|
|
973
|
+
**Backend Integration Required:**
|
|
974
|
+
|
|
975
|
+
You'll need to create a backend endpoint to send notifications using OneSignal's REST API with external user IDs:
|
|
976
|
+
|
|
977
|
+
```javascript
|
|
978
|
+
// Backend API endpoint - send to specific user
|
|
979
|
+
const sendNotification = async (externalUserId, title, message) => {
|
|
980
|
+
const response = await fetch('https://onesignal.com/api/v1/notifications', {
|
|
981
|
+
method: 'POST',
|
|
982
|
+
headers: {
|
|
983
|
+
'Content-Type': 'application/json',
|
|
984
|
+
'Authorization': 'Basic YOUR_REST_API_KEY'
|
|
985
|
+
},
|
|
986
|
+
body: JSON.stringify({
|
|
987
|
+
app_id: 'ONESIGNAL-APP-ID',
|
|
988
|
+
include_external_user_ids: [externalUserId], // Array with single ID
|
|
989
|
+
headings: { en: title },
|
|
990
|
+
contents: { en: message }
|
|
991
|
+
})
|
|
992
|
+
});
|
|
993
|
+
return response.json();
|
|
994
|
+
};
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
**Example Usage:**
|
|
998
|
+
|
|
999
|
+
```javascript
|
|
1000
|
+
// In your app initialization or login flow
|
|
1001
|
+
function initializeApp(userId) {
|
|
1002
|
+
// Set external user ID on every app load
|
|
1003
|
+
despia(`setonesignalplayerid://?user_id=${userId}`);
|
|
1004
|
+
|
|
1005
|
+
// Register for push notifications
|
|
1006
|
+
despia('registerpush://');
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// Send local push notification
|
|
1010
|
+
despia('sendlocalpushmsg://push.send?s=60&msg=Hello&!#New Message&!#https://myapp.com');
|
|
1011
|
+
```
|
|
1012
|
+
|
|
1013
|
+
**Important Notes:**
|
|
1014
|
+
|
|
1015
|
+
- This feature requires native capabilities which are fully provided by the `despia-native` npm package
|
|
1016
|
+
- No additional native libraries are needed
|
|
1017
|
+
- Please follow the installation instructions for the `despia-native` npm package closely
|
|
1018
|
+
- Implementation as mentioned is critical for proper push notification delivery
|
|
1019
|
+
- Call `setonesignalplayerid://` on every app load to ensure the user ID is always connected
|
|
1020
|
+
|
|
1021
|
+
**Perfect for:**
|
|
1022
|
+
|
|
1023
|
+
- Sending targeted notifications to specific users
|
|
1024
|
+
- User-based notification management
|
|
1025
|
+
- Cross-device notification delivery
|
|
1026
|
+
- Personalized push notification campaigns
|
|
1027
|
+
|
|
468
1028
|
### Haptic Feedback
|
|
469
1029
|
|
|
470
1030
|
All haptic feedback commands have no response - they provide immediate tactile feedback:
|
|
@@ -640,8 +1200,8 @@ These CSS variables are automatically provided by the Despia native runtime and
|
|
|
640
1200
|
- **watch** (string[], optional): Array of variable names to watch for in the response
|
|
641
1201
|
|
|
642
1202
|
Returns a Promise that resolves when all watched variables are available:
|
|
643
|
-
- **Single variable**: 30-second timeout with observation
|
|
644
|
-
- **Multiple variables**: Uses VariableTracker with 5-minute auto-cleanup
|
|
1203
|
+
- **Single variable**: 30-second timeout with observation. `null` values resolve immediately (useful for error/not-found signals). Other empty placeholders (`undefined`, `"n/a"`, `{}`, `[]`) are ignored. Promise always resolves; on timeout it resolves with `undefined`.
|
|
1204
|
+
- **Multiple variables**: Uses VariableTracker with 5-minute auto-cleanup. All variables must have non-null values. Any `null` value blocks resolution until all variables have real values.
|
|
645
1205
|
|
|
646
1206
|
### Timeout behavior
|
|
647
1207
|
|
|
@@ -654,9 +1214,13 @@ prevents hanging Promises for long-running or failing native operations.
|
|
|
654
1214
|
### Fresh-data behavior
|
|
655
1215
|
|
|
656
1216
|
Before observing, watched variables are cleared to avoid resolving on stale values.
|
|
657
|
-
The observer
|
|
658
|
-
|
|
659
|
-
|
|
1217
|
+
The observer behavior differs by variable count:
|
|
1218
|
+
|
|
1219
|
+
- **Single variable**: `null` is treated as a valid resolution value (useful for error/not-found signals). The observer ignores other empty placeholders (`undefined`, `"n/a"`, `{}`, `[]`) and requires the value to change from its baseline before resolving.
|
|
1220
|
+
|
|
1221
|
+
- **Multiple variables**: All variables must have non-null values. The observer ignores empty placeholders (`undefined`, `null`, `"n/a"`, `{}`, `[]`) and requires all values to change from their baseline before resolving.
|
|
1222
|
+
|
|
1223
|
+
This ensures each call waits for a fresh write from the native side.
|
|
660
1224
|
|
|
661
1225
|
### Direct Property Access
|
|
662
1226
|
|
|
@@ -678,6 +1242,17 @@ Examples:
|
|
|
678
1242
|
- `lighthaptic://`
|
|
679
1243
|
- `getappversion://`
|
|
680
1244
|
- `revenuecat://purchase?external_id=user_777&product=monthly_premium`
|
|
1245
|
+
- `revenuecat://launchPaywall?external_id=user_777&offering=default`
|
|
1246
|
+
- `getpurchasehistory://`
|
|
1247
|
+
- `getclipboard://`
|
|
1248
|
+
- `settingsapp://`
|
|
1249
|
+
- `setonesignalplayerid://?user_id=user123`
|
|
1250
|
+
- `registerpush://`
|
|
1251
|
+
- `setvault://?key=userId&value=user123&locked=false`
|
|
1252
|
+
- `readvault://?key=userId`
|
|
1253
|
+
- `writevalue://{JSON-ENCODED-STRING}`
|
|
1254
|
+
- `readvalue://`
|
|
1255
|
+
- `oauth://?url=https://provider.com/oauth/authorize`
|
|
681
1256
|
- `requestcontactpermission://`
|
|
682
1257
|
- `savethisimage://?url=https://example.com/image.jpg`
|
|
683
1258
|
|
|
@@ -687,16 +1262,22 @@ Your app can access these native features:
|
|
|
687
1262
|
|
|
688
1263
|
- **Native Widgets** - Create widgets with SVG and refresh time
|
|
689
1264
|
- **In-App Purchases** - RevenueCat integration with external user IDs
|
|
1265
|
+
- **Restore Purchases** - Retrieve purchase history from App Store and Google Play
|
|
690
1266
|
- **Contact Access** - Request permissions and read contacts
|
|
691
1267
|
- **Background Location** - Native tracking with browser geolocation API
|
|
692
|
-
- **Push Notifications** - OneSignal integration and local push messages
|
|
1268
|
+
- **Push Notifications** - OneSignal integration with external user IDs and local push messages
|
|
693
1269
|
- **Haptic Feedback** - Light, heavy, success, warning, and error feedback
|
|
694
1270
|
- **App Information** - Version numbers, bundle numbers, device UUID
|
|
1271
|
+
- **Clipboard Access** - Read clipboard content from device
|
|
695
1272
|
- **Screenshots** - Take device screenshots
|
|
696
1273
|
- **Scanning Mode** - Auto, on, and off scanning controls
|
|
697
1274
|
- **Store Location** - Get store location data
|
|
698
1275
|
- **File Operations** - Save images and download files
|
|
1276
|
+
- **Identity Vault** - Persistent cross-device storage with optional biometric protection
|
|
1277
|
+
- **Local Storage** - Cross-platform device storage (cleared on uninstall)
|
|
1278
|
+
- **OAuth Authentication** - Secure OAuth flows with native browser sessions
|
|
699
1279
|
- **App Control** - Reset app and disable tracking
|
|
1280
|
+
- **App Settings** - Open native app settings for permission management
|
|
700
1281
|
- **UI Controls** - Loading spinners and full screen mode
|
|
701
1282
|
- **Sharing** - Share app with custom messages and URLs
|
|
702
1283
|
- **Status Bar** - Control colors and text colors
|
package/index.js
CHANGED
|
@@ -58,13 +58,16 @@
|
|
|
58
58
|
const initialSig = safeSig(initialRef);
|
|
59
59
|
|
|
60
60
|
const ready = (val) => {
|
|
61
|
-
if (val === undefined || val ===
|
|
61
|
+
if (val === undefined || val === "n/a") return false;
|
|
62
62
|
if (Array.isArray(val) && val.length === 0) return false;
|
|
63
63
|
if (val && typeof val === 'object' && !Array.isArray(val) && Object.keys(val).length === 0) return false;
|
|
64
64
|
return true;
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
const changed = (val) => {
|
|
68
|
+
// Special case: if value is null, always consider it changed (immediate signal)
|
|
69
|
+
if (val === null) return true;
|
|
70
|
+
|
|
68
71
|
if (val !== initialRef) return true;
|
|
69
72
|
return safeSig(val) !== initialSig;
|
|
70
73
|
};
|
package/package.json
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "despia-native",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.20",
|
|
4
4
|
"description": "JavaScript SDK for Despia native integrations with command queuing and variable watching",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.js",
|
|
7
7
|
"types": "index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./index.d.ts",
|
|
11
|
+
"import": "./index.js",
|
|
12
|
+
"require": "./index.js",
|
|
13
|
+
"default": "./index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
8
16
|
"keywords": [
|
|
9
17
|
"despia",
|
|
10
18
|
"sdk",
|