Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expo Support #166

Open
2 tasks done
giorgiofellipe opened this issue Jul 23, 2024 · 11 comments
Open
2 tasks done

Expo Support #166

giorgiofellipe opened this issue Jul 23, 2024 · 11 comments
Assignees
Labels
enhancement New feature or request evergreen

Comments

@giorgiofellipe
Copy link
Contributor

giorgiofellipe commented Jul 23, 2024

Checklist

Description

As Expo is the default framework for React Native development this SDK should really consider supporting it.

Proposed Solution

That said, we've been recently implementing it in an Expo project and I'll share the plugin files needed to implement at least Push Open event (it may be extended to other features).
Maybe it clears the path for some people who are planning to use it and make maintainers realize it is not that difficult to add out-of-the-box Expo support.

withKlaviyo.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const withKlaviyoAndroid = require("./withKlaviyoAndroid").default;
const withKlaviyoIOS = require("./withKlaviyoIOS").default;

const withKlaviyo = expoConfig => {
  if (expoConfig.platforms.includes("android")) {
    expoConfig = withKlaviyoAndroid(expoConfig);
  }
  if (expoConfig.platforms.includes("ios")) {
    expoConfig = withKlaviyoIOS(expoConfig);
  }
  return expoConfig;
};

exports.default = withKlaviyo;
withKlaviyoIOS.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const { withAppDelegate, withDangerousMod } = require("@expo/config-plugins");
const fs = require("fs");
const path = require("path");

function applyiOSChanges(appDelegate) {
// Add imports and implement UNUserNotificationCenterDelegate methods
let imports = `
  #import "ExpoModulesCore-Swift.h"
  #import <UserNotifications/UserNotifications.h>
  #import "ProjectName-Swift.h"
`;

if (!appDelegate.includes(imports)) {
  appDelegate = imports + appDelegate;
}

let notificationMethods = `
  // UNUserNotificationCenterDelegate methods
  - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler
  {
    // If this notification is Klaviyo's notification we'll handle it
    // else pass it on to the next push notification service to which it may belong
    BOOL handled = [KlaviyoBridge handleNotificationResponse:response completionHandler:completionHandler];
    if (!handled) {
      completionHandler();
    }
  }

  - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
  {
    if (@available(iOS 14.0, *)) {
      completionHandler(UNNotificationPresentationOptionList | UNNotificationPresentationOptionBanner);
    } else {
      completionHandler(UNNotificationPresentationOptionAlert);
    }
  }
`;

if (!appDelegate.includes("// UNUserNotificationCenterDelegate methods")) {
  appDelegate = appDelegate.replace(
    /@implementation AppDelegate/,
    match => `${match}\n${notificationMethods}`
  );
}

let didFinishLaunchingWithOptionsCode = `
  // Register AppDelegate as UNUserNotificationCenterDelegate
  UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  center.delegate = self;
`;

if (
  !appDelegate.includes(
    "UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];"
  )
) {
  appDelegate = appDelegate.replace(
    /return \[super application:application didFinishLaunchingWithOptions:launchOptions\];/,
    match => `${didFinishLaunchingWithOptionsCode}\n  ${match}`
  );
}

return appDelegate;
}

function applyAppDelegateHChanges(appDelegateH) {
let importStatement = `#import <UserNotifications/UserNotifications.h>`;
let protocol = `<UNUserNotificationCenterDelegate>`;

if (!appDelegateH.includes(importStatement)) {
  appDelegateH = importStatement + "\n" + appDelegateH;
}

if (!appDelegateH.includes(protocol)) {
  appDelegateH = appDelegateH.replace(
    /@interface AppDelegate : [\w]+/,
    match => `${match} ${protocol}`
  );
}

return appDelegateH;
}

