diff --git a/README.md b/README.md
index 682a38a..a8d1204 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,114 @@
# react-native-contentpass
-Contentpass React Native SDK
+Contentpass React Native SDK enables easy integration of Contentpass functionality into your React Native applications.
## Installation
+Install the package using npm or Yarn:
```sh
npm install react-native-contentpass
```
+or
+
+```sh
+yarn add react-native-contentpass
+```
+
+### Peer Dependencies
+The following peer dependencies must also be installed:
+- [react](https://github.com/facebook/react) (Required for React Native projects.)
+- [react-native](https://github.com/facebook/react-native) (Core React Native framework)
+- [react-native-app-auth](https://github.com/FormidableLabs/react-native-app-auth) (Used for OAuth 2.0 authentication)
+- [react-native-encrypted-storage](https://github.com/emeraldsanto/react-native-encrypted-storage) (Ensures secure storage of authentication tokens)
+
+Some dependencies require additional setup in the native code. Refer to their official guides:
+- [react-native-app-auth setup](https://commerce.nearform.com/open-source/react-native-app-auth/docs#setup)
+- [react-native-encrypted-storage setup](https://github.com/emeraldsanto/react-native-encrypted-storage?tab=readme-ov-file#installation)
+
+### Expo support
+If you are using Expo, you need to run the following command to enable modifications to the `ios` and `android` directories:
+
+```sh
+npx expo prebuild
+```
+
## Usage
+### Initialization
+Wrap your app's root component with ContentpassSdkProvider. The provider requires a configuration object (contentpassConfig) with the following properties:
+- `propertyId` - Your unique property ID
+- `issuer` - The OAuth 2.0 server URL (e.g. `https://my.contentpass.net`)
+- `redirectUrl` - the redirect URL of your app to which the OAuth2 server will redirect after the authentication
+
+
+```jsx
+import React from 'react';
+import { ContentpassSdkProvider } from 'react-native-contentpass';
+
+const contentpassConfig = {
+ propertyId: 'your-property-id',
+ issuer: 'https://my.contentpass.net',
+ redirectUrl: 'com.yourapp://oauthredirect',
+};
+
+const App = () => {
+ return (
+
+
+
+ );
+};
+
+export default App;
+```
+
+### SDK Methods
+The SDK exposes the following methods through the `useContentpassSdk` hook:
+
+### authenticate
+Initiates the OAuth 2.0 authentication process via a modal interface. It validates the user’s active Contentpass subscriptions
+upon successful authentication.
+
+### registerObserver
+Registers a callback function to listen for changes in the user’s authentication and subscription status. The observer function
+receives a state object describing the current status (see the exported [ContentpassState](./src/types/ContentpassState.ts) type).
+
+### unregisterObserver
+Unregisters a previously registered observer. The observer will no longer receive updates.
+
+### logout
+Logs the user out by clearing all stored authentication tokens.
+
+### recoverFromError
+During background token refresh, an error state may occur due to poor or no internet connection. This is indicated by the
+`state` switching to `ERROR`. The state object includes a reference to the original exception that was thrown. As the SDK
+does not monitor the device's connection state, you must notify the SDK when the network connection has been reestablished
+or improved. The SDK will then refresh and revalidate the user's authentication tokens.
+
+```jsx
+import React, { useEffect } from 'react';
+import { useContentpassSdk } from 'react-native-contentpass';
+
+const YourApp = () => {
+ const { authenticate, registerObserver, unregisterObserver, logout, recoverFromError } = useContentpassSdk();
+
+ useEffect(() => {
+ const observer = (state) => {
+ console.log('Contentpass state changed:', state);
+ };
-```js
-import { multiply } from 'react-native-contentpass';
+ registerObserver(observer);
-// ...
+ return () => {
+ unregisterObserver(observer);
+ };
+ }, []);
-const result = await multiply(3, 7);
+ return (
+
+ );
+};
```
diff --git a/sharedExample/src/ContentpassUsage.tsx b/sharedExample/src/ContentpassUsage.tsx
index e5d6a05..4f52427 100644
--- a/sharedExample/src/ContentpassUsage.tsx
+++ b/sharedExample/src/ContentpassUsage.tsx
@@ -11,11 +11,15 @@ import {
import setupSourcepoint from './setupSourcepoint';
const styles = StyleSheet.create({
- sourcepointDataContainer: {
+ scrollViewLogsContainer: {
padding: 10,
- height: 400,
+ height: 200,
flexGrow: 0,
},
+ resultsText: {
+ marginTop: 20,
+ fontWeight: 'bold',
+ },
buttonsContainer: {
display: 'flex',
gap: 4,
@@ -84,10 +88,12 @@ export default function ContentpassUsage() {
- Authenticate result:
- {JSON.stringify(authResult, null, 2)}
- Sourcepoint user data:
-
+ Authenticate result:
+
+ {JSON.stringify(authResult, null, 2)}
+
+ Sourcepoint user data:
+
{JSON.stringify(sourcepointUserData, null, 2)}
diff --git a/src/Contentpass.test.ts b/src/Contentpass.test.ts
index 48f8c53..0a7aad7 100644
--- a/src/Contentpass.test.ts
+++ b/src/Contentpass.test.ts
@@ -186,7 +186,7 @@ describe('Contentpass', () => {
expect(contentpassStates).toHaveLength(2);
expect(contentpassStates[1]).toEqual({
state: 'ERROR',
- error: 'Authorize error',
+ error,
});
});
@@ -213,7 +213,8 @@ describe('Contentpass', () => {
contentpass.registerObserver((state) => {
contentpassStates.push(state);
});
- fetchContentpassTokenSpy.mockRejectedValue(new Error('Fetch error'));
+ const error = new Error('Fetch error');
+ fetchContentpassTokenSpy.mockRejectedValue(error);
await contentpass.authenticate();
@@ -223,7 +224,7 @@ describe('Contentpass', () => {
expect(contentpassStates).toHaveLength(2);
expect(contentpassStates[1]).toEqual({
state: 'ERROR',
- error: 'Fetch error',
+ error,
});
});
@@ -410,13 +411,14 @@ describe('Contentpass', () => {
contentpassStates.push(state);
});
- authorizeSpy.mockRejectedValue(new Error('Authorize error'));
+ const error = new Error('Authorize error');
+ authorizeSpy.mockRejectedValue(error);
await contentpass.authenticate();
expect(contentpassStates[1]).toEqual({
state: 'ERROR',
- error: 'Authorize error',
+ error,
});
await contentpass.recoverFromError();
diff --git a/src/Contentpass.ts b/src/Contentpass.ts
index ff77fd0..e3cc434 100644
--- a/src/Contentpass.ts
+++ b/src/Contentpass.ts
@@ -57,7 +57,7 @@ export default class Contentpass {
this.changeContentpassState({
state: ContentpassStateType.ERROR,
- error: 'message' in err ? err.message : 'Unknown error',
+ error: err,
});
return;
}
@@ -133,7 +133,7 @@ export default class Contentpass {
} catch (err: any) {
this.changeContentpassState({
state: ContentpassStateType.ERROR,
- error: err.message || 'Unknown error',
+ error: err,
});
}
};
diff --git a/src/index.tsx b/src/index.tsx
index d547522..d8199df 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,5 +1,6 @@
export type {
ContentpassState,
+ ContentpassStateType,
ErrorState,
AuthenticatedState,
InitialisingState,
@@ -9,7 +10,7 @@ export type {
export type { ContentpassConfig } from './types/ContentpassConfig';
export {
- default as Contentpass,
+ type default as Contentpass,
type ContentpassObserver,
} from './Contentpass';
diff --git a/src/types/ContentpassState.ts b/src/types/ContentpassState.ts
index 70d42ef..39d5890 100644
--- a/src/types/ContentpassState.ts
+++ b/src/types/ContentpassState.ts
@@ -28,7 +28,7 @@ export type AuthenticatedState = {
export type ErrorState = {
state: ContentpassStateType.ERROR;
hasValidSubscription?: never;
- error: string;
+ error: unknown;
};
export type ContentpassState =