diff --git a/app/lib/methods/helpers/index.ts b/app/lib/methods/helpers/index.ts index 77a6a8f75a0..c77650e294c 100644 --- a/app/lib/methods/helpers/index.ts +++ b/app/lib/methods/helpers/index.ts @@ -8,6 +8,7 @@ export * from './getAvatarUrl'; export * from './info'; export * from './isReadOnly'; export * from './media'; +export * from './normalizeDeepLinkingServerHost'; export * from './room'; export * from './server'; export * from './isSsl'; diff --git a/app/lib/methods/helpers/normalizeDeepLinkingServerHost.test.ts b/app/lib/methods/helpers/normalizeDeepLinkingServerHost.test.ts new file mode 100644 index 00000000000..46fceeb675e --- /dev/null +++ b/app/lib/methods/helpers/normalizeDeepLinkingServerHost.test.ts @@ -0,0 +1,24 @@ +import { normalizeDeepLinkingServerHost } from './normalizeDeepLinkingServerHost'; + +describe('normalizeDeepLinkingServerHost', () => { + it('returns empty string for empty input', () => { + expect(normalizeDeepLinkingServerHost('')).toBe(''); + }); + + it('adds https for host without scheme', () => { + expect(normalizeDeepLinkingServerHost('open.rocket.chat')).toBe('https://open.rocket.chat'); + }); + + it('uses http for localhost', () => { + expect(normalizeDeepLinkingServerHost('localhost')).toBe('http://localhost'); + expect(normalizeDeepLinkingServerHost('localhost:3000')).toBe('http://localhost:3000'); + }); + + it('upgrades http to https for non-localhost', () => { + expect(normalizeDeepLinkingServerHost('http://example.com')).toBe('https://example.com'); + }); + + it('strips trailing slash', () => { + expect(normalizeDeepLinkingServerHost('https://example.com/')).toBe('https://example.com'); + }); +}); diff --git a/app/lib/methods/helpers/normalizeDeepLinkingServerHost.ts b/app/lib/methods/helpers/normalizeDeepLinkingServerHost.ts new file mode 100644 index 00000000000..6614c205c6f --- /dev/null +++ b/app/lib/methods/helpers/normalizeDeepLinkingServerHost.ts @@ -0,0 +1,23 @@ +/** + * Normalize a Rocket.Chat server base URL for deep linking and VoIP host comparison. + * Matches the historical behavior in `app/sagas/deepLinking.js` (`handleOpen` host handling). + */ +export function normalizeDeepLinkingServerHost(rawHost: string): string { + let host = rawHost; + if (!host) { + return ''; + } + if (!/^(http|https)/.test(host)) { + if (/^localhost(:\d+)?/.test(host)) { + host = `http://${host}`; + } else { + host = `https://${host}`; + } + } else { + host = host.replace('http://', 'https://'); + } + if (host.slice(-1) === '/') { + host = host.slice(0, host.length - 1); + } + return host; +} diff --git a/app/lib/services/voip/MediaCallEvents.test.ts b/app/lib/services/voip/MediaCallEvents.test.ts index 32d2367caa7..4fc9b9d0e5f 100644 --- a/app/lib/services/voip/MediaCallEvents.test.ts +++ b/app/lib/services/voip/MediaCallEvents.test.ts @@ -18,10 +18,12 @@ jest.mock('../../methods/helpers', () => ({ isIOS: false })); +const mockServerSelector = jest.fn(() => 'https://workspace-a.example.com'); jest.mock('../../store', () => ({ __esModule: true, default: { - dispatch: (...args: unknown[]) => mockDispatch(...args) + dispatch: (...args: unknown[]) => mockDispatch(...args), + getState: () => ({ server: { server: mockServerSelector() } }) } })); @@ -51,7 +53,8 @@ jest.mock('react-native-callkeep', () => ({ jest.mock('./MediaSessionInstance', () => ({ mediaSessionInstance: { - endCall: jest.fn() + endCall: jest.fn(), + applyRestStateSignals: jest.fn(() => Promise.resolve()) } })); @@ -127,6 +130,21 @@ describe('MediaCallEvents cross-server accept (slice 3)', () => { }); }); + it('skips deepLinkingOpen and replays REST state signals when host matches active workspace', () => { + const { mediaSessionInstance } = jest.requireMock('./MediaSessionInstance'); + mockServerSelector.mockReturnValueOnce('https://workspace-a.example.com'); + const payload = buildIncomingPayload({ + callId: 'same-ws-call', + host: 'https://workspace-a.example.com' + }); + + DeviceEventEmitter.emit('VoipAcceptSucceeded', payload); + + expect(mockSetNativeAcceptedCallId).toHaveBeenCalledWith('same-ws-call'); + expect(mediaSessionInstance.applyRestStateSignals).toHaveBeenCalledTimes(1); + expect(mockDispatch).not.toHaveBeenCalled(); + }); + it('does not dispatch or set native id when type is not incoming_call', () => { DeviceEventEmitter.emit( 'VoipAcceptSucceeded', diff --git a/app/lib/services/voip/MediaCallEvents.ts b/app/lib/services/voip/MediaCallEvents.ts index 74dc10e7b37..0dcba950376 100644 --- a/app/lib/services/voip/MediaCallEvents.ts +++ b/app/lib/services/voip/MediaCallEvents.ts @@ -1,7 +1,7 @@ import RNCallKeep from 'react-native-callkeep'; import { DeviceEventEmitter, NativeEventEmitter } from 'react-native'; -import { isIOS } from '../../methods/helpers'; +import { isIOS, normalizeDeepLinkingServerHost } from '../../methods/helpers'; import store from '../../store'; import { deepLinkingOpen } from '../../../actions/deepLinking'; import { useCallStore } from './useCallStore'; @@ -17,6 +17,15 @@ const TAG = `[MediaCallEvents][${platform}]`; const EVENT_VOIP_ACCEPT_FAILED = 'VoipAcceptFailed'; const EVENT_VOIP_ACCEPT_SUCCEEDED = 'VoipAcceptSucceeded'; +/** True when normalized incoming host matches the active Redux workspace (no server switch needed). */ +function isVoipIncomingHostCurrentWorkspace(incomingHost: string): boolean { + const active = store.getState().server.server; + if (!active || !incomingHost) { + return false; + } + return normalizeDeepLinkingServerHost(incomingHost) === normalizeDeepLinkingServerHost(active); +} + /** Dedupe native emit + stash replay for the same failed accept. */ let lastHandledVoipAcceptFailureCallId: string | null = null; /** Idempotent warm delivery of native accept success. */ @@ -56,6 +65,12 @@ function handleVoipAcceptSucceededFromNative(data: VoipPayload) { console.log(`${TAG} VoipAcceptSucceeded:`, data); NativeVoipModule.clearInitialEvents(); useCallStore.getState().setNativeAcceptedCallId(data.callId); + if (data.host && isVoipIncomingHostCurrentWorkspace(data.host)) { + mediaSessionInstance.applyRestStateSignals().catch(error => { + console.error(`${TAG} applyRestStateSignals failed:`, error); + }); + return; + } store.dispatch( deepLinkingOpen({ callId: data.callId, @@ -109,8 +124,8 @@ export const setupMediaCallEvents = (): (() => void) => { // Note: there is intentionally no 'answerCall' listener here. // VoipService.swift handles accept natively: handleObservedCallChanged detects - // hasConnected = true and calls handleNativeAccept(), which sends the DDP accept - // signal before JS runs. JS receives VoipAcceptSucceeded after success. + // hasConnected = true and calls handleNativeAccept(), which sends the REST accept + // (POST /api/v1/media-calls.answer) before JS runs. JS receives VoipAcceptSucceeded after success. } /** Tracks OS-driven hold (competing call) so we only auto-resume that path, not manual hold. */ @@ -223,6 +238,14 @@ export const getInitialMediaCallEvents = async (): Promise => { if (wasAnswered) { useCallStore.getState().setNativeAcceptedCallId(initialEvents.callId); + if (initialEvents.host && isVoipIncomingHostCurrentWorkspace(initialEvents.host)) { + mediaSessionInstance.applyRestStateSignals().catch(error => { + console.error(`${TAG} applyRestStateSignals (initial) failed:`, error); + }); + console.log(`${TAG} Same workspace as VoIP host; skipped deepLinkingOpen`); + return true; + } + store.dispatch( deepLinkingOpen({ callId: initialEvents.callId, diff --git a/app/lib/services/voip/MediaSessionInstance.test.ts b/app/lib/services/voip/MediaSessionInstance.test.ts index f802c9f577f..a1c0a6f1e91 100644 --- a/app/lib/services/voip/MediaSessionInstance.test.ts +++ b/app/lib/services/voip/MediaSessionInstance.test.ts @@ -51,6 +51,12 @@ jest.mock('../sdk', () => ({ } })); +const mockMediaCallsStateSignals = jest.fn().mockResolvedValue({ signals: [], success: true }); + +jest.mock('../restApi', () => ({ + mediaCallsStateSignals: (...args: unknown[]) => mockMediaCallsStateSignals(...args) +})); + jest.mock('../../store/auxStore', () => ({ store: { getState: jest.fn(() => ({ @@ -185,6 +191,7 @@ function buildClientMediaCall(options: { describe('MediaSessionInstance', () => { beforeEach(() => { jest.clearAllMocks(); + mockMediaCallsStateSignals.mockResolvedValue({ signals: [], success: true }); createdSessions.length = 0; mockGetUidDirectMessage.mockReturnValue('other-user-id'); mockGetDMSubscriptionByUsername.mockResolvedValue(null); @@ -206,20 +213,25 @@ describe('MediaSessionInstance', () => { }); describe('init', () => { - it('should register stream-notify-user listener', () => { - mediaSessionInstance.init('user-1'); + it('should register stream-notify-user listener', async () => { + await mediaSessionInstance.init('user-1'); expect(mockOnStreamData).toHaveBeenCalledWith('stream-notify-user', expect.any(Function)); }); - it('should create session with userId', () => { - mediaSessionInstance.init('user-abc'); + it('should create session with userId', async () => { + await mediaSessionInstance.init('user-abc'); expect(createdSessions).toHaveLength(1); expect(createdSessions[0].userId).toBe('user-abc'); }); - it('should route sendSignal through sdk.methodCall with user media-calls channel', () => { + it('should fetch REST state signals on init', async () => { + await mediaSessionInstance.init('user-1'); + expect(mockMediaCallsStateSignals).toHaveBeenCalledWith('test-device-id'); + }); + + it('should route sendSignal through sdk.methodCall with user media-calls channel', async () => { const spy = jest.spyOn(mediaSessionStore, 'setSendSignalFn'); - mediaSessionInstance.init('user-xyz'); + await mediaSessionInstance.init('user-xyz'); expect(spy).toHaveBeenCalled(); const sendFn = spy.mock.calls[spy.mock.calls.length - 1][0] as (signal: { type: string }) => void; sendFn({ type: 'register' }); @@ -233,17 +245,17 @@ describe('MediaSessionInstance', () => { }); describe('teardown and user switch', () => { - it('should call endSession on previous session when init with different userId', () => { - mediaSessionInstance.init('user-1'); + it('should call endSession on previous session when init with different userId', async () => { + await mediaSessionInstance.init('user-1'); const first = createdSessions[0]; - mediaSessionInstance.init('user-2'); + await mediaSessionInstance.init('user-2'); expect(first.endSession).toHaveBeenCalled(); expect(createdSessions[createdSessions.length - 1].userId).toBe('user-2'); }); - it('should only have one active onChange handler after re-init (getInstance once per change emit)', () => { - mediaSessionInstance.init('user-1'); - mediaSessionInstance.init('user-2'); + it('should only have one active onChange handler after re-init (getInstance once per change emit)', async () => { + await mediaSessionInstance.init('user-1'); + await mediaSessionInstance.init('user-2'); const spy = jest.spyOn(mediaSessionStore, 'getInstance'); mediaSessionStore.emit('change'); expect(spy).toHaveBeenCalledTimes(1); @@ -251,21 +263,21 @@ describe('MediaSessionInstance', () => { spy.mockRestore(); }); - it('should throw existing makeInstance error when getInstance after reset without init', () => { - mediaSessionInstance.init('user-1'); + it('should throw existing makeInstance error when getInstance after reset without init', async () => { + await mediaSessionInstance.init('user-1'); mediaSessionInstance.reset(); - expect(() => mediaSessionStore.getInstance('any')).toThrow('WebRTC processor factory and send signal function must be set'); + expect(() => mediaSessionStore.getInstance('any')).toThrow(/must be set/); }); - it('should allow init after reset', () => { - mediaSessionInstance.init('user-1'); + it('should allow init after reset', async () => { + await mediaSessionInstance.init('user-1'); mediaSessionInstance.reset(); - mediaSessionInstance.init('user-2'); + await mediaSessionInstance.init('user-2'); expect(createdSessions[createdSessions.length - 1].userId).toBe('user-2'); }); - it('should not throw when reset is called twice', () => { - mediaSessionInstance.init('user-1'); + it('should not throw when reset is called twice', async () => { + await mediaSessionInstance.init('user-1'); expect(() => { mediaSessionInstance.reset(); mediaSessionInstance.reset(); @@ -274,7 +286,7 @@ describe('MediaSessionInstance', () => { }); describe('newCall (no JS busy-reject; native decides)', () => { - it('allows incoming callee newCall when store already has an active call', () => { + it('allows incoming callee newCall when store already has an active call', async () => { const mockSetCall = jest.fn(); mockUseCallStoreGetState.mockReturnValue({ reset: mockCallStoreReset, @@ -286,14 +298,14 @@ describe('MediaSessionInstance', () => { nativeAcceptedCallId: null, roomId: null }); - mediaSessionInstance.init('user-1'); + await mediaSessionInstance.init('user-1'); const incoming = buildClientMediaCall({ callId: 'incoming-b', role: 'callee' }); getNewCallHandler()({ call: incoming }); expect(incoming.reject).not.toHaveBeenCalled(); expect(RNCallKeep.endCall).not.toHaveBeenCalledWith('incoming-b'); }); - it('allows incoming callee newCall when nativeAcceptedCallId is set but differs from incoming callId', () => { + it('allows incoming callee newCall when nativeAcceptedCallId is set but differs from incoming callId', async () => { mockUseCallStoreGetState.mockReturnValue({ reset: mockCallStoreReset, setCall: jest.fn(), @@ -304,14 +316,14 @@ describe('MediaSessionInstance', () => { nativeAcceptedCallId: 'native-other', roomId: null }); - mediaSessionInstance.init('user-1'); + await mediaSessionInstance.init('user-1'); const incoming = buildClientMediaCall({ callId: 'incoming-b', role: 'callee' }); getNewCallHandler()({ call: incoming }); expect(incoming.reject).not.toHaveBeenCalled(); expect(RNCallKeep.endCall).not.toHaveBeenCalledWith('incoming-b'); }); - it('allows incoming callee newCall when nativeAcceptedCallId matches incoming callId', () => { + it('allows incoming callee newCall when nativeAcceptedCallId matches incoming callId', async () => { mockUseCallStoreGetState.mockReturnValue({ reset: mockCallStoreReset, setCall: jest.fn(), @@ -322,14 +334,14 @@ describe('MediaSessionInstance', () => { nativeAcceptedCallId: 'same-id', roomId: null }); - mediaSessionInstance.init('user-1'); + await mediaSessionInstance.init('user-1'); const incoming = buildClientMediaCall({ callId: 'same-id', role: 'callee' }); getNewCallHandler()({ call: incoming }); expect(incoming.reject).not.toHaveBeenCalled(); expect(RNCallKeep.endCall).not.toHaveBeenCalledWith('same-id'); }); - it('does not reject outgoing (caller) newCall; binds call and navigates', () => { + it('does not reject outgoing (caller) newCall; binds call and navigates', async () => { const mockSetCall = jest.fn(); mockUseCallStoreGetState.mockReturnValue({ reset: mockCallStoreReset, @@ -341,7 +353,7 @@ describe('MediaSessionInstance', () => { nativeAcceptedCallId: null, roomId: null }); - mediaSessionInstance.init('user-1'); + await mediaSessionInstance.init('user-1'); const outgoing = buildClientMediaCall({ callId: 'out-c', role: 'caller' }); getNewCallHandler()({ call: outgoing }); expect(outgoing.reject).not.toHaveBeenCalled(); @@ -353,7 +365,7 @@ describe('MediaSessionInstance', () => { describe('stream-notify-user (notification/accepted gated)', () => { it('does not call answerCall when nativeAcceptedCallId is null', async () => { const answerSpy = jest.spyOn(mediaSessionInstance, 'answerCall').mockResolvedValue(undefined); - mediaSessionInstance.init('user-1'); + await mediaSessionInstance.init('user-1'); const streamHandler = getStreamNotifyHandler(); streamHandler({ msg: 'changed', @@ -386,7 +398,7 @@ describe('MediaSessionInstance', () => { nativeAcceptedCallId: 'from-signal', roomId: null }); - mediaSessionInstance.init('user-1'); + await mediaSessionInstance.init('user-1'); const streamHandler = getStreamNotifyHandler(); streamHandler({ msg: 'changed', @@ -419,7 +431,7 @@ describe('MediaSessionInstance', () => { nativeAcceptedCallId: 'sticky-only', roomId: null }); - mediaSessionInstance.init('user-1'); + await mediaSessionInstance.init('user-1'); const streamHandler = getStreamNotifyHandler(); streamHandler({ msg: 'changed', @@ -452,7 +464,7 @@ describe('MediaSessionInstance', () => { nativeAcceptedCallId: 'from-signal', roomId: null }); - mediaSessionInstance.init('user-1'); + await mediaSessionInstance.init('user-1'); const streamHandler = getStreamNotifyHandler(); streamHandler({ msg: 'changed', @@ -474,9 +486,54 @@ describe('MediaSessionInstance', () => { }); }); + describe('REST state signals replay (native accept race)', () => { + it('calls answerCall from init when REST returns accepted and nativeAcceptedCallId already matches', async () => { + const answerSpy = jest.spyOn(mediaSessionInstance, 'answerCall').mockResolvedValue(undefined); + mockMediaCallsStateSignals.mockResolvedValue({ + success: true, + signals: [ + { + type: 'notification', + notification: 'accepted', + signedContractId: 'test-device-id', + callId: 'race-call' + } + ] + }); + mockUseCallStoreGetState.mockReturnValue({ + reset: mockCallStoreReset, + setCall: jest.fn(), + setRoomId: mockSetRoomId, + resetNativeCallId: jest.fn(), + call: null, + callId: null, + nativeAcceptedCallId: 'race-call', + roomId: null + }); + await mediaSessionInstance.init('user-1'); + await Promise.resolve(); + expect(answerSpy).toHaveBeenCalledWith('race-call'); + answerSpy.mockRestore(); + }); + + it('applyRestStateSignals skips REST when no instance', async () => { + mediaSessionInstance.reset(); + mockMediaCallsStateSignals.mockClear(); + await mediaSessionInstance.applyRestStateSignals(); + expect(mockMediaCallsStateSignals).not.toHaveBeenCalled(); + }); + + it('applyRestStateSignals refetches REST after init', async () => { + await mediaSessionInstance.init('user-1'); + mockMediaCallsStateSignals.mockClear(); + await mediaSessionInstance.applyRestStateSignals(); + expect(mockMediaCallsStateSignals).toHaveBeenCalledWith('test-device-id'); + }); + }); + describe('startCall', () => { - it('requests phone state permission fire-and-forget when starting a call', () => { - mediaSessionInstance.init('user-1'); + it('requests phone state permission fire-and-forget when starting a call', async () => { + await mediaSessionInstance.init('user-1'); mockRequestPhoneStatePermission.mockClear(); const session = createdSessions[0]; mediaSessionInstance.startCall('peer-1', 'user'); @@ -486,8 +543,8 @@ describe('MediaSessionInstance', () => { }); describe('roomId population', () => { - it('startCallByRoom sets roomId before startCall', () => { - mediaSessionInstance.init('user-1'); + it('startCallByRoom sets roomId before startCall', async () => { + await mediaSessionInstance.init('user-1'); const session = createdSessions[0]; const order: string[] = []; mockSetRoomId.mockImplementationOnce(() => { @@ -506,7 +563,7 @@ describe('MediaSessionInstance', () => { it('newCall caller triggers DM lookup when roomId is still null', async () => { mockGetDMSubscriptionByUsername.mockResolvedValue({ rid: 'from-db' } as any); - mediaSessionInstance.init('user-1'); + await mediaSessionInstance.init('user-1'); const session = createdSessions[0]; const newCallHandler = session.on.mock.calls.find((c: string[]) => c[0] === 'newCall')?.[1] as (p: { call: IClientMediaCall; @@ -527,7 +584,7 @@ describe('MediaSessionInstance', () => { }); it('newCall caller skips DM lookup when roomId already set', async () => { - mediaSessionInstance.init('user-1'); + await mediaSessionInstance.init('user-1'); mockUseCallStoreGetState.mockReturnValue({ reset: mockCallStoreReset, setCall: jest.fn(), @@ -558,7 +615,7 @@ describe('MediaSessionInstance', () => { }); it('newCall caller skips DM lookup for SIP contact', async () => { - mediaSessionInstance.init('user-1'); + await mediaSessionInstance.init('user-1'); const session = createdSessions[0]; const newCallHandler = session.on.mock.calls.find((c: string[]) => c[0] === 'newCall')?.[1] as (p: { call: IClientMediaCall; @@ -580,7 +637,7 @@ describe('MediaSessionInstance', () => { it('answerCall resolves roomId from DM for non-SIP callee', async () => { mockGetDMSubscriptionByUsername.mockResolvedValue({ rid: 'dm-rid' } as any); - mediaSessionInstance.init('user-1'); + await mediaSessionInstance.init('user-1'); const session = createdSessions[0]; const mainCall = { callId: 'call-ans', @@ -596,7 +653,7 @@ describe('MediaSessionInstance', () => { }); it('answerCall skips DM lookup for SIP contact', async () => { - mediaSessionInstance.init('user-1'); + await mediaSessionInstance.init('user-1'); const session = createdSessions[0]; const mainCall = { callId: 'call-sip', diff --git a/app/lib/services/voip/MediaSessionInstance.ts b/app/lib/services/voip/MediaSessionInstance.ts index 79e1c4ed70e..6a7183e74f8 100644 --- a/app/lib/services/voip/MediaSessionInstance.ts +++ b/app/lib/services/voip/MediaSessionInstance.ts @@ -5,6 +5,7 @@ import { type IClientMediaCall, type CallActorType, type MediaSignalingSession, + type ServerMediaSignal, type WebRTCProcessorConfig } from '@rocket.chat/media-signaling'; import RNCallKeep from 'react-native-callkeep'; @@ -23,6 +24,7 @@ import type { ISubscription, TSubscriptionModel } from '../../../definitions'; import { getDMSubscriptionByUsername } from '../../database/services/Subscription'; import { getUidDirectMessage } from '../../methods/helpers/helpers'; import { requestPhoneStatePermission } from '../../methods/voipPhoneStatePermission'; +import { mediaCallsStateSignals } from '../restApi'; class MediaSessionInstance { private iceServers: IceServer[] = []; @@ -33,7 +35,38 @@ class MediaSessionInstance { private storeTimeoutUnsubscribe: (() => void) | null = null; private storeIceServersUnsubscribe: (() => void) | null = null; - public init(userId: string): void { + private tryAnswerIfNativeAcceptedNotification(signal: ServerMediaSignal): void { + const { call, nativeAcceptedCallId } = useCallStore.getState(); + if ( + signal.type === 'notification' && + signal.notification === 'accepted' && + signal.signedContractId === getUniqueIdSync() && + nativeAcceptedCallId === signal.callId && + call == null + ) { + this.answerCall(signal.callId).catch(error => { + console.error('[VoIP] Error answering call on notification/accepted:', error); + }); + } + } + + /** Replays `media-calls.stateSignals`. Used on init and when native accept raced ahead of `nativeAcceptedCallId`. Caller must ensure SDK/session host matches the call (see MediaCallEvents host gate). `tryAnswerIfNativeAcceptedNotification` may also fire from the stream-notify-user path; `answerCall` is idempotent. */ + public async applyRestStateSignals(): Promise { + if (!this.instance) { + return; + } + try { + const { signals } = await mediaCallsStateSignals(getUniqueIdSync()); + for (const signal of signals) { + this.instance.processSignal(signal); + this.tryAnswerIfNativeAcceptedNotification(signal); + } + } catch (error) { + console.error('[VoIP] Failed to fetch or apply REST state signals:', error); + } + } + + public async init(userId: string): Promise { this.reset(); registerGlobals(); @@ -51,6 +84,13 @@ class MediaSessionInstance { sdk.methodCall('stream-notify-user', `${userId}/media-calls`, JSON.stringify(signal)); }); this.instance = mediaSessionStore.getInstance(userId); + + if (!this.instance) { + throw new Error('Failed to create media session instance'); + } + + await this.applyRestStateSignals(); + this.mediaSessionStoreChangeUnsubscribe = mediaSessionStore.onChange(() => { this.instance = mediaSessionStore.getInstance(userId); }); @@ -68,21 +108,7 @@ class MediaSessionInstance { console.log('🤙 [VoIP] Processed signal:', signal); - // Answer when native already accepted and stream matches device contract + callId. - const storeSlice = useCallStore.getState(); - const { call, nativeAcceptedCallId } = storeSlice; - - if ( - signal.type === 'notification' && - signal.notification === 'accepted' && - signal.signedContractId === getUniqueIdSync() && - nativeAcceptedCallId === signal.callId && - call == null - ) { - this.answerCall(signal.callId).catch(error => { - console.error('[VoIP] Error answering call on notification/accepted:', error); - }); - } + this.tryAnswerIfNativeAcceptedNotification(signal as ServerMediaSignal); }); this.instance?.on('newCall', ({ call }: { call: IClientMediaCall }) => { diff --git a/app/lib/services/voip/MediaSessionStore.ts b/app/lib/services/voip/MediaSessionStore.ts index f59cda874a1..ede6e89063a 100644 --- a/app/lib/services/voip/MediaSessionStore.ts +++ b/app/lib/services/voip/MediaSessionStore.ts @@ -67,8 +67,7 @@ class MediaSessionStore extends Emitter<{ change: void }> { randomStringFactory, logger: new MediaCallLogger(), features: ['audio'], - mobileDeviceId, - autoSync: true + mobileDeviceId }); this.change(); diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js index 2bbeb4c9be0..692e3bb8159 100644 --- a/app/sagas/deepLinking.js +++ b/app/sagas/deepLinking.js @@ -15,7 +15,7 @@ import database from '../lib/database'; import { getServerById } from '../lib/database/services/Server'; import { canOpenRoom } from '../lib/methods/canOpenRoom'; import { getServerInfo } from '../lib/methods/getServerInfo'; -import { emitter, getUidDirectMessage } from '../lib/methods/helpers'; +import { emitter, getUidDirectMessage, normalizeDeepLinkingServerHost } 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'; @@ -196,20 +196,8 @@ const handleOpen = function* handleOpen({ params }) { } // If there's host, continue - if (!/^(http|https)/.test(host)) { - if (/^localhost(:\d+)?/.test(host)) { - host = `http://${host}`; - } else { - host = `https://${host}`; - } - } else { - // Notification should always come from https - host = host.replace('http://', 'https://'); - } - // remove last "/" from host - if (host.slice(-1) === '/') { - host = host.slice(0, host.length - 1); - } + host = normalizeDeepLinkingServerHost(host); + params.host = host; const [server, user] = yield all([ UserPreferences.getString(CURRENT_SERVER), @@ -305,9 +293,11 @@ const handleClickCallPush = function* handleClickCallPush({ params }) { return; } - if (host.slice(-1) === '/') { - host = host.slice(0, host.length - 1); + host = normalizeDeepLinkingServerHost(host); + if (!host) { + return; } + params.host = host; const [server, user] = yield all([ UserPreferences.getString(CURRENT_SERVER), diff --git a/ios/Libraries/VoipService.swift b/ios/Libraries/VoipService.swift index f477a9a743c..c0376a0befb 100644 --- a/ios/Libraries/VoipService.swift +++ b/ios/Libraries/VoipService.swift @@ -60,11 +60,6 @@ public final class VoipService: NSObject { /// cleared when that call's DDP accept finishes or another exit path runs for that `callId`. private static var nativeAcceptHandledCallIds = Set() - private enum VoipMediaCallAnswerKind { - case accept - case reject - } - // MARK: - Static Methods (Called from VoipModule.mm and AppDelegate) /// Registers for VoIP push notifications via PushKit @@ -413,40 +408,6 @@ public final class VoipService: NSObject { ddpRegistry.stopAllClients() } - // MARK: - Native DDP signaling (accept / reject) - - /// `contractId` must match JS `getUniqueIdSync()` from react-native-device-info (`DeviceUID` on iOS; Android uses `Settings.Secure.ANDROID_ID` in VoipNotification). - private static func buildMediaCallAnswerParams(payload: VoipPayload, kind: VoipMediaCallAnswerKind) -> [Any]? { - let credentialStorage = Storage() - guard let credentials = credentialStorage.getCredentials(server: payload.host.removeTrailingSlash()) else { - #if DEBUG - print("[\(TAG)] Missing credentials, cannot build media-call answer params for \(payload.callId)") - #endif - stopDDPClientInternal(callId: payload.callId) - return nil - } - - var signal: [String: Any] = [ - "callId": payload.callId, - "contractId": DeviceUID.uid(), - "type": "answer", - "answer": kind == .accept ? "accept" : "reject" - ] - if kind == .accept { - signal["supportedFeatures"] = ["audio"] - } - - guard - let signalData = try? JSONSerialization.data(withJSONObject: signal), - let signalString = String(data: signalData, encoding: .utf8) - else { - stopDDPClientInternal(callId: payload.callId) - return nil - } - - return ["\(credentials.userId)/media-calls", signalString] - } - /// Native DDP accept when the user answers via CallKit (parity with Android `VoipNotification.handleAcceptAction`). private static func handleNativeAccept(payload: VoipPayload) { if nativeAcceptHandledCallIds.contains(payload.callId) { @@ -506,36 +467,28 @@ public final class VoipService: NSObject { } } - guard let client = ddpRegistry.clientFor(callId: payload.callId) else { + guard let api = API(server: payload.host) else { #if DEBUG - print("[\(TAG)] Native DDP client unavailable for accept \(payload.callId); relying on JS") + print("[\(TAG)] Failed to create API for host: \(payload.host)") #endif finishAccept(false) return } - guard let params = buildMediaCallAnswerParams(payload: payload, kind: .accept) else { - finishAccept(false) - return - } - - if ddpRegistry.isLoggedIn(callId: payload.callId) { - client.callMethod("stream-notify-user", params: params) { success in - #if DEBUG - print("[\(TAG)] Native accept signal result for \(payload.callId): \(success)") - #endif - DispatchQueue.main.async { finishAccept(success) } - } - } else { - client.queueMethodCall("stream-notify-user", params: params) { success in - #if DEBUG - print("[\(TAG)] Queued native accept signal result for \(payload.callId): \(success)") - #endif - DispatchQueue.main.async { finishAccept(success) } + api.fetch(request: MediaCallsAnswerRequest( + callId: payload.callId, + contractId: DeviceUID.uid(), + answer: "accept", + supportedFeatures: ["audio"] + )) { result in + DispatchQueue.main.async { + switch result { + case .resource(let response) where response.success: + finishAccept(true) + default: + finishAccept(false) + } } - #if DEBUG - print("[\(TAG)] Queued native accept signal for \(payload.callId)") - #endif } } @@ -556,55 +509,30 @@ public final class VoipService: NSObject { // End the just-reported CallKit call immediately (reason 2 = unanswered / declined). RNCallKeep.endCall(withUUID: payload.callId, reason: 2) - // Send reject signal via native DDP if available, otherwise queue it. - if ddpRegistry.isLoggedIn(callId: payload.callId) { - sendRejectSignal(payload: payload) - } else { - queueRejectSignal(payload: payload) - } + // Send reject signal via REST + reject(payload: payload) #if DEBUG print("[\(TAG)] Rejected busy call \(payload.callId) — user already on a call") #endif } - private static func sendRejectSignal(payload: VoipPayload) { - guard let client = ddpRegistry.clientFor(callId: payload.callId) else { + private static func reject(payload: VoipPayload) { + guard let api = API(server: payload.host) else { #if DEBUG - print("[\(TAG)] Native DDP client unavailable, cannot send reject for \(payload.callId)") - #endif - return - } - - guard let params = buildMediaCallAnswerParams(payload: payload, kind: .reject) else { - return - } - - client.callMethod("stream-notify-user", params: params) { success in - #if DEBUG - print("[\(TAG)] Native reject signal result for \(payload.callId): \(success)") + print("[\(TAG)] Failed to create API for reject: \(payload.host)") #endif stopDDPClientInternal(callId: payload.callId) - } - } - - private static func queueRejectSignal(payload: VoipPayload) { - guard let client = ddpRegistry.clientFor(callId: payload.callId) else { - #if DEBUG - print("[\(TAG)] Native DDP client unavailable, cannot queue reject for \(payload.callId)") - #endif - return - } - - guard let params = buildMediaCallAnswerParams(payload: payload, kind: .reject) else { return } - client.queueMethodCall("stream-notify-user", params: params) { success in - #if DEBUG - print("[\(TAG)] Queued native reject signal result for \(payload.callId): \(success)") - #endif - stopDDPClientInternal(callId: payload.callId) + api.fetch(request: MediaCallsAnswerRequest( + callId: payload.callId, + contractId: DeviceUID.uid(), + answer: "reject", + supportedFeatures: nil + )) { _ in + self.stopDDPClientInternal(callId: payload.callId) } } @@ -666,15 +594,12 @@ public final class VoipService: NSObject { return } + let endedCallId = observedCall.payload.callId observedIncomingCalls.removeValue(forKey: call.uuid) - cancelIncomingCallTimeout(for: observedCall.payload.callId) - clearNativeAcceptDedupe(for: observedCall.payload.callId) + cancelIncomingCallTimeout(for: endedCallId) + clearNativeAcceptDedupe(for: endedCallId) - let endedCallId = observedCall.payload.callId - if ddpRegistry.isLoggedIn(callId: endedCallId) { - sendRejectSignal(payload: observedCall.payload) - } else { - queueRejectSignal(payload: observedCall.payload) - } + RNCallKeep.endCall(withUUID: endedCallId, reason: 3) + reject(payload: observedCall.payload) } } diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e11f65c0f18..d3561367b0f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -228,8 +228,8 @@ PODS: - hermes-engine/Pre-built (= 0.81.5) - hermes-engine/Pre-built (0.81.5) - JitsiWebRTC (124.0.2) - - libavif/core (0.11.1) - - libavif/libdav1d (0.11.1): + - libavif/core (1.0.0) + - libavif/libdav1d (1.0.0): - libavif/core - libdav1d (>= 0.6.0) - libdav1d (1.2.0) @@ -3457,9 +3457,9 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - SDWebImage (5.21.5): - - SDWebImage/Core (= 5.21.5) - - SDWebImage/Core (5.21.5) + - SDWebImage (5.21.7): + - SDWebImage/Core (= 5.21.7) + - SDWebImage/Core (5.21.7) - SDWebImageAVIFCoder (0.11.1): - libavif/core (>= 0.11.0) - SDWebImage (~> 5.10) @@ -3950,7 +3950,7 @@ SPEC CHECKSUMS: GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 hermes-engine: 9f4dfe93326146a1c99eb535b1cb0b857a3cd172 JitsiWebRTC: b47805ab5668be38e7ee60e2258f49badfe8e1d0 - libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7 + libavif: 5f8e715bea24debec477006f21ef9e95432e254d libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 MobileCrypto: 41cd66d5588e979cf95d1ff6ae2eec88eb284ef1 @@ -4054,7 +4054,7 @@ SPEC CHECKSUMS: RNSVG: 94a1be05fab4043354bcf7104f0f9b0e2231ef05 RNTrueSheet: 53f29088da313dabff8b81d7c4d52afd8e609cfa RNWorklets: ab618bf7d1c7fd2cb793b9f0f39c3e29274b3ebf - SDWebImage: e9c98383c7572d713c1a0d7dd2783b10599b9838 + SDWebImage: e9fc87c1aab89a8ab1bbd74eba378c6f53be8abf SDWebImageAVIFCoder: afe194a084e851f70228e4be35ef651df0fc5c57 SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj index a0a449128d1..01c53cd5f2d 100644 --- a/ios/RocketChatRN.xcodeproj/project.pbxproj +++ b/ios/RocketChatRN.xcodeproj/project.pbxproj @@ -273,6 +273,7 @@ 1EFEB5982493B6640072EDC0 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EFEB5972493B6640072EDC0 /* NotificationService.swift */; }; 1EFEB59C2493B6640072EDC0 /* NotificationService.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 1EFEB5952493B6640072EDC0 /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 24A2AEF2383D44B586D31C01 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 06BB44DD4855498082A744AD /* libz.tbd */; }; + 28C72E03466F19380406FFF4 /* Pods_defaults_RocketChatRN.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BA6E8FC7989FF1E93670E634 /* Pods_defaults_RocketChatRN.framework */; }; 3F56D232A9EBA1C9C749F15D /* SecureStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B215A42CFB843397273C7EA /* SecureStorage.m */; }; 3F56D232A9EBA1C9C749F15E /* MMKVBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9B215A44CFB843397273C7EC /* MMKVBridge.mm */; }; 3F56D232A9EBA1C9C749F15F /* MMKVBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9B215A44CFB843397273C7EC /* MMKVBridge.mm */; }; @@ -288,12 +289,13 @@ 66C2701B2EBBCB570062725F /* MMKVKeyManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 66C2701A2EBBCB570062725F /* MMKVKeyManager.mm */; }; 66C2701C2EBBCB570062725F /* MMKVKeyManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 66C2701A2EBBCB570062725F /* MMKVKeyManager.mm */; }; 66C2701D2EBBCB570062725F /* MMKVKeyManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 66C2701A2EBBCB570062725F /* MMKVKeyManager.mm */; }; - 69919AD6FCE3D07C09A558C7 /* Pods_defaults_Rocket_Chat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D7349CC22903F52951D3D908 /* Pods_defaults_Rocket_Chat.framework */; }; + 782E0520ED599FF8DDC2318F /* Pods_defaults_Rocket_Chat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88ED4E003393D3A9256F4EB3 /* Pods_defaults_Rocket_Chat.framework */; }; 79D8C97F8CE2EC1B6882826B /* SecureStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B215A42CFB843397273C7EA /* SecureStorage.m */; }; 7A0000012F1BAFA700B6B4BD /* VoipService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0000032F1BAFA700B6B4BD /* VoipService.swift */; }; 7A0000022F1BAFA700B6B4BD /* VoipModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7A0000042F1BAFA700B6B4BD /* VoipModule.mm */; }; 7A0000052F1BAFA700B6B4BD /* VoipService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0000032F1BAFA700B6B4BD /* VoipService.swift */; }; 7A0000062F1BAFA700B6B4BD /* VoipModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7A0000042F1BAFA700B6B4BD /* VoipModule.mm */; }; + 7A0000072F1BAFA700B6B4BD /* MediaCallsAnswerRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F98EE33D91A93DF8E69F34 /* MediaCallsAnswerRequest.swift */; }; 7A006F14229C83B600803143 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7A006F13229C83B600803143 /* GoogleService-Info.plist */; }; 7A0129D42C6E8EC800F84A97 /* ShareRocketChatRN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0129D22C6E8B5900F84A97 /* ShareRocketChatRN.swift */; }; 7A0129D62C6E8F0700F84A97 /* ShareRocketChatRN.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = 1EC6AD6022CBA20C00A41C61 /* ShareRocketChatRN.entitlements */; }; @@ -307,6 +309,7 @@ 7A1B58452F5F63DB002A6BDE /* AppDelegate+Voip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1B58432F5F63DB002A6BDE /* AppDelegate+Voip.swift */; }; 7A3704562F7DB36E009085FC /* VoipPerCallDdpRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3704552F7DB36E009085FC /* VoipPerCallDdpRegistry.swift */; }; 7A3704572F7DB36E009085FC /* VoipPerCallDdpRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3704552F7DB36E009085FC /* VoipPerCallDdpRegistry.swift */; }; + 7A37D6FF2F896C360095EBA1 /* MediaCallsAnswerRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F98EE33D91A93DF8E69F34 /* MediaCallsAnswerRequest.swift */; }; 7A610CD227ECE38100B8ABDD /* custom.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7A610CD127ECE38100B8ABDD /* custom.ttf */; }; 7A610CD427ECE38100B8ABDD /* custom.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7A610CD127ECE38100B8ABDD /* custom.ttf */; }; 7A610CD527ECE38100B8ABDD /* custom.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7A610CD127ECE38100B8ABDD /* custom.ttf */; }; @@ -367,15 +370,15 @@ 7ACFE7DA2DDE48760090D9BC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ACFE7D82DDE48760090D9BC /* AppDelegate.swift */; }; 7AE10C0628A59530003593CB /* Inter.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7AE10C0528A59530003593CB /* Inter.ttf */; }; 7AE10C0828A59530003593CB /* Inter.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7AE10C0528A59530003593CB /* Inter.ttf */; }; + 7C91FF6756C0285FA3647829 /* MediaCallsAnswerRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F98EE33D91A93DF8E69F34 /* MediaCallsAnswerRequest.swift */; }; 85160EB6C143E0493FE5F014 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194D9A8897F4A486C2C6F89A /* ExpoModulesProvider.swift */; }; - 97A63D1D5CCF6E4EBA3FF24C /* Pods_defaults_NotificationService.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A084DF6CE624FA33266EAF2 /* Pods_defaults_NotificationService.framework */; }; A2C6E2DD38F8BEE19BFB2E1D /* SecureStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B215A42CFB843397273C7EA /* SecureStorage.m */; }; A48B46D92D3FFBD200945489 /* A11yFlowModule.m in Sources */ = {isa = PBXBuildFile; fileRef = A48B46D82D3FFBD200945489 /* A11yFlowModule.m */; }; A48B46DA2D3FFBD200945489 /* A11yFlowModule.m in Sources */ = {isa = PBXBuildFile; fileRef = A48B46D82D3FFBD200945489 /* A11yFlowModule.m */; }; AE692FD072A44EA955D0C0D8 /* DDPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BE2F3DC02A264F204E3EDE3 /* DDPClient.swift */; }; BC404914E86821389EEB543D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 391C4F7AA7023CD41EEBD106 /* ExpoModulesProvider.swift */; }; - BFAC11E0D1D11F6A518C17B3 /* Pods_defaults_RocketChatRN.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0A213823B576B6F1D91B8F07 /* Pods_defaults_RocketChatRN.framework */; }; CE4453310C9A08AB0DAC2307 /* DDPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BE2F3DC02A264F204E3EDE3 /* DDPClient.swift */; }; + D83EEDB28B6D2AE34CF583F6 /* Pods_defaults_NotificationService.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D5152D3FEFAE1AF8F5E9CCCC /* Pods_defaults_NotificationService.framework */; }; DD2BA30A89E64F189C2C24AC /* libWatermelonDB.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BA7E862283664608B3894E34 /* libWatermelonDB.a */; }; /* End PBXBuildFile section */ @@ -476,7 +479,7 @@ /* Begin PBXFileReference section */ 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file; path = main.jsbundle; sourceTree = ""; }; 06BB44DD4855498082A744AD /* libz.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; - 0A213823B576B6F1D91B8F07 /* Pods_defaults_RocketChatRN.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_defaults_RocketChatRN.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 1238492C71CF27C87CB2E976 /* Pods-defaults-NotificationService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-NotificationService.release.xcconfig"; path = "Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService.release.xcconfig"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* Rocket.Chat Experimental.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Rocket.Chat Experimental.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = RocketChatRN/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = RocketChatRN/Info.plist; sourceTree = ""; }; @@ -623,12 +626,10 @@ 1EFEB5972493B6640072EDC0 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; 1EFEB5992493B6640072EDC0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 1EFEB5A12493B67D0072EDC0 /* NotificationService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationService.entitlements; sourceTree = ""; }; - 2213748BB77228EBAE678F51 /* Pods-defaults-NotificationService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-NotificationService.release.xcconfig"; path = "Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService.release.xcconfig"; sourceTree = ""; }; + 2CF16884F9353D83C6EFBE81 /* Pods-defaults-Rocket.Chat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-Rocket.Chat.release.xcconfig"; path = "Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat.release.xcconfig"; sourceTree = ""; }; 391C4F7AA7023CD41EEBD106 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-defaults-Rocket.Chat/ExpoModulesProvider.swift"; sourceTree = ""; }; - 3A9D9EA04B1E3C75464811F5 /* Pods-defaults-RocketChatRN.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-RocketChatRN.release.xcconfig"; path = "Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN.release.xcconfig"; sourceTree = ""; }; - 3B42CC37681BAA6DD62CFF89 /* Pods-defaults-Rocket.Chat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-Rocket.Chat.release.xcconfig"; path = "Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat.release.xcconfig"; sourceTree = ""; }; + 45AECABFC0DD015C46B1D029 /* Pods-defaults-RocketChatRN.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-RocketChatRN.debug.xcconfig"; path = "Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN.debug.xcconfig"; sourceTree = ""; }; 45D5C142B655F8EFD006792C /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-defaults-RocketChatRN/ExpoModulesProvider.swift"; sourceTree = ""; }; - 5A084DF6CE624FA33266EAF2 /* Pods_defaults_NotificationService.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_defaults_NotificationService.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 60B2A6A31FC4588700BD58E5 /* RocketChatRN.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = RocketChatRN.entitlements; path = RocketChatRN/RocketChatRN.entitlements; sourceTree = ""; }; 65AD38362BFBDF4A00271B39 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 65B9A7192AFC24190088956F /* ringtone.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = ringtone.mp3; sourceTree = ""; }; @@ -654,18 +655,21 @@ 7ACD4853222860DE00442C55 /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; 7ACFE7D82DDE48760090D9BC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AE10C0528A59530003593CB /* Inter.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Inter.ttf; sourceTree = ""; }; + 83F98EE33D91A93DF8E69F34 /* MediaCallsAnswerRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaCallsAnswerRequest.swift; sourceTree = ""; }; + 88ED4E003393D3A9256F4EB3 /* Pods_defaults_Rocket_Chat.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_defaults_Rocket_Chat.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9B215A42CFB843397273C7EA /* SecureStorage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = SecureStorage.m; sourceTree = ""; }; 9B215A44CFB843397273C7EC /* MMKVBridge.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = MMKVBridge.mm; path = Shared/RocketChat/MMKVBridge.mm; sourceTree = ""; }; 9BE2F3DC02A264F204E3EDE3 /* DDPClient.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DDPClient.swift; sourceTree = ""; }; + 9E19E897C464DE3A91E1DAFD /* Pods-defaults-NotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-NotificationService.debug.xcconfig"; path = "Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService.debug.xcconfig"; sourceTree = ""; }; + 9F66BEF98C5CF2CFDD4B4C87 /* Pods-defaults-RocketChatRN.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-RocketChatRN.release.xcconfig"; path = "Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN.release.xcconfig"; sourceTree = ""; }; A48B46D72D3FFBD200945489 /* A11yFlowModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = A11yFlowModule.h; sourceTree = ""; }; A48B46D82D3FFBD200945489 /* A11yFlowModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = A11yFlowModule.m; sourceTree = ""; }; - B1693D1B84D0486251A22988 /* Pods-defaults-RocketChatRN.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-RocketChatRN.debug.xcconfig"; path = "Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN.debug.xcconfig"; sourceTree = ""; }; B179038FDD7AAF285047814B /* SecureStorage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = SecureStorage.h; sourceTree = ""; }; B37C79D9BD0742CE936B6982 /* libc++.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; + BA6E8FC7989FF1E93670E634 /* Pods_defaults_RocketChatRN.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_defaults_RocketChatRN.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BA7E862283664608B3894E34 /* libWatermelonDB.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libWatermelonDB.a; sourceTree = ""; }; - CE0F13E24E685F7597B9E7B0 /* Pods-defaults-Rocket.Chat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-Rocket.Chat.debug.xcconfig"; path = "Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat.debug.xcconfig"; sourceTree = ""; }; - D7349CC22903F52951D3D908 /* Pods_defaults_Rocket_Chat.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_defaults_Rocket_Chat.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - DC3EC396A755975FB1EB14EB /* Pods-defaults-NotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-NotificationService.debug.xcconfig"; path = "Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService.debug.xcconfig"; sourceTree = ""; }; + D5152D3FEFAE1AF8F5E9CCCC /* Pods_defaults_NotificationService.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_defaults_NotificationService.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E604ABC16022AB0B4819037E /* Pods-defaults-Rocket.Chat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-Rocket.Chat.debug.xcconfig"; path = "Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -686,7 +690,7 @@ 7ACD4897222860DE00442C55 /* JavaScriptCore.framework in Frameworks */, 24A2AEF2383D44B586D31C01 /* libz.tbd in Frameworks */, DD2BA30A89E64F189C2C24AC /* libWatermelonDB.a in Frameworks */, - BFAC11E0D1D11F6A518C17B3 /* Pods_defaults_RocketChatRN.framework in Frameworks */, + 28C72E03466F19380406FFF4 /* Pods_defaults_RocketChatRN.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -708,7 +712,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 97A63D1D5CCF6E4EBA3FF24C /* Pods_defaults_NotificationService.framework in Frameworks */, + D83EEDB28B6D2AE34CF583F6 /* Pods_defaults_NotificationService.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -729,7 +733,7 @@ 7AAB3E3D257E6A6E00707CF6 /* JavaScriptCore.framework in Frameworks */, 7AAB3E3E257E6A6E00707CF6 /* libz.tbd in Frameworks */, 7AAB3E3F257E6A6E00707CF6 /* libWatermelonDB.a in Frameworks */, - 69919AD6FCE3D07C09A558C7 /* Pods_defaults_Rocket_Chat.framework in Frameworks */, + 782E0520ED599FF8DDC2318F /* Pods_defaults_Rocket_Chat.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -884,6 +888,7 @@ 1E2F61622512954500871711 /* Requests */ = { isa = PBXGroup; children = ( + 83F98EE33D91A93DF8E69F34 /* MediaCallsAnswerRequest.swift */, 1E2F61652512958900871711 /* Push.swift */, 1E598AE825151A63002BDFBD /* SendMessage.swift */, ); @@ -1170,12 +1175,12 @@ 7AC2B09613AA7C3FEBAC9F57 /* Pods */ = { isa = PBXGroup; children = ( - DC3EC396A755975FB1EB14EB /* Pods-defaults-NotificationService.debug.xcconfig */, - 2213748BB77228EBAE678F51 /* Pods-defaults-NotificationService.release.xcconfig */, - CE0F13E24E685F7597B9E7B0 /* Pods-defaults-Rocket.Chat.debug.xcconfig */, - 3B42CC37681BAA6DD62CFF89 /* Pods-defaults-Rocket.Chat.release.xcconfig */, - B1693D1B84D0486251A22988 /* Pods-defaults-RocketChatRN.debug.xcconfig */, - 3A9D9EA04B1E3C75464811F5 /* Pods-defaults-RocketChatRN.release.xcconfig */, + 9E19E897C464DE3A91E1DAFD /* Pods-defaults-NotificationService.debug.xcconfig */, + 1238492C71CF27C87CB2E976 /* Pods-defaults-NotificationService.release.xcconfig */, + E604ABC16022AB0B4819037E /* Pods-defaults-Rocket.Chat.debug.xcconfig */, + 2CF16884F9353D83C6EFBE81 /* Pods-defaults-Rocket.Chat.release.xcconfig */, + 45AECABFC0DD015C46B1D029 /* Pods-defaults-RocketChatRN.debug.xcconfig */, + 9F66BEF98C5CF2CFDD4B4C87 /* Pods-defaults-RocketChatRN.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -1265,9 +1270,9 @@ 7ACD4853222860DE00442C55 /* JavaScriptCore.framework */, B37C79D9BD0742CE936B6982 /* libc++.tbd */, 06BB44DD4855498082A744AD /* libz.tbd */, - 5A084DF6CE624FA33266EAF2 /* Pods_defaults_NotificationService.framework */, - D7349CC22903F52951D3D908 /* Pods_defaults_Rocket_Chat.framework */, - 0A213823B576B6F1D91B8F07 /* Pods_defaults_RocketChatRN.framework */, + D5152D3FEFAE1AF8F5E9CCCC /* Pods_defaults_NotificationService.framework */, + 88ED4E003393D3A9256F4EB3 /* Pods_defaults_Rocket_Chat.framework */, + BA6E8FC7989FF1E93670E634 /* Pods_defaults_RocketChatRN.framework */, ); name = Frameworks; sourceTree = ""; @@ -1287,7 +1292,7 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "RocketChatRN" */; buildPhases = ( - DF3F7A3CBF3F944B68A9C061 /* [CP] Check Pods Manifest.lock */, + A9FDA76149CB53BBE6B86918 /* [CP] Check Pods Manifest.lock */, 06C10D4F29CD7532492AD29E /* [Expo] Configure project */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, @@ -1297,8 +1302,8 @@ 1E1EA8082326CCE300E22452 /* ShellScript */, 1ED0389C2B507B4F00C007D4 /* Embed Watch Content */, 7AAE9EB32891A0D20024F559 /* Upload source maps to Bugsnag */, - 36E1E4EA3CF1B40425C9E067 /* [CP] Embed Pods Frameworks */, - 04152DE991B5700DB05F1885 /* [CP] Copy Pods Resources */, + 4BC87B3B29B047DFDA0B6E90 /* [CP] Embed Pods Frameworks */, + 779AAF49493D47C970231A22 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1366,12 +1371,12 @@ isa = PBXNativeTarget; buildConfigurationList = 1EFEB5A02493B6640072EDC0 /* Build configuration list for PBXNativeTarget "NotificationService" */; buildPhases = ( - D564371D5C759573F0753D22 /* [CP] Check Pods Manifest.lock */, + DCBFFFA973306E6A320A3A37 /* [CP] Check Pods Manifest.lock */, 86A998705576AFA7CE938617 /* [Expo] Configure project */, 1EFEB5912493B6640072EDC0 /* Sources */, 1EFEB5922493B6640072EDC0 /* Frameworks */, 1EFEB5932493B6640072EDC0 /* Resources */, - 19FB95CAFFC54B349F10063E /* [CP] Copy Pods Resources */, + 89E2EFC06A15837E14EBD46A /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1386,7 +1391,7 @@ isa = PBXNativeTarget; buildConfigurationList = 7AAB3E4F257E6A6E00707CF6 /* Build configuration list for PBXNativeTarget "Rocket.Chat" */; buildPhases = ( - 360B0CB1D9593B8990FFC314 /* [CP] Check Pods Manifest.lock */, + AF84C79830C1EF9B0C90CE13 /* [CP] Check Pods Manifest.lock */, 6319FBDD06EF0030B48AD389 /* [Expo] Configure project */, 7AAB3E14257E6A6E00707CF6 /* Sources */, 7AAB3E32257E6A6E00707CF6 /* Frameworks */, @@ -1396,8 +1401,8 @@ 7A55BE3C2F1131C000D8744D /* ShellScript */, 1ED1ECE32B8699DD00F6620C /* Embed Watch Content */, 7A55BE3D2F11320C00D8744D /* Upload source maps to Bugsnag */, - 7F8EB9489218C65E04132D08 /* [CP] Embed Pods Frameworks */, - 0A9A264DB182D9B961EC0897 /* [CP] Copy Pods Resources */, + F8FA5EB1C05179679A1B0213 /* [CP] Embed Pods Frameworks */, + 6C507C5DC028E270B40EF89D /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1571,85 +1576,68 @@ shellPath = /bin/sh; shellScript = "export EXTRA_PACKAGER_ARGS=\"--sourcemap-output $TMPDIR/$(md5 -qs \"$CONFIGURATION_BUILD_DIR\")-main.jsbundle.map\"\nexport NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; }; - 04152DE991B5700DB05F1885 /* [CP] Copy Pods Resources */ = { + 06C10D4F29CD7532492AD29E /* [Expo] Configure project */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/BugsnagReactNative/Bugsnag.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/EXNotifications/ExpoNotifications_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/ExpoDevice/ExpoDevice_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore_Privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension/FirebaseCoreExtension_Privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal/FirebaseCoreInternal_Privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCrashlytics/FirebaseCrashlytics_Privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations/FirebaseInstallations_Privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport/GoogleDataTransport_Privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities_Privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_Privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift/Promises_Privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo/RNDeviceInfoPrivacyInfo.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/RNImageCropPickerPrivacyInfo.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController/TOCropViewControllerBundle.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb_Privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/react-native-cameraroll/RNCameraRollPrivacyInfo.bundle", + "$(SRCROOT)/.xcode.env", + "$(SRCROOT)/.xcode.env.local", + "$(SRCROOT)/RocketChatRN/RocketChatRN.entitlements", + "$(SRCROOT)/Pods/Target Support Files/Pods-defaults-RocketChatRN/expo-configure-project.sh", + ); + name = "[Expo] Configure project"; + outputFileListPaths = ( ); - name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Bugsnag.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoApplication_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoNotifications_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoDevice_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCore_Privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreExtension_Privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreInternal_Privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCrashlytics_Privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseInstallations_Privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleDataTransport_Privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleUtilities_Privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FBLPromises_Privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Promises_Privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNDeviceInfoPrivacyInfo.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNImageCropPickerPrivacyInfo.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SDWebImage.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/TOCropViewControllerBundle.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/nanopb_Privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCameraRollPrivacyInfo.bundle", + "$(SRCROOT)/Pods/Target Support Files/Pods-defaults-RocketChatRN/ExpoModulesProvider.swift", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-resources.sh\"\n"; + shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-defaults-RocketChatRN/expo-configure-project.sh\"\n"; + }; + 1E1EA8082326CCE300E22452 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"Target architectures: $ARCHS\"\n\nAPP_PATH=\"${TARGET_BUILD_DIR}/${WRAPPER_NAME}\"\n\nfind \"$APP_PATH\" -name '*.framework' -type d | while read -r FRAMEWORK\ndo\nFRAMEWORK_EXECUTABLE_NAME=$(defaults read \"$FRAMEWORK/Info.plist\" CFBundleExecutable)\nFRAMEWORK_EXECUTABLE_PATH=\"$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME\"\necho \"Executable is $FRAMEWORK_EXECUTABLE_PATH\"\necho $(lipo -info \"$FRAMEWORK_EXECUTABLE_PATH\")\n\nFRAMEWORK_TMP_PATH=\"$FRAMEWORK_EXECUTABLE_PATH-tmp\"\n\n# remove simulator's archs if location is not simulator's directory\ncase \"${TARGET_BUILD_DIR}\" in\n*\"iphonesimulator\")\necho \"No need to remove archs\"\n;;\n*)\nif $(lipo \"$FRAMEWORK_EXECUTABLE_PATH\" -verify_arch \"i386\") ; then\nlipo -output \"$FRAMEWORK_TMP_PATH\" -remove \"i386\" \"$FRAMEWORK_EXECUTABLE_PATH\"\necho \"i386 architecture removed\"\nrm \"$FRAMEWORK_EXECUTABLE_PATH\"\nmv \"$FRAMEWORK_TMP_PATH\" \"$FRAMEWORK_EXECUTABLE_PATH\"\nfi\nif $(lipo \"$FRAMEWORK_EXECUTABLE_PATH\" -verify_arch \"x86_64\") ; then\nlipo -output \"$FRAMEWORK_TMP_PATH\" -remove \"x86_64\" \"$FRAMEWORK_EXECUTABLE_PATH\"\necho \"x86_64 architecture removed\"\nrm \"$FRAMEWORK_EXECUTABLE_PATH\"\nmv \"$FRAMEWORK_TMP_PATH\" \"$FRAMEWORK_EXECUTABLE_PATH\"\nfi\n;;\nesac\n\necho \"Completed for executable $FRAMEWORK_EXECUTABLE_PATH\"\necho $\n\ndone\n"; + }; + 4BC87B3B29B047DFDA0B6E90 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-frameworks.sh", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/JitsiWebRTC/WebRTC.framework/WebRTC", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebRTC.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 06C10D4F29CD7532492AD29E /* [Expo] Configure project */ = { + 6319FBDD06EF0030B48AD389 /* [Expo] Configure project */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; buildActionMask = 2147483647; @@ -1661,19 +1649,19 @@ "$(SRCROOT)/.xcode.env", "$(SRCROOT)/.xcode.env.local", "$(SRCROOT)/RocketChatRN/RocketChatRN.entitlements", - "$(SRCROOT)/Pods/Target Support Files/Pods-defaults-RocketChatRN/expo-configure-project.sh", + "$(SRCROOT)/Pods/Target Support Files/Pods-defaults-Rocket.Chat/expo-configure-project.sh", ); name = "[Expo] Configure project"; outputFileListPaths = ( ); outputPaths = ( - "$(SRCROOT)/Pods/Target Support Files/Pods-defaults-RocketChatRN/ExpoModulesProvider.swift", + "$(SRCROOT)/Pods/Target Support Files/Pods-defaults-Rocket.Chat/ExpoModulesProvider.swift", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-defaults-RocketChatRN/expo-configure-project.sh\"\n"; + shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-defaults-Rocket.Chat/expo-configure-project.sh\"\n"; }; - 0A9A264DB182D9B961EC0897 /* [CP] Copy Pods Resources */ = { + 6C507C5DC028E270B40EF89D /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1751,13 +1739,13 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 19FB95CAFFC54B349F10063E /* [CP] Copy Pods Resources */ = { + 779AAF49493D47C970231A22 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService-resources.sh", + "${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/BugsnagReactNative/Bugsnag.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", @@ -1826,10 +1814,10 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 1E1EA8082326CCE300E22452 /* ShellScript */ = { + 7A55BE3B2F11316900D8744D /* Bundle React Native code and images */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1838,15 +1826,16 @@ ); inputPaths = ( ); + name = "Bundle React Native code and images"; outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "echo \"Target architectures: $ARCHS\"\n\nAPP_PATH=\"${TARGET_BUILD_DIR}/${WRAPPER_NAME}\"\n\nfind \"$APP_PATH\" -name '*.framework' -type d | while read -r FRAMEWORK\ndo\nFRAMEWORK_EXECUTABLE_NAME=$(defaults read \"$FRAMEWORK/Info.plist\" CFBundleExecutable)\nFRAMEWORK_EXECUTABLE_PATH=\"$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME\"\necho \"Executable is $FRAMEWORK_EXECUTABLE_PATH\"\necho $(lipo -info \"$FRAMEWORK_EXECUTABLE_PATH\")\n\nFRAMEWORK_TMP_PATH=\"$FRAMEWORK_EXECUTABLE_PATH-tmp\"\n\n# remove simulator's archs if location is not simulator's directory\ncase \"${TARGET_BUILD_DIR}\" in\n*\"iphonesimulator\")\necho \"No need to remove archs\"\n;;\n*)\nif $(lipo \"$FRAMEWORK_EXECUTABLE_PATH\" -verify_arch \"i386\") ; then\nlipo -output \"$FRAMEWORK_TMP_PATH\" -remove \"i386\" \"$FRAMEWORK_EXECUTABLE_PATH\"\necho \"i386 architecture removed\"\nrm \"$FRAMEWORK_EXECUTABLE_PATH\"\nmv \"$FRAMEWORK_TMP_PATH\" \"$FRAMEWORK_EXECUTABLE_PATH\"\nfi\nif $(lipo \"$FRAMEWORK_EXECUTABLE_PATH\" -verify_arch \"x86_64\") ; then\nlipo -output \"$FRAMEWORK_TMP_PATH\" -remove \"x86_64\" \"$FRAMEWORK_EXECUTABLE_PATH\"\necho \"x86_64 architecture removed\"\nrm \"$FRAMEWORK_EXECUTABLE_PATH\"\nmv \"$FRAMEWORK_TMP_PATH\" \"$FRAMEWORK_EXECUTABLE_PATH\"\nfi\n;;\nesac\n\necho \"Completed for executable $FRAMEWORK_EXECUTABLE_PATH\"\necho $\n\ndone\n"; + shellScript = "export EXTRA_PACKAGER_ARGS=\"--sourcemap-output $TMPDIR/$(md5 -qs \"$CONFIGURATION_BUILD_DIR\")-main.jsbundle.map\"\nexport NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; }; - 360B0CB1D9593B8990FFC314 /* [CP] Check Pods Manifest.lock */ = { + 7A55BE3C2F1131C000D8744D /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1854,65 +1843,36 @@ inputFileListPaths = ( ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", ); - name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-defaults-Rocket.Chat-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 36E1E4EA3CF1B40425C9E067 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/JitsiWebRTC/WebRTC.framework/WebRTC", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebRTC.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-frameworks.sh\"\n"; - showEnvVarsInLog = 0; + shellScript = "echo \"Target architectures: $ARCHS\"\n\nAPP_PATH=\"${TARGET_BUILD_DIR}/${WRAPPER_NAME}\"\n\nfind \"$APP_PATH\" -name '*.framework' -type d | while read -r FRAMEWORK\ndo\nFRAMEWORK_EXECUTABLE_NAME=$(defaults read \"$FRAMEWORK/Info.plist\" CFBundleExecutable)\nFRAMEWORK_EXECUTABLE_PATH=\"$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME\"\necho \"Executable is $FRAMEWORK_EXECUTABLE_PATH\"\necho $(lipo -info \"$FRAMEWORK_EXECUTABLE_PATH\")\n\nFRAMEWORK_TMP_PATH=\"$FRAMEWORK_EXECUTABLE_PATH-tmp\"\n\n# remove simulator's archs if location is not simulator's directory\ncase \"${TARGET_BUILD_DIR}\" in\n*\"iphonesimulator\")\necho \"No need to remove archs\"\n;;\n*)\nif $(lipo \"$FRAMEWORK_EXECUTABLE_PATH\" -verify_arch \"i386\") ; then\nlipo -output \"$FRAMEWORK_TMP_PATH\" -remove \"i386\" \"$FRAMEWORK_EXECUTABLE_PATH\"\necho \"i386 architecture removed\"\nrm \"$FRAMEWORK_EXECUTABLE_PATH\"\nmv \"$FRAMEWORK_TMP_PATH\" \"$FRAMEWORK_EXECUTABLE_PATH\"\nfi\nif $(lipo \"$FRAMEWORK_EXECUTABLE_PATH\" -verify_arch \"x86_64\") ; then\nlipo -output \"$FRAMEWORK_TMP_PATH\" -remove \"x86_64\" \"$FRAMEWORK_EXECUTABLE_PATH\"\necho \"x86_64 architecture removed\"\nrm \"$FRAMEWORK_EXECUTABLE_PATH\"\nmv \"$FRAMEWORK_TMP_PATH\" \"$FRAMEWORK_EXECUTABLE_PATH\"\nfi\n;;\nesac\n\necho \"Completed for executable $FRAMEWORK_EXECUTABLE_PATH\"\necho $\n\ndone\n"; }; - 6319FBDD06EF0030B48AD389 /* [Expo] Configure project */ = { + 7A55BE3D2F11320C00D8744D /* Upload source maps to Bugsnag */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( - "$(SRCROOT)/.xcode.env", - "$(SRCROOT)/.xcode.env.local", - "$(SRCROOT)/RocketChatRN/RocketChatRN.entitlements", - "$(SRCROOT)/Pods/Target Support Files/Pods-defaults-Rocket.Chat/expo-configure-project.sh", + $TARGET_BUILD_DIR/$INFOPLIST_PATH, ); - name = "[Expo] Configure project"; + name = "Upload source maps to Bugsnag"; outputFileListPaths = ( ); outputPaths = ( - "$(SRCROOT)/Pods/Target Support Files/Pods-defaults-Rocket.Chat/ExpoModulesProvider.swift", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-defaults-Rocket.Chat/expo-configure-project.sh\"\n"; + shellScript = "SOURCE_MAP=\"$TMPDIR/$(md5 -qs \"$CONFIGURATION_BUILD_DIR\")-main.jsbundle.map\" ../node_modules/@bugsnag/react-native/bugsnag-react-native-xcode.sh\n"; + showEnvVarsInLog = 0; }; - 7A55BE3B2F11316900D8744D /* Bundle React Native code and images */ = { + 7AAE9EB32891A0D20024F559 /* Upload source maps to Bugsnag */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1920,54 +1880,121 @@ inputFileListPaths = ( ); inputPaths = ( + $TARGET_BUILD_DIR/$INFOPLIST_PATH, ); - name = "Bundle React Native code and images"; + name = "Upload source maps to Bugsnag"; outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "export EXTRA_PACKAGER_ARGS=\"--sourcemap-output $TMPDIR/$(md5 -qs \"$CONFIGURATION_BUILD_DIR\")-main.jsbundle.map\"\nexport NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; + shellScript = "SOURCE_MAP=\"$TMPDIR/$(md5 -qs \"$CONFIGURATION_BUILD_DIR\")-main.jsbundle.map\" ../node_modules/@bugsnag/react-native/bugsnag-react-native-xcode.sh\n"; + showEnvVarsInLog = 0; }; - 7A55BE3C2F1131C000D8744D /* ShellScript */ = { + 86A998705576AFA7CE938617 /* [Expo] Configure project */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( + "$(SRCROOT)/.xcode.env", + "$(SRCROOT)/.xcode.env.local", + "$(SRCROOT)/NotificationService/NotificationService.entitlements", + "$(SRCROOT)/Pods/Target Support Files/Pods-defaults-NotificationService/expo-configure-project.sh", ); + name = "[Expo] Configure project"; outputFileListPaths = ( ); outputPaths = ( + "$(SRCROOT)/Pods/Target Support Files/Pods-defaults-NotificationService/ExpoModulesProvider.swift", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "echo \"Target architectures: $ARCHS\"\n\nAPP_PATH=\"${TARGET_BUILD_DIR}/${WRAPPER_NAME}\"\n\nfind \"$APP_PATH\" -name '*.framework' -type d | while read -r FRAMEWORK\ndo\nFRAMEWORK_EXECUTABLE_NAME=$(defaults read \"$FRAMEWORK/Info.plist\" CFBundleExecutable)\nFRAMEWORK_EXECUTABLE_PATH=\"$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME\"\necho \"Executable is $FRAMEWORK_EXECUTABLE_PATH\"\necho $(lipo -info \"$FRAMEWORK_EXECUTABLE_PATH\")\n\nFRAMEWORK_TMP_PATH=\"$FRAMEWORK_EXECUTABLE_PATH-tmp\"\n\n# remove simulator's archs if location is not simulator's directory\ncase \"${TARGET_BUILD_DIR}\" in\n*\"iphonesimulator\")\necho \"No need to remove archs\"\n;;\n*)\nif $(lipo \"$FRAMEWORK_EXECUTABLE_PATH\" -verify_arch \"i386\") ; then\nlipo -output \"$FRAMEWORK_TMP_PATH\" -remove \"i386\" \"$FRAMEWORK_EXECUTABLE_PATH\"\necho \"i386 architecture removed\"\nrm \"$FRAMEWORK_EXECUTABLE_PATH\"\nmv \"$FRAMEWORK_TMP_PATH\" \"$FRAMEWORK_EXECUTABLE_PATH\"\nfi\nif $(lipo \"$FRAMEWORK_EXECUTABLE_PATH\" -verify_arch \"x86_64\") ; then\nlipo -output \"$FRAMEWORK_TMP_PATH\" -remove \"x86_64\" \"$FRAMEWORK_EXECUTABLE_PATH\"\necho \"x86_64 architecture removed\"\nrm \"$FRAMEWORK_EXECUTABLE_PATH\"\nmv \"$FRAMEWORK_TMP_PATH\" \"$FRAMEWORK_EXECUTABLE_PATH\"\nfi\n;;\nesac\n\necho \"Completed for executable $FRAMEWORK_EXECUTABLE_PATH\"\necho $\n\ndone\n"; + shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-defaults-NotificationService/expo-configure-project.sh\"\n"; }; - 7A55BE3D2F11320C00D8744D /* Upload source maps to Bugsnag */ = { + 89E2EFC06A15837E14EBD46A /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - $TARGET_BUILD_DIR/$INFOPLIST_PATH, - ); - name = "Upload source maps to Bugsnag"; - outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/BugsnagReactNative/Bugsnag.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/EXNotifications/ExpoNotifications_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/ExpoDevice/ExpoDevice_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension/FirebaseCoreExtension_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal/FirebaseCoreInternal_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCrashlytics/FirebaseCrashlytics_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations/FirebaseInstallations_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport/GoogleDataTransport_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift/Promises_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo/RNDeviceInfoPrivacyInfo.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/RNImageCropPickerPrivacyInfo.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController/TOCropViewControllerBundle.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb_Privacy.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/react-native-cameraroll/RNCameraRollPrivacyInfo.bundle", ); + name = "[CP] Copy Pods Resources"; outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Bugsnag.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoApplication_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoNotifications_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoDevice_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCore_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreExtension_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreInternal_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCrashlytics_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseInstallations_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleDataTransport_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleUtilities_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FBLPromises_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Promises_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNDeviceInfoPrivacyInfo.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNImageCropPickerPrivacyInfo.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SDWebImage.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/TOCropViewControllerBundle.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/nanopb_Privacy.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCameraRollPrivacyInfo.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "SOURCE_MAP=\"$TMPDIR/$(md5 -qs \"$CONFIGURATION_BUILD_DIR\")-main.jsbundle.map\" ../node_modules/@bugsnag/react-native/bugsnag-react-native-xcode.sh\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 7AAE9EB32891A0D20024F559 /* Upload source maps to Bugsnag */ = { + A9FDA76149CB53BBE6B86918 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1975,63 +2002,43 @@ inputFileListPaths = ( ); inputPaths = ( - $TARGET_BUILD_DIR/$INFOPLIST_PATH, + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); - name = "Upload source maps to Bugsnag"; + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( ); outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-defaults-RocketChatRN-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "SOURCE_MAP=\"$TMPDIR/$(md5 -qs \"$CONFIGURATION_BUILD_DIR\")-main.jsbundle.map\" ../node_modules/@bugsnag/react-native/bugsnag-react-native-xcode.sh\n"; - showEnvVarsInLog = 0; - }; - 7F8EB9489218C65E04132D08 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/JitsiWebRTC/WebRTC.framework/WebRTC", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebRTC.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 86A998705576AFA7CE938617 /* [Expo] Configure project */ = { + AF84C79830C1EF9B0C90CE13 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( - "$(SRCROOT)/.xcode.env", - "$(SRCROOT)/.xcode.env.local", - "$(SRCROOT)/NotificationService/NotificationService.entitlements", - "$(SRCROOT)/Pods/Target Support Files/Pods-defaults-NotificationService/expo-configure-project.sh", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); - name = "[Expo] Configure project"; + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( ); outputPaths = ( - "$(SRCROOT)/Pods/Target Support Files/Pods-defaults-NotificationService/ExpoModulesProvider.swift", + "$(DERIVED_FILE_DIR)/Pods-defaults-Rocket.Chat-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-defaults-NotificationService/expo-configure-project.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; }; - D564371D5C759573F0753D22 /* [CP] Check Pods Manifest.lock */ = { + DCBFFFA973306E6A320A3A37 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -2053,26 +2060,24 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - DF3F7A3CBF3F944B68A9C061 /* [CP] Check Pods Manifest.lock */ = { + F8FA5EB1C05179679A1B0213 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat-frameworks.sh", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/JitsiWebRTC/WebRTC.framework/WebRTC", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", ); + name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-defaults-RocketChatRN-checkManifestLockResult.txt", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebRTC.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -2133,6 +2138,7 @@ 4C4C8603EF082F0A33A95522 /* ExpoModulesProvider.swift in Sources */, A2C6E2DD38F8BEE19BFB2E1D /* SecureStorage.m in Sources */, AE692FD072A44EA955D0C0D8 /* DDPClient.swift in Sources */, + 7C91FF6756C0285FA3647829 /* MediaCallsAnswerRequest.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2381,6 +2387,7 @@ 7AAB3E1F257E6A6E00707CF6 /* RocketChat.swift in Sources */, 7AAB3E20257E6A6E00707CF6 /* HTTPMethod.swift in Sources */, 7AAB3E21257E6A6E00707CF6 /* Payload.swift in Sources */, + 7A37D6FF2F896C360095EBA1 /* MediaCallsAnswerRequest.swift in Sources */, 7AAB3E23257E6A6E00707CF6 /* Data+Extensions.swift in Sources */, 7AAB3E24257E6A6E00707CF6 /* Date+Extensions.swift in Sources */, 7AAB3E25257E6A6E00707CF6 /* Database.swift in Sources */, @@ -2413,6 +2420,7 @@ BC404914E86821389EEB543D /* ExpoModulesProvider.swift in Sources */, 79D8C97F8CE2EC1B6882826B /* SecureStorage.m in Sources */, CE4453310C9A08AB0DAC2307 /* DDPClient.swift in Sources */, + 7A0000072F1BAFA700B6B4BD /* MediaCallsAnswerRequest.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2474,7 +2482,7 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B1693D1B84D0486251A22988 /* Pods-defaults-RocketChatRN.debug.xcconfig */; + baseConfigurationReference = 45AECABFC0DD015C46B1D029 /* Pods-defaults-RocketChatRN.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; APPLICATION_EXTENSION_API_ONLY = NO; @@ -2539,7 +2547,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3A9D9EA04B1E3C75464811F5 /* Pods-defaults-RocketChatRN.release.xcconfig */; + baseConfigurationReference = 9F66BEF98C5CF2CFDD4B4C87 /* Pods-defaults-RocketChatRN.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; APPLICATION_EXTENSION_API_ONLY = NO; @@ -2954,7 +2962,7 @@ }; 1EFEB59D2493B6640072EDC0 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = DC3EC396A755975FB1EB14EB /* Pods-defaults-NotificationService.debug.xcconfig */; + baseConfigurationReference = 9E19E897C464DE3A91E1DAFD /* Pods-defaults-NotificationService.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(EMBEDDED_CONTENT_CONTAINS_SWIFT)"; CLANG_ANALYZER_NONNULL = YES; @@ -3006,7 +3014,7 @@ }; 1EFEB59E2493B6640072EDC0 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2213748BB77228EBAE678F51 /* Pods-defaults-NotificationService.release.xcconfig */; + baseConfigurationReference = 1238492C71CF27C87CB2E976 /* Pods-defaults-NotificationService.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(EMBEDDED_CONTENT_CONTAINS_SWIFT)"; CLANG_ANALYZER_NONNULL = YES; @@ -3057,7 +3065,7 @@ }; 7AAB3E50257E6A6E00707CF6 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = CE0F13E24E685F7597B9E7B0 /* Pods-defaults-Rocket.Chat.debug.xcconfig */; + baseConfigurationReference = E604ABC16022AB0B4819037E /* Pods-defaults-Rocket.Chat.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; APPLICATION_EXTENSION_API_ONLY = NO; @@ -3122,7 +3130,7 @@ }; 7AAB3E51257E6A6E00707CF6 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3B42CC37681BAA6DD62CFF89 /* Pods-defaults-Rocket.Chat.release.xcconfig */; + baseConfigurationReference = 2CF16884F9353D83C6EFBE81 /* Pods-defaults-Rocket.Chat.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; APPLICATION_EXTENSION_API_ONLY = NO; diff --git a/ios/Shared/RocketChat/API/Requests/MediaCallsAnswerRequest.swift b/ios/Shared/RocketChat/API/Requests/MediaCallsAnswerRequest.swift new file mode 100644 index 00000000000..32e6be78eea --- /dev/null +++ b/ios/Shared/RocketChat/API/Requests/MediaCallsAnswerRequest.swift @@ -0,0 +1,41 @@ +// +// MediaCallsAnswerRequest.swift +// RocketChat +// +// Created by Diego Mello on 4/10/26. +// + +import Foundation + +struct MediaCallsAnswerRequest: Request { + typealias ResponseType = MediaCallsAnswerResponse + + let callId: String + let contractId: String + let answer: String + let supportedFeatures: [String]? + + let method: HTTPMethod = .post + let path = "/api/v1/media-calls.answer" + + init(callId: String, contractId: String, answer: String, supportedFeatures: [String]? = nil) { + self.callId = callId + self.contractId = contractId + self.answer = answer + self.supportedFeatures = supportedFeatures + } + + func body() -> Data? { + var dict: [String: Any] = [ + "callId": callId, + "contractId": contractId, + "answer": answer + ] + if let features = supportedFeatures { + dict["supportedFeatures"] = features + } + return try? JSONSerialization.data(withJSONObject: dict) + } +} + +typealias MediaCallsAnswerResponse = MessageResponse