const withKlaviyoIOS = expoConfig => {
expoConfig = withAppDelegate(expoConfig, config => {
  config.modResults.contents = applyiOSChanges(config.modResults.contents);
  return config;
});

expoConfig = withDangerousMod(expoConfig, [
  "ios",
  async config => {
    const projectRoot = config.modRequest.platformProjectRoot;
    const projectName =
      config.modRequest.projectName || config.name || config.slug;

    // Write the KlaviyoBridge.swift file
    const swiftFilePath = path.join(projectRoot, "KlaviyoBridge.swift");
    const swiftFileContent = `
      import Foundation
      import KlaviyoSwift
      import UserNotifications

      @objc class KlaviyoBridge: NSObject {
          @objc static func handleNotificationResponse(_ response: UNNotificationResponse, completionHandler: @escaping () -> Void) -> Bool {
              return KlaviyoSDK().handle(notificationResponse: response, withCompletionHandler: completionHandler)
          }
      }
    `;
    fs.writeFileSync(swiftFilePath, swiftFileContent, { encoding: "utf-8" });

    // Ensure bridging header exists and include import statement
    const bridgingHeaderPath = path.join(
      projectRoot,
      "ProjectName-Bridging-Header.h"
    );
    let bridgingHeaderContent = "";
    if (fs.existsSync(bridgingHeaderPath)) {
      bridgingHeaderContent = fs.readFileSync(bridgingHeaderPath, "utf-8");
    }
    if (!bridgingHeaderContent.includes('#import "ProjectName-Swift.h"')) {
      bridgingHeaderContent =
        `#import "ProjectName-Swift.h"\n` + bridgingHeaderContent;
      fs.writeFileSync(bridgingHeaderPath, bridgingHeaderContent, {
        encoding: "utf-8",
      });
    }

    // Ensure bridging header is referenced in the project
    const pbxprojPath = path.join(
      projectRoot,
      `${projectName}.xcodeproj`,
      "project.pbxproj"
    );
    let pbxprojContent = fs.readFileSync(pbxprojPath, "utf-8");

    // Add bridging header
    if (!pbxprojContent.includes("SWIFT_OBJC_BRIDGING_HEADER")) {
      const bridgingHeaderReference = `SWIFT_OBJC_BRIDGING_HEADER = ProjectName-Bridging-Header.h;`;
      pbxprojContent = pbxprojContent.replace(
        /lastKnownFileType = sourcecode\.swift;/,
        `lastKnownFileType = sourcecode.swift;\n\t\t\t\t${bridgingHeaderReference}`
      );
    }

    // Add KlaviyoBridge.swift to project
    if (!pbxprojContent.includes("24800769DB1A46C2A93294F3")) {
      const fileRef = `
24800769DB1A46C2A93294F3 /* KlaviyoBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = KlaviyoBridge.swift; path = KlaviyoBridge.swift; sourceTree = "<group>"; };
`;

      const buildFileRef = `
24800769DB1A46C2A93294F4 /* KlaviyoBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24800769DB1A46C2A93294F3 /* KlaviyoBridge.swift */; };
`;

      const pbxFileReferenceIndex = pbxprojContent.indexOf(
        "/* End PBXFileReference section */"
      );
      pbxprojContent = [
        pbxprojContent.slice(0, pbxFileReferenceIndex),
        fileRef,
        pbxprojContent.slice(pbxFileReferenceIndex),
      ].join("");

      const pbxBuildFileIndex = pbxprojContent.indexOf(
        "/* End PBXBuildFile section */"
      );
      pbxprojContent = [
        pbxprojContent.slice(0, pbxBuildFileIndex),
        buildFileRef,
        pbxprojContent.slice(pbxBuildFileIndex),
      ].join("");

      const pbxGroupIndex = pbxprojContent.indexOf(
        "13B07FAE1A68108700A75B9A /* ProjectName */ = {"
      );
      const childrenIndex =
        pbxprojContent.indexOf("children = (", pbxGroupIndex) +
        "children = (".length;
      pbxprojContent = [
        pbxprojContent.slice(0, childrenIndex),
        "\n24800769DB1A46C2A93294F3 /* KlaviyoBridge.swift */,",
        pbxprojContent.slice(childrenIndex),
      ].join("");

      const sourcesBuildPhaseIndex = pbxprojContent.indexOf(
        "/* Begin PBXSourcesBuildPhase section */"
      );
      const filesIndex =
        pbxprojContent.indexOf("files = (", sourcesBuildPhaseIndex) +
        "files = (".length;
      pbxprojContent = [
        pbxprojContent.slice(0, filesIndex),
        "\n24800769DB1A46C2A93294F4 /* KlaviyoBridge.swift in Sources */,",
        pbxprojContent.slice(filesIndex),
      ].join("");
    }

    fs.writeFileSync(pbxprojPath, pbxprojContent, { encoding: "utf-8" });

    // Modify AppDelegate.h
    const appDelegateHPath = path.join(
      projectRoot,
      projectName,
      "AppDelegate.h"
    );
    let appDelegateHContent = fs.readFileSync(appDelegateHPath, "utf-8");

    appDelegateHContent = applyAppDelegateHChanges(appDelegateHContent);

    fs.writeFileSync(appDelegateHPath, appDelegateHContent, {
      encoding: "utf-8",
    });

    return config;
  },
]);

return expoConfig;
};

