diff --git a/CHANGELOG.MD b/CHANGELOG.MD
index 629e145..1e11d65 100644
--- a/CHANGELOG.MD
+++ b/CHANGELOG.MD
@@ -1,5 +1,19 @@
# OST Wallet SDK Changelog
+## Version 2.4.1
+* User can authorize external session by scanning QR-Code. To use `Authorize session by scanning QR code`, please update downstream sdk.
+iOS: v2.4.1
+Android: v2.4.1
+* User can pass QR-Code payload to perform QR-Code actions without opening Scanner in OstWalletUI.
+This functionality is available for `scanQRCodeToAuthorizeSession`, `scanQRCodeToExecuteTransaction`, `scanQRCodeToAuthorizeDevice`,
+
+## Version 2.4.0
+* OstRedemption UI component is now available in SDK. To use `OstRedemption`, please update downstream sdk.
+iOS:`v2.4.0`
+Android:`v2.4.0`
+* `getRedeemableSkus` and `getRedeemableSkuDetails` apis added in `OstJsonApi`.
+
+
## Version 2.3.14
* ReadMe updates
* Removed unused imports
diff --git a/README.md b/README.md
index 8fedcc5..6dd74ba 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,8 @@ Ost React Native Wallet SDK...
- [Implementation](#implementation-1)
+ [Wallet Settings UI Component](#wallet-settings-ui-component)
- [Implementation](#implementation-2)
+ + [Redemption flow UI Component](#redemption-flow-ui-component)
+ - [Implementation](#implementation-3)
+ [OstTransactionHelper - Transaction and Session Integrated Workflow](#osttransactionhelper---transaction-and-session-integrated-workflow)
- [Implementation](#implementation-3)
* [Intermediate Usage - Ost Wallet SDK UI](#intermediate-usage---ost-wallet-sdk-ui)
@@ -240,6 +242,8 @@ Activate User workflow deploys user's wallet on the blockchain and whitelists th
#### Implementation
Please refer to [Activate User UI Workflow Documentation](./documentation/OstWalletUI.md#activate-user) for implementation details.
+
+
### 3. Wallet Settings UI Component
---
OstWallet Settings is a pre-built UI component available exclusively available in `ost-wallet-sdk-react-native` SDK.
@@ -249,7 +253,18 @@ It is a wallet settings page that can be used by end-users to perfrom 12 differe
#### Implementation
Please refer to [OstWallet Settings Documentation](./documentation/OstWalletSettings.md) for implementation details.
-### 4. OstvTransaction Helper - Transaction and Add Session Integrated Workflow
+
+
+### 4. Redemption Flow UI Component
+---
+OstRedemption component is a pre-built UI component available exclusively in `ost-wallet-sdk-react-native` SDK.
+It consist two pages - one displaying redeemable product list and another displaying product details and redemption options. It can be used by end-users to integrate redemption flow into their app.
+> IMPORTANT: This feature requires application to use [React Navigation](https://reactnavigation.org/docs/en/getting-started.html) package.
+
+#### Implementation
+Please reder to [OstRedemption flow Documentation](./documentation/OstRedemptionFlow.md) for implementation details.
+
+### 5. OstTransaction Helper - Transaction and Add Session Integrated Workflow
---
`OstTransactionHelper` is a transaction helper provided by the SDK that creates session keys before performing a transaction if needed. App developers can configure the session creation parameters (session buckets) as per application's need.
diff --git a/android/build.gradle b/android/build.gradle
index 96474e6..dc676c0 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -18,7 +18,6 @@ allprojects {
}
apply plugin: 'com.android.library'
-
android {
compileSdkVersion 28
buildToolsVersion '28.0.3'
@@ -45,5 +44,5 @@ repositories {
dependencies {
implementation 'com.facebook.react:react-native:+'
- api 'com.ost:ost-wallet-sdk-android:2.3.8'
+ api 'com.ost:ost-wallet-sdk-android:2.4.1'
}
diff --git a/android/src/main/java/com/ostwalletrnsdk/OstRNSdkJsonApiModule.java b/android/src/main/java/com/ostwalletrnsdk/OstRNSdkJsonApiModule.java
index 49db67e..121be82 100644
--- a/android/src/main/java/com/ostwalletrnsdk/OstRNSdkJsonApiModule.java
+++ b/android/src/main/java/com/ostwalletrnsdk/OstRNSdkJsonApiModule.java
@@ -150,6 +150,46 @@ public void getCurrentDeviceForUserId(
}
}
+ @ReactMethod
+ public void getRedeemableSkus(
+ String userId,
+ ReadableMap requestMap,
+ Callback successCallback,
+ Callback errorCallback
+ ) {
+ try {
+ Map requestPayload = new HashMap<>();
+ if (null != requestMap) {
+ requestPayload = requestMap.toHashMap();
+ }
+ OstJsonApi.getRedeemableSkus(userId ,requestPayload, new OstJsonApiCallbackImpl(successCallback, errorCallback));
+ } catch (Throwable e) {
+ errorCallback.invoke(Utils.getError(e, "rn_orsjam_grs_1"));
+ return;
+ }
+ }
+
+
+ @ReactMethod
+ public void getRedeemableSkuDetails(
+ String userId,
+ String skuId,
+ ReadableMap requestMap,
+ Callback successCallback,
+ Callback errorCallback
+ ) {
+ try {
+ Map requestPayload = new HashMap<>();
+ if (null != requestMap) {
+ requestPayload = requestMap.toHashMap();
+ }
+ OstJsonApi.getRedeemableSkuDetails(userId , skuId ,requestPayload, new OstJsonApiCallbackImpl(successCallback, errorCallback));
+ } catch (Throwable e) {
+ errorCallback.invoke(Utils.getError(e, "rn_orsjam_grsd_1"));
+ return;
+ }
+ }
+
private static class OstJsonApiCallbackImpl implements OstJsonApiCallback {
diff --git a/android/src/main/java/com/ostwalletrnsdk/Utils.java b/android/src/main/java/com/ostwalletrnsdk/Utils.java
index 51f7ff8..710f0d2 100644
--- a/android/src/main/java/com/ostwalletrnsdk/Utils.java
+++ b/android/src/main/java/com/ostwalletrnsdk/Utils.java
@@ -95,6 +95,8 @@ public static WritableMap convertJsonToMap(JSONObject jsonObject) throws JSONExc
map.putDouble(key, (Double) value);
} else if (value instanceof String) {
map.putString(key, (String) value);
+ } else if (null == value || "null".equalsIgnoreCase(value.toString())){
+ map.putNull(key);
} else {
map.putString(key, value.toString());
}
@@ -119,6 +121,8 @@ public static WritableArray convertJsonToArray(JSONArray jsonArray) throws JSONE
array.pushDouble((Double) value);
} else if (value instanceof String) {
array.pushString((String) value);
+ } else if (null == value || "null".equalsIgnoreCase(value.toString())) {
+ array.pushNull();
} else {
array.pushString(value.toString());
}
diff --git a/android/src/main/java/com/ostwalletrnsdk/ui/OstWalletUiRnSdkModule.java b/android/src/main/java/com/ostwalletrnsdk/ui/OstWalletUiRnSdkModule.java
index 0a4f7a9..c134251 100644
--- a/android/src/main/java/com/ostwalletrnsdk/ui/OstWalletUiRnSdkModule.java
+++ b/android/src/main/java/com/ostwalletrnsdk/ui/OstWalletUiRnSdkModule.java
@@ -165,20 +165,31 @@ public void getAddDeviceQRCode(String userId, String uuid) {
}
@ReactMethod
- public void scanQRCodeToAuthorizeDevice(String userId, String uuid) {
+ public void scanQRCodeToAuthorizeDevice(String userId, String qrPayload, String uuid) {
Activity currentActivity = getCurrentActivity();
OstUICallbackImpl ostUICallback = new OstUICallbackImpl( uuid, this.reactContext,
new OstWorkflowContext(OstWorkflowContext.WORKFLOW_TYPE.AUTHORIZE_DEVICE_WITH_QR_CODE));
- String workflowId = OstWalletUI.scanQRCodeToAuthorizeDevice(currentActivity, userId, ostUICallback);
+ String workflowId = OstWalletUI.scanQRCodeToAuthorizeDevice(currentActivity, qrPayload, userId, ostUICallback);
SdkInteract.getInstance().subscribe(workflowId, ostUICallback);
}
+
+ @ReactMethod
+ public void scanQRCodeToAuthorizeSession(String userId, String qrPayload, String uuid) {
+ Activity currentActivity = getCurrentActivity();
+ OstUICallbackImpl ostUICallback = new OstUICallbackImpl( uuid, this.reactContext,
+ new OstWorkflowContext(OstWorkflowContext.WORKFLOW_TYPE.AUTHORIZE_DEVICE_WITH_QR_CODE));
+ String workflowId = OstWalletUI.scanQRCodeToAuthorizeSession(currentActivity, qrPayload, userId, ostUICallback);
+ SdkInteract.getInstance().subscribe(workflowId, ostUICallback);
+ }
+
+
@ReactMethod
- public void scanQRCodeToExecuteTransaction(String userId, String uuid) {
+ public void scanQRCodeToExecuteTransaction(String userId, String qrPayload, String uuid) {
Activity currentActivity = getCurrentActivity();
OstUICallbackImpl ostUICallback = new OstUICallbackImpl( uuid, this.reactContext,
new OstWorkflowContext(OstWorkflowContext.WORKFLOW_TYPE.EXECUTE_TRANSACTION));
- String workflowId = OstWalletUI.scanQRCodeToExecuteTransaction(currentActivity, userId, ostUICallback);
+ String workflowId = OstWalletUI.scanQRCodeToExecuteTransaction(currentActivity, qrPayload, userId, ostUICallback);
SdkInteract.getInstance().subscribe(workflowId, ostUICallback);
}
diff --git a/assets/down-arrow.png b/assets/down-arrow.png
new file mode 100644
index 0000000..8e7202d
Binary files /dev/null and b/assets/down-arrow.png differ
diff --git a/assets/msg-icon.png b/assets/msg-icon.png
new file mode 100644
index 0000000..119aea6
Binary files /dev/null and b/assets/msg-icon.png differ
diff --git a/documentation/OstJsonApi.md b/documentation/OstJsonApi.md
index 97bbef1..07f5de8 100644
--- a/documentation/OstJsonApi.md
+++ b/documentation/OstJsonApi.md
@@ -24,6 +24,9 @@ OST JSON APIs are a set of *asynchronous* methods that make API calls to OST Pla
- [Usage](#usage-4)
- [Sample Response](#sample-response-4)
- [Sample Error](#sample-error)
+ - [Get Redeemable Sku Details](#get-redeemable-sku-details)
+ - [Usage](#usage-8)
+ - [Sample Response](#sample-response-8)
- [List API](#list-api)
- [Get Transactions](#get-transactions)
- [Usage](#usage-5)
@@ -31,6 +34,9 @@ OST JSON APIs are a set of *asynchronous* methods that make API calls to OST Pla
- [Get Devices](#get-devices)
- [Usage](#usage-6)
- [Sample Response](#sample-response-6)
+ - [Get Redeemable Skus](#get-redeemable-skus)
+ - [Usage](#usage-7)
+ - [Sample Response](#sample-response-7)
@@ -342,6 +348,141 @@ The `getPendingRecoveryForUserId` API will respond with `UNPROCESSABLE_ENTITY` A
}
```
+
+### Get Redeemable Sku Details
+API to get redeemable sku details.
+
+
+##### Usage
+```javascript
+/*
+ Please update userId as per your needs.
+ Since this userId does not belong to your economy, you will get an error if you do not change it.
+*/
+let userId = "71c59448-ff77-484c-99d8-abea8a419836";
+let skuDetailId = "2";
+let extraParams = {};
+
+/**
+ * Api to get redeemable skus
+ * @param {String} userId - Ost User id
+ * @param {String} skuDetailId - Sku detail id got from list of Redeemable skus
+ * @param {Object} extraParams (@nullable).
+ * @param {function} Success callback with success data
+ * @param {function} Failure callback with error and failure response
+ * @public
+ */
+
+
+OstJsonApi.getRedeemableSkuDetails(userId, skuDetailId ,extraParams, (response) => {
+ console.log(response);
+ }, (error)=> {
+ console.log("An error has occurred while fetching redeemable sku details.");
+ console.log( error );
+ });
+```
+
+
+##### Sample Response
+```json
+{
+ "result_type":"redemption_product",
+ "redemption_product":{
+ "status":"active",
+ "images":{
+ "detail":{
+ "original":{
+ "size":90821,
+ "url":"https://dxwfxs8b4lg24.cloudfront.net/ost-platform/rskus/stag-starbucks-d-original.png",
+ "width":150,
+ "height":150
+ }
+ },
+ "cover":{
+ "original":{
+ "size":193141,
+ "url":"https://dxwfxs8b4lg24.cloudfront.net/ost-platform/rskus/stag-starbucks-c-original.png",
+ "width":320,
+ "height":320
+ }
+ }
+ },
+ "availability":[
+ {
+ "country_iso_code":"USA",
+ "country":"USA",
+ "currency_iso_code":"USD",
+ "denominations":[
+ {
+ "amount_in_wei":"49938358",
+ "amount_in_fiat":5
+ },
+ {
+ "amount_in_wei":"99876717",
+ "amount_in_fiat":10
+ },
+ ...
+ ]
+ },
+ {
+ "country_iso_code":"CAN",
+ "country":"Canada",
+ "currency_iso_code":"CAD",
+ "denominations":[
+ {
+ "amount_in_wei":"37547638",
+ "amount_in_fiat":5
+ },
+ {
+ "amount_in_wei":"75095276",
+ "amount_in_fiat":10
+ },
+ ...
+ ]
+ },
+ {
+ "country_iso_code":"GBR",
+ "country":"United Kingdom",
+ "currency_iso_code":"GBP",
+ "denominations":[
+ {
+ "amount_in_wei":"64855011",
+ "amount_in_fiat":5
+ },
+ {
+ "amount_in_wei":"129710022",
+ "amount_in_fiat":10
+ },
+ ...
+ ]
+ },
+ {
+ "country_iso_code":"IND",
+ "country":"India",
+ "currency_iso_code":"INR",
+ "denominations":[
+ {
+ "amount_in_wei":"1396",
+ "amount_in_fiat":0.01
+ },
+ {
+ "amount_in_wei":"139609",
+ "amount_in_fiat":1
+ },
+ ...
+ ]
+ }
+ ],
+ "id":"2",
+ "updated_timestamp":1582024811,
+ "description":{
+ "text":null
+ },
+ "name":"Starbucks"
+ }
+}
+```
+
## List API
All `List` APIs support pagination. The response of all `List` APIs has an extra attribute `meta`.
@@ -546,3 +687,88 @@ OstJsonApi.getDeviceListForUserId(userId, nextPagePayload,
"result_type": "devices"
}
```
+
+
+### Get Redeemable Skus
+API to get redeemable skus.
+
+
+##### Usage
+```javascript
+/*
+ Please update userId as per your needs.
+ Since this userId does not belong to your economy, you will get an error if you do not change it.
+*/
+let userId = "71c59448-ff77-484c-99d8-abea8a419836";
+let nextPagePayload = null;
+
+/**
+ * Api to get redeemable skus
+ * @param {String} userId - Ost User id
+ * @param {Object} nextPagePayload (@nullable). Pass null to get first page.
+ * @param {function} Success callback with success data
+ * @param {function} Failure callback with error and failure response
+ * @public
+ */
+
+
+OstJsonApi.getRedeemableSkus(userId, nextPagePayload , (respones) => {
+ console.log(response);
+ // Let's check if more pages of data is available.
+ if ( response.meta ) {
+ let nextPagePayloadFromResponse = response.meta.next_page_payload || {};
+ if ( Object.keys(nextPagePayloadFromResponse).length > 0 ) {
+ // Next page is available.
+ // Update nextPagePayload
+ nextPagePayload = nextPagePayloadFromResponse;
+ // To fetch the next page, pass the updated nextPagePayload.
+ }
+ }
+ }, (error)=> {
+ console.log("An error has occurred while fetching redeemable skus.");
+ console.log( error );
+ });
+```
+
+
+##### Sample Response
+```json
+{
+ "meta":{
+ "next_page_payload":{
+ }
+ },
+ "result_type":"redemption_products",
+ "redemption_products":[
+ {
+ "status":"active",
+ "updated_timestamp":1582024811,
+ "id":"2",
+ "description":{
+ "text":null
+ },
+ "images":{
+ "detail":{
+ "original":{
+ "size":90821,
+ "url":"https://dxwfxs8b4lg24.cloudfront.net/ost-platform/rskus/stag-starbucks-d-original.png",
+ "width":150,
+ "height":150
+ }
+ },
+ "cover":{
+ "original":{
+ "size":193141,
+ "url":"https://dxwfxs8b4lg24.cloudfront.net/ost-platform/rskus/stag-starbucks-c-original.png",
+ "width":320,
+ "height":320
+ }
+ }
+ },
+ "name":"Starbucks"
+ },
+ ...
+ ...
+ ]
+}
+```
\ No newline at end of file
diff --git a/documentation/OstRedemptionConfig.md b/documentation/OstRedemptionConfig.md
new file mode 100644
index 0000000..efd5e6f
--- /dev/null
+++ b/documentation/OstRedemptionConfig.md
@@ -0,0 +1,46 @@
+# OstWallet Redemption Config
+
+## Introduction
+
+App developers can configure the text shown on redemption page.
+
+To configure the content, the sdk needs to be provided with [JSON object](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/JSON).
+
+## Dictionary Data Structure
+
+Here is the small sample json representation of the configuration.
+
+```json
+{
+ "common": {
+ "walletIcon": null,
+ "storeIcon": null
+ },
+ "skuListScreen": {
+ "navHeader": null,
+ "header": null,
+ "description": null
+ },
+ "skuDetailsScreen": {
+ "navHeader": null
+ }
+}
+```
+In the above example:
+
+* The key `common` includes options to configure the wallet icon in header and the store icon.
+* The key `skuListScreen` allows to configure the navigation header and the header and description shown on the redemption store list page.
+* The key `skuDetailsScreen` allows to configure the navigation header shown on the redemption store details page.
+
+## Redemption Config
+
+The following are the customizable options provided. These can be set in the above config.
+
+| customizable component | Config Keys | Config Type |
+| -------------------------------------- | -------------------------------------------------- | ------------------------------- |
+| Wallet icon | - walletIcon | Custom icon Image to be rendered. |
+| Store icon | - storeIcon | Custom icon Image to be rendered. |
+| Skulist Header | - skuListScreen.header | String |
+| Skulist Description | - skuListScreen.description | String |
+| SkuDetails Screen Navigation header | - skuDetailsScreen.navHeader | String |
+| Skulist Screen Navigation header | - skuListScreen.navHeader | String|
diff --git a/documentation/OstRedemptionFlow.md b/documentation/OstRedemptionFlow.md
new file mode 100644
index 0000000..1262591
--- /dev/null
+++ b/documentation/OstRedemptionFlow.md
@@ -0,0 +1,56 @@
+# OstRedemption Flow
+
+## Introduction
+
+OstRedemption component is a pre-built UI component available exclusively in `ost-wallet-sdk-react-native` SDK.
+It consist two pages - one displaying redeemable product list and another displaying product details and redemption options. It can be used by end-users to integrate redemption flow into their app.
+> IMPORTANT: This feature requires application to use [React Navigation](https://reactnavigation.org/docs/en/getting-started.html) package.
+
+## Usage
+
+### Create Redemption Flow stack navigation
+
+```js
+import {OstRedeemableSkus, OstRedeemableSkuDetails } from '@ostdotcom/ost-wallet-sdk-react-native';
+
+let redemptionStack = createStackNavigator(
+ {
+ RedeemableSkusScreen: OstRedeemableSkus,
+ RedeemableSkuDetails: OstRedeemableSkuDetails
+ }
+);
+```
+
+### Navigate to settings page
+`ostUserId` and `ostWalletUIWorkflowCallback` are mandetory parameters that need to be passed as params to the `RedeemableSkusScreen` screen.
+```js
+const ostUserId =
+const delegate = new OstWalletUIWorkflowCallback(ostUserId, {})
+this.props.navigation.push("RedeemableSkusScreen", {'ostUserId': ostUserId ,
+ 'ostWalletUIWorkflowCallback': delegate,
+ 'navTitle': 'My Store'});
+```
+
+>Note
+> Developer needs to create a class extends from `OstWalletUIWorkflowCallback` and write logic to get passphrase prefix from their application server.
+> Please refer [this](OstWalletUI.md#setup-your-passphrase-prefix-delegate) section for documentation.
+
+## UI Customization
+
+Developer can customize Redemption flow by updating respective properties mentioned in image. OstTheme config shown [here](./configs/ost-sdk-theme-config.js)
+
+![copy-framework-file](images/redemptionFlow.png)
+
+## Redemption Content
+
+Developer can make various modifications in redemption flow component. To modify contet, [refer here](./OstRedemptionConfig.md).
+
+```js
+import ost_sdk_redemption_config from "../../theme/ostsdk/ost-sdk-redemption-config";
+import { OstRedemableCustomConfig } from '@ostdotcom/ost-wallet-sdk-react-native';
+
+ OstRedemableCustomConfig.setConfig(ost_sdk_redemption_config);
+
+```
+
+
diff --git a/documentation/OstWalletSettingsConfig.md b/documentation/OstWalletSettingsConfig.md
index 9059d36..0cde409 100644
--- a/documentation/OstWalletSettingsConfig.md
+++ b/documentation/OstWalletSettingsConfig.md
@@ -74,4 +74,3 @@ Some workflows requires additional data. It can be passed to workflow by setting
| add_session | - spending_limit | String |
| | - expiration_time | Number |
-
diff --git a/documentation/OstWalletUI.md b/documentation/OstWalletUI.md
index 6310885..6c5b41d 100644
--- a/documentation/OstWalletUI.md
+++ b/documentation/OstWalletUI.md
@@ -236,6 +236,9 @@ OstWalletSdkUI.subscribe(workflowId, OstWalletSdkUI.EVENTS.flowInterrupt, (ostWo
#### Add Session
A session is a period of time during which a sessionKey is authorized to sign transactions under a pre-set limit per transaction on behalf of the user. The device manager, which controls the tokens, authorizes sessions.
+
+* By Calling function
+
```js
let uiCallback = new UserPassphrasePrefixDelegate()
@@ -271,6 +274,80 @@ OstWalletSdkUI.subscribe(workflowId, OstWalletSdkUI.EVENTS.flowInterrupt, (ostWo
});
```
+* By scanning QR-Code
+
+QR Code Sample
+```
+as|2.0.0|2a421359d02132e8161cda9518aeaa62647b648e|5369b4d7e0e53e1159d6379b989a8429a7b2dd59|1|1583308559|4d40c46a7302974134a67ce77bdfae0e1f78ee518e87b6cda861ffc5847dfaca11a653651c6cdfadf0224574f6f07e1a78aabacdfed66d8c78e1fb2c9bc750161c
+```
+
+```js
+
+let uiCallback = new UserPassphrasePrefixDelegate()
+
+/**
+ * Add user session
+ * @param {String} userId - Ost User id
+ * @param {OstWalletUIWorkflowCallback} uiCallback - callback implementation instances for application communication
+ * @public
+ */
+let workflowId = OstWalletSdkUI.scanQRCodeToAuthorizeSession(
+ userId,
+ uiCallback
+)
+
+// Subscribe to events
+OstWalletSdkUI.subscribe(workflowId, OstWalletSdkUI.EVENTS.requestAcknowledged, () => {
+ // Session is being added.
+});
+
+OstWalletSdkUI.subscribe(workflowId, OstWalletSdkUI.EVENTS.flowComplete, (ostWorkflowContext , ostContextEntity) => {
+ // Show success message to user.
+ // Session has been added.
+});
+
+OstWalletSdkUI.subscribe(workflowId, OstWalletSdkUI.EVENTS.flowInterrupt, (ostWorkflowContext , ostError) => {
+ // Show error to user.
+ // An error occoured during the workflow. The Session has NOT been added.
+});
+```
+
+
+* With QR Code Payload
+
+```js
+
+let uiCallback = new UserPassphrasePrefixDelegate()
+let payload = "as|2.0.0|2a421359d02132e8161cda9518aeaa62647b648e|5369b4d7e0e53e1159d6379b989a8429a7b2dd59|1|1583308559|4d40c46a7302974134a67ce77bdfae0e1f78ee518e87b6cda861ffc5847dfaca11a653651c6cdfadf0224574f6f07e1a78aabacdfed66d8c78e1fb2c9bc750161c"
+/**
+ * Authorize browser session with QR code payload
+ * @param {String} userId - Ost User id
+ * @param {String} qrPayload - QR-Code payload
+ * @param {OstWalletUIWorkflowCallback} uiCallback - callback implementation instances for application communication
+ * @return {*|number}
+ */
+let workflowId = OstWalletSdkUI.authorizeSessionWithQRPayload(
+ userId,
+ payload,
+ uiCallback
+)
+
+// Subscribe to events
+OstWalletSdkUI.subscribe(workflowId, OstWalletSdkUI.EVENTS.requestAcknowledged, () => {
+ // Session is being added.
+});
+
+OstWalletSdkUI.subscribe(workflowId, OstWalletSdkUI.EVENTS.flowComplete, (ostWorkflowContext , ostContextEntity) => {
+ // Show success message to user.
+ // Session has been added.
+});
+
+OstWalletSdkUI.subscribe(workflowId, OstWalletSdkUI.EVENTS.flowInterrupt, (ostWorkflowContext , ostError) => {
+ // Show error to user.
+ // An error occoured during the workflow. The Session has NOT been added.
+});
+```
+
#### Get Mnemonic Phrase
The mnemonic phrase represents a human-readable way to authorize a new device. This phrase is 12 words long.
```js
@@ -553,6 +630,8 @@ QR-Code Sample:
}
```
+* By scanning QR-Code
+
```js
let uiCallback = new UserPassphrasePrefixDelegate();
@@ -579,6 +658,37 @@ QR-Code Sample:
// An error occoured during the workflow.
});
```
+
+* with QR Code payload
+Developer can pass QR code payload to authorize device. QR code scanner view won't open if developer pass payload.
+
+```js
+ let uiCallback = new UserPassphrasePrefixDelegate();
+ let payload = "{\"dd\":\"AD\",\"ddv\":\"1.1.0\",\"d\":{\"da\":\"0x7701af46018fc57c443b63e839eb24872755a2f8\"}}"
+ /**
+ * Authorize device with QR code payload
+ * @param {String} userId - Ost User id
+ * @param {String} qrPayload - QR-Code payload
+ * @param {OstWalletUIWorkflowCallback} uiCallback - callback implementation instances for application communication
+ * @return {*|number}
+ */
+ let workflowId = OstWalletSdkUI.authorizeDeviceWithQRPayload(userId, payload, uiCallback);
+
+ // Subscribe to events
+ OstWalletSdkUI.subscribe(workflowId, OstWalletSdkUI.EVENTS.requestAcknowledged, () => {
+ // Device is being authorized.
+ });
+
+ OstWalletSdkUI.subscribe(workflowId, OstWalletSdkUI.EVENTS.flowComplete, (ostWorkflowContext , ostContextEntity) => {
+ // Show success message to user.
+ // Device has been authorized.
+ });
+
+ OstWalletSdkUI.subscribe(workflowId, OstWalletSdkUI.EVENTS.flowInterrupt, (ostWorkflowContext , ostError) => {
+ // Show error to user.
+ // An error occoured during the workflow.
+ });
+```
### Execute Transaction
* By Scanning QR-Code
@@ -641,6 +751,37 @@ QR-Code Sample:
});
```
+* with QR Code payload
+Developer can pass QR code payload to execute transaction. QR code scanner view won't appear if developer pass payload.
+
+```js
+ let uiCallback = new UserPassphrasePrefixDelegate();
+ let payload =
+ /**
+ * Execute transaction with QR code payload
+ * @param {String} userId - Ost User id
+ * @param {String} qrPayload - QR-Code payload
+ * @param {OstWalletUIWorkflowCallback} uiCallback - callback implementation instances for application communication
+ * @return {*|number}
+ */
+ let workflowId = OstWalletSdkUI.executeTransactionWithQRPayload(userId, payload, uiCallback);
+
+ // Subscribe to events
+ OstWalletSdkUI.subscribe(workflowId, OstWalletSdkUI.EVENTS.requestAcknowledged, () => {
+ // Device is being authorized.
+ });
+
+ OstWalletSdkUI.subscribe(workflowId, OstWalletSdkUI.EVENTS.flowComplete, (ostWorkflowContext , ostContextEntity) => {
+ // Show success message to user.
+ // Device has been authorized.
+ });
+
+ OstWalletSdkUI.subscribe(workflowId, OstWalletSdkUI.EVENTS.flowInterrupt, (ostWorkflowContext , ostError) => {
+ // Show error to user.
+ // An error occoured during the workflow.
+ });
+```
+
* By Calling function
Helper method creates session if active sessoin for transction amount is not available. To execute transaction via helper method, [ref here](./OstTransactionHelper.md)
diff --git a/documentation/configs/ost-sdk-theme-config.js b/documentation/configs/ost-sdk-theme-config.js
index 899e7d4..ce8bc18 100644
--- a/documentation/configs/ost-sdk-theme-config.js
+++ b/documentation/configs/ost-sdk-theme-config.js
@@ -1,5 +1,3 @@
-import colors from '../styles/Colors'
-
export default {
"nav_bar_logo_image": {
@@ -12,6 +10,10 @@ export default {
"system_font_weight": "bold",
"alignment": "left"
},
+ "b1":{
+ "color":"#ffffff",
+ "size":15
+ },
"c2": {
"size": 12,
@@ -21,7 +23,7 @@ export default {
},
"navigation_bar": {
- "tint_color": colors.brightSky
+ "tint_color": "#61B2C9"
},
"navigation_bar_header": {
@@ -33,7 +35,8 @@ export default {
"tint_color": "#ffffff"
},
"back":{
- "tint_color": "#ffffff"
+ "tint_color": "#ffffff",
+ "source" : null
}
},
@@ -53,5 +56,13 @@ export default {
"color": "#0F9D58",
"system_font_weight": "regular",
"alignment": "left",
+ },
+
+ "form_field": {
+ "size":15,
+ "system_font_weight":"regular",
+ "color":"#484848",
+ "border_color": "#484848",
+ "alignment": "left"
}
}
diff --git a/documentation/images/redemptionFlow.png b/documentation/images/redemptionFlow.png
new file mode 100644
index 0000000..d9ab106
Binary files /dev/null and b/documentation/images/redemptionFlow.png differ
diff --git a/documentation/ios_setup.md b/documentation/ios_setup.md
index 1fb4310..11ebef6 100644
--- a/documentation/ios_setup.md
+++ b/documentation/ios_setup.md
@@ -15,7 +15,7 @@ Carthage looks at a file called `Cartfile` to determine which libraries to insta
Add following entry in your `Cartfile`
```bash
- github "ostdotcom/ost-wallet-sdk-ios" == 2.3.6
+ github "ostdotcom/ost-wallet-sdk-ios" == 2.4.1
```
Now to actually install everything run the following in your terminal:
diff --git a/ios/ostwalletrnsdk/OstWalletRnSdkApi.m b/ios/ostwalletrnsdk/OstWalletRnSdkApi.m
index 3469003..a3094b5 100644
--- a/ios/ostwalletrnsdk/OstWalletRnSdkApi.m
+++ b/ios/ostwalletrnsdk/OstWalletRnSdkApi.m
@@ -75,4 +75,25 @@ @implementation OstWalletRnSdkApi
[OstJsonApi getCurrentDeviceForUserId:userId delegate:delegate];
}
+
+RCT_EXPORT_METHOD(getRedeemableSkus: (NSString * _Nonnull)userId
+ params: (NSDictionary * _Nullable)params
+ successCallback: (RCTResponseSenderBlock _Nonnull) successCallback
+ errorCallback: (RCTResponseSenderBlock _Nonnull) errorCallback )
+{
+ OstJsonApiCallbackImpl *delegate = [[OstJsonApiCallbackImpl alloc]initWithSuccess:successCallback errorCallback:errorCallback];
+ [OstJsonApi getRedeemableSkusWithUserId:userId params:params delegate:delegate];
+}
+
+
+RCT_EXPORT_METHOD(getRedeemableSkuDetails: (NSString * _Nonnull)userId
+ skuId: (NSString * _Nonnull)skuId
+ params: (NSDictionary * _Nullable)params
+ successCallback: (RCTResponseSenderBlock _Nonnull) successCallback
+ errorCallback: (RCTResponseSenderBlock _Nonnull) errorCallback )
+{
+ OstJsonApiCallbackImpl *delegate = [[OstJsonApiCallbackImpl alloc]initWithSuccess:successCallback errorCallback:errorCallback];
+ [OstJsonApi getRedeemableSkuDetailsWithUserId:userId skuId:skuId params:params delegate: delegate];
+}
+
@end
diff --git a/ios/ostwalletrnsdk/OstWorkFlowCallbackImpl.m b/ios/ostwalletrnsdk/OstWorkFlowCallbackImpl.m
index e55f43b..47135fb 100644
--- a/ios/ostwalletrnsdk/OstWorkFlowCallbackImpl.m
+++ b/ios/ostwalletrnsdk/OstWorkFlowCallbackImpl.m
@@ -199,6 +199,7 @@ - (NSString *) getWorkflowTypeName: (OstWorkflowType) workflowType {
case OstWorkflowTypeLogoutAllSessions: return @"LOGOUT_ALL_SESSIONS";
case OstWorkflowTypeUpdateBiometricPreference: return @"UPDATE_BIOMETRIC_PREFERENCE";
case OstWorkflowTypeShowDeviceQR: return @"SHOW_DEVICE_QR";
+ case OstWorkflowTypeAuthorizeSessionWithQRCode: return @"AUTHORIZE_SESSION_WITH_QR_CODE";
default: return @"UNKNOWN";
}
}
diff --git a/ios/ostwalletrnsdk/sdkWithUI/OstUICallbackImpl.m b/ios/ostwalletrnsdk/sdkWithUI/OstUICallbackImpl.m
index a9bfef5..7c93b19 100644
--- a/ios/ostwalletrnsdk/sdkWithUI/OstUICallbackImpl.m
+++ b/ios/ostwalletrnsdk/sdkWithUI/OstUICallbackImpl.m
@@ -144,6 +144,7 @@ - (NSString *) getWorkflowTypeName: (OstWorkflowType) workflowType {
case OstWorkflowTypeLogoutAllSessions: return @"LOGOUT_ALL_SESSIONS";
case OstWorkflowTypeUpdateBiometricPreference: return @"UPDATE_BIOMETRIC_PREFERENCE";
case OstWorkflowTypeShowDeviceQR: return @"SHOW_DEVICE_QR";
+ case OstWorkflowTypeAuthorizeSessionWithQRCode: return @"AUTHORIZE_SESSION_WITH_QR_CODE";
default: return @"UNKNOWN";
}
}
diff --git a/ios/ostwalletrnsdk/sdkWithUI/OstWalletRnSdkUI.m b/ios/ostwalletrnsdk/sdkWithUI/OstWalletRnSdkUI.m
index d72ee65..1550651 100644
--- a/ios/ostwalletrnsdk/sdkWithUI/OstWalletRnSdkUI.m
+++ b/ios/ostwalletrnsdk/sdkWithUI/OstWalletRnSdkUI.m
@@ -159,26 +159,44 @@ @implementation OstWalletRnSdkUI
}
RCT_EXPORT_METHOD(scanQRCodeToAuthorizeDevice: (NSString * _Nonnull) userId
+ addDevicePayload: (NSString * _Nullable) addDevicePayload
uuid:(NSString *)uuid) {
OstUICallbackImpl *uiCallbackImpl = [[OstUICallbackImpl alloc]initWithId:uuid];
NSString *worklfowId = [OstWalletUI scanQRCodeToAuthorizeDeviceWithUserId: userId
+ addDevicePayload: addDevicePayload
passphrasePrefixDelegate: uiCallbackImpl];
[OstWalletUI subscribeWithWorkflowId:worklfowId listner: uiCallbackImpl];
}
RCT_EXPORT_METHOD(scanQRCodeToExecuteTransaction: (NSString * _Nonnull) userId
+ executeTransactionPayload: (NSString * _Nullable) executeTransactionPayload
uuid:(NSString *)uuid) {
OstUICallbackImpl *uiCallbackImpl = [[OstUICallbackImpl alloc]initWithId:uuid];
NSString *worklfowId = [OstWalletUI scanQRCodeToExecuteTransactionWithUserId: userId
+ executeTransactionPayload: executeTransactionPayload
passphrasePrefixDelegate: uiCallbackImpl];
[OstWalletUI subscribeWithWorkflowId:worklfowId listner: uiCallbackImpl];
}
+
+RCT_EXPORT_METHOD(scanQRCodeToAuthorizeSession: (NSString * _Nonnull) userId
+ qrPayload: (NSString * _Nullable) qrPayload
+ uuid:(NSString *)uuid) {
+
+ OstUICallbackImpl *uiCallbackImpl = [[OstUICallbackImpl alloc]initWithId:uuid];
+
+ NSString *worklfowId = [OstWalletUI scanQRCodeToAuthorizeSessionWithUserId: userId
+ qrPayload: qrPayload
+ passphrasePrefixDelegate: uiCallbackImpl];
+
+ [OstWalletUI subscribeWithWorkflowId:worklfowId listner: uiCallbackImpl];
+}
+
@end
diff --git a/js/OstSdkErrorMessages.json b/js/OstSdkErrorMessages.json
index d3234d5..d6ba519 100644
--- a/js/OstSdkErrorMessages.json
+++ b/js/OstSdkErrorMessages.json
@@ -3,6 +3,8 @@
},
"ADD_SESSION": {
},
+ "AUTHORIZE_SESSION_WITH_QR_CODE": {
+ },
"GET_DEVICE_MNEMONICS": {
},
"PERFORM_QR_ACTION": {
@@ -24,21 +26,22 @@
"EXECUTE_TRANSACTION": {
},
"__DEFAULT_CONTEXT": {
+ "INVALID_SIGNATURE": "The QR code does not contain valid signature.",
"USER_UNAUTHORIZED": "Device is not authorized. Please authorize device again.",
"DEVICE_OUT_OF_SYNC": "Device time is out of sync. Please check the time on your device reflects current date and time.",
"NETWORK_ERROR": "Request could not be executed due to cancellation, a connectivity problem or timeout.",
"INVALID_MNEMONICS": "The 12 word passphrase you provided is incorrect. ",
- "INVALID_QR_TRANSACTION_DATA": "The QR code for executing a transaction is not well formed. To know the data definition for QR code based on type of operations please visit https://dev.ost.com/platform ",
+ "INVALID_QR_TRANSACTION_DATA": "The QR code for executing a transaction is not well formed.",
"INVALID_USER_PASSPHRASE": "The 6 digit PIN you entered is not correct.",
"MAX_PASSPHRASE_VERIFICATION_LIMIT_REACHED": "The maximum number of 'authenticating with PIN' attempts has been reached. Please try again a bit later.",
"DEVICE_CAN_NOT_BE_AUTHORIZED": "Unable to authorize this device. Please ensure the device is 'Registered' for this user with OST platform. Only a registered device can be authorized.",
- "SESSION_NOT_FOUND": "The device doesn't has any active session. Please authorize a session before doing any transaction. Workflow details provided at https://dev.ost.com/platform/docs/sdk/references ",
+ "SESSION_NOT_FOUND": "The device doesn't has any active session. Please authorize a session before doing any transaction.",
"INVALID_QR_CODE": "Incorrect QR code.",
"RECOVERY_KEY_GENERATION_FAILED": "Failed to generate Recovery key. Inspect if a correct input values required are being sent and re-submit the request. ",
"OUT_OF_MEMORY_ERROR": "Device is running low on memory. Reduce the number of App running on your device and re-enter the pin",
"WORKFLOW_FAILED": "Something went wrong, please try again",
"WORKFLOW_VIEW_DESTROYED": "The application interrupted the workflow. The view got terminated while performing the workflow",
- "DEVICE_UNAUTHORIZED": "Unable to perform the operation as the device not authorized. For details on how to authorize a device please visit https://dev.ost.com/platform/docs/sdk/references ",
+ "DEVICE_UNAUTHORIZED": "Unable to perform the operation as the device not authorized.",
"DEVICE_CAN_NOT_BE_REVOKED": "Cannot complete the revoke device operation. Only an authorized device can be revoked. Please ensure you are trying to revoke a valid device and re-submit the request.",
"WORKFLOW_CANCELED": "WORKFLOW_CANCELLED",
"WORKFLOW_CANCELLED": "WORKFLOW_CANCELLED"
diff --git a/js/OstWalletSdkUI.js b/js/OstWalletSdkUI.js
index c3d3709..1c33df4 100644
--- a/js/OstWalletSdkUI.js
+++ b/js/OstWalletSdkUI.js
@@ -54,27 +54,27 @@ class OstWalletRNSdkUI {
}
/**
- * Initiate device recovery
+ * Initiate device recovery
* @param {String} userId - Ost User id
* @param {String} deviceAddressToRecover - Device address which wants to recover
- * @param {OstWalletUIWorkflowCallback} uiCallback - callback implementation instances for application communication
+ * @param {OstWalletUIWorkflowCallback} uiCallback - callback implementation instances for application communication
* @public
*/
initiateDeviceRecovery(userId, deviceAddressToRecover, uiCallback ) {
let coreUiCallback = this._getCoreUiCallback(uiCallback);
- OstWalletSdkUI.initiateDeviceRecovery( userId, deviceAddressToRecover, coreUiCallback.uuid );
+ OstWalletSdkUI.initiateDeviceRecovery( userId, deviceAddressToRecover, coreUiCallback.uuid );
return coreUiCallback.uuid;
}
/**
- * Abort device recovery
+ * Abort device recovery
* @param {String} userId - Ost User id
- * @param {OstWalletUIWorkflowCallback} uiCallback - callback implementation instances for application communication
+ * @param {OstWalletUIWorkflowCallback} uiCallback - callback implementation instances for application communication
* @public
*/
abortDeviceRecovery( userId, uiCallback ) {
let coreUiCallback = this._getCoreUiCallback(uiCallback);
- OstWalletSdkUI.abortDeviceRecovery( userId, coreUiCallback.uuid);
+ OstWalletSdkUI.abortDeviceRecovery( userId, coreUiCallback.uuid);
return coreUiCallback.uuid;
}
@@ -180,7 +180,45 @@ class OstWalletRNSdkUI {
*/
scanQRCodeToAuthorizeDevice(userId, uiCallback) {
let coreUiCallback = this._getCoreUiCallback(uiCallback);
- OstWalletSdkUI.scanQRCodeToAuthorizeDevice( userId, coreUiCallback.uuid );
+ OstWalletSdkUI.scanQRCodeToAuthorizeDevice( userId, null, coreUiCallback.uuid );
+ return coreUiCallback.uuid;
+ }
+
+ /**
+ * Authorize device with QR code payload
+ * @param {String} userId - Ost User id
+ * @param {String} qrPayload - QR-Code payload
+ * @param {OstWalletUIWorkflowCallback} uiCallback - callback implementation instances for application communication
+ * @return {*|number}
+ */
+ authorizeDeviceWithQRPayload(userId, qrPayload, uiCallback) {
+ let coreUiCallback = this._getCoreUiCallback(uiCallback);
+ OstWalletSdkUI.scanQRCodeToAuthorizeDevice( userId, qrPayload, coreUiCallback.uuid );
+ return coreUiCallback.uuid;
+ }
+
+ /**
+ * Authorize browser session via QR code
+ * @param {String} userId - Ost User id
+ * @param {OstWalletUIWorkflowCallback} uiCallback - callback implementation instances for application communication
+ * @public
+ */
+ scanQRCodeToAuthorizeSession(userId, uiCallback) {
+ let coreUiCallback = this._getCoreUiCallback(uiCallback);
+ OstWalletSdkUI.scanQRCodeToAuthorizeSession( userId, null ,coreUiCallback.uuid );
+ return coreUiCallback.uuid;
+ }
+
+ /**
+ * Authorize browser session with QR code payload
+ * @param {String} userId - Ost User id
+ * @param {String} qrPayload - QR-Code payload
+ * @param {OstWalletUIWorkflowCallback} uiCallback - callback implementation instances for application communication
+ * @return {*|number}
+ */
+ authorizeSessionWithQRPayload(userId, qrPayload, uiCallback) {
+ let coreUiCallback = this._getCoreUiCallback(uiCallback);
+ OstWalletSdkUI.scanQRCodeToAuthorizeSession( userId, qrPayload, coreUiCallback.uuid );
return coreUiCallback.uuid;
}
@@ -193,7 +231,20 @@ class OstWalletRNSdkUI {
*/
scanQRCodeToExecuteTransaction(userId, uiCallback) {
let coreUiCallback = this._getCoreUiCallback(uiCallback);
- OstWalletSdkUI.scanQRCodeToExecuteTransaction( userId, coreUiCallback.uuid );
+ OstWalletSdkUI.scanQRCodeToExecuteTransaction( userId, null, coreUiCallback.uuid );
+ return coreUiCallback.uuid;
+ }
+
+ /**
+ * Execute transaction with QR code payload
+ * @param {String} userId - Ost User id
+ * @param {String} qrPayload - QR-Code payload
+ * @param {OstWalletUIWorkflowCallback} uiCallback
+ * @return {*|number}
+ */
+ executeTransactionWithQRPayload(userId, qrPayload, uiCallback) {
+ let coreUiCallback = this._getCoreUiCallback(uiCallback);
+ OstWalletSdkUI.scanQRCodeToExecuteTransaction( userId, qrPayload, coreUiCallback.uuid );
return coreUiCallback.uuid;
}
@@ -280,9 +331,9 @@ class OstWalletRNSdkUI {
_getCoreUiCallback(uiCallback) {
if ( !uiCallback || !(uiCallback instanceof OstWalletUIWorkflowCallback) ) {
let err = new Error('Invalid uiCallback. The argument \'uiCallback\' must be an instanceof OstWalletUIWorkflowCallback');
- }
+ }
return new OstWalletUICoreCallback(uiCallback);
}
}
-export default new OstWalletRNSdkUI();
\ No newline at end of file
+export default new OstWalletRNSdkUI();
diff --git a/js/Redemptions/CommonComponents/AlertBox.js b/js/Redemptions/CommonComponents/AlertBox.js
new file mode 100644
index 0000000..7818cd6
--- /dev/null
+++ b/js/Redemptions/CommonComponents/AlertBox.js
@@ -0,0 +1,30 @@
+import { Alert } from "react-native";
+
+class AlertBox{
+
+ constructor(config){
+ this.config = config || {};
+ }
+
+ showAlert(){
+ Alert.alert(
+ this.config.title,
+ this.config.Message,
+ [
+ {
+ text: this.config.cancelBtnText,
+ onPress: () => {this.config.cancelCallBack && this.config.cancelCallBack()},
+ style: this.config.cancelStyle,
+ },
+ {
+ text: this.config.successBtnText,
+ onPress: () => {this.config.successCallback && this.config.successCallback()}
+ },
+ ],
+ {cancelable: false},
+ )
+ }
+
+}
+
+export default AlertBox;
\ No newline at end of file
diff --git a/js/Redemptions/CommonComponents/BackArrow.js b/js/Redemptions/CommonComponents/BackArrow.js
new file mode 100644
index 0000000..8186d0a
--- /dev/null
+++ b/js/Redemptions/CommonComponents/BackArrow.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import {View , Image } from "react-native";
+import OstThemeConfigHelper from "../../helpers/OstThemeConfigHelper";
+import inlineStyle from "./styles";
+
+export default () => (
+
+
+
+);
diff --git a/js/Redemptions/CommonComponents/HeaderRight.js b/js/Redemptions/CommonComponents/HeaderRight.js
new file mode 100644
index 0000000..32178df
--- /dev/null
+++ b/js/Redemptions/CommonComponents/HeaderRight.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { View, Image, Text } from 'react-native';
+
+import OstRedemableCustomConfig from "../RedemableCustomConfig";
+import OstThemeConfigHelper from "../../helpers/OstThemeConfigHelper";
+import styles from './styles';
+
+export default (props) => {
+ const walletIcon = OstRedemableCustomConfig.getWalletIconUri()
+ return (
+
+ {walletIcon ? : }
+ {props.balance}
+
+ )
+}
\ No newline at end of file
diff --git a/js/Redemptions/CommonComponents/styles.js b/js/Redemptions/CommonComponents/styles.js
new file mode 100644
index 0000000..0e9a340
--- /dev/null
+++ b/js/Redemptions/CommonComponents/styles.js
@@ -0,0 +1,33 @@
+import { StyleSheet } from 'react-native';
+
+let stylesMap = StyleSheet.create({
+ backArrowImageWrapper : {
+ width: 50,
+ height: 50,
+ justifyContent: 'center',
+ alignItems: 'flex-start',
+ paddingLeft: 10
+ },
+ backArrowStyle : {
+ width: 10,
+ height: 18,
+ aspectRatio: 1
+ },
+ walletImgSkipFont : {
+ width: 20,
+ height: 18,
+ aspectRatio: 1,
+ marginRight: 8
+ },
+ headerRightWrapper : {
+ paddingRight: 15,
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center'
+ },
+ balanceText: {
+ alignSelf: 'center'
+ }
+});
+
+export default styles = stylesMap;
\ No newline at end of file
diff --git a/js/Redemptions/MultipleClickHandler.js b/js/Redemptions/MultipleClickHandler.js
new file mode 100644
index 0000000..871cb27
--- /dev/null
+++ b/js/Redemptions/MultipleClickHandler.js
@@ -0,0 +1,15 @@
+export default (func, wait = 500) => {
+ let tapCount = 0;
+ let handler;
+
+ return function() {
+ if (tapCount === 0) {
+ tapCount++;
+ func();
+ }
+ // Clear the previous timeout and set a new one.
+ clearTimeout(handler);
+ handler = setTimeout(() => (tapCount = 0), wait);
+ };
+ };
+
\ No newline at end of file
diff --git a/js/Redemptions/RedeemableSkuDetails/index.js b/js/Redemptions/RedeemableSkuDetails/index.js
new file mode 100644
index 0000000..2a7992a
--- /dev/null
+++ b/js/Redemptions/RedeemableSkuDetails/index.js
@@ -0,0 +1,578 @@
+import React,{PureComponent} from 'react';
+import {
+ View,
+ Text,
+ Image,
+ ScrollView,
+ KeyboardAvoidingView,
+ TextInput,
+ TouchableOpacity,
+ ActivityIndicator,
+ Platform
+} from 'react-native';
+import RNPickerSelect from 'react-native-picker-select';
+
+import HeaderRight from "../CommonComponents/HeaderRight";
+import BackArrow from '../CommonComponents/BackArrow';
+import OstJsonApi from "../../OstJsonApi";
+import OstRedemptionTransactionHelper from "../RedemptionTransactionHelper";
+import {OstTransactionHelper} from "../../TransactionHelper/OstTransactionHelper";
+import OstWalletSdkUI from "../../OstWalletSdkUI";
+import OstThemeConfigHelper from '../../helpers/OstThemeConfigHelper';
+import OstRedmptionConfig from "../ost-redemption-config";
+import tokenHelper from "../../helpers/TokenHelper";
+import AlertBox from "../CommonComponents/AlertBox";
+import msgIcon from '../../../assets/msg-icon.png';
+import downArrow from '../../../assets/down-arrow.png';
+import multipleClickHandler from '../MultipleClickHandler';
+import {sdkErrorHelper} from "../../helpers/OstSdkErrorHelper";
+import OstWalletSdkHelper from "../../helpers/OstWalletSdkHelper";
+import OstRedemableCustomConfig from "../RedemableCustomConfig";
+
+import {stylesMap} from './styles';
+
+const errorMsgs = {
+ generalError: "Failed to redeem, please try again later.",
+ emailRequired: "Email address is required.",
+ inSufficientbalance : "Insufficient wallet balance.",
+ invalidAmount : "Given token amount is invalid."
+}
+
+const apiErrorParameterKey = "redemption_meta";
+
+function __getParam(navigation , paramName) {
+ if(navigation && navigation.getParam){
+ return navigation.getParam(paramName);
+ }
+ return null;
+}
+
+
+class OstRedeemableSkuDetails extends PureComponent{
+ static navigationOptions = ({ navigation }) => {
+ const balance = __getParam(navigation , "balance") || 0 ,
+ isCustomBack = !!OstThemeConfigHelper.getBackArrowSource()
+ ;
+ let navigationOption = {
+ title: __getParam(navigation , "navTitle") || OstRedemableCustomConfig.getSkuDetailsScreenNavHeader(),
+ headerStyle: {
+ borderBottomWidth: 0,
+ shadowColor: '#000',
+ shadowOffset: {
+ width: 0,
+ height: 1
+ },
+ shadowOpacity: 0.1,
+ shadowRadius: 3
+ },
+ headerBackTitle: null,
+ headerRight:
+ };
+ if( isCustomBack ){
+ navigationOption["headerBackImage"] = ;
+ }
+ return Object.assign({}, navigationOption, OstThemeConfigHelper.getNavigationHeaderConfig());
+ };
+
+ constructor(props){
+ super(props);
+ this.navigation = props.navigation ;
+ this.initTheme();
+ this.ostUserId = props.ostUserId || __getParam(props.navigation, "ostUserId") ;
+ this.ostWalletUIWorkflowCallback = props.ostWalletUIWorkflowCallback || __getParam(props.navigation, "ostWalletUIWorkflowCallback");
+ this.skuDetails = props.redemptionSku || __getParam(props.navigation, "redemptionSku") || {};
+ this.onPurchaseSuccess = props.onPurchaseSuccess || __getParam(props.navigation, "onPurchaseSuccess") ;
+
+ if(!this.skuDetails) return;
+
+ this.inputRefs = {
+ countryPicker : null,
+ currencyPicker : null,
+ emailIdInput : null
+ };
+
+ this.state={
+ refreshing : true ,
+ selectedAvailability : null,
+ selectedDenomination : null,
+ transactionSuccess: false,
+ errorText : "",
+ isPurchasing: false,
+ emailId : ""
+ };
+ }
+
+ __setState = (state={}) => {
+ this.setState(state);
+ };
+
+ componentDidMount(){
+ this.init();
+ }
+
+ componentWillUnmount (){
+ this.__setState = () =>{};
+ this.inputRefs.countryPicker = null;
+ this.inputRefs.currencyPicker = null;
+ this.inputRefs.emailIdInput = null;
+ this.navigation = null ;
+ }
+
+ initTheme(){
+ OstThemeConfigHelper.updateConfig().then((res)=> {
+ this.props.navigation && this.props.navigation.setParams && this.props.navigation.setParams({"ostThemeUpdated": true});
+ }).catch((error)=> {})
+ }
+
+ init(){
+ if(!tokenHelper.token){
+ tokenHelper.init(this.ostUserId).then(()=>{
+ this.updateBalance();
+ }).catch(()=> {});
+ }else{
+ this.updateBalance();
+ }
+ this.fetchDetails();
+ }
+
+ updateBalance(){
+ OstJsonApi.getBalanceForUserId(this.ostUserId, (res) => {
+ let balance = res.balance && res.balance.available_balance;
+ balance = tokenHelper.toBtPrecision(tokenHelper.fromDecimal(balance));
+ this.navigation && this.navigation.setParams && this.navigation.setParams({
+ balance
+ })
+ }, () => {});
+ }
+
+ fetchDetails = () => {
+ if(this.skuDetails.id){
+ OstJsonApi.getRedeemableSkuDetails(this.ostUserId, this.skuDetails.id ,{}, this.onDetailsSuccess , this.onDetailsError)
+ }else{
+ this.__setState({
+ refreshing : false,
+ });
+ }
+
+ };
+
+ onDetailsSuccess = (data={}) =>{
+ const resultType = data["result_type"] || {};
+ this.skuDetails = data[resultType] || {};
+ this.setInitialAvailabilityAndDenomination();
+ this.__setState({
+ refreshing : false,
+ })
+ };
+
+ onDetailsError =( error)=> {
+ this.__setState({
+ refreshing : false
+ })
+ };
+
+ getBtnText = () => {
+ if(this.state.isPurchasing){
+ return "Processing...";
+ }else{
+ return `Purchase for ${this.getSelectedAmountInBT()} ${tokenHelper.getTokenSymbol()}`
+ }
+ };
+
+ setInitialAvailabilityAndDenomination = () => {
+ this.state.selectedAvailability = this.skuDetails.availability && this.skuDetails.availability[0] || {} ;
+ const denominations = this.state.selectedAvailability["denominations"] || [];
+ this.state.selectedDenomination = denominations[0] || {};
+ }
+
+ getAvailableCountryList = () =>{
+ let availabilityData = (this.skuDetails && this.skuDetails.availability) || [],
+ countryData = [];
+ if(!availabilityData) return countryData;
+ for(let cnt = 0; cnt< availabilityData.length ; cnt++){
+ let country = availabilityData[cnt] || {} ,
+ countryName = country && country.country;
+ countryData.push({label: countryName, value: country});
+ }
+ return countryData;
+ };
+
+ getAvailableCurrencyData = ( ) =>{
+ if(!this.state.selectedAvailability) return [];
+ let denominationsArray = this.state.selectedAvailability.denominations || [],
+ currencyIsoCode = this.state.selectedAvailability.currency_iso_code,
+ currencyItems = [] , sanitizedCurrencyItems = []
+ ;
+ for(let cnt = 0 ; cnt < denominationsArray.length ; cnt ++){
+ let currentDenomination = denominationsArray[cnt] || {},
+ btAmount = currentDenomination.amount_in_wei,
+ label = `${currentDenomination.amount_in_fiat} ${currencyIsoCode}`,
+ value = currentDenomination ;
+ if(!!OstTransactionHelper.getValidBucket(btAmount, tokenHelper.getDecimals())){
+ sanitizedCurrencyItems.push({label, value});
+ }
+ currencyItems.push({label, value});
+ }
+ if(sanitizedCurrencyItems.length > 0){
+ return sanitizedCurrencyItems;
+ }
+ return currencyItems;
+ }
+
+
+ onCountryChange = (value) =>{
+ this.__setState({
+ selectedAvailability: value
+ });
+ }
+
+ onDenominationChange = ( value ) =>{
+ this.__setState({
+ selectedDenomination : value
+ })
+ }
+
+ onEmailChange = (text) =>{
+ this.__setState({
+ emailId:text
+ })
+ this.onFormChange();
+ }
+
+ onPurchaseClick = () =>{
+ if(this.isInputValid()){
+ this.showConfirmationAlert();
+ }
+ };
+
+ showConfirmationAlert = () =>{
+ let config ={
+ title : "",
+ Message : `Confirm email address: ${this.state.emailId}`,
+ cancelBtnText : "Cancel",
+ successBtnText : "Confirm",
+ cancelCallback : this.onAlertCancel,
+ successCallback : this.onAlertConfirm,
+ cancelStyle : 'cancel'
+ }
+ let alertBox = new AlertBox(config);
+ alertBox.showAlert();
+ }
+
+ campareBalAndPurchaseValue = () =>{
+ let purchaseValueInBt = tokenHelper.toBtPrecision( tokenHelper.fromDecimal(this.getSelectedAmountInWei()), 2),
+ balance = __getParam(this.props.navigation,"balance");
+ return tokenHelper.isBalSufficient(balance,purchaseValueInBt);
+ }
+
+ isInputValid = () =>{
+ if(this.state.emailId == '' ){
+ this.__setState({
+ errorText: errorMsgs.emailRequired
+ });
+ return false;
+ }
+ let comparisonResult = this.campareBalAndPurchaseValue();
+ if(comparisonResult === -1){
+ this.__setState({
+ errorText: errorMsgs.inSufficientbalance
+ });
+ return false;
+ }else if(comparisonResult === null){
+ this.__setState({
+ errorText: errorMsgs.invalidAmount
+ });
+ }
+ return true
+ }
+
+ onAlertCancel = () => {}
+
+ onAlertConfirm = () => {
+ this.__setState({
+ isPurchasing: true
+ })
+ this.executeTranscaction();
+ }
+
+ onTransactionSuccess = () => {
+ this.__setState({
+ isPurchasing: false,
+ transactionSuccess :true
+ })
+ this.onPurchaseSuccess && this.onPurchaseSuccess();
+ }
+
+ onTransactionError =( error)=> {
+ this.__setState({
+ isPurchasing: false,
+ errorText : error,
+ transactionSuccess :false
+ })
+ }
+
+ onFormChange = () => {
+ this.__setState({
+ isPurchasing: false,
+ errorText : "",
+ transactionSuccess : false
+ });
+ }
+
+ setCountryPickerRef = (ref) =>{
+ this.inputRefs.countryPicker = ref;
+ }
+
+ setDenominationPickerRef = (ref) =>{
+ this.inputRefs.currencyPicker = ref;
+ }
+
+ setEmailINputPickerRef = (ref) =>{
+ this.inputRefs.emailIdInput = ref;
+ }
+
+ onDownArrowClickCountry = () =>{
+ this.inputRefs.currencyPicker && this.inputRefs.currencyPicker.togglePicker();
+ }
+
+ onDownArrowClickCurrency = () =>{
+ this.inputRefs.emailIdInput && this.inputRefs.emailIdInput.focus();
+ }
+
+ onUpArrowClickCurrency = () =>{
+ this.inputRefs.countryPicker && this.inputRefs.countryPicker.togglePicker();
+ }
+
+ getPickerIcon = () =>{
+ return (
+
+
+
+ )
+ }
+
+ getImage = () =>{
+ if(this.skuDetails.images && this.skuDetails.images.detail && this.skuDetails.images.detail.original && this.skuDetails.images.detail.original.url){
+ return (
+
+
+ )
+ }
+ }
+
+ getDescription = () =>{
+ if(this.skuDetails.description && this.skuDetails.description.text){
+ return(
+
+ {this.skuDetails.description.text}
+
+ )
+ }
+ }
+
+ getName = () =>{
+ if(this.skuDetails.name){
+ return(
+ {this.skuDetails.name}
+ )
+ }
+ }
+
+ getRNPickerStyles = () => {
+ let styles = {...stylesMap.input, ...OstThemeConfigHelper.getFormFieldConfig()};
+ return {
+ inputIOS : styles,
+ inputAndroid : styles
+ }
+ }
+
+ render(){
+ return(
+
+
+ {this.getName()}
+ {this.getImage()}
+ {this.getDescription()}
+
+ {(!this.skuDetails.availability && !this.state.refreshing) && (
+
+ Oops! Failed to load available options or no options available.
+
+
+ )}
+ {this.skuDetails.availability && (
+
+
+ Select Country
+
+
+
+
+ Card Amount
+
+
+
+
+ Your mail id
+
+
+ {this.state.transactionSuccess &&
+
+
+
+ We have received your order and will send an email shortly to {this.state.emailId}
+
+
+ }
+ {!this.state.transactionSuccess &&
+ {
+ this.onPurchaseClick()
+ })}
+ style={[stylesMap.purchaseBtn, OstThemeConfigHelper.getB1Config()]}
+ disabled = {this.state.isPurchasing}>
+
+ {this.getBtnText()}
+
+
+ }
+
+ {this.state.errorText}
+
+
+
+ )}
+
+
+ )
+ }
+
+ getSelectedAmountInBT() {
+ return tokenHelper.toBtPrecision( tokenHelper.fromDecimal(this.getSelectedAmountInWei()), 2);
+ }
+
+ getSelectedAmountInWei(){
+ return this.state.selectedDenomination && this.state.selectedDenomination["amount_in_wei"] || 0;
+ }
+
+ getSelectedAmountInFiat(){
+ return this.state.selectedDenomination && this.state.selectedDenomination["amount_in_fiat"] || 0;
+ }
+
+ getSelectedCountryISOCode(){
+ return this.state.selectedAvailability && this.state.selectedAvailability["country_iso_code"] || "";
+ }
+
+ getSelectedCurrencyISOCode(){
+ return this.state.selectedAvailability && this.state.selectedAvailability["currency_iso_code"] || "";
+ }
+
+ getTxMeta(){
+ const config = JSON.parse(JSON.stringify(OstRedmptionConfig)) || {};
+ return config["transactionMeta"] || {};
+ }
+
+ getRedemptionMeta(){
+ return {
+ "redeemable_sku_id": this.skuDetails.id,
+ "amount_in_fiat": this.getSelectedAmountInFiat(),
+ "country_iso_code": this.getSelectedCountryISOCode(),
+ "currency_iso_code": this.getSelectedCurrencyISOCode(),
+ "email": this.state.emailId
+ }
+ }
+
+ executeTranscaction = () => {
+ const amounts = [tokenHelper.fromDecimal(this.getSelectedAmountInWei())] ,
+ address = [tokenHelper.getTokenHolderAddress()]
+ ;
+ const uuid = OstRedemptionTransactionHelper.executeDirectTransfer( this.ostUserId,
+ amounts,
+ address,
+ this.getTxMeta(),
+ this.getRedemptionMeta(),
+ this.ostWalletUIWorkflowCallback );
+
+
+ OstWalletSdkUI.subscribe(uuid, OstWalletSdkUI.EVENTS.requestAcknowledged, (workflowContext, contextEntity) => {
+ this.requestAcknowledged();
+ const requestAcknowledged = this.ostWalletUIWorkflowCallback["requestAcknowledged"] ;
+ requestAcknowledged && requestAcknowledged.call(this.ostWalletUIWorkflowCallback , workflowContext, contextEntity);
+ });
+
+ OstWalletSdkUI.subscribe(uuid, OstWalletSdkUI.EVENTS.flowInterrupt, (workflowContext, ostError) => {
+ this.flowInterrupt(workflowContext, ostError);
+ const flowInterrupt = this.ostWalletUIWorkflowCallback["flowInterrupt"] ;
+ flowInterrupt && flowInterrupt.call(this.ostWalletUIWorkflowCallback , workflowContext, ostError);
+ });
+
+ OstWalletSdkUI.subscribe(uuid, OstWalletSdkUI.EVENTS.flowComplete, (workflowContext, contextEntity) => {
+ this.ostWalletUIWorkflowCallback["flowComplete"] && this.ostWalletUIWorkflowCallback["flowComplete"](workflowContext, contextEntity);
+ });
+
+ }
+
+ requestAcknowledged = () => {
+ this.onTransactionSuccess();
+ this.updateBalance();
+ }
+
+ flowInterrupt = ( workflowContext, error) => {
+ let errorMsg = errorMsgs.generalError;
+ if(error && error.isApiError()){
+ const errData = error.getApiErrorData() || [] ,
+ errObj = errData[0] || {};
+ if(errObj["parameter"] == apiErrorParameterKey){
+ errorMsg = errObj["msg"];
+ }
+ }else if( workflowContext ){
+ if(OstWalletSdkHelper.isUserCancelled(error)){
+ errorMsg = "";
+ } else {
+ errorMsg = sdkErrorHelper.getErrorMessage(workflowContext, error);
+ }
+ }
+ this.onTransactionError(errorMsg);
+ }
+
+}
+
+export default OstRedeemableSkuDetails;
+
diff --git a/js/Redemptions/RedeemableSkuDetails/styles.js b/js/Redemptions/RedeemableSkuDetails/styles.js
new file mode 100644
index 0000000..3ecfabf
--- /dev/null
+++ b/js/Redemptions/RedeemableSkuDetails/styles.js
@@ -0,0 +1,101 @@
+import { StyleSheet } from 'react-native';
+
+let stylesMap = StyleSheet.create({
+ container:{
+ flex:1,
+ flexDirection:'column'
+ },
+ scrollViewContainer:{
+ padding:20,
+ },
+ imageStyle:{
+ width:"100%",
+ aspectRatio:16/9,
+ marginBottom:20,
+ backgroundColor:'#DBDBDB'
+ },
+ heading:{
+ marginBottom:20
+ },
+ descText:{
+ marginBottom:30
+ },
+ labelStyle:{
+ marginBottom:5
+ },
+ wrapperFormInput:{
+ marginBottom:20
+ },
+ errorContainer:{
+ flex:1,
+ justifyContent:'center',
+ alignItems:'center',
+ },
+ errorText:{
+ fontSize:13,
+ color:'red',
+ marginTop:10,
+ marginBottom:50,
+ textAlign:'center'
+ },
+ iconWrapper:{
+ paddingRight : 17,
+ flex:1,
+ marginTop: 17 // [(inputbBoxHeight /2 ) - (heightOfArrow/2 )] where inputbBoxHeight = 46 and heightOfArrow = 12
+ },
+ downArrow : {
+ height : 12,
+ width :20
+ },
+ purchaseBtn:{
+ alignItems:'center',
+ justifyContent : 'center',
+ width:'100%',
+ height:46,
+ flex:1,
+ flexDirection:'column',
+ borderRadius:5
+ },
+ purchaseBtnText:{
+
+ },
+ successMessageWrapper : {
+ borderWidth: 1,
+ borderColor: '#2a293b',
+ flex:1,
+ flexDirection:'row',
+ padding:10,
+ borderRadius:8,
+ alignItems:'center',
+ justifyContent:'center'
+ },
+ imageSuccessMessage:{
+ height:60,
+ width:60,
+ marginRight : 10
+ },
+ successText:{
+ flex:1
+ },
+ input : {
+ fontSize: 15,
+ paddingVertical: 13,
+ paddingLeft: 21,
+ paddingRight: 54, // iconWidth(20) + iconPaddingRight(17) + iconPaddingLeft(17)
+ borderWidth: 1,
+ borderRadius:5,
+ height:46,
+ },
+ emptyProductDetailsWrapper : {
+ flex:1,
+ alignItems:'center',
+ justifyContent:'center',
+ borderWidth: 1,
+ borderColor: '#2a293b',
+ padding:10,
+ borderRadius:8,
+ borderStyle: 'dashed'
+ }
+});
+
+export {stylesMap};
\ No newline at end of file
diff --git a/js/Redemptions/RedeemableSkus/SkusList.js b/js/Redemptions/RedeemableSkus/SkusList.js
new file mode 100644
index 0000000..b196c88
--- /dev/null
+++ b/js/Redemptions/RedeemableSkus/SkusList.js
@@ -0,0 +1,158 @@
+import React from 'react';
+import { FlatList, ActivityIndicator, View, Image, Text, TouchableWithoutFeedback } from 'react-native';
+
+import RedemptionSkusModel from "../../services/OstJsonApiPagination/RedemptionSkusModel";
+import Pagination from '../../services/OstJsonApiPagination/Pagination';
+import OstRedemableCustomConfig from "../RedemableCustomConfig";
+import multipleClickHandler from '../MultipleClickHandler';
+import OstThemeConfigHelper from '../../helpers/OstThemeConfigHelper';
+
+import styles from './styles';
+
+class SkusList extends React.PureComponent{
+ constructor( props ){
+ super(props);
+ this.pagination = null;
+ this.redemptionSkusModel = null ;
+ this.state = {
+ list: null,
+ loadingNext: false,
+ refreshing: false
+ };
+ this.noDataCell = {
+ isEmpty: true
+ }
+ }
+
+ __setState = (state) => {
+ if(!state) return;
+ this.setState(state);
+ }
+
+ componentDidMount(){
+ this.init();
+ }
+
+ componentWillUnmount(){
+ this.__setState = () => {};
+ }
+
+ init(){
+ this.redemptionSkusModel = new RedemptionSkusModel(this.props.ostUserId);
+ this.pagination = new Pagination( this.redemptionSkusModel ,{
+ beforeRefresh : this.beforeRefresh ,
+ onRefresh : this.onRefresh ,
+ onRefreshError: this.onRefreshError,
+ beforeNext: this.beforeNext,
+ onNext: this.onNext,
+ onNextError : this.onNextError
+ } );
+ this.pagination && this.pagination.initPagination();
+ }
+
+ onItemClick = (item , index) => {
+ this.props.onItemClick && this.props.onItemClick(item , index);
+ }
+
+ _renderItem = ({item, index}) => {
+ if(item.isEmpty){
+ return (
+ No items selected for redemption.
+ );
+ }
+ let imageUrl = (item.images && item.images.cover && item.images.cover.original.url) || null;
+ return (
+ {
+ this.onItemClick(item , index)
+ })}
+ >
+
+
+ {imageUrl && }
+
+
+
+ );
+ };
+
+ getResults = () => {
+ let results = this.pagination.modelFetch.getAllResults();
+ return results.length == 0 ? [this.noDataCell] : results;
+ }
+
+ beforeRefresh = ( ) => {
+ this.__setState({ refreshing : true });
+ }
+
+ onRefresh = ( res ) => {
+ this.__setState({ refreshing : false, list : this.getResults() });
+ }
+
+ onRefreshError = ( error ) => {
+ this.__setState({ refreshing : false });
+ }
+
+ beforeNext =() => {
+ this.__setState({ loadingNext : true });
+ }
+
+ onNext = ( res ) => {
+ this.__setState({ loadingNext : false , list : this.getResults() });
+ }
+
+ onNextError = ( error ) => {
+ this.__setState({ loadingNext : false });
+ }
+
+ getNext = () => {
+ this.pagination.getNext();
+ }
+
+ refresh = () => {
+ this.pagination.refresh();
+ }
+
+ _keyExtractor = (item, index)=> {
+ return `id_${index}`
+ }
+
+ renderFooter = () => {
+ if (!this.state.loadingNext) return null;
+ return ;
+ };
+
+ render = () => {
+ if(!this.state.list) return null;
+ return (
+
+ )
+ }
+}
+
+
+const ListHeaderComponent = (props) => {
+ const storeLogo = OstRedemableCustomConfig.getStoreIconUri() ,
+ header = OstRedemableCustomConfig.getHeader(),
+ description = OstRedemableCustomConfig.getDescription()
+ ;
+ return (
+ {storeLogo && }
+ {header.length > 0 && {header} }
+ {description.length > 0 && {description} }
+ )
+}
+
+export default SkusList;
\ No newline at end of file
diff --git a/js/Redemptions/RedeemableSkus/index.js b/js/Redemptions/RedeemableSkus/index.js
new file mode 100644
index 0000000..87453bc
--- /dev/null
+++ b/js/Redemptions/RedeemableSkus/index.js
@@ -0,0 +1,136 @@
+import React from 'react';
+import { SafeAreaView } from 'react-native';
+import OstJsonApi from '../../OstJsonApi';;
+
+import HeaderRight from "../CommonComponents/HeaderRight";
+import OstRedemableCustomConfig from "../RedemableCustomConfig";
+import OstThemeConfigHelper from '../../helpers/OstThemeConfigHelper';
+import OstWalletUIWorkflowCallback from '../../OstWalletUIWorkflowCallback';
+import styles from './styles';
+import SkusList from './SkusList';
+import BackArrow from '../CommonComponents/BackArrow';
+import tokenHelper from "../../helpers/TokenHelper";
+
+function __getParam(navigation , paramName) {
+ if(navigation && navigation.getParam){
+ return navigation.getParam(paramName);
+ }
+ return null;
+}
+
+class OstRedeemableSkus extends React.PureComponent {
+
+ static navigationOptions = ({ navigation }) => {
+ const balance = __getParam(navigation , "balance") || 0 ,
+ isCustomBack = !!OstThemeConfigHelper.getBackArrowSource()
+ ;
+ let navigationOption = {
+ title: __getParam(navigation , "navTitle") || OstRedemableCustomConfig.getSkuListNavHeader() ,
+ headerStyle: {
+ borderBottomWidth: 0,
+ shadowColor: '#000',
+ shadowOffset: {
+ width: 0,
+ height: 1
+ },
+ shadowOpacity: 0.1,
+ shadowRadius: 3
+ },
+ headerBackTitle: null,
+ headerRight:
+ };
+ if( isCustomBack ){
+ navigationOption["headerBackImage"] = ;
+ }
+
+ return Object.assign({}, navigationOption, OstThemeConfigHelper.getNavigationHeaderConfig());
+ };
+
+ constructor( props ){
+ super(props);
+
+ this.initTheme();
+
+ this.ostUserId = props.ostUserId || __getParam(props.navigation , "ostUserId");
+ this.ostWalletUIWorkflowCallback = props.ostWalletUIWorkflowCallback || __getParam(props.navigation , "ostWalletUIWorkflowCallback");
+ this.onItemClick = props.onItemClick || __getParam(props.navigation , "onItemClick");
+ this.balanceInBt = 0;
+
+ if( !this.ostUserId ) {
+ let err = new Error("ostUserId can not be null");
+ throw err;
+ }
+
+ if( !this.ostWalletUIWorkflowCallback || !(this.ostWalletUIWorkflowCallback instanceof OstWalletUIWorkflowCallback) ) {
+ let err = new Error("ostWalletUIWorkflowCallback can not be null and must be an instanceof OstWalletUIWorkflowCallback");
+ throw err;
+ }
+
+ if(!props.navigation){
+ console.warn("navigation is required for ost redemption ui sdk flow.");
+ }
+
+ this.init();
+ }
+
+ initTheme(){
+ OstThemeConfigHelper.updateConfig().then((res)=> {
+ this.props.navigation && this.props.navigation.setParams && this.props.navigation.setParams({"ostThemeUpdated": true});
+ }).catch((error)=> {})
+ }
+
+ init(){
+ if(!tokenHelper.token){
+ tokenHelper.init(this.ostUserId).then(()=>{
+ this.updateBalance();
+ }).catch(()=>{})
+ }else{
+ this.updateBalance();
+ }
+ }
+
+ updateBalance(){
+ OstJsonApi.getBalanceForUserId(this.ostUserId, (res) => {
+ let balance = res.balance && res.balance.available_balance;
+ balance = tokenHelper.toBtPrecision(tokenHelper.fromDecimal(balance));
+ this.balanceInBt = balance;
+ this.props.navigation && this.props.navigation.setParams && this.props.navigation.setParams({
+ balance
+ })
+ }, () => {});
+ }
+
+ componentWillUnmount(){
+ this.__setState = () => {};
+ }
+
+ __setState = (state) => {
+ if(!state) return;
+ this.setState(state);
+ }
+
+ __onItemClick = (item , index) => {
+ if(typeof "function" == this.onItemClick){
+ this.onItemClick(item , index);
+ } else {
+ this.props.navigation && this.props.navigation.push && this.props.navigation.push('RedeemableSkuDetails', {'redemptionSku': item,
+ 'ostUserId':this.ostUserId,
+ 'ostWalletUIWorkflowCallback': this.ostWalletUIWorkflowCallback,
+ 'balance': this.balanceInBt,
+ 'onPurchaseSuccess': this.onPurchaseSuccess
+ });
+ }
+ }
+
+ onPurchaseSuccess = ()=> {
+ this.updateBalance();
+ }
+
+ render(){
+ return (
+
+
+ );}
+}
+
+export default OstRedeemableSkus;
\ No newline at end of file
diff --git a/js/Redemptions/RedeemableSkus/styles.js b/js/Redemptions/RedeemableSkus/styles.js
new file mode 100644
index 0000000..de18ce5
--- /dev/null
+++ b/js/Redemptions/RedeemableSkus/styles.js
@@ -0,0 +1,63 @@
+import { StyleSheet } from 'react-native';
+
+//@TODO cleanup
+
+let stylesMap = StyleSheet.create({
+ container : {
+ flex: 1,
+ paddingVertical: 4,
+ marginTop: 10
+ },
+ headingWrapper : {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ padding: 30,
+ marginBottom: 50
+ },
+ logoSkipFont : {
+ // width: 100,
+ // height: 100,
+ alignSelf: 'center'
+ },
+ title : {
+ alignSelf: 'center',
+ paddingHorizontal: 50,
+ marginBottom: 10
+ },
+ description : {
+ marginTop: 5,
+ paddingHorizontal: 50,
+ textAlign: 'center',
+ marginBottom: 30
+ },
+ item:{
+ flex: 1,
+ margin:10,
+ backgroundColor: '#DBDBDB',
+ alignItems:'center',
+ justifyContent: 'center'
+ },
+ itemWrapper: {
+ flex: 0.5,
+ aspectRatio:1
+ },
+ list:{
+ marginHorizontal:10,
+ marginBottom : 10
+ },
+ noDataWrapper : {
+ flex:1,
+ justifyContent:'center',
+ alignItems:'center',
+ borderWidth: 1,
+ borderColor: '#2a293b',
+ padding:10,
+ borderRadius:8,
+ borderStyle: 'dashed'
+ }
+});
+
+export default styles = stylesMap;
+
+
diff --git a/js/Redemptions/RedemableCustomConfig.js b/js/Redemptions/RedemableCustomConfig.js
new file mode 100644
index 0000000..b1dfc1b
--- /dev/null
+++ b/js/Redemptions/RedemableCustomConfig.js
@@ -0,0 +1,46 @@
+import OstRedemptionConfig from "./ost-redemption-config";
+import objectMerge from "lodash.merge";
+
+class OstRedemableCustomConfig {
+ constructor(){
+ const config = JSON.parse(JSON.stringify(OstRedemptionConfig)) || {};
+ this.defaultConfig = config["themeingConfig"];
+ this.config = {};
+ }
+
+ setConfig = (externalConfig={}) => {
+ this.config = objectMerge(this.defaultConfig, externalConfig);
+ }
+
+ getBackArrowUri = () => {
+ return this.config && this.config.common && this.config.common["backArrow"];
+ }
+
+ getWalletIconUri = () => {
+ return this.config && this.config.common && this.config.common["walletIcon"];
+ }
+
+ getStoreIconUri = () => {
+ return this.config && this.config.common && this.config.common["storeIcon"];
+ }
+
+ getHeader = () => {
+ return this.config && this.config.skuListScreen && this.config.skuListScreen["header"];
+ }
+
+ getDescription = () => {
+ return this.config && this.config.skuListScreen && this.config.skuListScreen["description"];
+ }
+
+ getSkuListNavHeader = () => {
+ return this.config && this.config.skuListScreen && this.config.skuListScreen["navHeader"];
+ }
+
+ getSkuDetailsScreenNavHeader = () => {
+ return this.config && this.config.skuDetailsScreen && this.config.skuDetailsScreen["navHeader"];
+ }
+
+
+}
+
+export default new OstRedemableCustomConfig();
\ No newline at end of file
diff --git a/js/Redemptions/RedemptionTransactionHelper.js b/js/Redemptions/RedemptionTransactionHelper.js
new file mode 100644
index 0000000..057f34e
--- /dev/null
+++ b/js/Redemptions/RedemptionTransactionHelper.js
@@ -0,0 +1,55 @@
+import TransactionHelper from "../TransactionHelper/OstTransactionHelper"
+import {OstTransactionHelper , OstTransactionExecutor} from "../TransactionHelper/OstTransactionHelper";
+import OstWalletSdk from '../OstWalletSdk';
+
+const duration = 60*10;
+
+
+class OstRedemptionTransactionHelper extends OstTransactionHelper{
+ constructor(){
+ super();
+ }
+
+ executeDirectTransfer(userId, amounts, addresses, txMeta, redemptionDetails, transferDelegate) {
+ let obj = new OstRedemptionTransactionExecutor(userId, 'direct transfer', amounts, addresses, txMeta, redemptionDetails, transferDelegate);
+ obj.perform();
+ return obj.uuid;
+ }
+}
+
+class OstRedemptionTransactionExecutor extends OstTransactionExecutor {
+
+ constructor(userId, ruleName, amounts, addresses, txMeta, redemptionDetails, transferDelegate ){
+ super(userId, ruleName, amounts, addresses, txMeta, transferDelegate);
+ this.redemptionDetails = redemptionDetails ;
+ }
+
+ getSpedingLimitAndExpiryTimeBucket(){
+ let validBucket = super.getSpedingLimitAndExpiryTimeBucket();
+ if (!TransactionHelper.isExternalConfig ) {
+ if( !validBucket ){
+ validBucket = {
+ spending_limit: this.totalTxAmount,
+ expiration_time: duration
+ }
+ }
+ }
+ return validBucket;
+ }
+
+ callExecuteTransfer(delegate){
+ OstWalletSdk.executeTransaction(
+ this.userId,
+ this.addresses,
+ this.decimalAmounts,
+ this.ruleName,
+ this.txMeta,
+ delegate,
+ {"redemption_meta": this.redemptionDetails}
+ )
+ }
+
+}
+
+
+export default new OstRedemptionTransactionHelper() ;
\ No newline at end of file
diff --git a/js/Redemptions/index.js b/js/Redemptions/index.js
new file mode 100644
index 0000000..7942657
--- /dev/null
+++ b/js/Redemptions/index.js
@@ -0,0 +1,5 @@
+import OstRedeemableSkus from "./RedeemableSkus";
+import OstRedeemableSkuDetails from "./RedeemableSkuDetails";
+import OstRedemableCustomConfig from "./RedemableCustomConfig"
+
+export { OstRedeemableSkus , OstRedeemableSkuDetails , OstRedemableCustomConfig} ;
\ No newline at end of file
diff --git a/js/Redemptions/ost-redemption-config.json b/js/Redemptions/ost-redemption-config.json
new file mode 100644
index 0000000..fe03e9b
--- /dev/null
+++ b/js/Redemptions/ost-redemption-config.json
@@ -0,0 +1,22 @@
+{
+ "themeingConfig": {
+ "common": {
+ "backArrow": null,
+ "walletIcon": null,
+ "storeIcon": null
+ },
+ "skuListScreen": {
+ "navHeader": null,
+ "header": null,
+ "description": null
+ },
+ "skuDetailsScreen": {
+ "navHeader": null
+ }
+ },
+ "transactionMeta" : {
+ "name": "redemption",
+ "type": "user_to_company"
+ }
+}
+
diff --git a/js/TransactionHelper/OstTransactionHelper.js b/js/TransactionHelper/OstTransactionHelper.js
index 85f43ab..4abba60 100644
--- a/js/TransactionHelper/OstTransactionHelper.js
+++ b/js/TransactionHelper/OstTransactionHelper.js
@@ -1,7 +1,6 @@
import BigNumber from 'bignumber.js';
import OstWalletSdk from '../OstWalletSdk';
import OstWalletSdkUI from '../OstWalletSdkUI';
-import {sdkErrorHelper, USER_UNAUTHORIZED, DEFAULT_CONTEXT} from '../helpers/OstSdkErrorHelper';
import OstWalletUIDelegate from '../OstWalletUIWorkflowCallback'
import OstWalletDelegate from '../OstWalletWorkFlowCallback'
import EventEmitter from 'eventemitter3';
@@ -12,13 +11,16 @@ import { setInstance } from '../callbackHandlers/OstWalletSdkUICallbackManager';
import OstWalletUIWorkFlowCallback from "../OstWalletUICoreCallback";
import defaultTransactionConfig from "./ost-transaction-config";
-import OstWalletUIWorkflowCallback from "@ostdotcom/ost-wallet-sdk-react-native/js/OstWalletUIWorkflowCallback";
-import OstRNError from "@ostdotcom/ost-wallet-sdk-react-native/js/OstRNError/OstRNError";
+import OstWalletUIWorkflowCallback from "../OstWalletUIWorkflowCallback";
+import OstRNError from "../OstRNError/OstRNError";
+import TokenHelper from "../helpers/TokenHelper";
let transactionConfig = {};
class OstTransactionHelper {
+
constructor() {
+ this.isExternalConfig = false;
this.setTxConfig();
}
@@ -34,6 +36,7 @@ class OstTransactionHelper {
setTxConfig(externalConfig) {
externalConfig = externalConfig || {};
+ this.isExternalConfig = !!Object.keys(externalConfig).length;
let masterConfig = JSON.parse(JSON.stringify(defaultTransactionConfig));
// Deep Merge.
@@ -51,6 +54,28 @@ class OstTransactionHelper {
return obj.uuid;
}
+
+ /**
+ * @param {*} amount - wei
+ * @param {*} decimal - Number
+ */
+ static getValidBucket(amount , decimal){
+ let validBucket = null;
+ if(!(amount instanceof BigNumber)){
+ amount = new BigNumber(amount);
+ }
+ for(bucket of transactionConfig.session_buckets) {
+ let decimalSpedingLimit = TokenHelper.toDecimal(bucket.spending_limit , decimal) ;
+ let bucketSpendingLimit = new BigNumber(decimalSpedingLimit);
+ if (amount.lte(bucketSpendingLimit)) {
+ validBucket = {};
+ Object.assign(validBucket, bucket)
+ validBucket.spending_limit = decimalSpedingLimit
+ break;
+ }
+ }
+ return validBucket
+ }
}
class OstTransactionExecutor {
@@ -104,11 +129,15 @@ class OstTransactionExecutor {
this.executeTransfer()
}catch(err) {
- let eName = OstWalletUIWorkFlowCallback.EVENTS.flowInterrupt;
- this.ee.emit(eName, err.ostWorkflowContext , err.ostError);
+ this.onPerformCatch(err);
}
}
+ onPerformCatch(err){
+ let eName = OstWalletUIWorkFlowCallback.EVENTS.flowInterrupt;
+ this.ee.emit(eName, err.ostWorkflowContext , err.ostError);
+ }
+
getOstUser() {
return new Promise((resolve, reject) => {
OstWalletSdk.getUser(this.userId, (ostUser) => {
@@ -136,7 +165,7 @@ class OstTransactionExecutor {
let totalAmount = new BigNumber('0');
for (amount of this.amounts) {
- let decimalAmount = this.toDecimal(amount);
+ let decimalAmount = TokenHelper.toDecimal(amount, this.getTokenDecimal());
this.decimalAmounts.push(decimalAmount)
let bnAmount = new BigNumber(decimalAmount);
@@ -196,25 +225,7 @@ class OstTransactionExecutor {
}
getSpedingLimitAndExpiryTimeBucket() {
- let validBucket = null;
- for(bucket of transactionConfig.session_buckets) {
- let decimalSpedingLimit = this.toDecimal(bucket.spending_limit)
- let bucketSpendingLimit = new BigNumber(decimalSpedingLimit);
- if (this.totalTxAmount.lte(bucketSpendingLimit)) {
- validBucket = {};
- Object.assign(validBucket, bucket)
- validBucket.spending_limit = decimalSpedingLimit
- break;
- }
- }
-
- return validBucket
- }
-
- toDecimal(val) {
- val = BigNumber(val);
- let exp = BigNumber(10).exponentiatedBy(this.getTokenDecimal());
- return val.multipliedBy(exp).toString(10);
+ return OstTransactionHelper.getValidBucket(this.totalTxAmount ,this.getTokenDecimal());
}
executeTransfer() {
@@ -234,6 +245,10 @@ class OstTransactionExecutor {
this.ee.emit(eName, workflowContext , contextEntity);
}
+ this.callExecuteTransfer(executeTxDelegate);
+ }
+
+ callExecuteTransfer( executeTxDelegate ){
OstWalletSdk.executeTransaction(
this.userId,
this.addresses,
@@ -249,4 +264,7 @@ class OstTransactionExecutor {
}
}
-export default new OstTransactionHelper()
\ No newline at end of file
+export {OstTransactionExecutor , OstTransactionHelper };
+
+export default new OstTransactionHelper();
+
diff --git a/js/WalletSettings/OstWalletSettings.js b/js/WalletSettings/OstWalletSettings.js
index 2d65530..6e5825b 100644
--- a/js/WalletSettings/OstWalletSettings.js
+++ b/js/WalletSettings/OstWalletSettings.js
@@ -3,7 +3,7 @@ import objectMerge from "lodash.merge";
import {sdkErrorHelper} from '../helpers/OstSdkErrorHelper'
-const ItemsThatNeedCamera = ["add_another_device"];
+const ItemsThatNeedCamera = ["add_another_device", "authorize_browser_session"];
class OstWalletSettingsClass {
constructor() {
diff --git a/js/WalletSettings/SettingsComponent.js b/js/WalletSettings/SettingsComponent.js
index 80fe455..06d2b8c 100644
--- a/js/WalletSettings/SettingsComponent.js
+++ b/js/WalletSettings/SettingsComponent.js
@@ -1,7 +1,7 @@
import React, {PureComponent} from 'react';
-import {Alert, FlatList, Linking, Platform, Text, TouchableWithoutFeedback, View} from 'react-native';
-import OstWalletSdkHelper from "../helpers/OstWalletSdkHelper";
-import WalletDetails from './WalletDetails'
+import {FlatList, Text, TouchableWithoutFeedback, View} from 'react-native';
+import WalletDetails from './WalletDetails';
+import BackArrow from '../Redemptions/CommonComponents/BackArrow';
import inlineStyle from './styles'
@@ -12,6 +12,7 @@ import OstWalletSettings from "@ostdotcom/ost-wallet-sdk-react-native/js/WalletS
class SettingsComponent extends PureComponent {
static navigationOptions = ({ navigation, navigationOptions }) => {
+ const isCustomBack = !!OstThemeConfigHelper.getBackArrowSource() ;
let navigationParams = {
title: navigation.getParam('navTitle', 'Wallet Settings'),
headerStyle: {
@@ -26,6 +27,10 @@ class SettingsComponent extends PureComponent {
}
};
+ if(isCustomBack){
+ navigationParams["headerBackImage"] = ;
+ }
+
return Object.assign(navigationParams, OstThemeConfigHelper.getNavigationHeaderConfig());
};
@@ -39,7 +44,7 @@ class SettingsComponent extends PureComponent {
this.flatListLayout = null
- OstThemeConfigHelper.updateConfig();
+ this.initTheme();
let ostUserId = this.props.ostUserId;
let delegate = this.props.ostWalletUIWorkflowCallback;
@@ -54,6 +59,12 @@ class SettingsComponent extends PureComponent {
this.controller = new WalletSettingsController(ostUserId, delegate);
}
+ initTheme(){
+ OstThemeConfigHelper.updateConfig().then((res)=> {
+ this.props.navigation && this.props.navigation.setParams && this.props.navigation.setParams({"ostThemeUpdated": true});
+ }).catch((error)=> {})
+ }
+
componentDidMount() {
this.refreshList();
}
diff --git a/js/WalletSettings/WalletSettingsController.js b/js/WalletSettings/WalletSettingsController.js
index 1ecc8af..8d074fa 100644
--- a/js/WalletSettings/WalletSettingsController.js
+++ b/js/WalletSettings/WalletSettingsController.js
@@ -18,6 +18,7 @@ const optionIds = {
viewMnemonics: "show_mnemonics",
authorizeWithMnemonics: "authorize_device_with_mnemonics",
authorizeWithQR: "add_another_device",
+ authorizeSessionWithQR: "authorize_browser_session",
showQR: "show_device_qr_code",
enableBiometrics: "enable_biometrics",
disableBiometrics: "disable_biometrics",
@@ -163,6 +164,7 @@ class WalletSettingsController {
this._updateOptionsData(optionIds.addSession, false, true);
this._updateOptionsData(optionIds.viewMnemonics, false, true);
this._updateOptionsData(optionIds.authorizeWithQR, false, true);
+ this._updateOptionsData(optionIds.authorizeSessionWithQR, false, true);
this._updateOptionsData(optionIds.resetPin, false, true);
this._updateOptionsData(optionIds.revokeDevice, false, true);
@@ -366,6 +368,10 @@ class WalletSettingsController {
workflowId = OstWalletSdkUI.scanQRCodeToAuthorizeDevice(userId, delegate);
break;
+ case optionIds.authorizeSessionWithQR:
+ workflowId = OstWalletSdkUI.scanQRCodeToAuthorizeSession(userId, delegate);
+ break;
+
case optionIds.showQR:
workflowId = OstWalletSdkUI.getAddDeviceQRCode(userId, delegate);
break;
diff --git a/js/WalletSettings/ost-wallet-settings-config.json b/js/WalletSettings/ost-wallet-settings-config.json
index b6d9bef..2fb06ee 100644
--- a/js/WalletSettings/ost-wallet-settings-config.json
+++ b/js/WalletSettings/ost-wallet-settings-config.json
@@ -2,14 +2,15 @@
"item_display_order": [
"activate_user",
"wallet_details",
- "recover_device",
+ "recover_device",
"abort_recovery",
- "add_session",
- "reset_pin",
- "show_mnemonics",
+ "add_session",
+ "reset_pin",
+ "show_mnemonics",
"authorize_device_with_mnemonics",
"revoke_device",
"add_another_device",
+ "authorize_browser_session",
"show_device_qr_code",
"enable_biometrics",
"disable_biometrics"
@@ -21,7 +22,7 @@
"description": "View your wallet details."
},
"config": {
- "ost_view_endpoint": ''
+ "ost_view_endpoint": ""
}
},
"activate_user": {
@@ -86,6 +87,12 @@
"description": "Scan QR Code to add another device."
}
},
+ "authorize_browser_session": {
+ "content_config": {
+ "heading": "Authorize Browser Session",
+ "description": "Scan QR Code to authorize browser session."
+ }
+ },
"show_device_qr_code": {
"content_config": {
"heading": "Show Device QR Code",
@@ -105,4 +112,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/js/helpers/OstThemeConfigHelper.js b/js/helpers/OstThemeConfigHelper.js
index 77e397a..0d5047d 100644
--- a/js/helpers/OstThemeConfigHelper.js
+++ b/js/helpers/OstThemeConfigHelper.js
@@ -3,7 +3,7 @@ import OstWalletSdkUI from '../OstWalletSdkUI'
class OstThemeConfigHelper {
constructor(){
this.themeConfig = {}
- this.updateConfig()
+ this.updateConfig();
}
getThemeConfig() {
@@ -11,7 +11,7 @@ class OstThemeConfigHelper {
}
updateConfig() {
- this._getThemeConfig()
+ return this._getThemeConfig()
.then((themeConfig) => {
this.themeConfig = themeConfig
})
@@ -20,6 +20,7 @@ class OstThemeConfigHelper {
_getThemeConfig() {
return new Promise((resolve, reject) => {
OstWalletSdkUI.getThemeConfig((themeConfig) => {
+ console.log("====", themeConfig);
resolve(themeConfig)
})
});
@@ -39,20 +40,83 @@ class OstThemeConfigHelper {
}
}
+ getH1Config() {
+ let h1Config = this.themeConfig.h1
+ return {
+ color: h1Config.color,
+ fontSize: h1Config.size,
+ alignSelf: this.getTextAlignment(h1Config.alignment),
+ fontFamily: h1Config.font
+ }
+ }
+
+ getH2Config() {
+ let h2Config = this.themeConfig.h2
+ return {
+ color: h2Config.color,
+ fontSize: h2Config.size,
+ alignSelf: this.getTextAlignment(h2Config.alignment),
+ fontFamily: h2Config.font
+ }
+ }
+
+ getH3Config() {
+ let h3Config = this.themeConfig.h3
+ return {
+ color: h3Config.color,
+ fontSize: h3Config.size,
+ alignSelf: this.getTextAlignment(h3Config.alignment),
+ fontFamily: h3Config.font
+ }
+ }
+
+ getH4Config() {
+ let h4Config = this.themeConfig.h4
+ return {
+ color: h4Config.color,
+ fontSize: h4Config.size,
+ alignSelf: this.getTextAlignment(h4Config.alignment),
+ fontFamily: h4Config.font
+ }
+ }
+
getC1Config() {
let c1Config = this.themeConfig.c1
- return {color: c1Config.color,
- fontSize: c1Config.size,
- alignSelf: this.getTextAlignment(c1Config.alignment),
- fontFamily: c1Config.font}
+ return {
+ color: c1Config.color,
+ fontSize: c1Config.size,
+ alignSelf: this.getTextAlignment(c1Config.alignment),
+ fontFamily: c1Config.font
+ }
}
getC2Config() {
let c2Config = this.themeConfig.c2;
- return {color: c2Config.color,
- fontSize: c2Config.size,
- alignSelf: this.getTextAlignment(c2Config.alignment),
- fontFamily: c2Config.font}
+ return {
+ color: c2Config.color,
+ fontSize: c2Config.size,
+ alignSelf: this.getTextAlignment(c2Config.alignment),
+ fontFamily: c2Config.font
+ }
+ }
+
+ getB1Config() {
+ let b1Config = this.themeConfig.b1;
+ return {
+ color: b1Config.color,
+ fontSize: b1Config.size,
+ backgroundColor: b1Config.background_color,
+ fontFamily: b1Config.font
+ }
+ }
+
+ getB1TextConfig() {
+ let b1Config = this.themeConfig.b1;
+ return {
+ color: b1Config.color,
+ fontSize: b1Config.size,
+ fontFamily: b1Config.font
+ }
}
getLinkConfig() {
@@ -91,6 +155,23 @@ class OstThemeConfigHelper {
headerTintColor: headerConfig.tint_color
}
}
+
+ getFormFieldConfig() {
+ let nsConfig = this.themeConfig.form_field;
+ return {
+ color: nsConfig.color,
+ fontSize: nsConfig.size,
+ fontFamily: nsConfig.font,
+ borderColor: nsConfig.border_color,
+ textAlign: nsConfig.alignment
+ }
+ }
+
+ getBackArrowSource(){
+ const icons = this.themeConfig.icons || {} ,
+ back = icons.back || {};
+ return back.source;
+ }
}
export default new OstThemeConfigHelper()
\ No newline at end of file
diff --git a/js/helpers/TokenHelper.js b/js/helpers/TokenHelper.js
new file mode 100644
index 0000000..097763c
--- /dev/null
+++ b/js/helpers/TokenHelper.js
@@ -0,0 +1,97 @@
+import OstWalletSdk from '../OstWalletSdk';
+import BigNumber from 'bignumber.js';
+
+class TokenHelper {
+
+ constructor(){
+ this.ostUserId = null;
+ this.tokenId = null;
+ this.user = null;
+ this.token = null;
+ }
+
+ init(ostUserId) {
+ if(!ostUserId) return Promise.reject();
+ this.ostUserId = ostUserId;
+ return this.getOstUser(ostUserId).then((user)=> {
+ const tokenId = user && user["token_id"];
+ this.tokenId = tokenId;
+ return this.getOstToken(tokenId).then((token)=> {
+ this.token = token;
+ return { user , token };
+ }).catch((error)=> {
+ //Ignore as there is no reject
+ })
+ }).catch((error)=>{
+ //Ignore as there is no reject
+ });
+ }
+
+ getOstUser(id) {
+ return new Promise((resolve, reject) => {
+ OstWalletSdk.getUser(id, (user) => {
+ if (user) {
+ this.user = user;
+ resolve(user);
+ }
+ })
+ })
+ }
+
+ getOstToken(id) {
+ id = id && id.toString() || "";
+ return new Promise((resolve, reject) => {
+ OstWalletSdk.getToken(id, (token) => {
+ this.token = token;
+ resolve(token)
+ });
+ });
+ }
+
+ getTokenHolderAddress(){
+ const auxChain = this.token && this.token.auxiliary_chains ,
+ firstAuxChain = auxChain && auxChain[0],
+ companyTokenHolders = firstAuxChain && firstAuxChain["company_token_holders"] ;
+ return companyTokenHolders && companyTokenHolders[0] || "";
+ }
+
+ getDecimals(){
+ return this.token["decimals"]
+ }
+
+ getTokenSymbol(){
+ return this.token["symbol"];
+ }
+
+ fromDecimal(val, decimals){
+ decimals = decimals || this.getDecimals();
+ if (!val || !decimals) return '';
+ val = BigNumber(val);
+ let exp = BigNumber(10).exponentiatedBy(decimals);
+ return val.dividedBy(exp).toString(10);
+ }
+
+ toDecimal(val, decimals){
+ decimals = decimals || this.getDecimals();
+ if (!val || !decimals) return '';
+ val = BigNumber(val);
+ let exp = BigNumber(10).exponentiatedBy(decimals);
+ return val.multipliedBy(exp).toString(10);
+ }
+
+ toBtPrecision(bt , precession=2){
+ if (!bt) return '';
+ bt = String(bt);
+ bt = BigNumber(bt);
+ return bt.decimalPlaces(precession, 1).toString(10);
+ }
+
+ isBalSufficient(bal,value){
+ let balance = new BigNumber(bal);
+ let purchaseValue = new BigNumber(value);
+ return balance.comparedTo(purchaseValue);
+ }
+
+}
+
+export default new TokenHelper();
\ No newline at end of file
diff --git a/js/index.js b/js/index.js
index 4b096e6..839fe3d 100644
--- a/js/index.js
+++ b/js/index.js
@@ -19,6 +19,7 @@ import OstWalletSdkUI from './OstWalletSdkUI';
import {OstWalletSettings, OstWalletSettingsComponent, OstWalletSettingsDefaultConfig} from './WalletSettings';
import OstUserStatus from "./constants/UserStatus";
import OstDeviceStatus from "./constants/DeviceStatus";
+import {OstRedeemableSkus, OstRedeemableSkuDetails, OstRedemableCustomConfig} from "./Redemptions";
import OstTransactionHelper from "./TransactionHelper/OstTransactionHelper";
export {
@@ -35,5 +36,8 @@ export {
OstWalletSettingsComponent,
OstUserStatus,
OstDeviceStatus,
- OstTransactionHelper
+ OstTransactionHelper,
+ OstRedeemableSkus,
+ OstRedeemableSkuDetails,
+ OstRedemableCustomConfig
}
diff --git a/js/services/OstJsonApiPagination/BaseModel.js b/js/services/OstJsonApiPagination/BaseModel.js
new file mode 100644
index 0000000..3fa38c4
--- /dev/null
+++ b/js/services/OstJsonApiPagination/BaseModel.js
@@ -0,0 +1,138 @@
+const VCErrors = {
+ AlreadyFetchingError: 'AlreadyFetchingError',
+ NoMoreRecords: 'NoMoreRecords',
+ InvalidApiResponse: 'InvalidApiResponse'
+};
+
+let idCnt = 1;
+
+let defaultOptions = {
+ removeDuplicate: true
+};
+
+class BaseModel {
+ constructor(params, id = 'ost_json_api_' + String(idCnt++), options = {}) {
+ this.id = id;
+ this.extraParams = params;
+ this.options = Object.assign({}, defaultOptions, options);
+ this.initVals();
+ }
+
+ initVals() {
+ this.isFetching = false;
+ this.hasNextPage = true;
+ this.nextPagePayload = null;
+ this.results = [];
+ this.meta = null;
+ this.resultMap = {};
+ }
+
+ refresh() {
+ this.initVals();
+ return this.fetch();
+ }
+
+ getParams() {
+ let params = {};
+ if (this.extraParams) {
+ Object.assign(params, this.extraParams);
+ }
+ if (this.nextPagePayload) {
+ Object.assign(params, this.nextPagePayload);
+ }
+ return params;
+ }
+
+ fetch() {
+ if (this.isFetching) {
+ return Promise.reject({
+ code_error: VCErrors.AlreadyFetchingError
+ });
+ }
+
+ if (!this.hasNextPage) {
+ return Promise.reject({
+ code_error: VCErrors.NoMoreRecords
+ });
+ }
+
+ this.isFetching = true;
+ return this.fetchFromJsonApi();
+ }
+
+ fetchFromJsonApi(){
+ //Overwrite
+ throw "fetchFromJsonApi";
+ }
+
+ dataReceived(response) {
+ let meta = response.meta;
+ this.nextPagePayload = meta ? meta.next_page_payload : null;
+ this.meta = meta;
+ this.hasNextPage = this.nextPagePayload ? true : false;
+ let dataToAppend = this.processData(response);
+ this.isFetching = false;
+ return dataToAppend;
+ }
+
+ processData(response) {
+ let resultType = response.result_type;
+ if (!resultType || !response[resultType]) {
+ response.code_error = VCErrors.InvalidApiResponse;
+ // Invalid response.
+ throw response;
+ }
+ let results = response[resultType];
+ if (!(results instanceof Array)) {
+ response.code_error = VCErrors.InvalidApiResponse;
+ // Invalid response.
+ throw response;
+ }
+ return this.processResults( results, response );
+ }
+
+ processResults(results, response) {
+ let cleanedUpList = [];
+ let cnt = 0,
+ len = results.length;
+ for (; cnt < len; cnt++) {
+ let result = results[cnt];
+ let resultId = result.id;
+
+ // Format response.
+ result = this.formatResult(result, response);
+ if (!result) {
+ // Some wrong entry.
+ continue;
+ }
+ let existingResult = this.resultMap[resultId];
+ // Update existing result if available.
+ if (existingResult && this.options.removeDuplicate) {
+ Object.assign(existingResult, result);
+ continue;
+ }
+
+ // Add new result.
+ this.resultMap[resultId] = result;
+ this.results.push(result);
+ cleanedUpList.push(result);
+ }
+ return cleanedUpList;
+ }
+
+ formatResult(result, response) {
+ return result;
+ }
+
+ getAllResults() {
+ return this.results;
+ }
+
+ clone() {
+ //Overwrite
+ throw "clone";
+ }
+
+}
+
+export { BaseModel, VCErrors };
diff --git a/js/services/OstJsonApiPagination/Pagination.js b/js/services/OstJsonApiPagination/Pagination.js
new file mode 100644
index 0000000..0cdc2d6
--- /dev/null
+++ b/js/services/OstJsonApiPagination/Pagination.js
@@ -0,0 +1,100 @@
+class Pagination {
+
+ /**
+ * @param {*} modelFetch
+ * @param {*} callbacks
+ * NOTE params were added later m ideally it should be second parameter to the constructor
+ * but can risk it now to change the signature everywhere
+ */
+ constructor(modelFetch, callbacks) {
+ this.modelFetch = modelFetch ;
+ this.callbacks = callbacks || {};
+ this.refreshing = false ;
+ this.loadingNext = false ;
+ }
+
+ initPagination() {
+ this.refresh(this.modelFetch);
+ }
+
+ refresh(modelFetch) {
+ if (this.refreshing) return;
+ if (modelFetch) {
+ this.modelFetch = modelFetch;
+ } else {
+ this.modelFetch = this.modelFetch && this.modelFetch.clone();
+ }
+ if(!this.modelFetch) return;
+ this.beforeRefresh();
+ this.modelFetch
+ .refresh()
+ .then((res) => {
+ this.onRefresh(res);
+ })
+ .catch((error) => {
+ this.onRefreshError(error);
+ });
+ };
+
+ beforeRefresh() {
+ this.refreshing = true ;
+ this.callbacks.beforeRefresh && this.callbacks.beforeRefresh();
+ }
+
+ onRefresh(res) {
+ this.refreshing = false ;
+ this.callbacks.onRefresh && this.callbacks.onRefresh( res );
+ }
+
+ onRefreshError(error) {
+ this.refreshing = false ;
+ this.callbacks.onRefreshError && this.callbacks.onRefreshError(error);
+ }
+
+ /**
+ * getNext monitors for 4 different checkpoints
+ * 1. It wont call next page if allready fetching data of previous page
+ * 2. Wont next page when pull to refresh is done
+ * 3. Will stop pagination if next page payload is not present
+ */
+ getNext(){
+ if (
+ this.loadingNext ||
+ this.refreshing ||
+ !this.modelFetch.hasNextPage
+ )
+ return;
+ this.beforeNext();
+ this.modelFetch
+ .fetch()
+ .then((res) => {
+ this.onNext(res);
+ })
+ .catch((error) => {
+ this.onNextError(error);
+ });
+ };
+
+ beforeNext() {
+ this.loadingNext = true ;
+ this.callbacks.beforeNext && this.callbacks.beforeNext();
+ }
+
+ onNext(res) {
+ this.loadingNext = false ;
+ this.callbacks.onNext && this.callbacks.onNext( res );
+ }
+
+ onNextError(error) {
+ this.loadingNext = false ;
+ this.callbacks.onNextError && this.callbacks.onNextError(error);
+ }
+
+ getResults(){
+ return this.modelFetch.getAllResults();
+ }
+
+ };
+
+
+export default Pagination ;
\ No newline at end of file
diff --git a/js/services/OstJsonApiPagination/RedemptionSkusModel.js b/js/services/OstJsonApiPagination/RedemptionSkusModel.js
new file mode 100644
index 0000000..b1106e2
--- /dev/null
+++ b/js/services/OstJsonApiPagination/RedemptionSkusModel.js
@@ -0,0 +1,30 @@
+import OstJsonApi from "../../OstJsonApi";
+import {BaseModel} from "./BaseModel";
+
+class RedemptionSkusModel extends BaseModel {
+
+ constructor(userId , params, id, options ){
+ super(params, id, options);
+ this.userId = userId;
+ }
+
+ fetchFromJsonApi(){
+ return new Promise((resolve, reject) => {
+ OstJsonApi.getRedeemableSkus(this.userId, this.getParams() , (respones) => {
+ this.isFetching = false;
+ return resolve(this.dataReceived(respones));
+ }, (error)=> {
+ this.isFetching = false;
+ return reject(error);
+ })
+ });
+ }
+
+ clone() {
+ let Constructor = this.constructor;
+ return new Constructor(this.userId , this.extraParams, this.id, this.options);
+ }
+
+}
+
+export default RedemptionSkusModel;
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..a7f7e08
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,21 @@
+{
+ "name": "@ostdotcom/ost-wallet-sdk-react-native",
+ "version": "2.3.14",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+ },
+ "react-native-picker-select": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/react-native-picker-select/-/react-native-picker-select-6.5.0.tgz",
+ "integrity": "sha512-VuzQAzs3h1PvKH91OAomWqCqzimSttUFHj1jNzC9K2FZ5OG/OuTo2ri3KkBdPU1PjtakPg6OMxxNBANAXgmceA==",
+ "requires": {
+ "lodash.isequal": "^4.5.0"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index 14bb376..71ab591 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@ostdotcom/ost-wallet-sdk-react-native",
- "version": "2.3.14",
+ "version": "2.4.1",
"description": "OST Wallet SDK for React Native applications.",
"main": "./js/index.js",
"repository": {
@@ -24,9 +24,10 @@
"author": "OST.com Inc.",
"license": " Apache-2.0",
"peerDependencies": {
- "react-native": "^0.60.0",
- "eventemitter3": "^4.0.0",
- "lodash.merge": "^4.6.2",
- "bignumber.js": "^8.1.1"
+ "react-native": "0.60.0",
+ "eventemitter3": "4.0.0",
+ "lodash.merge": "4.6.2",
+ "bignumber.js": "8.1.1",
+ "react-native-picker-select": "6.5.0"
}
}