PushNotificationContext.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. import React, { useEffect, useState, useContext, createContext } from 'react';
  2. import * as Notifications from 'expo-notifications';
  3. import { storage, StoreType } from 'src/storage';
  4. import { AppState, AppStateStatus, Linking, Platform } from 'react-native';
  5. import { CommonActions, useNavigation } from '@react-navigation/native';
  6. import { NAVIGATION_PAGES } from 'src/types';
  7. import { usePostSetSettingsMutation } from '@api/notifications';
  8. import { useMessagesStore } from 'src/stores/unreadMessagesStore';
  9. import { useFriendsNotificationsStore } from 'src/stores/friendsNotificationsStore';
  10. import {
  11. handleNotificationData,
  12. registerBackgroundNotificationTask,
  13. unregisterBackgroundNotificationTask
  14. } from 'src/utils/pushNotificationTask';
  15. const PushNotificationContext = createContext<{
  16. isSubscribed: boolean;
  17. toggleSubscription: () => Promise<void>;
  18. unsubscribeFromNotifications: () => Promise<void>;
  19. }>({
  20. isSubscribed: false,
  21. toggleSubscription: async () => {},
  22. unsubscribeFromNotifications: async () => {}
  23. });
  24. export const usePushNotification = () => useContext(PushNotificationContext);
  25. export const PushNotificationProvider = ({ children }: { children: React.ReactNode }) => {
  26. const token = storage.get('token', StoreType.STRING) as string;
  27. const [isSubscribed, setIsSubscribed] = useState(
  28. (storage.get('subscribed', StoreType.BOOLEAN) as boolean) ?? false
  29. );
  30. const { mutateAsync: setNotificationsSettings } = usePostSetSettingsMutation();
  31. const navigation = useNavigation();
  32. const updateNotificationStatus = useFriendsNotificationsStore(
  33. (state) => state.updateNotificationStatus
  34. );
  35. const updateUnreadMessagesCount = useMessagesStore((state) => state.updateUnreadMessagesCount);
  36. const [appState, setAppState] = useState(AppState.currentState);
  37. const lastNotificationResponse = Notifications.useLastNotificationResponse();
  38. useEffect(() => {
  39. const handleAppStateChange = (nextAppState: AppStateStatus) => {
  40. if (appState.match(/inactive|background/) && nextAppState === 'active' && isSubscribed) {
  41. Notifications.getBadgeCountAsync().then((badgeCount) => {
  42. if (badgeCount > 0) {
  43. Notifications.setBadgeCountAsync(-1);
  44. }
  45. });
  46. Notifications.getPresentedNotificationsAsync().then((notifications) => {
  47. if (Platform.OS === 'ios' && notifications.length > 0) {
  48. notifications.forEach((notification) => {
  49. let groupToken;
  50. let messageId;
  51. let fromUser;
  52. const parsedData =
  53. JSON.parse(
  54. (notification.request.trigger as Notifications.PushNotificationTrigger)?.payload
  55. ?.params as string
  56. ) ?? {};
  57. groupToken = parsedData?.group_token;
  58. messageId = (notification.request.trigger as Notifications.PushNotificationTrigger)
  59. ?.payload?.message_id as number;
  60. fromUser = parsedData?.id;
  61. handleNotificationData(groupToken, messageId, fromUser);
  62. });
  63. }
  64. });
  65. updateNotificationStatus();
  66. updateUnreadMessagesCount();
  67. }
  68. setAppState(nextAppState);
  69. };
  70. const subscription = AppState.addEventListener('change', handleAppStateChange);
  71. return () => {
  72. subscription.remove();
  73. };
  74. }, [appState]);
  75. useEffect(() => {
  76. if (lastNotificationResponse && Platform.OS === 'android') {
  77. const data = lastNotificationResponse.notification.request.content.data;
  78. if (data?.screen && data?.parentScreen) {
  79. if (data?.params) {
  80. const parsedParams = JSON.parse(data.params) ?? {};
  81. navigation.dispatch(
  82. CommonActions.reset({
  83. index: 1,
  84. routes: [
  85. {
  86. name: 'DrawerApp',
  87. state: {
  88. routes: [
  89. {
  90. name: data.parentScreen,
  91. state: {
  92. routes: [
  93. { name: NAVIGATION_PAGES.MAP_TAB },
  94. {
  95. name: data.screen,
  96. params: parsedParams
  97. }
  98. ]
  99. }
  100. }
  101. ]
  102. }
  103. }
  104. ]
  105. })
  106. );
  107. } else {
  108. navigation.dispatch(
  109. CommonActions.reset({
  110. index: 1,
  111. routes: [
  112. {
  113. name: 'DrawerApp',
  114. state: {
  115. routes: [
  116. {
  117. name: data.parentScreen,
  118. state: {
  119. routes: [
  120. { name: NAVIGATION_PAGES.MAP_TAB },
  121. {
  122. name: data.screen
  123. }
  124. ]
  125. }
  126. }
  127. ]
  128. }
  129. }
  130. ]
  131. })
  132. );
  133. }
  134. }
  135. if (data?.url) {
  136. Linking.openURL(data.url);
  137. }
  138. }
  139. }, [lastNotificationResponse]);
  140. const checkNotificationPermissions = async () => {
  141. const { status } = await Notifications.getPermissionsAsync();
  142. return status;
  143. };
  144. useEffect(() => {
  145. const getPermissionsStatus = async () => {
  146. const status = await checkNotificationPermissions();
  147. if (status !== 'granted' && isSubscribed) {
  148. await unsubscribeFromNotifications();
  149. return;
  150. }
  151. };
  152. if (isSubscribed) {
  153. getPermissionsStatus();
  154. Notifications.setNotificationHandler({
  155. handleNotification: async () => ({
  156. shouldShowAlert: true,
  157. shouldPlaySound: false,
  158. shouldSetBadge: false
  159. })
  160. });
  161. registerBackgroundNotificationTask();
  162. const notificationListener = Notifications.addNotificationReceivedListener((notification) => {
  163. updateNotificationStatus();
  164. updateUnreadMessagesCount();
  165. let groupToken;
  166. let messageId;
  167. let fromUser;
  168. if (Platform.OS === 'ios') {
  169. const parsedData =
  170. JSON.parse(
  171. (notification.request.trigger as Notifications.PushNotificationTrigger)?.payload
  172. ?.params as string
  173. ) ?? {};
  174. groupToken = parsedData?.group_token;
  175. messageId = (notification.request.trigger as Notifications.PushNotificationTrigger)
  176. ?.payload?.message_id as number;
  177. fromUser = parsedData?.id;
  178. handleNotificationData(groupToken, messageId, fromUser);
  179. }
  180. });
  181. const responseListener = Notifications.addNotificationResponseReceivedListener((response) => {
  182. let screenName;
  183. let url;
  184. let parentScreen;
  185. let params;
  186. if (Platform.OS === 'ios') {
  187. parentScreen = (
  188. response.notification.request.trigger as Notifications.PushNotificationTrigger
  189. )?.payload?.parentScreen;
  190. screenName = (
  191. response.notification.request.trigger as Notifications.PushNotificationTrigger
  192. )?.payload?.screen;
  193. params = (response.notification.request.trigger as Notifications.PushNotificationTrigger)
  194. ?.payload?.params;
  195. url = (response.notification.request.trigger as Notifications.PushNotificationTrigger)
  196. ?.payload?.url;
  197. }
  198. if (screenName && parentScreen) {
  199. if (params) {
  200. const parsedParams = JSON.parse(params as string) ?? {};
  201. navigation.dispatch(
  202. CommonActions.reset({
  203. index: 1,
  204. routes: [
  205. {
  206. name: 'DrawerApp',
  207. state: {
  208. routes: [
  209. {
  210. name: parentScreen as string,
  211. state: {
  212. routes: [
  213. { name: NAVIGATION_PAGES.MAP_TAB },
  214. {
  215. name: screenName as string,
  216. params: parsedParams
  217. }
  218. ]
  219. }
  220. }
  221. ]
  222. }
  223. }
  224. ]
  225. })
  226. );
  227. } else {
  228. navigation.dispatch(
  229. CommonActions.reset({
  230. index: 1,
  231. routes: [
  232. {
  233. name: 'DrawerApp',
  234. state: {
  235. routes: [
  236. {
  237. name: parentScreen as string,
  238. state: {
  239. routes: [
  240. { name: NAVIGATION_PAGES.MAP_TAB },
  241. {
  242. name: screenName as string
  243. }
  244. ]
  245. }
  246. }
  247. ]
  248. }
  249. }
  250. ]
  251. })
  252. );
  253. }
  254. }
  255. if (url) {
  256. Linking.openURL(url as string);
  257. }
  258. });
  259. Notifications.getBadgeCountAsync().then((badgeCount) => {
  260. if (badgeCount > 0) {
  261. Notifications.setBadgeCountAsync(-1);
  262. }
  263. });
  264. return () => {
  265. notificationListener.remove();
  266. responseListener.remove();
  267. };
  268. } else {
  269. unregisterBackgroundNotificationTask();
  270. }
  271. }, [isSubscribed]);
  272. const subscribeToNotifications = async () => {
  273. storage.set('subscribed', true);
  274. setIsSubscribed(true);
  275. };
  276. const unsubscribeFromNotifications = async () => {
  277. const settings = {
  278. [Platform.OS === 'ios' ? 'app-ios' : 'app-android']: 0
  279. };
  280. await setNotificationsSettings({
  281. token,
  282. settings: JSON.stringify(settings)
  283. });
  284. storage.remove('deviceToken');
  285. storage.set('subscribed', false);
  286. setIsSubscribed(false);
  287. unregisterBackgroundNotificationTask();
  288. };
  289. const toggleSubscription = async () => {
  290. if (isSubscribed) {
  291. await unsubscribeFromNotifications();
  292. } else {
  293. await subscribeToNotifications();
  294. }
  295. };
  296. return (
  297. <PushNotificationContext.Provider
  298. value={{ isSubscribed, toggleSubscription, unsubscribeFromNotifications }}
  299. >
  300. {children}
  301. </PushNotificationContext.Provider>
  302. );
  303. };