exports.default = withKlaviyoIOS;
withKlaviyoAndroid.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const { withMainActivity } = require("@expo/config-plugins");

function applyMainActivity(mainActivity) {
let klaviyoImport = "import com.klaviyo.analytics.Klaviyo\n";
let intentImport = "import android.content.Intent\n";
let firebaseTriggerImport =
  "import expo.modules.notifications.notifications.model.triggers.FirebaseNotificationTrigger\n";
let notificationsServiceImport =
  "import expo.modules.notifications.service.NotificationsService\n";

let klaviyoOnNewIntent = `
// this handler is needed in order to use it with expo-notifications, 
// that changes the intent extras and makes impossible to Klaviyo SDK
// identify that it is a Klaviyo push
private fun handleNotificationIntent(intent: Intent?): Intent? {
  if (intent === null) {
    return null
  }

  var newIntent = Intent(intent)
  val response = NotificationsService.getNotificationResponseFromOpenIntent(newIntent)
  if (response === null) {
    return newIntent
  }

  val KLAVIYO_PACKAGE_NAME = "com.klaviyo"
  val KLAVIYO_KEY = "_k"
  val value = (response.notification.notificationRequest.trigger as FirebaseNotificationTrigger).remoteMessage.data
  if (value != null && value[KLAVIYO_KEY] != null) {
    val extras = newIntent.extras ?: Bundle()  // Initialize extras if null
    if (extras.getString(KLAVIYO_KEY) == null) {
      extras.putString(KLAVIYO_KEY, value[KLAVIYO_KEY])
    }
    if (extras.getString("$KLAVIYO_PACKAGE_NAME.$KLAVIYO_KEY") == null) {
      extras.putString("$KLAVIYO_PACKAGE_NAME.$KLAVIYO_KEY", value[KLAVIYO_KEY])
    }
    newIntent.putExtras(extras)  // Apply the modified extras back to the new intent
  }
  return newIntent
}

override fun onNewIntent(intent: Intent?) {
  var newIntent = handleNotificationIntent(intent)
  super.onNewIntent(newIntent)
  Klaviyo.handlePush(newIntent)
}
`;

// Ensure onNewIntent(intent) is called at the bottom of onCreate
if (!mainActivity.includes("onNewIntent(intent)")) {
  mainActivity = mainActivity.replace(
    /super.onCreate\(null\)/,
    match => `${match}\n    onNewIntent(intent)`
  );
}

// Add import statements if not already present
if (!mainActivity.includes(klaviyoImport)) {
  mainActivity = mainActivity.replace(
    /package [\w.]+/,
    match => `${match}\n${klaviyoImport}`
  );
}

if (!mainActivity.includes(intentImport)) {
  mainActivity = mainActivity.replace(
    /package [\w.]+/,
    match => `${match}\n${intentImport}`
  );
}

if (!mainActivity.includes(firebaseTriggerImport)) {
  mainActivity = mainActivity.replace(
    /package [\w.]+/,
    match => `${match}\n${firebaseTriggerImport}`
  );
}

if (!mainActivity.includes(notificationsServiceImport)) {
  mainActivity = mainActivity.replace(
    /package [\w.]+/,
    match => `${match}\n${notificationsServiceImport}`
  );
}

// Add onNewIntent method if not already present
if (!mainActivity.includes("override fun onNewIntent(intent: Intent?)")) {
  mainActivity = mainActivity.replace(
    /class MainActivity : ReactActivity\(\) \{/,
    match => `${match}\n${klaviyoOnNewIntent}`
  );
} else {
  // Update existing onNewIntent method
  mainActivity = mainActivity.replace(
    /override fun onNewIntent\(intent: Intent\?\) \{[^}]*\}/,
    match => `${klaviyoOnNewIntent}`
  );
}

return mainActivity;
}

