# Push Notifications

Complete guide to Firebase Cloud Messaging push notifications in OrchardGo.

## Overview

OrchardGo uses Firebase Cloud Messaging (FCM) for push notifications on both iOS and Android platforms.

## Firebase Setup

### Create Firebase Project

1. Login to [Firebase Console](https://console.firebase.google.com)
2. Create new project or select existing
3. Open the project

### Add iOS Application

1. Click **Add app** → **iOS**
2. Set bundle identifier (e.g., `com.theorchard.OrchardGo`)
3. Download `GoogleService-Info.plist`
4. Place in `brands/[brand]/ios/GoogleService-Info/[env].plist`

### Add Android Application

1. Click **Add app** → **Android**
2. Set package name (e.g., `com.theorchard.OrchardGo`)
3. Download `google-services.json`
4. Place in `brands/[brand]/android/google-services/[env].json`

### Get Server Key

1. Open Firebase project
2. Go to **Project Settings** → **Cloud Messaging**
3. Copy **Server key** (needed for backend)

## Configuration

### iOS Configuration

Firebase configuration is automatically selected based on brand and environment:

```
brands/
└── orchard/
    └── ios/
        └── GoogleService-Info/
            ├── prod.plist
            ├── qa.plist
            └── dev.plist
```

The correct plist is copied during build based on:
- Brand: `--brand orchard`
- Environment: `--env prod`

### Android Configuration

Similar structure for Android:

```
brands/
└── orchard/
    └── android/
        └── google-services/
            ├── prod.json
            ├── qa.json
            └── dev.json
```

Apply the correct configuration:
```bash
# Automatically applied during build
yarn start --platform android --brand orchard --env prod
```

Or manually:
```bash
# Copy specific environment config
cp brands/orchard/android/google-services/prod.json android/app/google-services.json
```

## Testing Push Notifications

### Method 1: Jenkins (Recommended)

Send test notifications via Jenkins pipeline:

1. **Get Your Identity ID**
   ```
   http://prod-ows-users.theorchard.io/users/identity/email/YOUR_EMAIL@theorchard.com
   ```
   Copy the `id` field from response

2. **Check Registered Devices**
   ```
   http://prod-ows-users.theorchard.io/users/identity/YOUR_IDENTITY_ID/device
   ```
   Verify your device is registered

3. **Send Notification via Jenkins**
   - Open [prod-push-notification](https://pipeline.theorchard.io/job/prod-push-notification) job
   - Click **Build with Parameters**
   - Fill in:
     - **identity_ids**: Your identity ID
     - **json_data**: Notification data, e.g.:
       ```json
       {"participantId":"a5a8db41-a394-4fc5-8885-2a77ea77b64d"}
       ```
   - Click **Build**

### Method 2: iOS Simulator

Test on iOS simulator (Xcode 11.4+):

1. **Run app on iOS simulator**
   ```bash
   yarn start:ios
   ```

2. **Use APNS notification file**

   Ready-to-use `.apns` files are available in [`docs/guides/apns-examples/`](./apns-examples/):

   - **[basic-notification.apns](./apns-examples/basic-notification.apns)** - Simple test notification
   - **[social-spike.apns](./apns-examples/social-spike.apns)** - Social spike with participantId (production format)
   - **[playlist-placements.apns](./apns-examples/playlist-placements.apns)** - Playlist placements with isrc
   - **[trending-tracks.apns](./apns-examples/trending-tracks.apns)** - Trending tracks notification

   Or create your own by copying one of the examples and modifying the payload.

3. **Send notification**
   - Drag and drop any `.apns` file from the examples folder onto the iOS simulator
   - The notification should appear immediately

   > **Note:** Make sure the `Simulator Target Bundle` in the `.apns` file matches your app's bundle identifier (e.g., `com.theorchard.OrchardGo`)

### Method 3: Postman (Android)

Send push notification directly to Android device:

1. **Get Device Token**
   - Run app on device
   - Device token is logged on first launch
   - Or retrieve from: `http://prod-ows-users.theorchard.io/users/identity/YOUR_IDENTITY_ID/device`

2. **Get Server Key**
   - Firebase Console → Project Settings → Cloud Messaging
   - Copy Server Key

3. **Send via Postman**

   **POST** `https://fcm.googleapis.com/fcm/send`

   **Headers:**
   ```
   Content-Type: application/json
   Authorization: key=YOUR_SERVER_KEY
   ```

   **Body (basic notification):**
   ```json
   {
     "to": "DEVICE_TOKEN",
     "notification": {
       "title": "Test Notification",
       "body": "This is a test message",
       "sound": "default"
     },
     "data": {
       "participantId": "123-456-789",
       "customData": "value"
     }
   }
   ```

   **Body (production format with social spike):**
   ```json
   {
     "to": "DEVICE_TOKEN",
     "notification": {
       "body": "Message"
     },
     "data": {
       "data": {
         "payload": {
           "participantId": "3352d48b-9f5e-4d33-88e5-cab492dba0db"
         },
         "identityId": "1a2fee06-0e80-483d-8e64-bbf709ec9012",
         "notificationId": "fbdc4798-880d-4ece-9975-88917d42596b",
         "type": "social_spike",
         "socialPlatform": "instagram"
       }
     }
   }
   ```

   > **Note:** You can also import the complete request into Postman by creating a new request with the settings above.

## Notification Handling

### iOS Implementation

Notifications are handled by `@react-native-firebase/messaging`:

```typescript
import messaging from '@react-native-firebase/messaging';

// Request permission
const requestPermission = async () => {
  const authStatus = await messaging().requestPermission();
  const enabled =
    authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
    authStatus === messaging.AuthorizationStatus.PROVISIONAL;

  if (enabled) {
    console.log('Authorization status:', authStatus);
  }
};

// Get FCM token
const getToken = async () => {
  const token = await messaging().getToken();
  console.log('FCM Token:', token);
  // Send to backend
};

// Foreground notifications
messaging().onMessage(async remoteMessage => {
  console.log('Notification received:', remoteMessage);
  // Handle notification
});

// Background/quit notifications
messaging().setBackgroundMessageHandler(async remoteMessage => {
  console.log('Background notification:', remoteMessage);
});

// Notification opened app
messaging().onNotificationOpenedApp(remoteMessage => {
  console.log('Notification opened app:', remoteMessage);
  // Navigate to specific screen
});

// App opened from quit state
messaging()
  .getInitialNotification()
  .then(remoteMessage => {
    if (remoteMessage) {
      console.log('Notification caused app to open:', remoteMessage);
    }
  });
```

### Android Implementation

Similar to iOS, using the same Firebase Messaging API:

```typescript
// Request permission (Android 13+)
import { PermissionsAndroid, Platform } from 'react-native';

const requestAndroidPermission = async () => {
  if (Platform.OS === 'android' && Platform.Version >= 33) {
    const granted = await PermissionsAndroid.request(
      PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
    );
    return granted === PermissionsAndroid.RESULTS.GRANTED;
  }
  return true;
};
```

## Notification Types

### Basic Notification

```json
{
  "notification": {
    "title": "New Message",
    "body": "You have a new message"
  }
}
```

### Data-Only Notification

```json
{
  "data": {
    "type": "silent_update",
    "participantId": "123"
  }
}
```

### Rich Notification

```json
{
  "notification": {
    "title": "New Album",
    "body": "Check out the latest release",
    "image": "https://example.com/album.jpg"
  },
  "data": {
    "albumId": "456",
    "action": "view_album"
  }
}
```

## Troubleshooting

### iOS: Notifications Not Received

1. **Check Capabilities**
   - Xcode → Target → Signing & Capabilities
   - Ensure "Push Notifications" is enabled

2. **Check Provisioning Profile**
   - Profile must include Push Notifications capability

3. **Check APNs Certificates**
   - Firebase Console → Cloud Messaging
   - Upload APNs authentication key or certificate

4. **Check Bundle ID**
   - Must match Firebase configuration
   - Check `GoogleService-Info.plist`

5. **Check Permissions**
   ```swift
   // In app delegate, verify permission status
   UNUserNotificationCenter.current().getNotificationSettings { settings in
     print("Notification settings:", settings.authorizationStatus.rawValue)
   }
   ```

### Android: Notifications Not Received

1. **Check google-services.json**
   - Located in `android/app/google-services.json`
   - Must match package name in `build.gradle`

2. **Check Package Name**
   - `android/app/build.gradle`:
     ```gradle
     applicationId "com.theorchard.OrchardGo"
     ```
   - Must match Firebase project

3. **Check Firebase SDK**
   - Verify in `android/app/build.gradle`:
     ```gradle
     implementation platform('com.google.firebase:firebase-bom:X.X.X')
     implementation 'com.google.firebase:firebase-messaging'
     ```

4. **Check Permissions**
   - Android 13+ requires runtime permission
   - Request `POST_NOTIFICATIONS` permission

5. **Check Background Restrictions**
   - Device may restrict background apps
   - Settings → Apps → OrchardGo → Battery → Unrestricted

### Device Not Registered

1. **Check device token is generated**
   - Should appear in logs on first launch

2. **Verify token sent to backend**
   - Check network requests

3. **Check API endpoint**
   ```
   http://prod-ows-users.theorchard.io/users/identity/YOUR_IDENTITY_ID/device
   ```
   - Your device should be listed

### Notification Not Opening App

1. **Check notification handler**
   - Verify `onNotificationOpenedApp` is set up
   - Verify `getInitialNotification` is checked

2. **Check navigation logic**
   - Ensure navigation ref is ready
   - Handle deep link properly

## Best Practices

### 1. Handle All States

- Foreground (app open)
- Background (app in background)
- Quit (app closed)
- Opened from notification

### 2. Request Permission Thoughtfully

- Don't request immediately on launch
- Explain why notifications are useful
- Request at appropriate time

### 3. Update Token on Change

```typescript
messaging().onTokenRefresh(token => {
  // Send updated token to backend
  updateDeviceToken(token);
});
```

### 4. Handle Notification Taps

```typescript
const handleNotificationOpen = (remoteMessage) => {
  const { data } = remoteMessage;

  if (data.participantId) {
    navigation.navigate('Participant', { id: data.participantId });
  } else if (data.albumId) {
    navigation.navigate('Album', { id: data.albumId });
  }
};
```

### 5. Test on Real Devices

- Simulators have limitations
- Test on various Android versions
- Test on various iOS versions

## Related Documentation

- [Firebase Console Setup](https://console.firebase.google.com)
- [React Native Firebase Docs](https://rnfirebase.io/messaging/usage)
- [FCM HTTP API](https://firebase.google.com/docs/cloud-messaging/http-server-ref)
- [Configuration Guide](../getting-started/configuration.md)
- [Branding Guide](./branding.md)
