From ef352b7cdba2a90ec729200039f3fd4abd1a9849 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Fri, 11 Oct 2024 11:00:21 -0400 Subject: [PATCH 01/23] Add new package directory for expo SDK --- packages/expo/package.json | 93 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 packages/expo/package.json diff --git a/packages/expo/package.json b/packages/expo/package.json new file mode 100644 index 00000000..b9900376 --- /dev/null +++ b/packages/expo/package.json @@ -0,0 +1,93 @@ +{ + "name": "@knocklabs/expo", + "version": "0.0.1", + "author": "@knocklabs", + "license": "MIT", + "main": "dist/cjs/index.js", + "module": "dist/esm/index.mjs", + "types": "dist/types/index.d.ts", + "typings": "dist/types/index.d.ts", + "react-native": "./src/index.ts", + "exports": { + ".": { + "require": "./dist/cjs/index.js", + "types": "./dist/types/index.d.ts", + "react-native": "./src/index.ts", + "default": "./dist/esm/index.mjs" + } + }, + "files": [ + "dist", + "src", + "README.md" + ], + "scripts": { + "clean": "rimraf dist", + "dev": "tsc && vite build --watch", + "build": "yarn clean && yarn build:esm && yarn build:cjs", + "build:esm": "BUILD_TARGET=esm; tsc && vite build", + "build:cjs": "BUILD_TARGET=cjs; tsc && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "format": "prettier \"src/**/*.{js,ts,tsx}\" --write", + "format:check": "prettier \"src/**/*.{js,ts,tsx}\" --check", + "type:check": "tsc --noEmit", + "preview": "vite preview" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/knocklabs/javascript.git" + }, + "bugs": { + "url": "https://github.com/knocklabs/javascript/issues" + }, + "peerDependencies": { + "expo": "*", + "expo-constants": "*", + "expo-device": "*", + "expo-notifications": "*", + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + }, + "expo-constants": { + "optional": true + }, + "expo-device": { + "optional": true + }, + "expo-notifications": { + "optional": true + } + }, + "dependencies": { + "@knocklabs/client": "workspace:^", + "@knocklabs/react-core": "workspace:^", + "react-native-gesture-handler": "^2.19.0", + "react-native-render-html": "^6.3.4", + "react-native-svg": "^15.6.0" + }, + "devDependencies": { + "@types/react": "^18.3.6", + "@types/react-native-htmlview": "^0.16.5", + "@typescript-eslint/eslint-plugin": "^6.20.0", + "@typescript-eslint/parser": "^8.8.0", + "@vitejs/plugin-react": "^4.3.2", + "eslint": "^8.56.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.4", + "expo": ">=51.0.24", + "expo-constants": ">=16.0.2", + "expo-device": ">=6.0.2", + "expo-notifications": ">=0.28.16", + "react": "^18.2.0", + "react-native": "^0.73.4", + "rimraf": "^6.0.1", + "typescript": "^5.6.2", + "vite": "^5.0.0", + "vite-plugin-dts": "^3.6.3", + "vite-plugin-no-bundle": "^4.0.0" + } +} From 7f2d7f800e0451c258e5cae2f3ec4c1f497d5d14 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Fri, 11 Oct 2024 11:03:52 -0400 Subject: [PATCH 02/23] Add tooling config --- packages/expo/.eslintrc.js | 11 ++++++++ packages/expo/Babel.config.json | 17 +++++++++++ packages/expo/tsconfig.json | 5 ++++ packages/expo/tsconfig.node.json | 4 +++ packages/expo/vite.config.mts | 48 ++++++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+) create mode 100644 packages/expo/.eslintrc.js create mode 100644 packages/expo/Babel.config.json create mode 100644 packages/expo/tsconfig.json create mode 100644 packages/expo/tsconfig.node.json create mode 100644 packages/expo/vite.config.mts diff --git a/packages/expo/.eslintrc.js b/packages/expo/.eslintrc.js new file mode 100644 index 00000000..d4f51959 --- /dev/null +++ b/packages/expo/.eslintrc.js @@ -0,0 +1,11 @@ +/** @type {import("eslint").Linter.Config} */ +module.exports = { + root: true, + extends: [ + "@knocklabs/eslint-config/library.js", + "plugin:react-hooks/recommended", + ], + parserOptions: { + projects: ["tsconfig.json", "tsconfig.node.json"], + }, +}; diff --git a/packages/expo/Babel.config.json b/packages/expo/Babel.config.json new file mode 100644 index 00000000..edbfbb26 --- /dev/null +++ b/packages/expo/Babel.config.json @@ -0,0 +1,17 @@ +{ + "presets": [ + [ + "@nx/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": ["module:react-native-dotenv"], + "env": { + "test": { + "presets": ["module:metro-react-native-babel-preset"] + } + } +} diff --git a/packages/expo/tsconfig.json b/packages/expo/tsconfig.json new file mode 100644 index 00000000..cd7cd18d --- /dev/null +++ b/packages/expo/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@knocklabs/typescript-config/react-library.json", + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/expo/tsconfig.node.json b/packages/expo/tsconfig.node.json new file mode 100644 index 00000000..c8d60c17 --- /dev/null +++ b/packages/expo/tsconfig.node.json @@ -0,0 +1,4 @@ +{ + "extends": "@knocklabs/typescript-config/node.json", + "include": ["vite.config.ts"] +} diff --git a/packages/expo/vite.config.mts b/packages/expo/vite.config.mts new file mode 100644 index 00000000..63233a3f --- /dev/null +++ b/packages/expo/vite.config.mts @@ -0,0 +1,48 @@ +import react from "@vitejs/plugin-react"; +import { resolve } from "path"; +import { LibraryFormats, defineConfig, loadEnv } from "vite"; +import dts from "vite-plugin-dts"; +import noBundlePlugin from "vite-plugin-no-bundle"; + +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), ""); + const CJS = env.BUILD_TARGET?.toLocaleLowerCase()?.match("cjs"); + const formats: LibraryFormats[] = CJS ? ["cjs"] : ["es"]; + + return { + plugins: [ + dts({ + outDir: "dist/types", + }), + react(), + noBundlePlugin({ copy: "**/*.css", root: "./src" }), + ], + build: { + outDir: CJS ? "dist/cjs" : "dist/esm", + sourcemap: true, + lib: { + entry: resolve(__dirname, "src/index.ts"), + name: "expo", + formats, + }, + rollupOptions: { + // External packages that should not be bundled into your library. + external: [ + "react", + "react-native", + "expo", + "expo-constants", + "expo-device", + "expo-notifications", + ], + output: { + interop: "compat", + format: formats[0], + globals: { + react: "React", + }, + }, + }, + }, + }; +}); From 8d205bc47587c96097326453748b6c3dbb618ad6 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Fri, 11 Oct 2024 11:07:27 -0400 Subject: [PATCH 03/23] Copy KnockExpoPushNotificationProvider to expo package --- .../KnockExpoPushNotificationProvider.tsx | 323 ++++++++++++++++++ packages/expo/src/modules/push/index.ts | 1 + 2 files changed, 324 insertions(+) create mode 100644 packages/expo/src/modules/push/KnockExpoPushNotificationProvider.tsx create mode 100644 packages/expo/src/modules/push/index.ts diff --git a/packages/expo/src/modules/push/KnockExpoPushNotificationProvider.tsx b/packages/expo/src/modules/push/KnockExpoPushNotificationProvider.tsx new file mode 100644 index 00000000..9b4c1502 --- /dev/null +++ b/packages/expo/src/modules/push/KnockExpoPushNotificationProvider.tsx @@ -0,0 +1,323 @@ +import { + ChannelData, + Message, + MessageEngagementStatus, +} from "@knocklabs/client"; +import { useKnockClient } from "@knocklabs/react-core"; +import Constants from "expo-constants"; +import * as Device from "expo-device"; +import * as Notifications from "expo-notifications"; +import React, { + createContext, + useCallback, + useContext, + useEffect, + useState, +} from "react"; + +export interface KnockExpoPushNotificationContextType { + expoPushToken: string | null; + registerForPushNotifications: () => Promise; + registerPushTokenToChannel(token: string, channelId: string): Promise; + unregisterPushTokenFromChannel( + token: string, + channelId: string, + ): Promise; + onNotificationReceived: ( + handler: (notification: Notifications.Notification) => void, + ) => void; + onNotificationTapped: ( + handler: (response: Notifications.NotificationResponse) => void, + ) => void; +} + +Notifications.setNotificationHandler({ + handleNotification: async () => { + return { + shouldShowAlert: true, + shouldPlaySound: true, + shouldSetBadge: true, + }; + }, +}); + +const defaultNotificationHandler = async ( + _notification: Notifications.Notification, +): Promise => { + return { + shouldShowAlert: true, + shouldPlaySound: true, + shouldSetBadge: true, + }; +}; + +const KnockExpoPushNotificationContext = createContext< + KnockExpoPushNotificationContextType | undefined +>(undefined); + +export interface KnockExpoPushNotificationProviderProps { + knockExpoChannelId: string; + customNotificationHandler?: ( + notification: Notifications.Notification, + ) => Promise; + children?: React.ReactElement; + autoRegister?: boolean; +} + +async function requestPushPermissionIfNeeded(): Promise { + const { status: existingStatus } = await Notifications.getPermissionsAsync(); + + if (existingStatus !== "granted") { + const { status } = await Notifications.requestPermissionsAsync(); + return status; + } + + return existingStatus; +} + +async function getExpoPushToken(): Promise { + try { + if ( + !Constants.expoConfig || + !Constants.expoConfig.extra || + !Constants.expoConfig.extra.eas + ) { + console.error( + "[Knock] Expo Project ID is not defined in the app configuration.", + ); + return null; + } + const token = await Notifications.getExpoPushTokenAsync({ + projectId: Constants.expoConfig.extra.eas.projectId, + }); + return token; + } catch (error) { + console.error("[Knock] Error getting Expo push token:", error); + return null; + } +} + +async function requestPermissionAndGetPushToken(): Promise { + // Check for device support + if (!Device.isDevice) { + console.warn("[Knock] Must use physical device for Push Notifications"); + return null; + } + + const permissionStatus = await requestPushPermissionIfNeeded(); + + if (permissionStatus !== "granted") { + console.warn("[Knock] Push notification permission not granted"); + return null; + } + + return getExpoPushToken(); +} + +export const KnockExpoPushNotificationProvider: React.FC< + KnockExpoPushNotificationProviderProps +> = ({ + knockExpoChannelId, + customNotificationHandler, + children, + autoRegister = true, +}) => { + const [expoPushToken, setExpoPushToken] = useState(null); + const knockClient = useKnockClient(); + + const [notificationReceivedHandler, setNotificationReceivedHandler] = + useState<(notification: Notifications.Notification) => void>( + () => () => {}, + ); + + const [notificationTappedHandler, setNotificationTappedHandler] = useState< + (response: Notifications.NotificationResponse) => void + >(() => () => {}); + + const handleNotificationReceived = useCallback( + (handler: (notification: Notifications.Notification) => void) => { + setNotificationReceivedHandler(() => handler); + }, + [], + ); + + const handleNotificationTapped = useCallback( + (handler: (response: Notifications.NotificationResponse) => void) => { + setNotificationTappedHandler(() => handler); + }, + [], + ); + + const registerForPushNotifications = useCallback(async (): Promise => { + try { + knockClient.log(`[Knock] Registering for push notifications`); + const token = await requestPermissionAndGetPushToken(); + knockClient.log(`[Knock] Token received: ${token?.data}`); + if (token?.data) { + knockClient.log(`[Knock] Setting push token: ${token.data}`); + setExpoPushToken(token.data); + } + } catch (error) { + console.error(`[Knock] Error registering for push notifications:`, error); + } + }, [knockClient]); + + const updateKnockMessageStatusFromNotification = useCallback( + async ( + notification: Notifications.Notification, + status: MessageEngagementStatus, + ): Promise => { + const messageId = notification.request.content.data["knock_message_id"]; + return knockClient.messages.updateStatus(messageId, status); + }, + [knockClient], + ); + + const registerNewTokenDataOnServer = useCallback( + async (tokens: string[], channelId: string): Promise => { + return knockClient.user.setChannelData({ + channelId: channelId, + channelData: { tokens: tokens }, + }); + }, + [knockClient], + ); + + const registerPushTokenToChannel = useCallback( + async (token: string, channelId: string): Promise => { + knockClient.user + .getChannelData({ channelId: channelId }) + .then((result: ChannelData) => { + const tokens: string[] = result.data["tokens"]; + if (!tokens.includes(token)) { + tokens.push(token); + return registerNewTokenDataOnServer(tokens, channelId); + } + knockClient.log("[Knock] registerPushTokenToChannel success"); + }) + .catch((_) => { + // No data registered on that channel for that user, we'll create a new record + return registerNewTokenDataOnServer([token], channelId); + }); + }, + [knockClient, registerNewTokenDataOnServer], + ); + + const unregisterPushTokenFromChannel = useCallback( + async (token: string, channelId: string): Promise => { + knockClient.user + .getChannelData({ channelId: channelId }) + .then((result: ChannelData) => { + const tokens: string[] = result.data["tokens"]; + const updatedTokens = tokens.filter( + (channelToken) => channelToken !== token, + ); + knockClient.log("unregisterPushTokenFromChannel success"); + return registerNewTokenDataOnServer(updatedTokens, channelId); + }) + .catch((error) => { + console.error( + `[Knock] Error unregistering push token from channel:`, + error, + ); + }); + }, + [knockClient, registerNewTokenDataOnServer], + ); + + useEffect(() => { + Notifications.setNotificationHandler({ + handleNotification: + customNotificationHandler ?? defaultNotificationHandler, + }); + + if (autoRegister) { + registerForPushNotifications() + .then(() => { + if (expoPushToken) { + registerPushTokenToChannel(expoPushToken, knockExpoChannelId) + .then((_result) => { + knockClient.log("[Knock] setChannelData success"); + }) + .catch((_error) => { + console.error( + "[Knock] Error in setting push token or channel data", + _error, + ); + }); + } + }) + .catch((_error) => { + console.error( + "[Knock] Error in setting push token or channel data", + _error, + ); + }); + } + + const notificationReceivedSubscription = + Notifications.addNotificationReceivedListener((notification) => { + knockClient.log( + "[Knock] Expo Push Notification received in foreground:", + ); + updateKnockMessageStatusFromNotification(notification, "interacted"); + notificationReceivedHandler(notification); + }); + + const notificationResponseSubscription = + Notifications.addNotificationResponseReceivedListener((response) => { + knockClient.log("[Knock] Expo Push Notification was interacted with"); + updateKnockMessageStatusFromNotification( + response.notification, + "interacted", + ); + notificationTappedHandler(response); + }); + + return () => { + Notifications.removeNotificationSubscription( + notificationReceivedSubscription, + ); + Notifications.removeNotificationSubscription( + notificationResponseSubscription, + ); + }; + + // TODO: Remove when possible and ensure dependency array is correct + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + registerForPushNotifications, + notificationReceivedHandler, + notificationTappedHandler, + customNotificationHandler, + expoPushToken, + knockExpoChannelId, + knockClient, + ]); + + return ( + + {children} + + ); +}; + +export const useExpoPushNotifications = + (): KnockExpoPushNotificationContextType => { + const context = useContext(KnockExpoPushNotificationContext); + if (context === undefined) { + throw new Error( + "[Knock] useExpoPushNotifications must be used within a PushNotificationProvider", + ); + } + return context; + }; diff --git a/packages/expo/src/modules/push/index.ts b/packages/expo/src/modules/push/index.ts new file mode 100644 index 00000000..a65cc36c --- /dev/null +++ b/packages/expo/src/modules/push/index.ts @@ -0,0 +1 @@ +export * from "./KnockExpoPushNotificationProvider"; From 8c6313f915c804899e2ad021ecd85c9bead1afe9 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Fri, 11 Oct 2024 11:13:20 -0400 Subject: [PATCH 04/23] Add root index.ts to expo package --- packages/expo/src/index.ts | 2 ++ yarn.lock | 47 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 packages/expo/src/index.ts diff --git a/packages/expo/src/index.ts b/packages/expo/src/index.ts new file mode 100644 index 00000000..25943bce --- /dev/null +++ b/packages/expo/src/index.ts @@ -0,0 +1,2 @@ +export * from "./modules/push"; +export * from "@knocklabs/react-core"; diff --git a/yarn.lock b/yarn.lock index 3dd3576b..0189a6b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4988,6 +4988,53 @@ __metadata: languageName: unknown linkType: soft +"@knocklabs/expo@workspace:packages/expo": + version: 0.0.0-use.local + resolution: "@knocklabs/expo@workspace:packages/expo" + dependencies: + "@knocklabs/client": "workspace:^" + "@knocklabs/react-core": "workspace:^" + "@types/react": "npm:^18.3.6" + "@types/react-native-htmlview": "npm:^0.16.5" + "@typescript-eslint/eslint-plugin": "npm:^6.20.0" + "@typescript-eslint/parser": "npm:^8.8.0" + "@vitejs/plugin-react": "npm:^4.3.2" + eslint: "npm:^8.56.0" + eslint-plugin-react-hooks: "npm:^4.6.0" + eslint-plugin-react-refresh: "npm:^0.4.4" + expo: "npm:>=51.0.24" + expo-constants: "npm:>=16.0.2" + expo-device: "npm:>=6.0.2" + expo-notifications: "npm:>=0.28.16" + react: "npm:^18.2.0" + react-native: "npm:^0.73.4" + react-native-gesture-handler: "npm:^2.19.0" + react-native-render-html: "npm:^6.3.4" + react-native-svg: "npm:^15.6.0" + rimraf: "npm:^6.0.1" + typescript: "npm:^5.6.2" + vite: "npm:^5.0.0" + vite-plugin-dts: "npm:^3.6.3" + vite-plugin-no-bundle: "npm:^4.0.0" + peerDependencies: + expo: "*" + expo-constants: "*" + expo-device: "*" + expo-notifications: "*" + react: "*" + react-native: "*" + peerDependenciesMeta: + expo: + optional: true + expo-constants: + optional: true + expo-device: + optional: true + expo-notifications: + optional: true + languageName: unknown + linkType: soft + "@knocklabs/javascript@workspace:.": version: 0.0.0-use.local resolution: "@knocklabs/javascript@workspace:." From 4bbf3dc0d092c255b83394bb30e5c778a1a31a7c Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Fri, 11 Oct 2024 11:14:56 -0400 Subject: [PATCH 05/23] Re-export everything from react native package --- packages/expo/package.json | 1 + packages/expo/src/index.ts | 1 + yarn.lock | 1 + 3 files changed, 3 insertions(+) diff --git a/packages/expo/package.json b/packages/expo/package.json index b9900376..38ae75cb 100644 --- a/packages/expo/package.json +++ b/packages/expo/package.json @@ -65,6 +65,7 @@ "dependencies": { "@knocklabs/client": "workspace:^", "@knocklabs/react-core": "workspace:^", + "@knocklabs/react-native": "workspace:^", "react-native-gesture-handler": "^2.19.0", "react-native-render-html": "^6.3.4", "react-native-svg": "^15.6.0" diff --git a/packages/expo/src/index.ts b/packages/expo/src/index.ts index 25943bce..c71c1ee0 100644 --- a/packages/expo/src/index.ts +++ b/packages/expo/src/index.ts @@ -1,2 +1,3 @@ export * from "./modules/push"; +export * from "@knocklabs/react-native"; export * from "@knocklabs/react-core"; diff --git a/yarn.lock b/yarn.lock index 0189a6b8..acaa7f0d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4994,6 +4994,7 @@ __metadata: dependencies: "@knocklabs/client": "workspace:^" "@knocklabs/react-core": "workspace:^" + "@knocklabs/react-native": "workspace:^" "@types/react": "npm:^18.3.6" "@types/react-native-htmlview": "npm:^0.16.5" "@typescript-eslint/eslint-plugin": "npm:^6.20.0" From 4c221de7a443ff6e03b23a94b9209d4cfa25a142 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Fri, 11 Oct 2024 11:19:04 -0400 Subject: [PATCH 06/23] Remove push code from react native SDK --- packages/react-native/src/index.ts | 1 - .../KnockExpoPushNotificationProvider.tsx | 323 ------------------ .../react-native/src/modules/push/index.ts | 1 - 3 files changed, 325 deletions(-) delete mode 100644 packages/react-native/src/modules/push/KnockExpoPushNotificationProvider.tsx delete mode 100644 packages/react-native/src/modules/push/index.ts diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index bbce3d31..5ea4e960 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -1,4 +1,3 @@ export * from "./modules/feed"; -export * from "./modules/push"; export * from "@knocklabs/react-core"; export * from "./assets"; diff --git a/packages/react-native/src/modules/push/KnockExpoPushNotificationProvider.tsx b/packages/react-native/src/modules/push/KnockExpoPushNotificationProvider.tsx deleted file mode 100644 index 9b4c1502..00000000 --- a/packages/react-native/src/modules/push/KnockExpoPushNotificationProvider.tsx +++ /dev/null @@ -1,323 +0,0 @@ -import { - ChannelData, - Message, - MessageEngagementStatus, -} from "@knocklabs/client"; -import { useKnockClient } from "@knocklabs/react-core"; -import Constants from "expo-constants"; -import * as Device from "expo-device"; -import * as Notifications from "expo-notifications"; -import React, { - createContext, - useCallback, - useContext, - useEffect, - useState, -} from "react"; - -export interface KnockExpoPushNotificationContextType { - expoPushToken: string | null; - registerForPushNotifications: () => Promise; - registerPushTokenToChannel(token: string, channelId: string): Promise; - unregisterPushTokenFromChannel( - token: string, - channelId: string, - ): Promise; - onNotificationReceived: ( - handler: (notification: Notifications.Notification) => void, - ) => void; - onNotificationTapped: ( - handler: (response: Notifications.NotificationResponse) => void, - ) => void; -} - -Notifications.setNotificationHandler({ - handleNotification: async () => { - return { - shouldShowAlert: true, - shouldPlaySound: true, - shouldSetBadge: true, - }; - }, -}); - -const defaultNotificationHandler = async ( - _notification: Notifications.Notification, -): Promise => { - return { - shouldShowAlert: true, - shouldPlaySound: true, - shouldSetBadge: true, - }; -}; - -const KnockExpoPushNotificationContext = createContext< - KnockExpoPushNotificationContextType | undefined ->(undefined); - -export interface KnockExpoPushNotificationProviderProps { - knockExpoChannelId: string; - customNotificationHandler?: ( - notification: Notifications.Notification, - ) => Promise; - children?: React.ReactElement; - autoRegister?: boolean; -} - -async function requestPushPermissionIfNeeded(): Promise { - const { status: existingStatus } = await Notifications.getPermissionsAsync(); - - if (existingStatus !== "granted") { - const { status } = await Notifications.requestPermissionsAsync(); - return status; - } - - return existingStatus; -} - -async function getExpoPushToken(): Promise { - try { - if ( - !Constants.expoConfig || - !Constants.expoConfig.extra || - !Constants.expoConfig.extra.eas - ) { - console.error( - "[Knock] Expo Project ID is not defined in the app configuration.", - ); - return null; - } - const token = await Notifications.getExpoPushTokenAsync({ - projectId: Constants.expoConfig.extra.eas.projectId, - }); - return token; - } catch (error) { - console.error("[Knock] Error getting Expo push token:", error); - return null; - } -} - -async function requestPermissionAndGetPushToken(): Promise { - // Check for device support - if (!Device.isDevice) { - console.warn("[Knock] Must use physical device for Push Notifications"); - return null; - } - - const permissionStatus = await requestPushPermissionIfNeeded(); - - if (permissionStatus !== "granted") { - console.warn("[Knock] Push notification permission not granted"); - return null; - } - - return getExpoPushToken(); -} - -export const KnockExpoPushNotificationProvider: React.FC< - KnockExpoPushNotificationProviderProps -> = ({ - knockExpoChannelId, - customNotificationHandler, - children, - autoRegister = true, -}) => { - const [expoPushToken, setExpoPushToken] = useState(null); - const knockClient = useKnockClient(); - - const [notificationReceivedHandler, setNotificationReceivedHandler] = - useState<(notification: Notifications.Notification) => void>( - () => () => {}, - ); - - const [notificationTappedHandler, setNotificationTappedHandler] = useState< - (response: Notifications.NotificationResponse) => void - >(() => () => {}); - - const handleNotificationReceived = useCallback( - (handler: (notification: Notifications.Notification) => void) => { - setNotificationReceivedHandler(() => handler); - }, - [], - ); - - const handleNotificationTapped = useCallback( - (handler: (response: Notifications.NotificationResponse) => void) => { - setNotificationTappedHandler(() => handler); - }, - [], - ); - - const registerForPushNotifications = useCallback(async (): Promise => { - try { - knockClient.log(`[Knock] Registering for push notifications`); - const token = await requestPermissionAndGetPushToken(); - knockClient.log(`[Knock] Token received: ${token?.data}`); - if (token?.data) { - knockClient.log(`[Knock] Setting push token: ${token.data}`); - setExpoPushToken(token.data); - } - } catch (error) { - console.error(`[Knock] Error registering for push notifications:`, error); - } - }, [knockClient]); - - const updateKnockMessageStatusFromNotification = useCallback( - async ( - notification: Notifications.Notification, - status: MessageEngagementStatus, - ): Promise => { - const messageId = notification.request.content.data["knock_message_id"]; - return knockClient.messages.updateStatus(messageId, status); - }, - [knockClient], - ); - - const registerNewTokenDataOnServer = useCallback( - async (tokens: string[], channelId: string): Promise => { - return knockClient.user.setChannelData({ - channelId: channelId, - channelData: { tokens: tokens }, - }); - }, - [knockClient], - ); - - const registerPushTokenToChannel = useCallback( - async (token: string, channelId: string): Promise => { - knockClient.user - .getChannelData({ channelId: channelId }) - .then((result: ChannelData) => { - const tokens: string[] = result.data["tokens"]; - if (!tokens.includes(token)) { - tokens.push(token); - return registerNewTokenDataOnServer(tokens, channelId); - } - knockClient.log("[Knock] registerPushTokenToChannel success"); - }) - .catch((_) => { - // No data registered on that channel for that user, we'll create a new record - return registerNewTokenDataOnServer([token], channelId); - }); - }, - [knockClient, registerNewTokenDataOnServer], - ); - - const unregisterPushTokenFromChannel = useCallback( - async (token: string, channelId: string): Promise => { - knockClient.user - .getChannelData({ channelId: channelId }) - .then((result: ChannelData) => { - const tokens: string[] = result.data["tokens"]; - const updatedTokens = tokens.filter( - (channelToken) => channelToken !== token, - ); - knockClient.log("unregisterPushTokenFromChannel success"); - return registerNewTokenDataOnServer(updatedTokens, channelId); - }) - .catch((error) => { - console.error( - `[Knock] Error unregistering push token from channel:`, - error, - ); - }); - }, - [knockClient, registerNewTokenDataOnServer], - ); - - useEffect(() => { - Notifications.setNotificationHandler({ - handleNotification: - customNotificationHandler ?? defaultNotificationHandler, - }); - - if (autoRegister) { - registerForPushNotifications() - .then(() => { - if (expoPushToken) { - registerPushTokenToChannel(expoPushToken, knockExpoChannelId) - .then((_result) => { - knockClient.log("[Knock] setChannelData success"); - }) - .catch((_error) => { - console.error( - "[Knock] Error in setting push token or channel data", - _error, - ); - }); - } - }) - .catch((_error) => { - console.error( - "[Knock] Error in setting push token or channel data", - _error, - ); - }); - } - - const notificationReceivedSubscription = - Notifications.addNotificationReceivedListener((notification) => { - knockClient.log( - "[Knock] Expo Push Notification received in foreground:", - ); - updateKnockMessageStatusFromNotification(notification, "interacted"); - notificationReceivedHandler(notification); - }); - - const notificationResponseSubscription = - Notifications.addNotificationResponseReceivedListener((response) => { - knockClient.log("[Knock] Expo Push Notification was interacted with"); - updateKnockMessageStatusFromNotification( - response.notification, - "interacted", - ); - notificationTappedHandler(response); - }); - - return () => { - Notifications.removeNotificationSubscription( - notificationReceivedSubscription, - ); - Notifications.removeNotificationSubscription( - notificationResponseSubscription, - ); - }; - - // TODO: Remove when possible and ensure dependency array is correct - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - registerForPushNotifications, - notificationReceivedHandler, - notificationTappedHandler, - customNotificationHandler, - expoPushToken, - knockExpoChannelId, - knockClient, - ]); - - return ( - - {children} - - ); -}; - -export const useExpoPushNotifications = - (): KnockExpoPushNotificationContextType => { - const context = useContext(KnockExpoPushNotificationContext); - if (context === undefined) { - throw new Error( - "[Knock] useExpoPushNotifications must be used within a PushNotificationProvider", - ); - } - return context; - }; diff --git a/packages/react-native/src/modules/push/index.ts b/packages/react-native/src/modules/push/index.ts deleted file mode 100644 index a65cc36c..00000000 --- a/packages/react-native/src/modules/push/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./KnockExpoPushNotificationProvider"; From 6c550a59964c5bce1f3648403e30080645d76c59 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Fri, 11 Oct 2024 11:29:07 -0400 Subject: [PATCH 07/23] Remove peerDependenciesMeta from expo package.json --- packages/expo/package.json | 14 -------------- yarn.lock | 9 --------- 2 files changed, 23 deletions(-) diff --git a/packages/expo/package.json b/packages/expo/package.json index 38ae75cb..1d085c57 100644 --- a/packages/expo/package.json +++ b/packages/expo/package.json @@ -48,20 +48,6 @@ "react": "*", "react-native": "*" }, - "peerDependenciesMeta": { - "expo": { - "optional": true - }, - "expo-constants": { - "optional": true - }, - "expo-device": { - "optional": true - }, - "expo-notifications": { - "optional": true - } - }, "dependencies": { "@knocklabs/client": "workspace:^", "@knocklabs/react-core": "workspace:^", diff --git a/yarn.lock b/yarn.lock index acaa7f0d..e48e4ad7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5024,15 +5024,6 @@ __metadata: expo-notifications: "*" react: "*" react-native: "*" - peerDependenciesMeta: - expo: - optional: true - expo-constants: - optional: true - expo-device: - optional: true - expo-notifications: - optional: true languageName: unknown linkType: soft From 1b6575f07b9501ce3ed52730cebbafdf889b2730 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Fri, 11 Oct 2024 11:50:38 -0400 Subject: [PATCH 08/23] Remove expo dependencies from react native sdk --- packages/react-native/package.json | 22 ---------------------- yarn.lock | 17 ----------------- 2 files changed, 39 deletions(-) diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 75902cd8..11511107 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -41,27 +41,9 @@ "url": "https://github.com/knocklabs/javascript/issues" }, "peerDependencies": { - "expo": "*", - "expo-constants": "*", - "expo-device": "*", - "expo-notifications": "*", "react": "*", "react-native": "*" }, - "peerDependenciesMeta": { - "expo": { - "optional": true - }, - "expo-constants": { - "optional": true - }, - "expo-device": { - "optional": true - }, - "expo-notifications": { - "optional": true - } - }, "dependencies": { "@knocklabs/client": "workspace:^", "@knocklabs/react-core": "workspace:^", @@ -78,10 +60,6 @@ "eslint": "^8.56.0", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.4", - "expo": ">=51.0.24", - "expo-constants": ">=16.0.2", - "expo-device": ">=6.0.2", - "expo-notifications": ">=0.28.16", "react": "^18.2.0", "react-native": "^0.73.4", "rimraf": "^6.0.1", diff --git a/yarn.lock b/yarn.lock index e48e4ad7..ce8e0813 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5121,10 +5121,6 @@ __metadata: eslint: "npm:^8.56.0" eslint-plugin-react-hooks: "npm:^4.6.0" eslint-plugin-react-refresh: "npm:^0.4.4" - expo: "npm:>=51.0.24" - expo-constants: "npm:>=16.0.2" - expo-device: "npm:>=6.0.2" - expo-notifications: "npm:>=0.28.16" react: "npm:^18.2.0" react-native: "npm:^0.73.4" react-native-gesture-handler: "npm:^2.19.0" @@ -5136,21 +5132,8 @@ __metadata: vite-plugin-dts: "npm:^3.6.3" vite-plugin-no-bundle: "npm:^4.0.0" peerDependencies: - expo: "*" - expo-constants: "*" - expo-device: "*" - expo-notifications: "*" react: "*" react-native: "*" - peerDependenciesMeta: - expo: - optional: true - expo-constants: - optional: true - expo-device: - optional: true - expo-notifications: - optional: true languageName: unknown linkType: soft From 7a3947b71edc3982ebd0018516a43b0e8393d6f3 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Fri, 11 Oct 2024 11:52:19 -0400 Subject: [PATCH 09/23] Remove unnecessary export --- packages/expo/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/expo/src/index.ts b/packages/expo/src/index.ts index c71c1ee0..153b3d68 100644 --- a/packages/expo/src/index.ts +++ b/packages/expo/src/index.ts @@ -1,3 +1,2 @@ export * from "./modules/push"; export * from "@knocklabs/react-native"; -export * from "@knocklabs/react-core"; From 286aaa62a1c9bd1e8d17f4f457925834726a317a Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Fri, 11 Oct 2024 11:55:57 -0400 Subject: [PATCH 10/23] Update sample expo app to use expo sdk --- examples/react-native-example/package.json | 2 +- examples/react-native-example/src/App.tsx | 2 +- yarn.lock | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/react-native-example/package.json b/examples/react-native-example/package.json index bef2eb90..4abc71e7 100644 --- a/examples/react-native-example/package.json +++ b/examples/react-native-example/package.json @@ -11,7 +11,7 @@ "format:check": "prettier \"src/**/*.{js,ts,tsx}\" --check" }, "dependencies": { - "@knocklabs/react-native": "workspace:^", + "@knocklabs/expo": "workspace:^", "expo": ">=51.0.24", "expo-constants": ">=16.0.2", "expo-device": ">=6.0.2", diff --git a/examples/react-native-example/src/App.tsx b/examples/react-native-example/src/App.tsx index 92e4a858..f187ea6c 100644 --- a/examples/react-native-example/src/App.tsx +++ b/examples/react-native-example/src/App.tsx @@ -1,5 +1,5 @@ +import { KnockExpoPushNotificationProvider } from "@knocklabs/expo"; import { - KnockExpoPushNotificationProvider, KnockFeedProvider, KnockProvider, NotificationIconButton, diff --git a/yarn.lock b/yarn.lock index ce8e0813..44fc0df5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4988,7 +4988,7 @@ __metadata: languageName: unknown linkType: soft -"@knocklabs/expo@workspace:packages/expo": +"@knocklabs/expo@workspace:^, @knocklabs/expo@workspace:packages/expo": version: 0.0.0-use.local resolution: "@knocklabs/expo@workspace:packages/expo" dependencies: @@ -5095,7 +5095,7 @@ __metadata: resolution: "@knocklabs/react-native-example@workspace:examples/react-native-example" dependencies: "@babel/core": "npm:^7.25.7" - "@knocklabs/react-native": "workspace:^" + "@knocklabs/expo": "workspace:^" expo: "npm:>=51.0.24" expo-constants: "npm:>=16.0.2" expo-device: "npm:>=6.0.2" From 228290afb88a5ab3e5c567ccd0958097b4ac66a3 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Fri, 11 Oct 2024 11:59:27 -0400 Subject: [PATCH 11/23] Update more sample app imports --- examples/react-native-example/src/App.tsx | 4 ++-- .../react-native-example/src/NotificationFeedContainer.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/react-native-example/src/App.tsx b/examples/react-native-example/src/App.tsx index f187ea6c..8b1c6e2d 100644 --- a/examples/react-native-example/src/App.tsx +++ b/examples/react-native-example/src/App.tsx @@ -1,9 +1,9 @@ -import { KnockExpoPushNotificationProvider } from "@knocklabs/expo"; import { + KnockExpoPushNotificationProvider, KnockFeedProvider, KnockProvider, NotificationIconButton, -} from "@knocklabs/react-native"; +} from "@knocklabs/expo"; import { StatusBar } from "expo-status-bar"; import React, { useCallback, useState } from "react"; import { StyleSheet, View } from "react-native"; diff --git a/examples/react-native-example/src/NotificationFeedContainer.tsx b/examples/react-native-example/src/NotificationFeedContainer.tsx index 5ff5b25c..fe8d5294 100644 --- a/examples/react-native-example/src/NotificationFeedContainer.tsx +++ b/examples/react-native-example/src/NotificationFeedContainer.tsx @@ -1,5 +1,5 @@ import { ActionButton, FeedItem } from "@knocklabs/client"; -import { NotificationFeed } from "@knocklabs/react-native"; +import { NotificationFeed } from "@knocklabs/expo"; import React, { useCallback } from "react"; import { StyleSheet, Text, TouchableOpacity, View } from "react-native"; From b891b4c60dfcb659a71cfca44b9839103564b43a Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Fri, 11 Oct 2024 12:03:34 -0400 Subject: [PATCH 12/23] Use a type import in sample app --- examples/react-native-example/src/NotificationFeedContainer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/react-native-example/src/NotificationFeedContainer.tsx b/examples/react-native-example/src/NotificationFeedContainer.tsx index fe8d5294..3d876bc3 100644 --- a/examples/react-native-example/src/NotificationFeedContainer.tsx +++ b/examples/react-native-example/src/NotificationFeedContainer.tsx @@ -1,4 +1,4 @@ -import { ActionButton, FeedItem } from "@knocklabs/client"; +import type { ActionButton, FeedItem } from "@knocklabs/client"; import { NotificationFeed } from "@knocklabs/expo"; import React, { useCallback } from "react"; import { StyleSheet, Text, TouchableOpacity, View } from "react-native"; From badb8274e18e8da24efebbbe8100422a573e8ec4 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Fri, 11 Oct 2024 13:18:19 -0400 Subject: [PATCH 13/23] Add expo README --- packages/expo/README.md | 90 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 packages/expo/README.md diff --git a/packages/expo/README.md b/packages/expo/README.md new file mode 100644 index 00000000..63b9672e --- /dev/null +++ b/packages/expo/README.md @@ -0,0 +1,90 @@ +# Knock Expo SDK + +A set of components for integrating [Knock](https://knock.app) in-app notifications into a React Native application. + +> Not using Expo? See our vanilla [React Native SDK](../react-native/README.md). + +## Installation + +Via NPM: + +``` +npm install @knocklabs/expo +``` + +Via Yarn: + +``` +yarn add @knocklabs/expo +``` + +## Configuration + +To configure the feed you will need: + +1. A public API key (found in the Knock dashboard) +1. A user ID, and optionally an auth token for production environments +1. If integrating an in-app feed, a feed channel ID (found in the Knock dashboard) + +## Usage + +You can integrate the feed into your app as follows: + +```jsx +import { + KnockFeedProvider, + KnockProvider, + NotificationFeedContainer, +} from "@knocklabs/expo"; + +const YourAppLayout = () => { + const [isVisible, setIsVisible] = useState(false); + const notifButtonRef = useRef(null); + + return ( + + + + Notifications go in here! + + + + ); +}; +``` + +## Headless usage + +Alternatively, if you don't want to use our components you can render the feed in a headless mode using our hooks: + +```jsx +import { useAuthenticatedKnockClient, useNotifications } from "@knocklabs/expo"; +import create from "zustand"; + +const YourAppLayout = () => { + const knockClient = useAuthenticatedKnockClient( + process.env.KNOCK_PUBLIC_API_KEY, + currentUser.id, + ); + + const notificationFeed = useNotifications( + knockClient, + process.env.KNOCK_FEED_ID, + ); + + const useNotificationStore = create(notificationFeed.store); + const { metadata } = useNotificationStore(); + + useEffect(() => { + notificationFeed.fetch(); + }, [notificationFeed]); + + return Total unread: {metadata.unread_count}; +}; +``` + +## Related links + +- [Signup for Knock](https://knock.app) +- [Knock documentation](https://docs.knock.app) +- [Knock dashboard](https://dashboard.knock.app) From eef5435a809516c4dbd76ebeb59ff183f7a6b125 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Fri, 11 Oct 2024 13:18:26 -0400 Subject: [PATCH 14/23] Update rn sdk README --- packages/react-native/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/react-native/README.md b/packages/react-native/README.md index a040710f..d444d7dd 100644 --- a/packages/react-native/README.md +++ b/packages/react-native/README.md @@ -4,6 +4,8 @@ A set of components for integrating [Knock](https://knock.app) in-app notificati [Full documentation](https://docs.knock.app/in-app-ui/react-native/overview) +> Using Expo? See our [Expo SDK](../expo/README.md). + ## Installation Via NPM: From 94952fb7891ec2ba20c662d1724eca5b9a72ec53 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Fri, 11 Oct 2024 13:26:52 -0400 Subject: [PATCH 15/23] Update rn sdk vite config --- packages/react-native/vite.config.mts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/react-native/vite.config.mts b/packages/react-native/vite.config.mts index 1827fbaa..6b34c263 100644 --- a/packages/react-native/vite.config.mts +++ b/packages/react-native/vite.config.mts @@ -27,14 +27,7 @@ export default defineConfig(({ mode }) => { }, rollupOptions: { // External packages that should not be bundled into your library. - external: [ - "react", - "react-native", - "expo", - "expo-constants", - "expo-device", - "expo-notifications", - ], + external: ["react", "react-native"], output: { interop: "compat", format: formats[0], From 1eef956852e30be06a4e5b9ea95f0948d4085c63 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Fri, 11 Oct 2024 13:27:25 -0400 Subject: [PATCH 16/23] Update README --- packages/expo/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/expo/README.md b/packages/expo/README.md index 63b9672e..94062374 100644 --- a/packages/expo/README.md +++ b/packages/expo/README.md @@ -1,6 +1,6 @@ # Knock Expo SDK -A set of components for integrating [Knock](https://knock.app) in-app notifications into a React Native application. +A set of components for integrating [Knock](https://knock.app) in-app notifications into an Expo + React Native application. > Not using Expo? See our vanilla [React Native SDK](../react-native/README.md). From 02bfff7782b91ceca02990fbe0bec89dee9614f1 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Mon, 14 Oct 2024 10:28:24 -0400 Subject: [PATCH 17/23] Update @typescript-eslint/parser --- packages/expo/package.json | 2 +- yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/expo/package.json b/packages/expo/package.json index 1d085c57..8c3db664 100644 --- a/packages/expo/package.json +++ b/packages/expo/package.json @@ -60,7 +60,7 @@ "@types/react": "^18.3.6", "@types/react-native-htmlview": "^0.16.5", "@typescript-eslint/eslint-plugin": "^6.20.0", - "@typescript-eslint/parser": "^8.8.0", + "@typescript-eslint/parser": "^8.8.1", "@vitejs/plugin-react": "^4.3.2", "eslint": "^8.56.0", "eslint-plugin-react-hooks": "^4.6.0", diff --git a/yarn.lock b/yarn.lock index 4a6fd1e2..47d1a553 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4998,7 +4998,7 @@ __metadata: "@types/react": "npm:^18.3.6" "@types/react-native-htmlview": "npm:^0.16.5" "@typescript-eslint/eslint-plugin": "npm:^6.20.0" - "@typescript-eslint/parser": "npm:^8.8.0" + "@typescript-eslint/parser": "npm:^8.8.1" "@vitejs/plugin-react": "npm:^4.3.2" eslint: "npm:^8.56.0" eslint-plugin-react-hooks: "npm:^4.6.0" From 6adc41d5675e0e2b20d8a520bf45f668e18a92b2 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Mon, 14 Oct 2024 14:48:25 -0400 Subject: [PATCH 18/23] Update version of expo package --- packages/expo/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/expo/package.json b/packages/expo/package.json index 8c3db664..81e5d2e3 100644 --- a/packages/expo/package.json +++ b/packages/expo/package.json @@ -1,6 +1,6 @@ { "name": "@knocklabs/expo", - "version": "0.0.1", + "version": "0.3.1", "author": "@knocklabs", "license": "MIT", "main": "dist/cjs/index.js", From f8fc1dea9a968a033dd3236fd1456c0d3b83182f Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Mon, 14 Oct 2024 15:10:21 -0400 Subject: [PATCH 19/23] Add changeset --- .changeset/clean-singers-switch.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/clean-singers-switch.md diff --git a/.changeset/clean-singers-switch.md b/.changeset/clean-singers-switch.md new file mode 100644 index 00000000..deaa0307 --- /dev/null +++ b/.changeset/clean-singers-switch.md @@ -0,0 +1,7 @@ +--- +"@knocklabs/react-native-example": minor +"@knocklabs/react-native": minor +"@knocklabs/expo": minor +--- + +Move KnockExpoPushNotificationProvider to @knocklabs/expo From f13bde323df04da6bb1f59c6c1067fdb2b5d8216 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Mon, 14 Oct 2024 15:23:08 -0400 Subject: [PATCH 20/23] Update Expo README --- packages/expo/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/expo/README.md b/packages/expo/README.md index 94062374..38eda36b 100644 --- a/packages/expo/README.md +++ b/packages/expo/README.md @@ -18,6 +18,14 @@ Via Yarn: yarn add @knocklabs/expo ``` +## Migrating from `@knocklabs/react-native` + +As of `@knocklabs/react-native` v0.4.0, `KnockExpoPushNotificationProvider` has moved to our Expo SDK. To migrate: + +1. Remove `@knocklabs/react-native` from your project (`npm uninstall @knocklabs/react-native` OR `yarn remove @knocklabs/react-native`) +2. Install `@knocklabs/expo` (`npm install @knocklabs/expo` OR `yarn add @knocklabs/expo`) +3. Update any import statements from `@knocklabs/react-native` to `@knocklabs/expo` + ## Configuration To configure the feed you will need: From 69c414dd37880d42890dd1716abd1085d9d19373 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Mon, 14 Oct 2024 15:25:57 -0400 Subject: [PATCH 21/23] Update RN SDK README --- packages/react-native/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/README.md b/packages/react-native/README.md index d444d7dd..b4c0a704 100644 --- a/packages/react-native/README.md +++ b/packages/react-native/README.md @@ -4,7 +4,7 @@ A set of components for integrating [Knock](https://knock.app) in-app notificati [Full documentation](https://docs.knock.app/in-app-ui/react-native/overview) -> Using Expo? See our [Expo SDK](../expo/README.md). +> Using Expo? See our [Expo SDK](../expo/README.md) and our [migration guide](../expo/README.md#migrating-from-knocklabsreact-native). ## Installation From a652c6abe86539b8caa33cecb865cdd7246fd4c8 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Mon, 14 Oct 2024 15:32:54 -0400 Subject: [PATCH 22/23] Update expo SDK README --- packages/expo/README.md | 54 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/packages/expo/README.md b/packages/expo/README.md index 38eda36b..ba0fb07a 100644 --- a/packages/expo/README.md +++ b/packages/expo/README.md @@ -22,9 +22,57 @@ yarn add @knocklabs/expo As of `@knocklabs/react-native` v0.4.0, `KnockExpoPushNotificationProvider` has moved to our Expo SDK. To migrate: -1. Remove `@knocklabs/react-native` from your project (`npm uninstall @knocklabs/react-native` OR `yarn remove @knocklabs/react-native`) -2. Install `@knocklabs/expo` (`npm install @knocklabs/expo` OR `yarn add @knocklabs/expo`) -3. Update any import statements from `@knocklabs/react-native` to `@knocklabs/expo` +1. Remove `@knocklabs/react-native` from your project + + NPM: + + ```bash + npm uninstall @knocklabs/react-native + ``` + + Yarn: + + ```bash + yarn remove @knocklabs/react-native + ``` + +1. Install `@knocklabs/expo` + + NPM: + + ```bash + npm install @knocklabs/expo + ``` + + Yarn: + + ```bash + yarn add @knocklabs/expo + ``` + +1. Update any import statements from `@knocklabs/react-native` to `@knocklabs/expo` + + From: + + ```js + import { + KnockExpoPushNotificationProvider, + KnockFeedProvider, + KnockProvider, + NotificationIconButton, + } from "@knocklabs/react-native"; + ``` + + To: + + ```js + import { + KnockExpoPushNotificationProvider, + KnockFeedProvider, + KnockProvider, + NotificationIconButton, + } from "@knocklabs/expo"; + ``` ## Configuration From 30f6c35ae0434dbaa12462db71b5777f1cb83953 Mon Sep 17 00:00:00 2001 From: Matthew Mikolay Date: Mon, 14 Oct 2024 17:11:48 -0400 Subject: [PATCH 23/23] Set @knocklabs/expo version to 0.0.0 --- packages/expo/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/expo/package.json b/packages/expo/package.json index 81e5d2e3..f22eddfd 100644 --- a/packages/expo/package.json +++ b/packages/expo/package.json @@ -1,6 +1,6 @@ { "name": "@knocklabs/expo", - "version": "0.3.1", + "version": "0.0.0", "author": "@knocklabs", "license": "MIT", "main": "dist/cjs/index.js",