const withKlaviyoAndroid = expoConfig => {
expoConfig = withMainActivity(expoConfig, config => {
  config.modResults.contents = applyMainActivity(config.modResults.contents);
  return config;
});
return expoConfig;
};

exports.default = withKlaviyoAndroid;

It may need some tweaking.

@giorgiofellipe giorgiofellipe added the enhancement New feature or request label Jul 23, 2024
@ajaysubra
Copy link
Collaborator

Thanks for the feedback and the code you provided. We will take this as a feature request on our end. Unfortunately, we don't have a timeline on this but will keep this issue open and update it whenever we have something to share on this topic.

@ajaysubra ajaysubra self-assigned this Jul 23, 2024
@ajaysubra ajaysubra pinned this issue Jul 23, 2024
@giorgiofellipe
Copy link
Contributor Author

A heads up regarding my solution: it breaks the default deep link handling from Expo (when app is terminated).
I appreciate if anyone from Klaviyo can jump in and help me make both work (Opened Push Event and Deep Link handling).

@ajaysubra
Copy link
Collaborator

Hey giorgiofellipe, unfortunately we have set commitments to improve SDKs across the board that at this point we can't help with your request. However, as I mentioned earlier, we do have this as a feature request on our end and will update this thread as and when we have updates to share. Thanks!

@jakemadash
Copy link

Checklist

