diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a377592 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,73 @@ +name: CI Pipeline + +on: + push: + branches: + - main + +jobs: + pipeline: + name: Lint, Type Check & Publish + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Cache node_modules + id: cache-modules + uses: actions/cache@v4 + with: + path: node_modules + key: 20.x-${{ runner.OS }}-build-${{ hashFiles('package.json') }} + + - name: Install Dependencies + if: steps.cache-modules.outputs.cache-hit != 'true' + run: npm install + + - name: Lint and format check + run: | + npm run lint & + npm run prettier-check & + wait + + - name: Type Check + run: npm run type-check + + - name: Publish + id: publish + uses: JS-DevTools/npm-publish@v4 + with: + token: ${{ secrets.NPM_AUTH_TOKEN }} + + - name: Send Slack notification for Success + if: success() + uses: slackapi/slack-github-action@v2.1.1 + with: + webhook: ${{ secrets.SLACK_SUCCESS_WEBHOOK_URL }} + webhook-type: incoming-webhook + payload: | + text: "✅ CI Pipeline Success - Published to NPM: ${{ steps.publish.outputs.old-version }} → ${{ steps.publish.outputs.version }}" + blocks: + - type: "section" + text: + type: "mrkdwn" + text: "*✅ CI Pipeline Success*\n\n*Published to NPM:* `${{ steps.publish.outputs.old-version }}` → `${{ steps.publish.outputs.version }}`\n*Repository:* ${{ github.repository }}\n*Commit:* <${{ github.event.head_commit.url }}|${{ github.sha }}>\n*Workflow:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Workflow>" + + - name: Send Slack notification for Failure + if: failure() || cancelled() + uses: slackapi/slack-github-action@v2.1.1 + with: + webhook: ${{ secrets.SLACK_FAILED_WEBHOOK_URL }} + webhook-type: incoming-webhook + payload: | + text: "❌ CI Pipeline Failed" + blocks: + - type: "section" + text: + type: "mrkdwn" + text: "*❌ CI Pipeline Failed*\n\n*Repository:* ${{ github.repository }}\n*Commit:* <${{ github.event.head_commit.url }}|${{ github.sha }}>\n*Workflow:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Workflow>" diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 46b93b5..01f80dc 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -2,6 +2,7 @@ name: Pull Request Pipeline on: pull_request: + types: [opened, synchronize, reopened, ready_for_review] branches: - main @@ -9,14 +10,33 @@ jobs: pull_request_pipeline: name: Pull Request Pipeline runs-on: ubuntu-latest - + if: github.event.pull_request.draft == false + steps: - - uses: actions/checkout@v3 - - - name: Setup Node.js - uses: actions/setup-node@v3 - with: - node-version: '18.x' - - - name: Install Dependencies - run: npm install \ No newline at end of file + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Cache node_modules + id: cache-modules + uses: actions/cache@v4 + with: + path: node_modules + key: 20.x-${{ runner.OS }}-build-${{ hashFiles('package.json') }} + + - name: Install Dependencies + if: steps.cache-modules.outputs.cache-hit != 'true' + run: npm ci --prefer-offline + + - name: Lint and format check + run: | + npm run lint & + npm run prettier-check & + wait + + - name: Type Check + run: npm run type-check diff --git a/.gitignore b/.gitignore index f778c2c..368e4e4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,12 @@ node_modules .DS_Store **/local.properties android/.gradle +android/build +android/app/build +ios/build +ios/Pods +ios/*.xcworkspace +ios/*.xcuserdata .history +dist +*.log diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..6f3427e --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "trailingComma": "none", + "singleQuote": true, + "printWidth": 80 +} + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d064967 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2024 NotificationAPI + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..6d73c2b --- /dev/null +++ b/README.md @@ -0,0 +1,619 @@ +# NotificationAPI React Native SDK + +A React Native SDK for integrating [NotificationAPI](https://notificationapi.com) push notifications into your mobile app. + +## 🚀 Push Notification Support + +**Cross-platform push notifications with native performance:** + +- **Android**: Full FCM (Firebase Cloud Messaging) support +- **iOS**: Direct APN (Apple Push Notifications) integration + +This means you get native push notifications on both platforms with optimal performance. + +## 🚀 Quick Start + +### 1. Installation + +```bash +npm install @notificationapi/react-native +# or +yarn add @notificationapi/react-native +``` + +### 2. Setup (One Line!) + +```typescript +import NotificationAPI from '@notificationapi/react-native'; + +// That's it! This handles initialization, user identification, and permission requests +await NotificationAPI.setup({ + clientId: 'your_client_id_here', + userId: 'user123', + autoRequestPermission: true, // automatically request push permissions + region: 'us' // 'us' (default), 'eu', or 'ca' +}); +``` + +### 3. Listen to Notifications (Optional) + +```typescript +import { getEventEmitter, Events } from '@notificationapi/react-native'; + +const eventEmitter = getEventEmitter(); + +// Listen to notifications received while app is open +eventEmitter.addListener(Events.NOTIFICATION_RECEIVED, (notification) => { + console.log('Received notification:', notification.title); +}); + +// Listen to notifications that opened the app +eventEmitter.addListener(Events.NOTIFICATION_ON_CLICK, (notification) => { + console.log('App opened from notification:', notification.title); + // Handle deep linking or navigation +}); + +// Listen to push token updates +eventEmitter.addListener(Events.PUSH_TOKEN_RECEIVED, (event) => { + console.log('Push token received:', event.token); +}); +``` + +### 4. Check Status + +```typescript +// Check if SDK is ready +if (NotificationAPI.isReady) { + console.log('NotificationAPI is ready!'); +} + +// Get the current user +const userId = NotificationAPI.currentUser; + +// Get push token +const token = await NotificationAPI.getPushToken(); +``` + +## 📱 Complete Example + +```typescript +import React, { useEffect } from 'react'; +import { View, Button, Alert } from 'react-native'; +import NotificationAPI, { getEventEmitter, Events } from '@notificationapi/react-native'; + +function App() { + useEffect(() => { + // Setup NotificationAPI + NotificationAPI.setup({ + clientId: 'your_client_id', + userId: 'user123', + autoRequestPermission: true, + }).catch((error) => { + console.error('Failed to setup NotificationAPI:', error); + }); + + // Listen for notifications + const eventEmitter = getEventEmitter(); + + const receivedListener = eventEmitter.addListener( + Events.NOTIFICATION_RECEIVED, + (notification) => { + Alert.alert('New Notification', notification.title); + } + ); + + const clickListener = eventEmitter.addListener( + Events.NOTIFICATION_ON_CLICK, + (notification) => { + Alert.alert('Notification Clicked', notification.title); + // Handle navigation or deep linking + } + ); + + return () => { + receivedListener.remove(); + clickListener.remove(); + }; + }, []); + + const handleRequestPermission = async () => { + const granted = await NotificationAPI.requestPermission(); + Alert.alert( + 'Permission', + granted ? 'Permission granted' : 'Permission denied' + ); + }; + + return ( + +