diff --git a/android/app/src/main/res/raw/ringtone.mp3 b/android/app/src/main/res/raw/ringtone.mp3 new file mode 100644 index 000000000..6447e1354 Binary files /dev/null and b/android/app/src/main/res/raw/ringtone.mp3 differ diff --git a/android/app/src/play/java/chat/rocket/reactnative/CustomPushNotification.java b/android/app/src/play/java/chat/rocket/reactnative/CustomPushNotification.java index 2232517ca..e77cf5eb3 100644 --- a/android/app/src/play/java/chat/rocket/reactnative/CustomPushNotification.java +++ b/android/app/src/play/java/chat/rocket/reactnative/CustomPushNotification.java @@ -97,9 +97,13 @@ public class CustomPushNotification extends PushNotification { bundle.putString("senderId", hasSender ? loadedEjson.sender._id : "1"); bundle.putString("avatarUri", loadedEjson.getAvatarUri()); - notificationMessages.get(notId).add(bundle); - postNotification(Integer.parseInt(notId)); - notifyReceivedToJS(); + if (loadedEjson.notificationType.equals("videoconf")) { + notifyReceivedToJS(); + } else { + notificationMessages.get(notId).add(bundle); + postNotification(Integer.parseInt(notId)); + notifyReceivedToJS(); + } } @Override diff --git a/app/actions/actionsTypes.ts b/app/actions/actionsTypes.ts index e0377db51..2273e3363 100644 --- a/app/actions/actionsTypes.ts +++ b/app/actions/actionsTypes.ts @@ -65,7 +65,7 @@ export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DI export const LOGOUT = 'LOGOUT'; // logout is always success export const DELETE_ACCOUNT = 'DELETE_ACCOUNT'; export const SNIPPETED_MESSAGES = createRequestTypes('SNIPPETED_MESSAGES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED']); -export const DEEP_LINKING = createRequestTypes('DEEP_LINKING', ['OPEN']); +export const DEEP_LINKING = createRequestTypes('DEEP_LINKING', ['OPEN', 'OPEN_VIDEO_CONF']); export const SORT_PREFERENCES = createRequestTypes('SORT_PREFERENCES', ['SET_ALL', 'SET']); export const SET_CUSTOM_EMOJIS = 'SET_CUSTOM_EMOJIS'; export const ACTIVE_USERS = createRequestTypes('ACTIVE_USERS', ['SET', 'CLEAR']); diff --git a/app/actions/deepLinking.ts b/app/actions/deepLinking.ts index 5b946daf7..966aed845 100644 --- a/app/actions/deepLinking.ts +++ b/app/actions/deepLinking.ts @@ -22,3 +22,10 @@ export function deepLinkingOpen(params: Partial): IDeepLinkingOpen { params }; } + +export function deepLinkingClickCallPush(params: any): IDeepLinkingOpen { + return { + type: DEEP_LINKING.OPEN_VIDEO_CONF, + params + }; +} diff --git a/app/containers/Ringer/index.tsx b/app/containers/Ringer/index.tsx index 2b91c7943..a5c83a5f7 100644 --- a/app/containers/Ringer/index.tsx +++ b/app/containers/Ringer/index.tsx @@ -8,8 +8,6 @@ export enum ERingerSounds { } const Ringer = React.memo(({ ringer }: { ringer: ERingerSounds }) => { - console.log('Ringer', ringer); - const sound = useRef(null); useEffect(() => { (async () => { diff --git a/app/i18n/locales/en.json b/app/i18n/locales/en.json index d7cdb3034..076a9f737 100644 --- a/app/i18n/locales/en.json +++ b/app/i18n/locales/en.json @@ -749,6 +749,7 @@ "Continue": "Continue", "Message_has_been_shared": "Message has been shared", "No_channels_in_team": "No Channels on this team", + "conference_call": "Conference call", "Room_not_found": "Room not found", "The_room_does_not_exist": "The room does not exist or you may not have access permission", "Supported_versions_expired_title": "{{workspace_name}} is running an unsupported version of Rocket.Chat", @@ -758,5 +759,6 @@ "The_user_will_be_able_to_type_in_roomName": "The user will be able to type in {{roomName}}", "Enable_writing_in_room": "Enable writing in room", "Disable_writing_in_room": "Disable writing in room", - "Pinned_a_message": "Pinned a message:" + "Pinned_a_message": "Pinned a message:", + "Missed_call": "Missed call" } diff --git a/app/i18n/locales/pt-BR.json b/app/i18n/locales/pt-BR.json index 6a89d015e..f4adacf92 100644 --- a/app/i18n/locales/pt-BR.json +++ b/app/i18n/locales/pt-BR.json @@ -747,7 +747,6 @@ "decline": "Recusar", "accept": "Aceitar", "Incoming_call_from": "Chamada recebida de", - "Call_started": "Chamada Iniciada", "Room_not_found": "Sala não encontrada", "The_room_does_not_exist": "A sala não existe ou você pode não ter permissão de acesso", "Call_started": "Chamada iniciada", @@ -757,5 +756,6 @@ "The_user_wont_be_able_to_type_in_roomName": "O usuário não poderá digitar em {{roomName}}", "The_user_will_be_able_to_type_in_roomName": "O usuário poderá digitar em {{roomName}}", "Enable_writing_in_room": "Permitir escrita na sala", - "Disable_writing_in_room": "Desabilitar escrita na sala" + "Disable_writing_in_room": "Desabilitar escrita na sala", + "Missed_call": "Chamada perdida" } \ No newline at end of file diff --git a/app/index.tsx b/app/index.tsx index 58eced6f5..2b29c3f6b 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -1,30 +1,26 @@ import React from 'react'; import { Dimensions, Linking } from 'react-native'; -import { initialWindowMetrics, SafeAreaProvider } from 'react-native-safe-area-context'; -import RNScreens from 'react-native-screens'; -import { Provider } from 'react-redux'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; import Orientation from 'react-native-orientation-locker'; +import { SafeAreaProvider, initialWindowMetrics } from 'react-native-safe-area-context'; +import RNScreens from 'react-native-screens'; +import { Provider } from 'react-redux'; +import AppContainer from './AppContainer'; import { appInit, appInitLocalSettings, setMasterDetail as setMasterDetailAction } from './actions/app'; import { deepLinkingOpen } from './actions/deepLinking'; -import AppContainer from './AppContainer'; import { ActionSheetProvider } from './containers/ActionSheet'; import InAppNotification from './containers/InAppNotification'; +import Loading from './containers/Loading'; import Toast from './containers/Toast'; import TwoFactor from './containers/TwoFactor'; -import Loading from './containers/Loading'; import { IThemePreference } from './definitions/ITheme'; import { DimensionsContext } from './dimensions'; -import { colors, isFDroidBuild, MIN_WIDTH_MASTER_DETAIL_LAYOUT, themes } from './lib/constants'; +import { MIN_WIDTH_MASTER_DETAIL_LAYOUT, colors, isFDroidBuild, themes } from './lib/constants'; import { getAllowAnalyticsEvents, getAllowCrashReport } from './lib/methods'; -import parseQuery from './lib/methods/helpers/parseQuery'; -import { initializePushNotifications, onNotification } from './lib/notifications'; -import store from './lib/store'; -import { initStore } from './lib/store/auxStore'; -import { ThemeContext, TSupportedThemes } from './theme'; import { debounce, isTablet } from './lib/methods/helpers'; import { toggleAnalyticsEventsReport, toggleCrashErrorsReport } from './lib/methods/helpers/log'; +import parseQuery from './lib/methods/helpers/parseQuery'; import { getTheme, initialTheme, @@ -33,6 +29,11 @@ import { subscribeTheme, unsubscribeTheme } from './lib/methods/helpers/theme'; +import { initializePushNotifications, onNotification } from './lib/notifications'; +import { getInitialNotification } from './lib/notifications/videoConf/getInitialNotification'; +import store from './lib/store'; +import { initStore } from './lib/store/auxStore'; +import { TSupportedThemes, ThemeContext } from './theme'; import ChangePasscodeView from './views/ChangePasscodeView'; import ScreenLockedView from './views/ScreenLockedView'; @@ -126,6 +127,8 @@ export default class Root extends React.Component<{}, IState> { return; } + await getInitialNotification(); + // Open app from deep linking const deepLinking = await Linking.getInitialURL(); const parsedDeepLinkingURL = parseDeepLinking(deepLinking!); diff --git a/app/lib/constants/colors.ts b/app/lib/constants/colors.ts index 5dce0d6cc..0315fa15e 100644 --- a/app/lib/constants/colors.ts +++ b/app/lib/constants/colors.ts @@ -1,3 +1,5 @@ +export const BACKGROUND_PUSH_COLOR = '#F5455C'; + export const STATUS_COLORS: any = { online: '#2de0a5', busy: '#f5455c', diff --git a/app/lib/methods/helpers/goRoom.ts b/app/lib/methods/helpers/goRoom.ts index 0e0be7ce6..006360ff7 100644 --- a/app/lib/methods/helpers/goRoom.ts +++ b/app/lib/methods/helpers/goRoom.ts @@ -118,3 +118,5 @@ export const goRoom = async ({ return navigate({ item, isMasterDetail, popToRoot, ...props }); }; + +export const navigateToRoom = navigate; diff --git a/app/lib/methods/videoConf.ts b/app/lib/methods/videoConf.ts index 0603dcdb9..65adc8b51 100644 --- a/app/lib/methods/videoConf.ts +++ b/app/lib/methods/videoConf.ts @@ -26,7 +26,7 @@ export const handleAndroidBltPermission = async (): Promise => { } }; -export const videoConfJoin = async (callId: string, cam?: boolean, mic?: boolean): Promise => { +export const videoConfJoin = async (callId: string, cam?: boolean, mic?: boolean, fromPush?: boolean): Promise => { try { const result = await Services.videoConferenceJoin(callId, cam, mic); if (result.success) { @@ -38,7 +38,11 @@ export const videoConfJoin = async (callId: string, cam?: boolean, mic?: boolean } } } catch (e) { - showErrorAlert(i18n.t('error-init-video-conf')); + if (fromPush) { + showErrorAlert(i18n.t('Missed_call')); + } else { + showErrorAlert(i18n.t('error-init-video-conf')); + } log(e); } }; diff --git a/app/lib/notifications/index.ts b/app/lib/notifications/index.ts index 8a0899576..6eb077352 100644 --- a/app/lib/notifications/index.ts +++ b/app/lib/notifications/index.ts @@ -1,10 +1,10 @@ import EJSON from 'ejson'; -import { store } from '../store/auxStore'; import { deepLinkingOpen } from '../../actions/deepLinking'; -import { isFDroidBuild } from '../constants'; -import { deviceToken, pushNotificationConfigure, setNotificationsBadgeCount, removeAllNotifications } from './push'; import { INotification, SubscriptionType } from '../../definitions'; +import { isFDroidBuild } from '../constants'; +import { store } from '../store/auxStore'; +import { deviceToken, pushNotificationConfigure, removeAllNotifications, setNotificationsBadgeCount } from './push'; interface IEjson { rid: string; diff --git a/app/lib/notifications/videoConf/backgroundNotificationHandler.ts b/app/lib/notifications/videoConf/backgroundNotificationHandler.ts new file mode 100644 index 000000000..84dfaad33 --- /dev/null +++ b/app/lib/notifications/videoConf/backgroundNotificationHandler.ts @@ -0,0 +1,122 @@ +import notifee, { AndroidCategory, AndroidImportance, AndroidVisibility, Event } from '@notifee/react-native'; +import messaging from '@react-native-firebase/messaging'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +import ejson from 'ejson'; + +import { deepLinkingClickCallPush } from '../../../actions/deepLinking'; +import i18n from '../../../i18n'; +import { BACKGROUND_PUSH_COLOR } from '../../constants'; +import { store } from '../../store/auxStore'; + +const VIDEO_CONF_CHANNEL = 'video-conf-call'; +const VIDEO_CONF_TYPE = 'videoconf'; + +interface Caller { + _id?: string; + name?: string; +} + +interface NotificationData { + notificationType?: string; + status?: number; + rid?: string; + caller?: Caller; +} + +const createChannel = () => + notifee.createChannel({ + id: VIDEO_CONF_CHANNEL, + name: 'Video Call', + lights: true, + vibration: true, + importance: AndroidImportance.HIGH, + sound: 'ringtone' + }); + +const handleBackgroundEvent = async (event: Event) => { + const { pressAction, notification } = event.detail; + const notificationData = notification?.data; + if ( + typeof notificationData?.caller === 'object' && + (notificationData.caller as Caller)?._id && + (event.type === 1 || event.type === 2) + ) { + if (store?.getState()?.app.ready) { + store.dispatch(deepLinkingClickCallPush({ ...notificationData, event: pressAction?.id })); + } else { + AsyncStorage.setItem('pushNotification', JSON.stringify({ ...notificationData, event: pressAction?.id })); + } + await notifee.cancelNotification( + `${notificationData.rid}${(notificationData.caller as Caller)._id}`.replace(/[^A-Za-z0-9]/g, '') + ); + } +}; + +const backgroundNotificationHandler = () => { + notifee.onBackgroundEvent(handleBackgroundEvent); +}; + +const displayVideoConferenceNotification = async (notification: NotificationData) => { + const id = `${notification.rid}${notification.caller?._id}`.replace(/[^A-Za-z0-9]/g, ''); + const actions = [ + { + title: i18n.t('accept'), + pressAction: { + id: 'accept', + launchActivity: 'default' + } + }, + { + title: i18n.t('decline'), + pressAction: { + id: 'decline', + launchActivity: 'default' + } + } + ]; + + await notifee.displayNotification({ + id, + title: i18n.t('conference_call'), + body: `${i18n.t('Incoming_call_from')} ${notification.caller?.name}`, + data: notification as { [key: string]: string | number | object }, + android: { + channelId: VIDEO_CONF_CHANNEL, + category: AndroidCategory.CALL, + visibility: AndroidVisibility.PUBLIC, + importance: AndroidImportance.HIGH, + smallIcon: 'ic_notification', + color: BACKGROUND_PUSH_COLOR, + actions, + lightUpScreen: true, + loopSound: true, + sound: 'ringtone', + autoCancel: false, + ongoing: true, + pressAction: { + id: 'default', + launchActivity: 'default' + } + } + }); +}; + +const setBackgroundNotificationHandler = () => { + createChannel(); + messaging().setBackgroundMessageHandler(async message => { + const notification: NotificationData = ejson.parse(message?.data?.ejson as string); + if (notification?.notificationType === VIDEO_CONF_TYPE) { + if (notification.status === 0) { + await displayVideoConferenceNotification(notification); + } else if (notification.status === 4) { + const id = `${notification.rid}${notification.caller?._id}`.replace(/[^A-Za-z0-9]/g, ''); + await notifee.cancelNotification(id); + } + } + + return null; + }); +}; + +setBackgroundNotificationHandler(); +backgroundNotificationHandler(); diff --git a/app/lib/notifications/videoConf/getInitialNotification.ts b/app/lib/notifications/videoConf/getInitialNotification.ts new file mode 100644 index 000000000..5e48a842c --- /dev/null +++ b/app/lib/notifications/videoConf/getInitialNotification.ts @@ -0,0 +1,15 @@ +import { deepLinkingClickCallPush } from '../../../actions/deepLinking'; +import { isAndroid } from '../../methods/helpers'; +import { store } from '../../store/auxStore'; + +export const getInitialNotification = async (): Promise => { + if (isAndroid) { + const notifee = require('@notifee/react-native').default; + const initialNotification = await notifee.getInitialNotification(); + if (initialNotification?.notification?.data?.notificationType === 'videoconf') { + store.dispatch( + deepLinkingClickCallPush({ ...initialNotification?.notification?.data, event: initialNotification?.pressAction?.id }) + ); + } + } +}; diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js index b746ea294..4e7213401 100644 --- a/app/sagas/deepLinking.js +++ b/app/sagas/deepLinking.js @@ -1,20 +1,21 @@ -import { all, delay, put, select, take, takeLatest } from 'redux-saga/effects'; +import { all, call, delay, put, select, take, takeLatest } from 'redux-saga/effects'; -import UserPreferences from '../lib/methods/userPreferences'; import * as types from '../actions/actionsTypes'; -import { selectServerRequest, serverInitAdd } from '../actions/server'; -import { inviteLinksRequest, inviteLinksSetToken } from '../actions/inviteLinks'; -import database from '../lib/database'; -import EventEmitter from '../lib/methods/helpers/events'; import { appInit, appStart } from '../actions/app'; -import { localAuthenticate } from '../lib/methods/helpers/localAuthentication'; -import { goRoom } from '../lib/methods/helpers/goRoom'; -import { getUidDirectMessage } from '../lib/methods/helpers'; +import { inviteLinksRequest, inviteLinksSetToken } from '../actions/inviteLinks'; import { loginRequest } from '../actions/login'; -import log from '../lib/methods/helpers/log'; +import { selectServerRequest, serverInitAdd } from '../actions/server'; import { RootEnum } from '../definitions'; import { CURRENT_SERVER, TOKEN_KEY } from '../lib/constants'; +import database from '../lib/database'; import { canOpenRoom, getServerInfo } from '../lib/methods'; +import { getUidDirectMessage } from '../lib/methods/helpers'; +import EventEmitter from '../lib/methods/helpers/events'; +import { goRoom, navigateToRoom } from '../lib/methods/helpers/goRoom'; +import { localAuthenticate } from '../lib/methods/helpers/localAuthentication'; +import log from '../lib/methods/helpers/log'; +import UserPreferences from '../lib/methods/userPreferences'; +import { videoConfJoin } from '../lib/methods/videoConf'; import { Services } from '../lib/services'; const roomTypes = { @@ -168,7 +169,90 @@ const handleOpen = function* handleOpen({ params }) { } }; +const handleNavigateCallRoom = function* handleNavigateCallRoom({ params }) { + yield put(appStart({ root: RootEnum.ROOT_INSIDE })); + const db = database.active; + const subsCollection = db.get('subscriptions'); + const room = yield subsCollection.find(params.rid); + if (room) { + const isMasterDetail = yield select(state => state.app.isMasterDetail); + yield navigateToRoom({ item: room, isMasterDetail, popToRoot: true }); + const uid = params.caller._id; + const { rid, callId, event } = params; + if (event === 'accept') { + yield call(Services.notifyUser, `${uid}/video-conference`, { + action: 'accepted', + params: { uid, rid, callId } + }); + yield videoConfJoin(callId, true, false, true); + } else if (event === 'decline') { + yield call(Services.notifyUser, `${uid}/video-conference`, { + action: 'rejected', + params: { uid, rid, callId } + }); + } + } +}; + +const handleClickCallPush = function* handleOpen({ params }) { + const serversDB = database.servers; + const serversCollection = serversDB.get('servers'); + + let { host } = params; + + if (host.slice(-1) === '/') { + host = host.slice(0, host.length - 1); + } + + const [server, user] = yield all([ + UserPreferences.getString(CURRENT_SERVER), + UserPreferences.getString(`${TOKEN_KEY}-${host}`) + ]); + + if (server === host && user) { + const connected = yield select(state => state.server.connected); + if (!connected) { + yield localAuthenticate(host); + yield put(selectServerRequest(host)); + yield take(types.LOGIN.SUCCESS); + } + yield handleNavigateCallRoom({ params }); + } else { + // search if deep link's server already exists + try { + const hostServerRecord = yield serversCollection.find(host); + if (hostServerRecord && user) { + yield localAuthenticate(host); + yield put(selectServerRequest(host, hostServerRecord.version, true, true)); + yield take(types.LOGIN.SUCCESS); + yield handleNavigateCallRoom({ params }); + return; + } + } catch (e) { + // do nothing? + } + // if deep link is from a different server + const result = yield Services.getServerInfo(host); + if (!result.success) { + // Fallback to prevent the app from being stuck on splash screen + yield fallbackNavigation(); + return; + } + yield put(appStart({ root: RootEnum.ROOT_OUTSIDE })); + yield put(serverInitAdd(server)); + yield delay(1000); + EventEmitter.emit('NewServer', { server: host }); + if (params.token) { + yield take(types.SERVER.SELECT_SUCCESS); + yield put(loginRequest({ resume: params.token }, true)); + yield take(types.LOGIN.SUCCESS); + yield handleNavigateCallRoom({ params }); + } + } +}; + const root = function* root() { yield takeLatest(types.DEEP_LINKING.OPEN, handleOpen); + yield takeLatest(types.DEEP_LINKING.OPEN_VIDEO_CONF, handleClickCallPush); }; export default root; diff --git a/app/sagas/init.js b/app/sagas/init.js index a9ad638a8..5de30417d 100644 --- a/app/sagas/init.js +++ b/app/sagas/init.js @@ -1,5 +1,6 @@ -import { put, takeLatest } from 'redux-saga/effects'; +import { call, put, takeLatest } from 'redux-saga/effects'; import RNBootSplash from 'react-native-bootsplash'; +import AsyncStorage from '@react-native-async-storage/async-storage'; import { BIOMETRY_ENABLED_KEY, CURRENT_SERVER, TOKEN_KEY } from '../lib/constants'; import UserPreferences from '../lib/methods/userPreferences'; @@ -12,6 +13,7 @@ import { localAuthenticate } from '../lib/methods/helpers/localAuthentication'; import { appReady, appStart } from '../actions/app'; import { RootEnum } from '../definitions'; import { getSortPreferences } from '../lib/methods'; +import { deepLinkingClickCallPush } from '../actions/deepLinking'; export const initLocalSettings = function* initLocalSettings() { const sortPreferences = getSortPreferences(); @@ -70,6 +72,11 @@ const restore = function* restore() { } yield put(appReady({})); + const pushNotification = yield call(AsyncStorage.getItem, 'pushNotification'); + if (pushNotification) { + const pushNotification = yield call(AsyncStorage.removeItem, 'pushNotification'); + yield call(deepLinkingClickCallPush, JSON.parse(pushNotification)); + } } catch (e) { log(e); yield put(appStart({ root: RootEnum.ROOT_OUTSIDE })); diff --git a/index.js b/index.js index e73a837d9..87d2a4a34 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,8 @@ import 'react-native-console-time-polyfill'; import { AppRegistry } from 'react-native'; import { name as appName, share as shareName } from './app.json'; +import { isFDroidBuild } from './app/lib/constants'; +import { isAndroid } from './app/lib/methods/helpers'; if (__DEV__) { require('./app/ReactotronConfig'); @@ -18,6 +20,11 @@ if (__DEV__) { console.info = () => {}; } +if (!isFDroidBuild && isAndroid) { + require('./app/lib/notifications/videoConf/backgroundNotificationHandler'); +} + + AppRegistry.registerComponent(appName, () => require('./app/index').default); AppRegistry.registerComponent(shareName, () => require('./app/share').default); diff --git a/package.json b/package.json index 4a86b1250..f70559324 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@codler/react-native-keyboard-aware-scroll-view": "^2.0.1", "@gorhom/bottom-sheet": "^4.3.1", "@hookform/resolvers": "^2.9.10", + "@notifee/react-native": "^7.8.0", "@nozbe/watermelondb": "0.23.0", "@react-native-async-storage/async-storage": "^1.17.11", "@react-native-camera-roll/camera-roll": "5.6.1", @@ -46,6 +47,7 @@ "@react-native-firebase/analytics": "^14.11.0", "@react-native-firebase/app": "^14.11.0", "@react-native-firebase/crashlytics": "^14.11.0", + "@react-native-firebase/messaging": "^18.5.0", "@react-native-masked-view/masked-view": "^0.2.8", "@react-navigation/drawer": "6.4.1", "@react-navigation/elements": "^1.3.6", diff --git a/patches/@notifee+react-native+7.8.0.patch b/patches/@notifee+react-native+7.8.0.patch new file mode 100644 index 000000000..04bfbe16c --- /dev/null +++ b/patches/@notifee+react-native+7.8.0.patch @@ -0,0 +1,20 @@ +--- a/node_modules/@notifee/react-native/android/src/main/java/io/invertase/notifee/NotifeeApiModule.java ++++ b/node_modules/@notifee/react-native/android/src/main/java/io/invertase/notifee/NotifeeApiModule.java +@@ -238,7 +238,7 @@ public class NotifeeApiModule extends ReactContextBaseJavaModule implements Perm + @ReactMethod + public void requestPermission(Promise promise) { + // For Android 12 and below, we return the notification settings +- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { ++ if (Build.VERSION.SDK_INT < 33) { + Notifee.getInstance() + .getNotificationSettings( + (e, aBundle) -> NotifeeReactUtils.promiseResolver(promise, e, aBundle)); +@@ -265,7 +265,7 @@ public class NotifeeApiModule extends ReactContextBaseJavaModule implements Perm + (e, aBundle) -> NotifeeReactUtils.promiseResolver(promise, e, aBundle)); + + activity.requestPermissions( +- new String[] {Manifest.permission.POST_NOTIFICATIONS}, ++ new String[] {"android.permission.POST_NOTIFICATIONS"}, + Notifee.REQUEST_CODE_NOTIFICATION_PERMISSION, + this); + } diff --git a/react-native.config.js b/react-native.config.js index 9e78b41ad..274dfa161 100644 --- a/react-native.config.js +++ b/react-native.config.js @@ -14,6 +14,16 @@ module.exports = { platforms: { android: null } + }, + '@react-native-firebase/messaging': { + platforms: { + ios: null + } + }, + '@notifee/react-native': { + platforms: { + ios: null + } } } }; diff --git a/yarn.lock b/yarn.lock index 11daeb081..217fdd18b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5153,6 +5153,11 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@notifee/react-native@^7.8.0": + version "7.8.0" + resolved "https://registry.yarnpkg.com/@notifee/react-native/-/react-native-7.8.0.tgz#2990883753990f3585aa0cb5becc5cbdbcd87a43" + integrity sha512-sx8h62U4FrR4pqlbN1rkgPsdamDt9Tad0zgfO6VtP6rNJq/78k8nxUnh0xIX3WPDcCV8KAzdYCE7+UNvhF1CpQ== + "@nozbe/simdjson@0.9.6-fix2": version "0.9.6-fix2" resolved "https://registry.yarnpkg.com/@nozbe/simdjson/-/simdjson-0.9.6-fix2.tgz#00d1c8ec76bfac25c022b07511c8fff4568b2973" @@ -5488,6 +5493,11 @@ "@expo/config-plugins" "^4.1.5" stacktrace-js "^2.0.0" +"@react-native-firebase/messaging@^18.5.0": + version "18.5.0" + resolved "https://registry.yarnpkg.com/@react-native-firebase/messaging/-/messaging-18.5.0.tgz#2a80b25816470e9843682e031a3a113566067ce6" + integrity sha512-y1FApYxBMcygmbWBqUPFC+fCfvx6Yf6TdZewun7kPwx+S+tkYzoKx1IsXtxOXtqyJjCNEYirjFgNrs5SSd02zA== + "@react-native-masked-view/masked-view@^0.2.8": version "0.2.8" resolved "https://registry.yarnpkg.com/@react-native-masked-view/masked-view/-/masked-view-0.2.8.tgz#34405a4361882dae7c81b1b771fe9f5fbd545a97"