* [x]  I have read the [contributing guidelines](https://github.com/klaviyo/klaviyo-react-native-sdk/blob/master/.github/CONTRIBUTING.md)

* [x]  This issue hasn't been addressed in an [existing issue](https://github.com/klaviyo/klaviyo-react-native-sdk/issues) or [pull request](https://github.com/klaviyo/klaviyo-react-native-sdk/pulls)

Description

As Expo is the default framework for React Native development this SDK should really consider supporting it.

Proposed Solution

That said, we've been recently implementing it in an Expo project and I'll share the plugin files needed to implement at least Push Open event (it may be extended to other features). Maybe it clears the path for some people who are planning to use it and make maintainers realize it is not that difficult to add out-of-the-box Expo support.
withKlaviyo.js
withKlaviyoIOS.js
withKlaviyoAndroid.js

It may need some tweaking.

Hi Giorgio! Does your project use Expo's managed workflow? Our does, and we're also looking into using this library, so I'm trying to figure out if it will be feasible. Thanks!

@giorgiofellipe
Copy link
Contributor Author

@jakemadash yes, we are currently using managed workflow.

@xgenem
Copy link

xgenem commented Sep 4, 2024

Thanks for this PR @giorgiofellipe! RNs direction is really towards using frameworks now, especially Expo. But most library providers have yet to transition

@joshmohrer
Copy link

Hi @ajaysubra I was wondering if there's any update on Expo support? I'd love to use this in my Expo project.
Thank you!

@YYODHH
Copy link

YYODHH commented Nov 5, 2024

Using expo-notifications with Klaviyo it does work as intended, even Push Open events. (only tested on iOS)

Install the Klaviyo SDK in your Expo project with:

npx expo install klaviyo-react-native-sdk

Then, use the following hook to handle push notifications with the Expo Notifications API. This hook listens for notification responses and triggers the $opened_push event in Klaviyo when a push notification is opened.

import { useEffect } from "react";
import * as Notifications from "expo-notifications";
import { Klaviyo } from "klaviyo-react-native-sdk";

export function useNotificationObserver() {
  useEffect(() => {
    let isMounted = true;

    async function handleNotification(notification: Notifications.Notification) {
      if (notification.request.trigger.type === "push") {
        const event = {
          name: "$opened_push",
          properties: notification.request.trigger.payload as Record<string, object>,
        };
        Klaviyo.createEvent(event);
      }
      // Optionally, clear the last notification response if needed
      // await Notifications.clearLastNotificationResponseAsync();
    }

    // Check for the last notification response on component mount
    Notifications.getLastNotificationResponseAsync().then((response) => {
      if (isMounted && response?.notification) {
        handleNotification(response.notification);
      }
    });

    // Subscribe to future notification responses
    const subscription = Notifications.addNotificationResponseReceivedListener(
      (response) => handleNotification(response.notification)
    );

    return () => {
      isMounted = false;
      subscription.remove();
    };
  }, []);
}

Explanation

  • useEffect Hook: Initializes the notification observer and cleans it up when the component unmounts.
  • handleNotification Function: Checks if the notification is a push notification and then creates the $opened_push event with Klaviyo.
  • Notifications.getLastNotificationResponseAsync: Handles any user interacted notification that may have been received before the hook was initialized.
  • Listener Subscription: Reacts to new notification responses as they are received.

Expo Notifications Reference

Optional: Uncomment Notifications.clearLastNotificationResponseAsync() if you need to clear the last notification response after processing it.

This setup should ensure that your app tracks opened push notifications accurately with Klaviyo.

@ajaysubra
Copy link
Collaborator

@joshmohrer sorry for the late response, I was away from work for a bit. Unfortunately no real updates at this point, other than it's on our near term road map and I that can keep this thread updated as and when we have more clear timelines. Thanks for all your patience.

@pinguin999
Copy link

Hi I'm also working on getting Push working with Expo and without modify the android or ios folder.

I was able to get and push the Token with expo-notifications

import * as Notifications from "expo-notifications";

...

      Klaviyo.initialize("XXXYYY");


      const { status: existingStatus } = await Notifications.getPermissionsAsync();
      let finalStatus = existingStatus;
      if (existingStatus !== "granted") {
        const { status } = await Notifications.requestPermissionsAsync();
        finalStatus = status;
      }
      if (finalStatus !== "granted") {
        console.error("Failed to get push token for push notification!");
        return;
      }

      const deviceToken = await Notifications.getDevicePushTokenAsync();
      console.log(deviceToken?.data);
      
      
      if (deviceToken != null && deviceToken.data.length > 0) {
        Klaviyo.setPushToken(deviceToken.data);
      }

With that I can get Pushes on iOS and Android (you need to add the google-services.json to get the token.)

To handle the Klaviyo.handlePush I created a Expo Modul with
npx create-expo-module@latest --local

Android

package expo.modules.klaviyo

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import expo.modules.core.interfaces.ReactActivityLifecycleListener
import com.klaviyo.analytics.Klaviyo
import com.klaviyo.analytics.model.Event
import com.klaviyo.analytics.model.EventMetric
import com.klaviyo.analytics.model.EventKey


class ActivityLifecycleListener : ReactActivityLifecycleListener {
    override fun onCreate(activity: Activity?, savedInstanceState: Bundle?) {
        super.onCreate(activity, savedInstanceState)

        Klaviyo.initialize("XXXYYY", activity!!.applicationContext)

        val event = Event(EventMetric.VIEWED_PRODUCT)
            .setProperty(EventKey.CUSTOM("Product"), "Coffee Mug")
            .setValue(10.0)
        Klaviyo.createEvent(event)

        onNewIntent(activity.intent)
    }

    override fun onNewIntent(intent: Intent?): Boolean {

        // Tracks when a system tray notification is opened
        Klaviyo.handlePush(intent)
        return super.onNewIntent(intent)
    }
}

iOS

import ExpoModulesCore
import KlaviyoSwift

public class AppLifecycleDelegate: ExpoAppDelegateSubscriber {
  public func applicationDidBecomeActive(_ application: UIApplication) {

        let center = UNUserNotificationCenter.current()
        center.delegate = self
        let options: UNAuthorizationOptions = [.alert, .sound, .badge]
        // use the below options if you are interested in using provisional push notifications. Note that using this will not
        // show the push notifications prompt to the user.
        // let options: UNAuthorizationOptions = [.alert, .sound, .badge, provisional]
        center.requestAuthorization(options: options) { _, error in
            if let error = error {
                // Handle the error here.
                print("error = ", error)
            }

            // Enable or disable features based on the authorization status.
        }

        UIApplication.shared.registerForRemoteNotifications()


    print("Wasserwerk")
    // The app has become active.

    if #available(iOS 16.0, *) {
        UNUserNotificationCenter.current().setBadgeCount(0)
    } else {
        UIApplication.shared.applicationIconBadgeNumber = 0
    }
  }

  public func applicationWillResignActive(_ application: UIApplication) {
    // The app is about to become inactive.
  }

  public func applicationDidEnterBackground(_ application: UIApplication) {
    // The app is now in the background.
  }

  public func applicationWillEnterForeground(_ application: UIApplication) {
    // The app is about to enter the foreground.
  }

  public func applicationWillTerminate(_ application: UIApplication) {
    // The app is about to terminate.
  }
}


