diff --git a/README.md b/README.md index 4570d03..140b46e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ ![Platform React Native SDK](./assets/github-reactNative-sdk-banner.png) # YouVersion React Native SDK + [![npm version](https://badge.fury.io/js/@youversion%2Freact-native-sdk.svg)](https://www.npmjs.com/package/@youversion/platform-sdk-reactnative) [![CI Status](https://github.com/youversion/platform-sdk-reactnative/workflows/CI/badge.svg)](https://github.com/youversion/platform-sdk-reactnative/actions) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) @@ -15,7 +16,6 @@ A lightweight React Native SDK for integrating YouVersion Platform features into - [Release Process](RELEASING.md) - [Changelog](CHANGELOG.md) - > [!important] > The React Native SDK is currently **iOS-only**. Android support is under active development and not yet available for use. @@ -272,17 +272,8 @@ See `BibleReference` properties under the `BibleTextView` component section abov Presents the YouVersion login flow to the user and resolves with the login result on completion. -```tsx -import { YouVersionAPI } from '@youversion/platform-sdk-reactnative'; - -const result = await YouVersionAPI.Users.signIn(['bibles', 'highlights']); -``` - **Parameters:** -An object with the following optional properties: - -- `requiredPermissions?: SignInWithYouVersionPermission[]` - Array of permissions that must be granted by the user for successful login. -- `optionalPermissions?: SignInWithYouVersionPermission[]` - Array of permissions that are requested but not required for login. +An array of permissions you're requesting from the user when they sign in. Enum values for `SignInWithYouVersionPermission`: @@ -293,13 +284,18 @@ Enum values for `SignInWithYouVersionPermission`: - `bibleActivity` **Returns:** -`Promise` - An object containing the following details: - -| Property | Type | Description | -| ------------- | ---------------------------------- | -------------------------------------------- | -| `accessToken` | `string` | The access token for the authenticated user. | -| `permissions` | `SignInWithYouVersionPermission[]` | The permissions granted by the user. | -| `yvpUserId` | `string` | The YouVersion Platform user ID. | +`Promise` - An object containing the following details: + +| Property | Type | Description | +| ---------------- | ---------------------------------- | -------------------------------------------- | +| `accessToken` | `string` | Access token for the authenticated user. | +| `permissions` | `SignInWithYouVersionPermission[]` | Permissions granted by the user. | +| `yvpUserId` | `string` | YouVersion Platform user ID. | +| `expiryDate` | `string` | Expiration date of the access token. | +| `refreshToken` | `string` | Refresh token for renewing the access token. | +| `name` | `string` | Name of the authenticated user. | +| `profilePicture` | `string` | URL to the user's profile picture. | +| `email` | `string` | Email address of the authenticated user. | #### `signOut` diff --git a/example/src/screens/ProfileScreen.tsx b/example/src/screens/ProfileScreen.tsx index e74fcf3..48168d3 100644 --- a/example/src/screens/ProfileScreen.tsx +++ b/example/src/screens/ProfileScreen.tsx @@ -41,16 +41,20 @@ export function ProfileScreen() { async function handleSignIn() { try { - await YouVersionAPI.Users.signIn({ - requiredPermissions: ["bibles"], - optionalPermissions: ["highlights"], - }); + const signInResult = await YouVersionAPI.Users.signIn(["bibles"]); + console.log("Sign-in result:", signInResult); + } catch (error) { + Alert.alert("Error signing in"); + console.error("Error signing in:", error); + return; + } + try { const userInfo = await YouVersionAPI.Users.userInfo(); setCurrentUser(userInfo); } catch (error) { - Alert.alert("Error signing in"); - console.error("Error signing in:", error); + Alert.alert("Error getting user info after sign-in"); + console.error("Error getting user info:", error); } } diff --git a/ios/RNYouVersionPlatformModule.swift b/ios/RNYouVersionPlatformModule.swift index fc7bda8..64f4f2c 100644 --- a/ios/RNYouVersionPlatformModule.swift +++ b/ios/RNYouVersionPlatformModule.swift @@ -17,13 +17,11 @@ public class RNYouVersionPlatformModule: Module { } AsyncFunction("signIn") { ( - requiredPermissions: [String], - optionalPermissions: [String], + permissions: [String], promise: Promise ) in YVPAuthAPI.signIn( - requiredPermissions: requiredPermissions, - optionalPermissions: optionalPermissions, + permissions: permissions, promise: promise ) } diff --git a/ios/YVPAuthAPI.swift b/ios/YVPAuthAPI.swift index 1dbb9f7..b8da651 100644 --- a/ios/YVPAuthAPI.swift +++ b/ios/YVPAuthAPI.swift @@ -3,31 +3,27 @@ import YouVersionPlatform import AuthenticationServices struct YVPAuthAPI { - static func signIn(requiredPermissions: [String], optionalPermissions: [String], promise: Promise) { - let required: Set = Set( - requiredPermissions.compactMap(SignInWithYouVersionPermission.init(rawValue:)) - ) - let optional: Set = Set( - optionalPermissions.compactMap(SignInWithYouVersionPermission.init(rawValue:)) - ) + static func signIn(permissions: [String], promise: Promise) { + let permissionsSet = Set( + permissions.compactMap(SignInWithYouVersionPermission.init(rawValue:)) + ) Task { do { let response = try await YouVersionAPI.Users.signIn( - requiredPermissions: required, - optionalPermissions: optional, - contextProvider: ContextProvider() + permissions: permissionsSet, + contextProvider: ContextProvider() ) - if let msg = response.errorMsg, !msg.isEmpty { - promise.reject(YVPError.signInError(message: msg)) - return - } - promise.resolve([ "accessToken": response.accessToken, "permissions": response.permissions.map(\.rawValue), - "yvpUserId": response.yvpUserId + "yvpUserId": response.yvpUserId, + "expiryDate": formatExpiryDate(response.expiryDate), + "refreshToken": response.refreshToken, + "name": response.name, + "profilePicture": response.profilePicture, + "email": response.email ]) } catch { promise.reject(error) @@ -59,6 +55,16 @@ struct YVPAuthAPI { } } +private let isoFormatter: ISO8601DateFormatter = { + let f = ISO8601DateFormatter() + f.formatOptions = [.withInternetDateTime, .withFractionalSeconds] + return f +}() + +func formatExpiryDate(_ expiryDate: Date?) -> String? { + expiryDate.map { isoFormatter.string(from: $0) } +} + class ContextProvider: NSObject, ASWebAuthenticationPresentationContextProviding { func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { @@ -69,7 +75,3 @@ class ContextProvider: NSObject, ASWebAuthenticationPresentationContextProviding return window } } - -enum YVPError : Error { - case signInError(message: String) -} diff --git a/mocks/RNYouVersionPlatform.ts b/mocks/RNYouVersionPlatform.ts index f3184ef..e06dc5a 100644 --- a/mocks/RNYouVersionPlatform.ts +++ b/mocks/RNYouVersionPlatform.ts @@ -4,7 +4,7 @@ import { HighlightResponse, LanguageOverview, SignInWithYouVersionPermission, - YouVersionLoginResult, + SignInWithYouVersionResult, YouVersionUserInfo, YouVersionVerseOfTheDay, } from "../src"; @@ -18,7 +18,7 @@ export function getAccessToken(): string | null { export function signIn( requiredPermissions: SignInWithYouVersionPermission[] = [], optionalPermissions: SignInWithYouVersionPermission[] = [], -): Promise { +): Promise { return Promise.resolve({ accessToken: "mock-access-token", permissions: [...requiredPermissions, ...optionalPermissions], diff --git a/src/__tests__/RNYouVersionPlatform/native-test.native.ts b/src/__tests__/RNYouVersionPlatform/native-test.native.ts index b3b844e..fe0f5a6 100644 --- a/src/__tests__/RNYouVersionPlatform/native-test.native.ts +++ b/src/__tests__/RNYouVersionPlatform/native-test.native.ts @@ -31,22 +31,18 @@ describe("YouVersionPlatform", () => { describe("YouVersionAPI.Users", () => { describe("#signIn", () => { - it("passes the required and optional permissions", async () => { - const result = await YouVersionAPI.Users.signIn({ - requiredPermissions: ["bible_activity"], - optionalPermissions: ["demographics"], - }); + it("passes the provided permissions", async () => { + const result = await YouVersionAPI.Users.signIn(["bible_activity"]); - expect(Module.signIn).toHaveBeenCalledWith( - ["bible_activity"], - ["demographics"], - ); + expect(Module.signIn).toHaveBeenCalledWith(["bible_activity"]); - expect(result).toEqual({ - accessToken: "mock-access-token", - permissions: ["bible_activity", "demographics"], - yvpUserId: "mock-yvp-user-id", - }); + expect(result).toEqual( + expect.objectContaining({ + accessToken: "mock-access-token", + permissions: ["bible_activity"], + yvpUserId: "mock-yvp-user-id", + }), + ); }); }); diff --git a/src/api/users.ts b/src/api/users.ts index 8b26fbe..1ac5c14 100644 --- a/src/api/users.ts +++ b/src/api/users.ts @@ -1,33 +1,21 @@ import { Module } from "../native"; import { SignInWithYouVersionPermission, - YouVersionLoginResult, + SignInWithYouVersionResult, YouVersionUserInfo, } from "../types"; -type SignInOptions = { - /** Array of permissions that are required for a successful login */ - requiredPermissions?: SignInWithYouVersionPermission[]; - - /** Array of permissions that are optional for the user to select */ - optionalPermissions?: SignInWithYouVersionPermission[]; -}; - export const UsersAPI = { /** * Presents the YouVersion login flow to the user and returns the login result upon completion. * - * @param params - An object ({@link SignInOptions}) containing an array of permissions that are optional for your app and an array of permissions that are required for a successful login. - * @returns A promise that resolves to the login result as a {@link YouVersionLoginResult} object. + * @param permissions - An array of permissions to request during sign-in. + * @returns A promise that resolves to the login result as a {@link SignInWithYouVersionResult} object. */ - signIn: ({ - requiredPermissions: providedRequired, - optionalPermissions: providedOptional, - }: SignInOptions = {}): Promise => { - const requiredPermissions = providedRequired || []; - const optionalPermissions = providedOptional || []; - - return Module.signIn(requiredPermissions, optionalPermissions); + signIn: ( + permissions: SignInWithYouVersionPermission[] = [], + ): Promise => { + return Module.signIn(permissions); }, /** Signs out the current user by clearing the access token from local storage */ diff --git a/src/native.ts b/src/native.ts index 48da902..7db4de1 100644 --- a/src/native.ts +++ b/src/native.ts @@ -5,7 +5,7 @@ import { BibleVersion, HighlightResponse, LanguageOverview, - YouVersionLoginResult, + SignInWithYouVersionResult, YouVersionUserInfo, YouVersionVerseOfTheDay, } from "./types"; @@ -15,10 +15,7 @@ declare class RNYouVersionPlatformModule extends NativeModule { setApiHost(apiHost: string): void; - signIn( - requiredPermissions: string[], - optionalPermissions: string[], - ): Promise; + signIn(permissions: string[]): Promise; signOut(): Promise; diff --git a/src/types.ts b/src/types.ts index cff3211..61dab51 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,9 +1,14 @@ import { ColorValue } from "react-native"; -export type YouVersionLoginResult = { - accessToken: string; +export type SignInWithYouVersionResult = { + accessToken?: string; permissions: SignInWithYouVersionPermission[]; - yvpUserId: string; + yvpUserId?: string; + expiryDate?: string; + refreshToken?: string; + name?: string; + profilePicture?: string; + email?: string; }; export type SignInWithYouVersionPermission =