index.tsx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import React, { useCallback, useEffect, useState } from 'react';
  2. import { View, Linking, Text, Switch, Platform, TouchableOpacity, AppState } from 'react-native';
  3. import * as Notifications from 'expo-notifications';
  4. import { Header, Loading, MenuButton, PageWrapper, WarningModal } from 'src/components';
  5. import { usePushNotification } from 'src/contexts/PushNotificationContext';
  6. import { usePostSaveNotificationTokenMutation } from '@api/user';
  7. import { styles } from 'src/components/MenuButton/style';
  8. import { StoreType, storage } from 'src/storage';
  9. import { Colors } from 'src/theme';
  10. import { NAVIGATION_PAGES } from 'src/types';
  11. import ListIcon from 'assets/icons/notifications/list-check.svg';
  12. import PeopleIcon from 'assets/icons/notifications/people-group-solid.svg';
  13. import ChatIcon from 'assets/icons/notifications/messages.svg';
  14. import GearIcon from 'assets/icons/notifications/gear-solid.svg';
  15. import BellIcon from 'assets/icons/notifications/bell-solid.svg';
  16. import { useGetSettingsQuery } from '@api/notifications';
  17. import { useFocusEffect } from '@react-navigation/native';
  18. const NotificationsScreen = ({ navigation }: { navigation: any }) => {
  19. const token = storage.get('token', StoreType.STRING) as string;
  20. const { data: notificationsSettings, refetch } = useGetSettingsQuery(token, !!token);
  21. const { mutateAsync: saveNotificationToken } = usePostSaveNotificationTokenMutation();
  22. const { isSubscribed, toggleSubscription, unsubscribeFromNotifications } = usePushNotification();
  23. const [modalInfo, setModalInfo] = useState({
  24. visible: false,
  25. type: 'confirm',
  26. message: '',
  27. action: () => {}
  28. });
  29. const [shouldOpenWarningModal, setShouldOpenWarningModal] = useState(false);
  30. const [initialPermissionStatus, setInitialPermissionStatus] = useState<
  31. 'granted' | 'denied' | 'undetermined' | null
  32. >(null);
  33. const closeModal = () => {
  34. setModalInfo({ ...modalInfo, visible: false });
  35. };
  36. useEffect(() => {
  37. if (notificationsSettings) {
  38. const { settings } = notificationsSettings;
  39. const isServerSubscribed =
  40. Platform.OS === 'ios'
  41. ? settings.some((setting) => setting.name === 'app-ios' && setting.active === 1)
  42. : settings.some((setting) => setting.name === 'app-android' && setting.active === 1);
  43. // if (isServerSubscribed !== isSubscribed) {
  44. // toggleSubscription();
  45. // }
  46. }
  47. }, [notificationsSettings]);
  48. useEffect(() => {
  49. const subscription = AppState.addEventListener('change', async (nextAppState) => {
  50. if (nextAppState === 'active' && initialPermissionStatus !== null) {
  51. const currentStatus = await checkNotificationPermissions();
  52. if (initialPermissionStatus !== 'granted' && currentStatus === 'granted') {
  53. await handleSubscribe();
  54. setInitialPermissionStatus(currentStatus);
  55. } else if (
  56. currentStatus !== 'granted' &&
  57. (isSubscribed || initialPermissionStatus === 'granted')
  58. ) {
  59. unsubscribeFromNotifications();
  60. }
  61. }
  62. });
  63. return () => {
  64. subscription.remove();
  65. };
  66. }, [initialPermissionStatus]);
  67. useEffect(() => {
  68. const getInitialPermissionsStatus = async () => {
  69. const status = await checkNotificationPermissions();
  70. if (status !== 'granted' && isSubscribed) {
  71. unsubscribeFromNotifications();
  72. }
  73. setInitialPermissionStatus(status);
  74. };
  75. getInitialPermissionsStatus();
  76. }, []);
  77. useFocusEffect(() => {
  78. refetchData();
  79. });
  80. const checkNotificationPermissions = async () => {
  81. const { status } = await Notifications.getPermissionsAsync();
  82. return status;
  83. };
  84. const refetchData = async () => {
  85. await refetch();
  86. };
  87. if (!notificationsSettings) return <Loading />;
  88. const toggleSwitch = async () => {
  89. if (isSubscribed) {
  90. toggleSubscription();
  91. } else {
  92. const status = await checkNotificationPermissions();
  93. if (status !== 'granted') {
  94. setModalInfo({
  95. visible: true,
  96. type: 'success',
  97. message:
  98. 'To use this feature we need your permission to access your notifications. If you press OK your system will ask you to confirm permission to receive notifications from NomadMania.',
  99. action: () => setShouldOpenWarningModal(true)
  100. });
  101. } else {
  102. handleSubscribe();
  103. }
  104. }
  105. };
  106. const handleSubscribe = async () => {
  107. const deviceData = await registerForPushNotificationsAsync();
  108. if (deviceData?.notificationToken) {
  109. toggleSubscription();
  110. await saveNotificationToken({
  111. token,
  112. platform: deviceData.platform,
  113. n_token: deviceData.notificationToken
  114. });
  115. refetchData();
  116. }
  117. };
  118. async function registerForPushNotificationsAsync() {
  119. const existingStatus = await checkNotificationPermissions();
  120. let finalStatus = existingStatus;
  121. if (existingStatus !== 'granted') {
  122. const { status } = await Notifications.requestPermissionsAsync();
  123. finalStatus = status;
  124. }
  125. if (finalStatus !== 'granted') {
  126. setModalInfo({
  127. visible: true,
  128. type: 'success',
  129. message:
  130. 'NomadMania app needs notification permissions to function properly. Open settings?',
  131. action: () =>
  132. Platform.OS === 'ios' ? Linking.openURL('app-settings:') : Linking.openSettings()
  133. });
  134. return null;
  135. }
  136. const deviceData = await Notifications.getDevicePushTokenAsync();
  137. console.log('deviceData', deviceData);
  138. if (Platform.OS === 'android') {
  139. Notifications.setNotificationChannelAsync('default', {
  140. name: 'default',
  141. importance: Notifications.AndroidImportance.MAX,
  142. vibrationPattern: [0, 250, 250, 250],
  143. lightColor: '#FF231F7C'
  144. });
  145. }
  146. storage.set('deviceToken', deviceData.data);
  147. return { notificationToken: deviceData.data ?? '', platform: deviceData.type ?? '' };
  148. }
  149. return (
  150. <PageWrapper>
  151. <Header label="Notifications" />
  152. <TouchableOpacity
  153. style={[
  154. styles.alignStyle,
  155. styles.buttonWrapper,
  156. {
  157. justifyContent: 'space-between'
  158. }
  159. ]}
  160. onPress={toggleSwitch}
  161. >
  162. <View style={styles.alignStyle}>
  163. <BellIcon fill={Colors.DARK_BLUE} width={20} height={20} />
  164. <Text style={styles.buttonLabel}>Enable notifications</Text>
  165. </View>
  166. <View>
  167. <Switch
  168. trackColor={{ false: Colors.LIGHT_GRAY, true: Colors.DARK_BLUE }}
  169. thumbColor={Colors.WHITE}
  170. onValueChange={toggleSwitch}
  171. value={isSubscribed}
  172. style={{ transform: 'scale(0.8)' }}
  173. />
  174. </View>
  175. </TouchableOpacity>
  176. {/* <MenuButton
  177. label="Notifications list"
  178. icon={
  179. <ListIcon
  180. fill={isSubscribed ? Colors.DARK_BLUE : Colors.LIGHT_GRAY}
  181. width={20}
  182. height={20}
  183. />
  184. }
  185. red={false}
  186. buttonFn={() => navigation.navigate(NAVIGATION_PAGES.NOTIFICATIONS_LIST as never)}
  187. disabled={!isSubscribed}
  188. /> */}
  189. <MenuButton
  190. label="Friends"
  191. icon={
  192. <PeopleIcon
  193. fill={isSubscribed ? Colors.DARK_BLUE : Colors.LIGHT_GRAY}
  194. width={20}
  195. height={20}
  196. />
  197. }
  198. red={false}
  199. buttonFn={() =>
  200. navigation.navigate(NAVIGATION_PAGES.FRIENDS_NOTIFICATIONS, {
  201. settings: notificationsSettings.settings,
  202. token
  203. } as never)
  204. }
  205. disabled={!isSubscribed}
  206. />
  207. {/* <MenuButton
  208. label="Messages"
  209. icon={
  210. <ChatIcon
  211. fill={isSubscribed ? Colors.DARK_BLUE : Colors.LIGHT_GRAY}
  212. width={20}
  213. height={20}
  214. />
  215. }
  216. red={false}
  217. buttonFn={() => navigation.navigate(NAVIGATION_PAGES.MESSAGES_NOTIFICATIONS as never)}
  218. disabled={!isSubscribed}
  219. /> */}
  220. {/* <MenuButton
  221. label="System"
  222. icon={
  223. <GearIcon
  224. fill={isSubscribed ? Colors.DARK_BLUE : Colors.LIGHT_GRAY}
  225. width={20}
  226. height={20}
  227. />
  228. }
  229. red={false}
  230. buttonFn={() => navigation.navigate(NAVIGATION_PAGES.SYSTEM_NOTIFICATIONS as never)}
  231. disabled={!isSubscribed}
  232. /> */}
  233. <WarningModal
  234. isVisible={modalInfo.visible}
  235. onClose={closeModal}
  236. type={modalInfo.type}
  237. message={modalInfo.message}
  238. action={() => {
  239. modalInfo.action();
  240. closeModal();
  241. }}
  242. onModalHide={() => {
  243. if (shouldOpenWarningModal) {
  244. setShouldOpenWarningModal(false);
  245. handleSubscribe();
  246. }
  247. }}
  248. />
  249. </PageWrapper>
  250. );
  251. };
  252. export default NotificationsScreen;