// be sure to set the UNUserNotificationCenterDelegate to self in the didFinishLaunchingWithOptions method (refer the requesting push notification permission section above for more details on this)
extension AppLifecycleDelegate: UNUserNotificationCenterDelegate {

    // below method will be called when the user interacts with the push notification
    public func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        didReceive response: UNNotificationResponse,
        withCompletionHandler completionHandler: @escaping () -> Void) {
        print("Push Push")
        // If this notification is Klaviyo's notification we'll handle it
        // else pass it on to the next push notification service to which it may belong
        let handled = KlaviyoSDK().handle(notificationResponse: response, withCompletionHandler: completionHandler)
        if !handled {
            completionHandler()
        }
    }

    // below method is called when the app receives push notifications when the app is the foreground
    public func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        willPresent notification: UNNotification,
        withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        print("Push Push 2")

        if #available(iOS 14.0, *) {
            completionHandler([.list, .banner])
        } else {
            completionHandler([.alert])
        }
    }
}

But no success getting opened events in the campaigns.

Any help is welcome.
I'm still unsure if the code runs after opening a push.
Also I'm not sure if I have to do Klaviyo.initialize in the native code to handle pushes.

@YYODHH
Copy link

YYODHH commented Nov 19, 2024

@pinguin999 This is what has been working for us on both android and ios, using expo-notifications without needing to add any modules. Also does deep-linking via router.push(url);.

import * as Notifications from "expo-notifications";
import { router } from "expo-router";
import { Klaviyo } from "klaviyo-react-native-sdk";
import { useEffect } from "react";
import { Platform } from "react-native";

export function useNotificationObserver() {
  useEffect(() => {
    let isMounted = true;
    async function redirect(notification: Notifications.Notification) {
      if (notification.request.trigger.type === "push") {
        const properties = (
          Platform.OS === "ios"
            ? notification.request.trigger.payload
            : Object.fromEntries(
                Object.entries(notification.request.content.data).map(
                  ([key, value]) => [key.replace(/^com\.klaviyo\./, ""), value],
                ),
              )
        ) as Record<string, object>;
        if (properties) {
          const event = {
            name: "$opened_push",
            properties,
          };
          Klaviyo.createEvent(event);
          await Notifications.setBadgeCountAsync(0);
          await Notifications.clearLastNotificationResponseAsync();
          console.log("Notification opened", notification, event);
        }
        const url =
          Platform.OS === "ios"
            ? notification.request.trigger.payload?.url
            : notification.request.trigger.remoteMessage?.data?.url ||
              notification.request.content.data["com.klaviyo.url"] ||
              notification.request.content.data["url"];
        if (url) {
          console.log("Redirecting to", url);
          router.push(url);
        }
      }
    }

    Notifications.getLastNotificationResponseAsync().then((response) => {
      console.log("Last notification response", response);
      if (!isMounted || !response?.notification) {
        return;
      }
      redirect(response.notification);
    });

    const subscriptionOpened =
      Notifications.addNotificationResponseReceivedListener((response) => {
        console.log("Notification response received", response);
        redirect(response.notification);
      });

    const subscription = Notifications.addNotificationReceivedListener((_) => {
      Notifications.setBadgeCountAsync(0);
    });

    return () => {
      isMounted = false;
      subscription.remove();
      subscriptionOpened.remove();
    };
  }, []);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request evergreen
Projects
None yet
Development

No branches or pull requests

7 participants