diff --git a/lib/media/file-utils.js b/lib/media/file-utils.js index c2c74953e..5b7a331f0 100644 --- a/lib/media/file-utils.js +++ b/lib/media/file-utils.js @@ -1,218 +1,218 @@ // @flow import fileType from 'file-type'; import invariant from 'invariant'; import type { Shape } from '../types/core'; import type { MediaType } from '../types/media-types'; type ResultMIME = 'image/png' | 'image/jpeg'; type MediaConfig = { +mediaType: 'photo' | 'video' | 'photo_or_video', +extension: string, +serverCanHandle: boolean, +serverTranscodesImage: boolean, +imageConfig?: Shape<{ +convertTo: ResultMIME, }>, +videoConfig?: Shape<{ +loop: boolean, }>, }; const mediaConfig: { [mime: string]: MediaConfig } = Object.freeze({ 'image/png': { mediaType: 'photo', extension: 'png', serverCanHandle: true, serverTranscodesImage: true, }, 'image/jpeg': { mediaType: 'photo', extension: 'jpg', serverCanHandle: true, serverTranscodesImage: true, }, 'image/gif': { - mediaType: 'photo', + mediaType: 'photo_or_video', extension: 'gif', serverCanHandle: true, serverTranscodesImage: true, imageConfig: { convertTo: 'image/png', }, videoConfig: { loop: true, }, }, 'image/heic': { mediaType: 'photo', extension: 'heic', serverCanHandle: false, serverTranscodesImage: false, imageConfig: { convertTo: 'image/jpeg', }, }, 'image/webp': { mediaType: 'photo', extension: 'webp', serverCanHandle: true, serverTranscodesImage: true, imageConfig: { convertTo: 'image/jpeg', }, }, 'image/tiff': { mediaType: 'photo', extension: 'tiff', serverCanHandle: true, serverTranscodesImage: true, imageConfig: { convertTo: 'image/jpeg', }, }, 'image/svg+xml': { mediaType: 'photo', extension: 'svg', serverCanHandle: true, serverTranscodesImage: true, imageConfig: { convertTo: 'image/png', }, }, 'image/bmp': { mediaType: 'photo', extension: 'bmp', serverCanHandle: true, serverTranscodesImage: true, imageConfig: { convertTo: 'image/png', }, }, 'video/mp4': { mediaType: 'video', extension: 'mp4', // serverCanHandle set to false pending future video message progress serverCanHandle: false, serverTranscodesImage: false, }, 'video/quicktime': { mediaType: 'video', extension: 'mp4', // serverCanHandle set to false pending future video message progress serverCanHandle: false, serverTranscodesImage: false, }, }); const serverTranscodableTypes: Set<$Keys> = new Set(); const serverCanHandleTypes: Set<$Keys> = new Set(); for (const mime in mediaConfig) { if (mediaConfig[mime].serverTranscodesImage) { serverTranscodableTypes.add(mime); } if (mediaConfig[mime].serverCanHandle) { serverCanHandleTypes.add(mime); } } function getTargetMIME(inputMIME: string): ResultMIME { const config = mediaConfig[inputMIME]; if (!config) { return 'image/jpeg'; } const targetMIME = config.imageConfig && config.imageConfig.convertTo; if (targetMIME) { return targetMIME; } invariant( inputMIME === 'image/png' || inputMIME === 'image/jpeg', 'all images must be converted to jpeg or png', ); return inputMIME; } const bytesNeededForFileTypeCheck = 64; export type FileDataInfo = { mime: ?string, mediaType: ?MediaType, }; function fileInfoFromData( data: Uint8Array | Buffer | ArrayBuffer, ): FileDataInfo { const fileTypeResult = fileType(data); if (!fileTypeResult) { return { mime: null, mediaType: null }; } const { mime } = fileTypeResult; const rawMediaType = mediaConfig[mime] && mediaConfig[mime].mediaType; const mediaType = rawMediaType === 'photo_or_video' ? 'photo' : rawMediaType; return { mime, mediaType }; } function replaceExtension(filename: string, ext: string): string { const lastIndex = filename.lastIndexOf('.'); let name = lastIndex >= 0 ? filename.substring(0, lastIndex) : filename; if (!name) { name = Math.random().toString(36).slice(-5); } const maxReadableLength = 255 - ext.length - 1; return `${name.substring(0, maxReadableLength)}.${ext}`; } function readableFilename(filename: string, mime: string): ?string { const ext = mediaConfig[mime] && mediaConfig[mime].extension; if (!ext) { return null; } return replaceExtension(filename, ext); } const extRegex = /\.([0-9a-z]+)$/i; function extensionFromFilename(filename: string): ?string { const matches = filename.match(extRegex); if (!matches) { return null; } const match = matches[1]; if (!match) { return null; } return match.toLowerCase(); } const pathRegex = /^file:\/\/(.*)$/; function pathFromURI(uri: string): ?string { const matches = uri.match(pathRegex); if (!matches) { return null; } return matches[1] ? matches[1] : null; } const filenameRegex = /[^/]+$/; function filenameFromPathOrURI(pathOrURI: string): ?string { const matches = pathOrURI.match(filenameRegex); if (!matches) { return null; } return matches[0] ? matches[0] : null; } export { mediaConfig, serverTranscodableTypes, serverCanHandleTypes, getTargetMIME, bytesNeededForFileTypeCheck, fileInfoFromData, replaceExtension, readableFilename, extensionFromFilename, pathFromURI, filenameFromPathOrURI, }; diff --git a/lib/reducers/message-reducer.js b/lib/reducers/message-reducer.js index 1b4aa164a..b777e29de 100644 --- a/lib/reducers/message-reducer.js +++ b/lib/reducers/message-reducer.js @@ -1,1519 +1,1520 @@ // @flow import invariant from 'invariant'; import _difference from 'lodash/fp/difference'; import _flow from 'lodash/fp/flow'; import _isEqual from 'lodash/fp/isEqual'; import _keyBy from 'lodash/fp/keyBy'; import _map from 'lodash/fp/map'; import _mapKeys from 'lodash/fp/mapKeys'; import _mapValues from 'lodash/fp/mapValues'; import _omit from 'lodash/fp/omit'; import _omitBy from 'lodash/fp/omitBy'; import _pick from 'lodash/fp/pick'; import _pickBy from 'lodash/fp/pickBy'; import _uniq from 'lodash/fp/uniq'; import { createEntryActionTypes, saveEntryActionTypes, deleteEntryActionTypes, restoreEntryActionTypes, } from '../actions/entry-actions'; import { fetchMessagesBeforeCursorActionTypes, fetchMostRecentMessagesActionTypes, sendTextMessageActionTypes, sendMultimediaMessageActionTypes, saveMessagesActionType, processMessagesActionType, messageStorePruneActionType, createLocalMessageActionType, setMessageStoreMessages, } from '../actions/message-actions'; import { changeThreadSettingsActionTypes, deleteThreadActionTypes, leaveThreadActionTypes, newThreadActionTypes, removeUsersFromThreadActionTypes, changeThreadMemberRolesActionTypes, joinThreadActionTypes, } from '../actions/thread-actions'; import { updateMultimediaMessageMediaActionType } from '../actions/upload-actions'; import { logOutActionTypes, deleteAccountActionTypes, logInActionTypes, registerActionTypes, } from '../actions/user-actions'; import { locallyUniqueToRealizedThreadIDsSelector } from '../selectors/thread-selectors'; import { messageID, combineTruncationStatuses, sortMessageInfoList, sortMessageIDs, mergeThreadMessageInfos, } from '../shared/message-utils'; import { threadHasPermission, threadInChatList, threadIsPending, } from '../shared/thread-utils'; import threadWatcher from '../shared/thread-watcher'; import { unshimMessageInfos } from '../shared/unshim-utils'; import type { Media, Image } from '../types/media-types'; import { type RawMessageInfo, type LocalMessageInfo, type MessageStore, type ReplaceMessageOperation, type MessageStoreOperation, type MessageTruncationStatus, type ThreadMessageInfo, type MessageTruncationStatuses, messageTruncationStatus, messageTypes, defaultNumberPerThread, } from '../types/message-types'; import type { RawImagesMessageInfo } from '../types/messages/images'; import type { RawMediaMessageInfo } from '../types/messages/media'; import { type BaseAction, rehydrateActionType } from '../types/redux-types'; import { processServerRequestsActionType } from '../types/request-types'; import { fullStateSyncActionType, incrementalStateSyncActionType, } from '../types/socket-types'; import { type RawThreadInfo, threadPermissions } from '../types/thread-types'; import { updateTypes, type ClientUpdateInfo, processUpdatesActionType, } from '../types/update-types'; import { setNewSessionActionType } from '../utils/action-utils'; +import { isDev } from '../utils/dev-utils'; import { translateClientDBMessageInfosToRawMessageInfos } from '../utils/message-ops-utils'; import { assertObjectsAreEqual } from '../utils/objects'; -const PROCESSED_MSG_STORE_INVARIANTS_DISABLED = false; +const PROCESSED_MSG_STORE_INVARIANTS_DISABLED = !isDev; const _mapValuesWithKeys = _mapValues.convert({ cap: false }); // Input must already be ordered! function threadsToMessageIDsFromMessageInfos( orderedMessageInfos: $ReadOnlyArray, ): { [threadID: string]: string[] } { const threads: { [threadID: string]: string[] } = {}; for (const messageInfo of orderedMessageInfos) { const key = messageID(messageInfo); if (!threads[messageInfo.threadID]) { threads[messageInfo.threadID] = [key]; } else { threads[messageInfo.threadID].push(key); } } return threads; } function threadIsWatched( threadID: string, threadInfo: ?RawThreadInfo, watchedIDs: $ReadOnlyArray, ) { return ( threadIsPending(threadID) || (threadInfo && threadHasPermission(threadInfo, threadPermissions.VISIBLE) && (threadInChatList(threadInfo) || watchedIDs.includes(threadID))) ); } function assertMessageStoreMessagesAreEqual( processedMessageStore: MessageStore, expectedMessageStore: MessageStore, location: string, ) { if (PROCESSED_MSG_STORE_INVARIANTS_DISABLED) { return; } assertObjectsAreEqual( processedMessageStore.messages, expectedMessageStore.messages, `MessageStore.messages - ${location}`, ); } type FreshMessageStoreResult = { +messageStoreOperations: $ReadOnlyArray, +messageStore: MessageStore, }; function freshMessageStore( messageInfos: $ReadOnlyArray, truncationStatus: { [threadID: string]: MessageTruncationStatus }, currentAsOf: number, threadInfos: { +[threadID: string]: RawThreadInfo }, ): FreshMessageStoreResult { const unshimmed = unshimMessageInfos(messageInfos); const orderedMessageInfos = sortMessageInfoList(unshimmed); const messages = _keyBy(messageID)(orderedMessageInfos); const messageStoreOperations = orderedMessageInfos.map(messageInfo => ({ type: 'replace', payload: { id: messageID(messageInfo), messageInfo }, })); const threadsToMessageIDs = threadsToMessageIDsFromMessageInfos( orderedMessageInfos, ); const lastPruned = Date.now(); const threads = _mapValuesWithKeys( (messageIDs: string[], threadID: string) => ({ messageIDs, startReached: truncationStatus[threadID] === messageTruncationStatus.EXHAUSTIVE, lastNavigatedTo: 0, lastPruned, }), )(threadsToMessageIDs); const watchedIDs = threadWatcher.getWatchedIDs(); for (const threadID in threadInfos) { const threadInfo = threadInfos[threadID]; if ( threads[threadID] || !threadIsWatched(threadID, threadInfo, watchedIDs) ) { continue; } threads[threadID] = { messageIDs: [], startReached: false, lastNavigatedTo: 0, lastPruned, }; } return { messageStoreOperations, messageStore: { messages, threads, local: {}, currentAsOf }, }; } type ReassignmentResult = { +messageStoreOperations: $ReadOnlyArray, +messageStore: MessageStore, +reassignedThreadIDs: $ReadOnlyArray, }; function reassignMessagesToRealizedThreads( messageStore: MessageStore, threadInfos: { +[threadID: string]: RawThreadInfo }, ): ReassignmentResult { const locallyUniqueToRealizedThreadIDs = locallyUniqueToRealizedThreadIDsSelector( threadInfos, ); const messageStoreOperations: MessageStoreOperation[] = []; const messages = {}; for (const storeMessageID in messageStore.messages) { const message = messageStore.messages[storeMessageID]; const newThreadID = locallyUniqueToRealizedThreadIDs.get(message.threadID); messages[storeMessageID] = newThreadID ? { ...message, threadID: newThreadID, time: threadInfos[newThreadID]?.creationTime ?? message.time, } : message; if (!newThreadID) { continue; } const updateMsgOperation: ReplaceMessageOperation = { type: 'replace', payload: { id: storeMessageID, messageInfo: messages[storeMessageID] }, }; messageStoreOperations.push(updateMsgOperation); } const threads = {}; const reassignedThreadIDs = []; for (const threadID in messageStore.threads) { const threadMessageInfo = messageStore.threads[threadID]; const newThreadID = locallyUniqueToRealizedThreadIDs.get(threadID); if (!newThreadID) { threads[threadID] = threadMessageInfo; continue; } const realizedThread = messageStore.threads[newThreadID]; if (!realizedThread) { reassignedThreadIDs.push(newThreadID); threads[newThreadID] = threadMessageInfo; continue; } threads[newThreadID] = mergeThreadMessageInfos( threadMessageInfo, realizedThread, messages, ); } return { messageStoreOperations, messageStore: { ...messageStore, threads, messages, }, reassignedThreadIDs, }; } type MergeNewMessagesResult = { +messageStoreOperations: $ReadOnlyArray, +messageStore: MessageStore, }; // oldMessageStore is from the old state // newMessageInfos, truncationStatus come from server function mergeNewMessages( oldMessageStore: MessageStore, newMessageInfos: $ReadOnlyArray, truncationStatus: { [threadID: string]: MessageTruncationStatus }, threadInfos: { +[threadID: string]: RawThreadInfo }, actionType: *, ): MergeNewMessagesResult { const { messageStoreOperations: updateWithLatestThreadInfosOps, messageStore: updatedMessageStore, reassignedThreadIDs, } = updateMessageStoreWithLatestThreadInfos(oldMessageStore, threadInfos); const messageStoreAfterUpdateOps = processMessageStoreOperations( oldMessageStore, updateWithLatestThreadInfosOps, ); assertMessageStoreMessagesAreEqual( messageStoreAfterUpdateOps, updatedMessageStore, `${actionType}| reassignment and filtering`, ); const localIDsToServerIDs: Map = new Map(); const watchedThreadIDs = [ ...threadWatcher.getWatchedIDs(), ...reassignedThreadIDs, ]; const unshimmedNewMessages = unshimMessageInfos(newMessageInfos); const unshimmedNewMessagesOfWatchedThreads = unshimmedNewMessages.filter( msg => threadIsWatched( msg.threadID, threadInfos[msg.threadID], watchedThreadIDs, ), ); const orderedNewMessageInfos = _flow( _map((messageInfo: RawMessageInfo) => { const { id: inputID } = messageInfo; invariant(inputID, 'new messageInfos should have serverID'); invariant( !threadIsPending(messageInfo.threadID), 'new messageInfos should have realized thread id', ); const currentMessageInfo = updatedMessageStore.messages[inputID]; if ( messageInfo.type === messageTypes.TEXT || messageInfo.type === messageTypes.IMAGES || messageInfo.type === messageTypes.MULTIMEDIA ) { const { localID: inputLocalID } = messageInfo; const currentLocalMessageInfo = inputLocalID ? updatedMessageStore.messages[inputLocalID] : null; if (currentMessageInfo && currentMessageInfo.localID) { // If the client already has a RawMessageInfo with this serverID, keep // any localID associated with the existing one. This is because we // use localIDs as React keys and changing React keys leads to loss of // component state. (The conditional below is for Flow) if (messageInfo.type === messageTypes.TEXT) { messageInfo = { ...messageInfo, localID: currentMessageInfo.localID, }; } else if (messageInfo.type === messageTypes.MULTIMEDIA) { messageInfo = ({ ...messageInfo, localID: currentMessageInfo.localID, }: RawMediaMessageInfo); } else { messageInfo = ({ ...messageInfo, localID: currentMessageInfo.localID, }: RawImagesMessageInfo); } } else if (currentLocalMessageInfo && currentLocalMessageInfo.localID) { // If the client has a RawMessageInfo with this localID, but not with // the serverID, that means the message creation succeeded but the // success action never got processed. We set a key in // localIDsToServerIDs here to fix the messageIDs for the rest of the // MessageStore too. (The conditional below is for Flow) invariant(inputLocalID, 'inputLocalID should be set'); localIDsToServerIDs.set(inputLocalID, inputID); if (messageInfo.type === messageTypes.TEXT) { messageInfo = { ...messageInfo, localID: currentLocalMessageInfo.localID, }; } else if (messageInfo.type === messageTypes.MULTIMEDIA) { messageInfo = ({ ...messageInfo, localID: currentLocalMessageInfo.localID, }: RawMediaMessageInfo); } else { messageInfo = ({ ...messageInfo, localID: currentLocalMessageInfo.localID, }: RawImagesMessageInfo); } } else { // If neither the serverID nor the localID from the delivered // RawMessageInfo exists in the client store, then this message is // brand new to us. Ignore any localID provided by the server. // (The conditional below is for Flow) const { localID, ...rest } = messageInfo; if (rest.type === messageTypes.TEXT) { messageInfo = { ...rest }; } else if (rest.type === messageTypes.MULTIMEDIA) { messageInfo = ({ ...rest }: RawMediaMessageInfo); } else { messageInfo = ({ ...rest }: RawImagesMessageInfo); } } } else if ( currentMessageInfo && messageInfo.time > currentMessageInfo.time ) { // When thick threads will be introduced it will be possible for two // clients to create the same message (e.g. when they create the same // sidebar at the same time). We're going to use deterministic ids for // messages which should be unique within a thread and we have to find // a way for clients to agree which message to keep. We can't rely on // always choosing incoming messages nor messages from the store, // because a message that is in one user's store, will be send to // another user. One way to deal with it is to always choose a message // which is older, according to its timestamp. We can use this strategy // only for messages that can start a thread, because for other types // it might break the "contiguous" property of message ids (we can // consider selecting younger messages in that case, but for now we use // an invariant). invariant( messageInfo.type === messageTypes.CREATE_SIDEBAR || messageInfo.type === messageTypes.CREATE_THREAD || messageInfo.type === messageTypes.SIDEBAR_SOURCE, `Two different messages of type ${messageInfo.type} with the same ` + 'id found', ); return currentMessageInfo; } return _isEqual(messageInfo)(currentMessageInfo) ? currentMessageInfo : messageInfo; }), sortMessageInfoList, )(unshimmedNewMessagesOfWatchedThreads); const newMessageOps: MessageStoreOperation[] = []; const threadsToMessageIDs = threadsToMessageIDsFromMessageInfos( orderedNewMessageInfos, ); const oldMessageInfosToCombine = []; const threadsThatNeedMessageIDsResorted = []; const lastPruned = Date.now(); const local = {}; const threads = _flow( _mapValuesWithKeys((messageIDs: string[], threadID: string) => { const oldThread = updatedMessageStore.threads[threadID]; const truncate = truncationStatus[threadID]; if (!oldThread) { return { messageIDs, startReached: truncate === messageTruncationStatus.EXHAUSTIVE, lastNavigatedTo: 0, lastPruned, }; } let oldMessageIDsUnchanged = true; const oldMessageIDs = oldThread.messageIDs.map(oldID => { const newID = localIDsToServerIDs.get(oldID); if (newID !== null && newID !== undefined) { oldMessageIDsUnchanged = false; return newID; } return oldID; }); if (truncate === messageTruncationStatus.TRUNCATED) { // If the result set in the payload isn't contiguous with what we have // now, that means we need to dump what we have in the state and replace // it with the result set. We do this to achieve our two goals for the // messageStore: currentness and contiguousness. newMessageOps.push({ type: 'remove_messages_for_threads', payload: { threadIDs: [threadID] }, }); return { messageIDs, startReached: false, lastNavigatedTo: oldThread.lastNavigatedTo, lastPruned: oldThread.lastPruned, }; } const oldNotInNew = _difference(oldMessageIDs)(messageIDs); for (const id of oldNotInNew) { const oldMessageInfo = updatedMessageStore.messages[id]; invariant(oldMessageInfo, `could not find ${id} in messageStore`); oldMessageInfosToCombine.push(oldMessageInfo); const localInfo = updatedMessageStore.local[id]; if (localInfo) { local[id] = localInfo; } } const startReached = oldThread.startReached || truncate === messageTruncationStatus.EXHAUSTIVE; if (_difference(messageIDs)(oldMessageIDs).length === 0) { if (startReached === oldThread.startReached && oldMessageIDsUnchanged) { return oldThread; } return { messageIDs: oldMessageIDs, startReached, lastNavigatedTo: oldThread.lastNavigatedTo, lastPruned: oldThread.lastPruned, }; } const mergedMessageIDs = [...messageIDs, ...oldNotInNew]; threadsThatNeedMessageIDsResorted.push(threadID); return { messageIDs: mergedMessageIDs, startReached, lastNavigatedTo: oldThread.lastNavigatedTo, lastPruned: oldThread.lastPruned, }; }), _pickBy(thread => !!thread), )(threadsToMessageIDs); for (const threadID in updatedMessageStore.threads) { if (threads[threadID]) { continue; } let thread = updatedMessageStore.threads[threadID]; const truncate = truncationStatus[threadID]; if (truncate === messageTruncationStatus.EXHAUSTIVE) { thread = { ...thread, startReached: true, }; } threads[threadID] = thread; for (const id of thread.messageIDs) { const messageInfo = updatedMessageStore.messages[id]; if (messageInfo) { oldMessageInfosToCombine.push(messageInfo); } const localInfo = updatedMessageStore.local[id]; if (localInfo) { local[id] = localInfo; } } } const messages = _flow( sortMessageInfoList, _keyBy(messageID), )([...orderedNewMessageInfos, ...oldMessageInfosToCombine]); const newMessages = _keyBy(messageID)(orderedNewMessageInfos); for (const id in newMessages) { newMessageOps.push({ type: 'replace', payload: { id, messageInfo: newMessages[id] }, }); } if (localIDsToServerIDs.size > 0) { newMessageOps.push({ type: 'remove', payload: { ids: [...localIDsToServerIDs.keys()] }, }); } for (const threadID of threadsThatNeedMessageIDsResorted) { threads[threadID].messageIDs = sortMessageIDs(messages)( threads[threadID].messageIDs, ); } const currentAsOf = Math.max( orderedNewMessageInfos.length > 0 ? orderedNewMessageInfos[0].time : 0, updatedMessageStore.currentAsOf, ); const mergedMessageStore = { messages, threads, local, currentAsOf }; const processedMessageStore = processMessageStoreOperations( updatedMessageStore, newMessageOps, ); assertMessageStoreMessagesAreEqual( processedMessageStore, mergedMessageStore, `${actionType}|processed`, ); return { messageStoreOperations: [ ...updateWithLatestThreadInfosOps, ...newMessageOps, ], messageStore: mergedMessageStore, }; } type UpdateMessageStoreWithLatestThreadInfosResult = { +messageStoreOperations: $ReadOnlyArray, +messageStore: MessageStore, +reassignedThreadIDs: $ReadOnlyArray, }; function updateMessageStoreWithLatestThreadInfos( messageStore: MessageStore, threadInfos: { +[id: string]: RawThreadInfo }, ): UpdateMessageStoreWithLatestThreadInfosResult { const messageStoreOperations: MessageStoreOperation[] = []; const { messageStore: reassignedMessageStore, messageStoreOperations: reassignMessagesOps, reassignedThreadIDs, } = reassignMessagesToRealizedThreads(messageStore, threadInfos); messageStoreOperations.push(...reassignMessagesOps); const watchedIDs = [...threadWatcher.getWatchedIDs(), ...reassignedThreadIDs]; const watchedThreadInfos = _pickBy((threadInfo: RawThreadInfo) => threadIsWatched(threadInfo.id, threadInfo, watchedIDs), )(threadInfos); const filteredThreads = _pick(Object.keys(watchedThreadInfos))( reassignedMessageStore.threads, ); const messageIDsToRemove = []; const threadsToRemoveMessagesFrom = []; for (const threadID in reassignedMessageStore.threads) { if (watchedThreadInfos[threadID]) { continue; } threadsToRemoveMessagesFrom.push(threadID); for (const id of reassignedMessageStore.threads[threadID].messageIDs) { messageIDsToRemove.push(id); } } for (const threadID in threadInfos) { const threadInfo = threadInfos[threadID]; if ( threadIsWatched(threadID, threadInfo, watchedIDs) && !filteredThreads[threadID] ) { filteredThreads[threadID] = { messageIDs: [], // We can conclude that startReached, since no messages were returned. startReached: true, lastNavigatedTo: 0, lastPruned: Date.now(), }; } } messageStoreOperations.push({ type: 'remove_messages_for_threads', payload: { threadIDs: threadsToRemoveMessagesFrom }, }); return { messageStoreOperations, messageStore: { messages: _omit(messageIDsToRemove)(reassignedMessageStore.messages), threads: filteredThreads, local: _omit(messageIDsToRemove)(reassignedMessageStore.local), currentAsOf: reassignedMessageStore.currentAsOf, }, reassignedThreadIDs, }; } function ensureRealizedThreadIDIsUsedWhenPossible( payload: T, threadInfos: { +[id: string]: RawThreadInfo }, ): T { const locallyUniqueToRealizedThreadIDs = locallyUniqueToRealizedThreadIDsSelector( threadInfos, ); const realizedThreadID = locallyUniqueToRealizedThreadIDs.get( payload.threadID, ); return realizedThreadID ? { ...payload, threadID: realizedThreadID } : payload; } function processMessageStoreOperations( messageStore: MessageStore, messageStoreOperations: $ReadOnlyArray, ): MessageStore { if (messageStoreOperations.length === 0) { return messageStore; } let processedMessages = { ...messageStore.messages }; for (const operation of messageStoreOperations) { if (operation.type === 'replace') { processedMessages[operation.payload.id] = operation.payload.messageInfo; } else if (operation.type === 'remove') { for (const id of operation.payload.ids) { delete processedMessages[id]; } } else if (operation.type === 'remove_messages_for_threads') { for (const msgID in processedMessages) { if ( operation.payload.threadIDs.includes( processedMessages[msgID].threadID, ) ) { delete processedMessages[msgID]; } } } else if (operation.type === 'rekey') { processedMessages[operation.payload.to] = processedMessages[operation.payload.from]; delete processedMessages[operation.payload.from]; } else if (operation.type === 'remove_all') { processedMessages = {}; } } return { ...messageStore, messages: processedMessages }; } type ReduceMessageStoreResult = { +messageStoreOperations: $ReadOnlyArray, +messageStore: MessageStore, }; function reduceMessageStore( messageStore: MessageStore, action: BaseAction, newThreadInfos: { +[id: string]: RawThreadInfo }, ): ReduceMessageStoreResult { if (action.type === logInActionTypes.success) { const messagesResult = action.payload.messagesResult; const { messageStoreOperations, messageStore: freshStore, } = freshMessageStore( messagesResult.messageInfos, messagesResult.truncationStatus, messagesResult.currentAsOf, newThreadInfos, ); const processedMessageStore = processMessageStoreOperations( messageStore, messageStoreOperations, ); assertMessageStoreMessagesAreEqual( processedMessageStore, freshStore, action.type, ); return { messageStoreOperations, messageStore: freshStore }; } else if (action.type === incrementalStateSyncActionType) { if ( action.payload.messagesResult.rawMessageInfos.length === 0 && action.payload.updatesResult.newUpdates.length === 0 ) { return { messageStoreOperations: [], messageStore }; } const messagesResult = mergeUpdatesWithMessageInfos( action.payload.messagesResult.rawMessageInfos, action.payload.updatesResult.newUpdates, action.payload.messagesResult.truncationStatuses, ); const { messageStoreOperations, messageStore: mergedMessageStore, } = mergeNewMessages( messageStore, messagesResult.rawMessageInfos, messagesResult.truncationStatuses, newThreadInfos, action.type, ); return { messageStoreOperations, messageStore: mergedMessageStore }; } else if (action.type === processUpdatesActionType) { if (action.payload.updatesResult.newUpdates.length === 0) { return { messageStoreOperations: [], messageStore }; } const messagesResult = mergeUpdatesWithMessageInfos( [], action.payload.updatesResult.newUpdates, ); const { messageStoreOperations, messageStore: newMessageStore, } = mergeNewMessages( messageStore, messagesResult.rawMessageInfos, messagesResult.truncationStatuses, newThreadInfos, action.type, ); return { messageStoreOperations, messageStore: { messages: newMessageStore.messages, threads: newMessageStore.threads, local: newMessageStore.local, currentAsOf: messageStore.currentAsOf, }, }; } else if ( action.type === fullStateSyncActionType || action.type === processMessagesActionType ) { const { messagesResult } = action.payload; const { messageStoreOperations, messageStore: mergedMessageStore, } = mergeNewMessages( messageStore, messagesResult.rawMessageInfos, messagesResult.truncationStatuses, newThreadInfos, action.type, ); return { messageStoreOperations, messageStore: mergedMessageStore }; } else if ( action.type === fetchMessagesBeforeCursorActionTypes.success || action.type === fetchMostRecentMessagesActionTypes.success ) { const { messageStoreOperations, messageStore: mergedMessageStore, } = mergeNewMessages( messageStore, action.payload.rawMessageInfos, { [action.payload.threadID]: action.payload.truncationStatus }, newThreadInfos, action.type, ); return { messageStoreOperations, messageStore: mergedMessageStore }; } else if ( action.type === logOutActionTypes.success || action.type === deleteAccountActionTypes.success || action.type === deleteThreadActionTypes.success || action.type === leaveThreadActionTypes.success || action.type === setNewSessionActionType ) { const { messageStoreOperations, messageStore: filteredMessageStore, } = updateMessageStoreWithLatestThreadInfos(messageStore, newThreadInfos); const processedMessageStore = processMessageStoreOperations( messageStore, messageStoreOperations, ); assertMessageStoreMessagesAreEqual( processedMessageStore, filteredMessageStore, action.type, ); return { messageStoreOperations, messageStore: filteredMessageStore }; } else if (action.type === newThreadActionTypes.success) { const messagesResult = mergeUpdatesWithMessageInfos( action.payload.newMessageInfos, action.payload.updatesResult.newUpdates, ); const { messageStoreOperations, messageStore: mergedMessageStore, } = mergeNewMessages( messageStore, messagesResult.rawMessageInfos, messagesResult.truncationStatuses, newThreadInfos, action.type, ); return { messageStoreOperations, messageStore: mergedMessageStore }; } else if (action.type === registerActionTypes.success) { const truncationStatuses = {}; for (const messageInfo of action.payload.rawMessageInfos) { truncationStatuses[messageInfo.threadID] = messageTruncationStatus.EXHAUSTIVE; } const { messageStoreOperations, messageStore: mergedMessageStore, } = mergeNewMessages( messageStore, action.payload.rawMessageInfos, truncationStatuses, newThreadInfos, action.type, ); return { messageStoreOperations, messageStore: mergedMessageStore }; } else if ( action.type === changeThreadSettingsActionTypes.success || action.type === removeUsersFromThreadActionTypes.success || action.type === changeThreadMemberRolesActionTypes.success ) { const { messageStoreOperations, messageStore: mergedMessageStore, } = mergeNewMessages( messageStore, action.payload.newMessageInfos, { [action.payload.threadID]: messageTruncationStatus.UNCHANGED }, newThreadInfos, action.type, ); return { messageStoreOperations, messageStore: mergedMessageStore }; } else if ( action.type === createEntryActionTypes.success || action.type === saveEntryActionTypes.success ) { const { messageStoreOperations, messageStore: mergedMessageStore, } = mergeNewMessages( messageStore, action.payload.newMessageInfos, { [action.payload.threadID]: messageTruncationStatus.UNCHANGED }, newThreadInfos, action.type, ); return { messageStoreOperations, messageStore: mergedMessageStore }; } else if (action.type === deleteEntryActionTypes.success) { const payload = action.payload; if (payload) { const { messageStoreOperations, messageStore: mergedMessageStore, } = mergeNewMessages( messageStore, payload.newMessageInfos, { [payload.threadID]: messageTruncationStatus.UNCHANGED }, newThreadInfos, action.type, ); return { messageStoreOperations, messageStore: mergedMessageStore }; } } else if (action.type === restoreEntryActionTypes.success) { const { threadID } = action.payload; const { messageStoreOperations, messageStore: mergedMessageStore, } = mergeNewMessages( messageStore, action.payload.newMessageInfos, { [threadID]: messageTruncationStatus.UNCHANGED }, newThreadInfos, action.type, ); return { messageStoreOperations, messageStore: mergedMessageStore }; } else if (action.type === joinThreadActionTypes.success) { const messagesResult = mergeUpdatesWithMessageInfos( action.payload.rawMessageInfos, action.payload.updatesResult.newUpdates, ); const { messageStoreOperations, messageStore: mergedMessageStore, } = mergeNewMessages( messageStore, messagesResult.rawMessageInfos, messagesResult.truncationStatuses, newThreadInfos, action.type, ); return { messageStoreOperations, messageStore: mergedMessageStore }; } else if ( action.type === sendTextMessageActionTypes.started || action.type === sendMultimediaMessageActionTypes.started ) { const payload = ensureRealizedThreadIDIsUsedWhenPossible( action.payload, newThreadInfos, ); const { localID, threadID } = payload; invariant(localID, `localID should be set on ${action.type}`); const messageStoreOperations = [ { type: 'replace', payload: { id: localID, messageInfo: payload }, }, ]; const processedMessageStore = processMessageStoreOperations( messageStore, messageStoreOperations, ); const now = Date.now(); const messageIDs = messageStore.threads[threadID]?.messageIDs ?? []; if (messageStore.messages[localID]) { const messages = { ...messageStore.messages, [localID]: payload }; const local = _pickBy( (localInfo: LocalMessageInfo, key: string) => key !== localID, )(messageStore.local); const thread = messageStore.threads[threadID]; const threads = { ...messageStore.threads, [threadID]: { messageIDs: sortMessageIDs(messages)(messageIDs), startReached: thread?.startReached ?? true, lastNavigatedTo: thread?.lastNavigatedTo ?? now, lastPruned: thread?.lastPruned ?? now, }, }; const newMessageStore = { ...messageStore, messages, threads, local }; assertMessageStoreMessagesAreEqual( processedMessageStore, newMessageStore, action.type, ); return { messageStoreOperations, messageStore: newMessageStore }; } for (const existingMessageID of messageIDs) { const existingMessageInfo = messageStore.messages[existingMessageID]; if (existingMessageInfo && existingMessageInfo.localID === localID) { return { messageStoreOperations: [], messageStore }; } } const threadState: ThreadMessageInfo = messageStore.threads[threadID] ? { ...messageStore.threads[threadID], messageIDs: [localID, ...messageIDs], } : { messageIDs: [localID], startReached: true, lastNavigatedTo: now, lastPruned: now, }; const newMessageStore = { messages: { ...messageStore.messages, [localID]: payload, }, threads: { ...messageStore.threads, [threadID]: threadState, }, local: messageStore.local, currentAsOf: messageStore.currentAsOf, }; assertMessageStoreMessagesAreEqual( processedMessageStore, newMessageStore, action.type, ); return { messageStoreOperations, messageStore: newMessageStore }; } else if ( action.type === sendTextMessageActionTypes.failed || action.type === sendMultimediaMessageActionTypes.failed ) { const { localID } = action.payload; return { messageStoreOperations: [], messageStore: { messages: messageStore.messages, threads: messageStore.threads, local: { ...messageStore.local, [localID]: { sendFailed: true }, }, currentAsOf: messageStore.currentAsOf, }, }; } else if ( action.type === sendTextMessageActionTypes.success || action.type === sendMultimediaMessageActionTypes.success ) { const { payload } = action; invariant( !threadIsPending(payload.threadID), 'Successful message action should have realized thread id', ); const replaceMessageKey = (messageKey: string) => messageKey === payload.localID ? payload.serverID : messageKey; let newMessages; const messageStoreOperations = []; if (messageStore.messages[payload.serverID]) { // If somehow the serverID got in there already, we'll just update the // serverID message and scrub the localID one newMessages = _omitBy( (messageInfo: RawMessageInfo) => messageInfo.type === messageTypes.TEXT && !messageInfo.id && messageInfo.localID === payload.localID, )(messageStore.messages); messageStoreOperations.push({ type: 'remove', payload: { ids: [payload.localID] }, }); } else if (messageStore.messages[payload.localID]) { // The normal case, the localID message gets replaced by the serverID one newMessages = _mapKeys(replaceMessageKey)(messageStore.messages); messageStoreOperations.push({ type: 'rekey', payload: { from: payload.localID, to: payload.serverID }, }); } else { // Well this is weird, we probably got deauthorized between when the // action was dispatched and when we ran this reducer... return { messageStoreOperations, messageStore }; } const newMessage = { ...newMessages[payload.serverID], id: payload.serverID, localID: payload.localID, time: payload.time, }; newMessages[payload.serverID] = newMessage; messageStoreOperations.push({ type: 'replace', payload: { id: payload.serverID, messageInfo: newMessage }, }); const threadID = payload.threadID; const newMessageIDs = _flow( _uniq, sortMessageIDs(newMessages), )(messageStore.threads[threadID].messageIDs.map(replaceMessageKey)); const currentAsOf = payload.interface === 'socket' ? Math.max(payload.time, messageStore.currentAsOf) : messageStore.currentAsOf; const local = _pickBy( (localInfo: LocalMessageInfo, key: string) => key !== payload.localID, )(messageStore.local); const updatedMessageStore = { messages: newMessages, threads: { ...messageStore.threads, [threadID]: { ...messageStore.threads[threadID], messageIDs: newMessageIDs, }, }, local, currentAsOf, }; const processedMessageStore = processMessageStoreOperations( messageStore, messageStoreOperations, ); assertMessageStoreMessagesAreEqual( processedMessageStore, updatedMessageStore, action.type, ); return { messageStoreOperations, messageStore: updatedMessageStore }; } else if (action.type === saveMessagesActionType) { const truncationStatuses = {}; for (const messageInfo of action.payload.rawMessageInfos) { truncationStatuses[messageInfo.threadID] = messageTruncationStatus.UNCHANGED; } const { messageStoreOperations, messageStore: newMessageStore, } = mergeNewMessages( messageStore, action.payload.rawMessageInfos, truncationStatuses, newThreadInfos, action.type, ); return { messageStoreOperations, messageStore: { messages: newMessageStore.messages, threads: newMessageStore.threads, local: newMessageStore.local, // We avoid bumping currentAsOf because notifs may include a contracted // RawMessageInfo, so we want to make sure we still fetch it currentAsOf: messageStore.currentAsOf, }, }; } else if (action.type === messageStorePruneActionType) { const now = Date.now(); const messageIDsToPrune = []; const newThreads = { ...messageStore.threads }; for (const threadID of action.payload.threadIDs) { let thread = newThreads[threadID]; if (!thread) { continue; } thread = { ...thread, lastPruned: now }; const newMessageIDs = [...thread.messageIDs]; const removed = newMessageIDs.splice(defaultNumberPerThread); if (removed.length > 0) { thread = { ...thread, messageIDs: newMessageIDs, startReached: false, }; } for (const id of removed) { messageIDsToPrune.push(id); } newThreads[threadID] = thread; } const prunedMessageStore = { messages: _omit(messageIDsToPrune)(messageStore.messages), threads: newThreads, local: _omit(messageIDsToPrune)(messageStore.local), currentAsOf: messageStore.currentAsOf, }; const messageStoreOperations = [ { type: 'remove', payload: { ids: messageIDsToPrune }, }, ]; const processedMessageStore = processMessageStoreOperations( messageStore, messageStoreOperations, ); assertMessageStoreMessagesAreEqual( processedMessageStore, prunedMessageStore, action.type, ); return { messageStoreOperations, messageStore: prunedMessageStore }; } else if (action.type === updateMultimediaMessageMediaActionType) { const { messageID: id, currentMediaID, mediaUpdate } = action.payload; const message = messageStore.messages[id]; invariant(message, `message with ID ${id} could not be found`); invariant( message.type === messageTypes.IMAGES || message.type === messageTypes.MULTIMEDIA, `message with ID ${id} is not multimedia`, ); let updatedMessage; let replaced = false; if (message.type === messageTypes.IMAGES) { const media: Image[] = []; for (const singleMedia of message.media) { if (singleMedia.id !== currentMediaID) { media.push(singleMedia); } else { let updatedMedia: Image = { id: mediaUpdate.id ?? singleMedia.id, type: 'photo', uri: mediaUpdate.uri ?? singleMedia.uri, dimensions: mediaUpdate.dimensions ?? singleMedia.dimensions, }; if ( 'localMediaSelection' in singleMedia && !('localMediaSelection' in mediaUpdate) ) { updatedMedia = { ...updatedMedia, localMediaSelection: singleMedia.localMediaSelection, }; } else if (mediaUpdate.localMediaSelection) { updatedMedia = { ...updatedMedia, localMediaSelection: mediaUpdate.localMediaSelection, }; } media.push(updatedMedia); replaced = true; } } updatedMessage = { ...message, media }; } else { const media: Media[] = []; for (const singleMedia of message.media) { if (singleMedia.id !== currentMediaID) { media.push(singleMedia); } else if ( singleMedia.type === 'photo' && mediaUpdate.type === 'photo' ) { media.push({ ...singleMedia, ...mediaUpdate }); replaced = true; } else if ( singleMedia.type === 'video' && mediaUpdate.type === 'video' ) { media.push({ ...singleMedia, ...mediaUpdate }); replaced = true; } } updatedMessage = { ...message, media }; } invariant( replaced, `message ${id} did not contain media with ID ${currentMediaID}`, ); const updatedMessageStore = { ...messageStore, messages: { ...messageStore.messages, [id]: updatedMessage, }, }; const messageStoreOperations = [ { type: 'replace', payload: { id, messageInfo: updatedMessage, }, }, ]; const processedMessageStore = processMessageStoreOperations( messageStore, messageStoreOperations, ); assertMessageStoreMessagesAreEqual( processedMessageStore, updatedMessageStore, action.type, ); return { messageStoreOperations, messageStore: updatedMessageStore }; } else if (action.type === createLocalMessageActionType) { const messageInfo = ensureRealizedThreadIDIsUsedWhenPossible( action.payload, newThreadInfos, ); const { localID, threadID } = messageInfo; const messageIDs = messageStore.threads[messageInfo.threadID]?.messageIDs ?? []; const now = Date.now(); const threadState: ThreadMessageInfo = messageStore.threads[threadID] ? { ...messageStore.threads[threadID], messageIDs: [localID, ...messageIDs], } : { messageIDs: [localID], startReached: true, lastNavigatedTo: now, lastPruned: now, }; const updatedMessageStore = { ...messageStore, messages: { ...messageStore.messages, [localID]: messageInfo, }, threads: { ...messageStore.threads, [threadID]: threadState, }, }; const messageStoreOperations = [ { type: 'replace', payload: { id: localID, messageInfo }, }, ]; const processedMessageStore = processMessageStoreOperations( messageStore, messageStoreOperations, ); assertMessageStoreMessagesAreEqual( updatedMessageStore, processedMessageStore, action.type, ); return { messageStoreOperations, messageStore: updatedMessageStore }; } else if (action.type === rehydrateActionType) { // When starting the app on native, we filter out any local-only multimedia // messages because the relevant context is no longer available const { messages, threads, local } = messageStore; const newMessages = {}; let newThreads = threads, newLocal = local; const messageIDsToBeRemoved = []; for (const id in messages) { const message = messages[id]; if ( (message.type !== messageTypes.IMAGES && message.type !== messageTypes.MULTIMEDIA) || message.id ) { newMessages[id] = message; continue; } messageIDsToBeRemoved.push(id); const { threadID } = message; newThreads = { ...newThreads, [threadID]: { ...newThreads[threadID], messageIDs: newThreads[threadID].messageIDs.filter( curMessageID => curMessageID !== id, ), }, }; newLocal = _pickBy( (localInfo: LocalMessageInfo, key: string) => key !== id, )(newLocal); } if (newThreads === threads) { return { messageStoreOperations: [], messageStore }; } const newMessageStore = { ...messageStore, messages: newMessages, threads: newThreads, local: newLocal, }; const messageStoreOperations: MessageStoreOperation[] = [ { type: 'remove', payload: { ids: messageIDsToBeRemoved }, }, ]; const processedMessageStore = processMessageStoreOperations( messageStore, messageStoreOperations, ); assertMessageStoreMessagesAreEqual( processedMessageStore, newMessageStore, action.type, ); return { messageStoreOperations, messageStore: newMessageStore }; } else if (action.type === processServerRequestsActionType) { const { messageStoreOperations, messageStore: messageStoreAfterReassignment, } = reassignMessagesToRealizedThreads(messageStore, newThreadInfos); const processedMessageStore = processMessageStoreOperations( messageStore, messageStoreOperations, ); assertMessageStoreMessagesAreEqual( processedMessageStore, messageStoreAfterReassignment, action.type, ); return { messageStoreOperations, messageStore: messageStoreAfterReassignment, }; } else if (action.type === setMessageStoreMessages) { return { messageStoreOperations: [], messageStore: { ...messageStore, messages: translateClientDBMessageInfosToRawMessageInfos( action.payload, ), }, }; } return { messageStoreOperations: [], messageStore }; } type MergedUpdatesWithMessages = { +rawMessageInfos: $ReadOnlyArray, +truncationStatuses: MessageTruncationStatuses, }; function mergeUpdatesWithMessageInfos( messageInfos: $ReadOnlyArray, newUpdates: $ReadOnlyArray, truncationStatuses?: MessageTruncationStatuses, ): MergedUpdatesWithMessages { const messageIDs = new Set(messageInfos.map(messageInfo => messageInfo.id)); const mergedMessageInfos = [...messageInfos]; const mergedTruncationStatuses = { ...truncationStatuses }; for (const updateInfo of newUpdates) { if (updateInfo.type !== updateTypes.JOIN_THREAD) { continue; } for (const messageInfo of updateInfo.rawMessageInfos) { if (messageIDs.has(messageInfo.id)) { continue; } mergedMessageInfos.push(messageInfo); messageIDs.add(messageInfo.id); } mergedTruncationStatuses[ updateInfo.threadInfo.id ] = combineTruncationStatuses( updateInfo.truncationStatus, mergedTruncationStatuses[updateInfo.threadInfo.id], ); } return { rawMessageInfos: mergedMessageInfos, truncationStatuses: mergedTruncationStatuses, }; } export { freshMessageStore, reduceMessageStore }; diff --git a/lib/reducers/thread-reducer.js b/lib/reducers/thread-reducer.js index ba531417b..aa4d84d90 100644 --- a/lib/reducers/thread-reducer.js +++ b/lib/reducers/thread-reducer.js @@ -1,603 +1,604 @@ // @flow import _isEqual from 'lodash/fp/isEqual'; import { setThreadUnreadStatusActionTypes, updateActivityActionTypes, } from '../actions/activity-actions'; import { saveMessagesActionType } from '../actions/message-actions'; import { changeThreadSettingsActionTypes, deleteThreadActionTypes, newThreadActionTypes, removeUsersFromThreadActionTypes, changeThreadMemberRolesActionTypes, joinThreadActionTypes, leaveThreadActionTypes, setThreadStoreActionType, } from '../actions/thread-actions'; import { logOutActionTypes, deleteAccountActionTypes, logInActionTypes, registerActionTypes, updateSubscriptionActionTypes, } from '../actions/user-actions'; import type { BaseAction } from '../types/redux-types'; import { type ClientThreadInconsistencyReportCreationRequest, reportTypes, } from '../types/report-types'; import { serverRequestTypes, processServerRequestsActionType, } from '../types/request-types'; import { fullStateSyncActionType, incrementalStateSyncActionType, } from '../types/socket-types'; import type { RawThreadInfo, ThreadStore, ThreadStoreOperation, } from '../types/thread-types'; import { updateTypes, type ClientUpdateInfo, processUpdatesActionType, } from '../types/update-types'; import { actionLogger } from '../utils/action-logger'; import { setNewSessionActionType } from '../utils/action-utils'; import { getConfig } from '../utils/config'; +import { isDev } from '../utils/dev-utils'; import { assertObjectsAreEqual } from '../utils/objects'; import { sanitizeActionSecrets } from '../utils/sanitization'; -const PROCESSED_THREAD_STORE_INVARIANTS_DISABLED = false; +const PROCESSED_THREAD_STORE_INVARIANTS_DISABLED = !isDev; function reduceThreadUpdates( threadInfos: { +[id: string]: RawThreadInfo }, payload: { +updatesResult: { +newUpdates: $ReadOnlyArray, ... }, ... }, ): { +threadStoreOperations: $ReadOnlyArray, +threadInfos: { +[id: string]: RawThreadInfo }, } { const updatedThreadInfos = { ...threadInfos }; let someThreadUpdated = false; const threadOperations: ThreadStoreOperation[] = []; for (const update of payload.updatesResult.newUpdates) { if ( (update.type === updateTypes.UPDATE_THREAD || update.type === updateTypes.JOIN_THREAD) && !_isEqual(threadInfos[update.threadInfo.id])(update.threadInfo) ) { someThreadUpdated = true; updatedThreadInfos[update.threadInfo.id] = update.threadInfo; threadOperations.push({ type: 'replace', payload: { id: update.threadInfo.id, threadInfo: update.threadInfo, }, }); } else if ( update.type === updateTypes.UPDATE_THREAD_READ_STATUS && threadInfos[update.threadID] && threadInfos[update.threadID].currentUser.unread !== update.unread ) { someThreadUpdated = true; const updatedThread = { ...threadInfos[update.threadID], currentUser: { ...threadInfos[update.threadID].currentUser, unread: update.unread, }, }; updatedThreadInfos[update.threadID] = updatedThread; threadOperations.push({ type: 'replace', payload: { id: update.threadID, threadInfo: updatedThread, }, }); } else if ( update.type === updateTypes.DELETE_THREAD && threadInfos[update.threadID] ) { someThreadUpdated = true; delete updatedThreadInfos[update.threadID]; threadOperations.push({ type: 'remove', payload: { ids: [update.threadID], }, }); } else if (update.type === updateTypes.DELETE_ACCOUNT) { for (const threadID in threadInfos) { const threadInfo = threadInfos[threadID]; const newMembers = threadInfo.members.filter( member => member.id !== update.deletedUserID, ); if (newMembers.length < threadInfo.members.length) { someThreadUpdated = true; const updatedThread = { ...threadInfo, members: newMembers, }; updatedThreadInfos[threadID] = updatedThread; threadOperations.push({ type: 'replace', payload: { id: threadID, threadInfo: updatedThread, }, }); } } } } if (!someThreadUpdated) { return { threadStoreOperations: [], threadInfos }; } return { threadStoreOperations: threadOperations, threadInfos: updatedThreadInfos, }; } const emptyArray = []; function findInconsistencies( action: BaseAction, beforeStateCheck: { +[id: string]: RawThreadInfo }, afterStateCheck: { +[id: string]: RawThreadInfo }, ): ClientThreadInconsistencyReportCreationRequest[] { if (_isEqual(beforeStateCheck)(afterStateCheck)) { return emptyArray; } return [ { type: reportTypes.THREAD_INCONSISTENCY, platformDetails: getConfig().platformDetails, beforeAction: beforeStateCheck, action: sanitizeActionSecrets(action), pushResult: afterStateCheck, lastActions: actionLogger.interestingActionSummaries, time: Date.now(), }, ]; } function reduceThreadInfos( state: ThreadStore, action: BaseAction, ): { threadStore: ThreadStore, newThreadInconsistencies: $ReadOnlyArray, threadStoreOperations: $ReadOnlyArray, } { if ( action.type === logInActionTypes.success || action.type === registerActionTypes.success || action.type === fullStateSyncActionType ) { const newThreadInfos = action.payload.threadInfos; const updatedStore = { threadInfos: newThreadInfos, }; const threadStoreOperations = [ { type: 'remove_all', }, ...Object.keys(newThreadInfos).map((id: string) => ({ type: 'replace', payload: { id, threadInfo: newThreadInfos[id] }, })), ]; const processedStore = processThreadStoreOperations( state, threadStoreOperations, ); assertThreadStoreThreadsAreEqual(processedStore, updatedStore, action.type); return { threadStore: updatedStore, newThreadInconsistencies: [], threadStoreOperations, }; } else if ( action.type === logOutActionTypes.success || action.type === deleteAccountActionTypes.success || (action.type === setNewSessionActionType && action.payload.sessionChange.cookieInvalidated) ) { if (Object.keys(state.threadInfos).length === 0) { return { threadStore: state, newThreadInconsistencies: [], threadStoreOperations: [], }; } const updatedStore = { threadInfos: {}, }; const threadStoreOperations = [ { type: 'remove_all', }, ]; const processedStore = processThreadStoreOperations( state, threadStoreOperations, ); assertThreadStoreThreadsAreEqual(processedStore, updatedStore, action.type); return { threadStore: updatedStore, newThreadInconsistencies: [], threadStoreOperations, }; } else if ( action.type === joinThreadActionTypes.success || action.type === leaveThreadActionTypes.success || action.type === deleteThreadActionTypes.success || action.type === changeThreadSettingsActionTypes.success || action.type === removeUsersFromThreadActionTypes.success || action.type === changeThreadMemberRolesActionTypes.success || action.type === incrementalStateSyncActionType || action.type === processUpdatesActionType || action.type === newThreadActionTypes.success ) { if (action.payload.updatesResult.newUpdates.length === 0) { return { threadStore: state, newThreadInconsistencies: [], threadStoreOperations: [], }; } const { threadStoreOperations, threadInfos: newThreadInfos, } = reduceThreadUpdates(state.threadInfos, action.payload); const updatedStore = { threadInfos: newThreadInfos, }; const processedStore = processThreadStoreOperations( state, threadStoreOperations, ); assertThreadStoreThreadsAreEqual(processedStore, updatedStore, action.type); return { threadStore: updatedStore, newThreadInconsistencies: [], threadStoreOperations, }; } else if (action.type === updateSubscriptionActionTypes.success) { const newThreadInfo = { ...state.threadInfos[action.payload.threadID], currentUser: { ...state.threadInfos[action.payload.threadID].currentUser, subscription: action.payload.subscription, }, }; const updatedStore = { threadInfos: { ...state.threadInfos, [action.payload.threadID]: newThreadInfo, }, }; const threadStoreOperations = [ { type: 'replace', payload: { id: action.payload.threadID, threadInfo: newThreadInfo, }, }, ]; const processedStore = processThreadStoreOperations( state, threadStoreOperations, ); assertThreadStoreThreadsAreEqual(processedStore, updatedStore, action.type); return { threadStore: updatedStore, newThreadInconsistencies: [], threadStoreOperations, }; } else if (action.type === saveMessagesActionType) { const threadIDToMostRecentTime = new Map(); for (const messageInfo of action.payload.rawMessageInfos) { const current = threadIDToMostRecentTime.get(messageInfo.threadID); if (!current || current < messageInfo.time) { threadIDToMostRecentTime.set(messageInfo.threadID, messageInfo.time); } } const changedThreadInfos = {}; for (const [threadID, mostRecentTime] of threadIDToMostRecentTime) { const threadInfo = state.threadInfos[threadID]; if ( !threadInfo || threadInfo.currentUser.unread || action.payload.updatesCurrentAsOf > mostRecentTime ) { continue; } changedThreadInfos[threadID] = { ...state.threadInfos[threadID], currentUser: { ...state.threadInfos[threadID].currentUser, unread: true, }, }; } if (Object.keys(changedThreadInfos).length !== 0) { const updatedStore = { threadInfos: { ...state.threadInfos, ...changedThreadInfos, }, }; const threadStoreOperations = Object.keys(changedThreadInfos).map(id => ({ type: 'replace', payload: { id, threadInfo: changedThreadInfos[id], }, })); const processedStore = processThreadStoreOperations( state, threadStoreOperations, ); assertThreadStoreThreadsAreEqual( processedStore, updatedStore, action.type, ); return { threadStore: updatedStore, newThreadInconsistencies: [], threadStoreOperations, }; } } else if (action.type === processServerRequestsActionType) { const checkStateRequest = action.payload.serverRequests.find( candidate => candidate.type === serverRequestTypes.CHECK_STATE, ); if (!checkStateRequest || !checkStateRequest.stateChanges) { return { threadStore: state, newThreadInconsistencies: [], threadStoreOperations: [], }; } const { rawThreadInfos, deleteThreadIDs } = checkStateRequest.stateChanges; if (!rawThreadInfos && !deleteThreadIDs) { return { threadStore: state, newThreadInconsistencies: [], threadStoreOperations: [], }; } const newThreadInfos = { ...state.threadInfos }; const threadStoreOperations: ThreadStoreOperation[] = []; if (rawThreadInfos) { for (const rawThreadInfo of rawThreadInfos) { newThreadInfos[rawThreadInfo.id] = rawThreadInfo; threadStoreOperations.push({ type: 'replace', payload: { id: rawThreadInfo.id, threadInfo: rawThreadInfo, }, }); } } if (deleteThreadIDs) { for (const deleteThreadID of deleteThreadIDs) { delete newThreadInfos[deleteThreadID]; } threadStoreOperations.push({ type: 'remove', payload: { ids: deleteThreadIDs, }, }); } const newThreadInconsistencies = findInconsistencies( action, state.threadInfos, newThreadInfos, ); const updatedStore = { threadInfos: newThreadInfos, }; const processedStore = processThreadStoreOperations( state, threadStoreOperations, ); assertThreadStoreThreadsAreEqual(processedStore, updatedStore, action.type); return { threadStore: updatedStore, newThreadInconsistencies, threadStoreOperations, }; } else if (action.type === updateActivityActionTypes.success) { const updatedThreadInfos = {}; for (const setToUnread of action.payload.result.unfocusedToUnread) { const threadInfo = state.threadInfos[setToUnread]; if (threadInfo && !threadInfo.currentUser.unread) { updatedThreadInfos[setToUnread] = { ...threadInfo, currentUser: { ...threadInfo.currentUser, unread: true, }, }; } } if (Object.keys(updatedThreadInfos).length === 0) { return { threadStore: state, newThreadInconsistencies: [], threadStoreOperations: [], }; } const updatedStore = { threadInfos: { ...state.threadInfos, ...updatedThreadInfos }, }; const threadStoreOperations = Object.keys(updatedThreadInfos).map(id => ({ type: 'replace', payload: { id, threadInfo: updatedThreadInfos[id], }, })); const processedStore = processThreadStoreOperations( state, threadStoreOperations, ); assertThreadStoreThreadsAreEqual(processedStore, updatedStore, action.type); return { threadStore: updatedStore, newThreadInconsistencies: [], threadStoreOperations, }; } else if (action.type === setThreadUnreadStatusActionTypes.started) { const { threadID, unread } = action.payload; const updatedThreadInfo = { ...state.threadInfos[threadID], currentUser: { ...state.threadInfos[threadID].currentUser, unread, }, }; const updatedStore = { threadInfos: { ...state.threadInfos, [threadID]: updatedThreadInfo, }, }; const threadStoreOperations = [ { type: 'replace', payload: { id: threadID, threadInfo: updatedThreadInfo, }, }, ]; const processedStore = processThreadStoreOperations( state, threadStoreOperations, ); assertThreadStoreThreadsAreEqual(processedStore, updatedStore, action.type); return { threadStore: updatedStore, newThreadInconsistencies: [], threadStoreOperations, }; } else if (action.type === setThreadUnreadStatusActionTypes.success) { const { threadID, resetToUnread } = action.payload; const currentUser = state.threadInfos[threadID].currentUser; if (!resetToUnread || currentUser.unread) { return { threadStore: state, newThreadInconsistencies: [], threadStoreOperations: [], }; } const updatedUser = { ...currentUser, unread: true, }; const updatedThread = { ...state.threadInfos[threadID], currentUser: updatedUser, }; const updatedStore = { threadInfos: { ...state.threadInfos, [threadID]: updatedThread, }, }; const threadStoreOperations = [ { type: 'replace', payload: { id: threadID, threadInfo: updatedThread, }, }, ]; const processedStore = processThreadStoreOperations( state, threadStoreOperations, ); assertThreadStoreThreadsAreEqual(processedStore, updatedStore, action.type); return { threadStore: updatedStore, newThreadInconsistencies: [], threadStoreOperations, }; } else if (action.type === setThreadStoreActionType) { return { threadStore: action.payload, newThreadInconsistencies: [], threadStoreOperations: [], }; } return { threadStore: state, newThreadInconsistencies: [], threadStoreOperations: [], }; } function processThreadStoreOperations( threadStore: ThreadStore, threadStoreOperations: $ReadOnlyArray, ): ThreadStore { if (threadStoreOperations.length === 0) { return threadStore; } let processedThreads = { ...threadStore.threadInfos }; for (const operation of threadStoreOperations) { if (operation.type === 'replace') { processedThreads[operation.payload.id] = operation.payload.threadInfo; } else if (operation.type === 'remove') { for (const id of operation.payload.ids) { delete processedThreads[id]; } } else if (operation.type === 'remove_all') { processedThreads = {}; } } return { ...threadStore, threadInfos: processedThreads }; } function assertThreadStoreThreadsAreEqual( processedThreadStore: ThreadStore, expectedThreadStore: ThreadStore, actionType: string, ) { if (PROCESSED_THREAD_STORE_INVARIANTS_DISABLED) { return; } assertObjectsAreEqual( processedThreadStore.threadInfos, expectedThreadStore.threadInfos, `ThreadStore.threadInfos - ${actionType}`, ); } export { reduceThreadInfos, processThreadStoreOperations, assertThreadStoreThreadsAreEqual, }; diff --git a/native/android/app/src/cpp/jsiInstaller.cpp b/native/android/app/src/cpp/jsiInstaller.cpp index 45a173cdf..b4eaabbd8 100644 --- a/native/android/app/src/cpp/jsiInstaller.cpp +++ b/native/android/app/src/cpp/jsiInstaller.cpp @@ -1,56 +1,69 @@ #include "CommCoreModule.h" +#include "CommSecureStore.h" #include "GlobalNetworkSingletonJNIHelper.h" #include "SQLiteQueryExecutor.h" #include "jniHelpers.h" #include #include #include namespace jni = facebook::jni; namespace jsi = facebook::jsi; namespace react = facebook::react; class CommHybrid : public jni::HybridClass { public: static auto constexpr kJavaDescriptor = "Lapp/comm/android/fbjni/CommHybrid;"; static void initHybrid( jni::alias_ref jThis, jlong jsContext, jni::alias_ref jsCallInvokerHolder, comm::HashMap additionalParameters) { jsi::Runtime *rt = (jsi::Runtime *)jsContext; auto jsCallInvoker = jsCallInvokerHolder->cthis()->getCallInvoker(); std::shared_ptr nativeModule = std::make_shared(jsCallInvoker); if (rt != nullptr) { rt->global().setProperty( *rt, jsi::PropNameID::forAscii(*rt, "CommCoreModule"), jsi::Object::createFromHostObject(*rt, nativeModule)); } jni::local_ref sqliteFilePathObj = additionalParameters.get("sqliteFilePath"); - std::string sqliteFilePath = sqliteFilePathObj->toString(); + comm::SQLiteQueryExecutor::sqliteFilePath = sqliteFilePathObj->toString(); - comm::SQLiteQueryExecutor::initialize(sqliteFilePath); + comm::CommSecureStore commSecureStore; + folly::Optional maybeEncryptionKey = + commSecureStore.get("comm.encryptionKey"); + + if (maybeEncryptionKey) { + comm::SQLiteQueryExecutor::encryptionKey = maybeEncryptionKey.value(); + } else { + int sqlcipherEncryptionKeySize = 64; + std::string encryptionKey = comm::crypto::Tools::generateRandomHexString( + sqlcipherEncryptionKeySize); + commSecureStore.set("comm.encryptionKey", encryptionKey); + comm::SQLiteQueryExecutor::encryptionKey = encryptionKey; + } } static void registerNatives() { javaClassStatic()->registerNatives({ makeNativeMethod("initHybrid", CommHybrid::initHybrid), }); } private: friend HybridBase; }; JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) { return jni::initialize(vm, [] { CommHybrid::registerNatives(); comm::GlobalNetworkSingletonJNIHelper::registerNatives(); }); } diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp index 6e48ec40f..49372c26c 100644 --- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp +++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp @@ -1,773 +1,748 @@ #include "SQLiteQueryExecutor.h" -#include "CommSecureStore.h" #include "Logger.h" #include "sqlite_orm.h" #include "entities/Media.h" #include "entities/Metadata.h" #include #include #include #include #include #include #include #include #include #include -#include #define ACCOUNT_ID 1 namespace comm { using namespace sqlite_orm; std::string SQLiteQueryExecutor::sqliteFilePath; std::string SQLiteQueryExecutor::encryptionKey; -std::once_flag SQLiteQueryExecutor::initialized; -int SQLiteQueryExecutor::sqlcipherEncryptionKeySize = 64; -std::string SQLiteQueryExecutor::secureStoreEncryptionKeyID = - "comm.encryptionKey"; bool create_table(sqlite3 *db, std::string query, std::string tableName) { char *error; sqlite3_exec(db, query.c_str(), nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream stringStream; stringStream << "Error creating '" << tableName << "' table: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } bool create_drafts_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS drafts (threadID TEXT UNIQUE PRIMARY KEY, " "text TEXT);"; return create_table(db, query, "drafts"); } bool rename_threadID_to_key(sqlite3 *db) { sqlite3_stmt *key_column_stmt; sqlite3_prepare_v2( db, "SELECT name AS col_name FROM pragma_table_xinfo ('drafts') WHERE " "col_name='key';", -1, &key_column_stmt, nullptr); sqlite3_step(key_column_stmt); auto num_bytes = sqlite3_column_bytes(key_column_stmt, 0); sqlite3_finalize(key_column_stmt); if (num_bytes) { return true; } char *error; sqlite3_exec( db, "ALTER TABLE drafts RENAME COLUMN `threadID` TO `key`;", nullptr, nullptr, &error); if (error) { std::ostringstream stringStream; stringStream << "Error occurred renaming threadID column in drafts table " << "to key: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } return true; } bool create_persist_account_table(sqlite3 *db) { std::string query = "CREATE TABLE olm_persist_account(" "id INTEGER UNIQUE PRIMARY KEY NOT NULL, " "account_data TEXT NOT NULL);"; return create_table(db, query, "olm_persist_account"); } bool create_persist_sessions_table(sqlite3 *db) { std::string query = "CREATE TABLE olm_persist_sessions(" "target_user_id TEXT UNIQUE PRIMARY KEY NOT NULL, " "session_data TEXT NOT NULL);"; return create_table(db, query, "olm_persist_sessions"); } bool drop_messages_table(sqlite3 *db) { char *error; sqlite3_exec(db, "DROP TABLE IF EXISTS messages;", nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream stringStream; stringStream << "Error dropping 'messages' table: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } bool recreate_messages_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS messages ( " "id TEXT UNIQUE PRIMARY KEY NOT NULL, " "local_id TEXT, " "thread TEXT NOT NULL, " "user TEXT NOT NULL, " "type INTEGER NOT NULL, " "future_type INTEGER, " "content TEXT, " "time INTEGER NOT NULL);"; return create_table(db, query, "messages"); } bool create_messages_idx_thread_time(sqlite3 *db) { char *error; sqlite3_exec( db, "CREATE INDEX messages_idx_thread_time " "ON messages (thread, time);", nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream stringStream; stringStream << "Error creating (thread, time) index on messages table: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } bool create_media_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS media ( " "id TEXT UNIQUE PRIMARY KEY NOT NULL, " "container TEXT NOT NULL, " "thread TEXT NOT NULL, " "uri TEXT NOT NULL, " "type TEXT NOT NULL, " "extras TEXT NOT NULL);"; return create_table(db, query, "media"); } bool create_media_idx_container(sqlite3 *db) { char *error; sqlite3_exec( db, "CREATE INDEX media_idx_container " "ON media (container);", nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream stringStream; stringStream << "Error creating (container) index on media table: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } bool create_threads_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS threads ( " "id TEXT UNIQUE PRIMARY KEY NOT NULL, " "type INTEGER NOT NULL, " "name TEXT, " "description TEXT, " "color TEXT NOT NULL, " "creation_time BIGINT NOT NULL, " "parent_thread_id TEXT, " "containing_thread_id TEXT, " "community TEXT, " "members TEXT NOT NULL, " "roles TEXT NOT NULL, " "current_user TEXT NOT NULL, " "source_message_id TEXT, " "replies_count INTEGER NOT NULL);"; return create_table(db, query, "threads"); } bool update_threadID_for_pending_threads_in_drafts(sqlite3 *db) { char *error; sqlite3_exec( db, "UPDATE drafts SET key = " "REPLACE(REPLACE(REPLACE(REPLACE(key, 'type4/', '')," "'type5/', ''),'type6/', ''),'type7/', '')" "WHERE key LIKE 'pending/%'", nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream stringStream; stringStream << "Error update pending threadIDs on drafts table: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } bool enable_write_ahead_logging_mode(sqlite3 *db) { char *error; sqlite3_exec(db, "PRAGMA journal_mode=wal;", nullptr, nullptr, &error); if (!error) { return true; } std::ostringstream stringStream; stringStream << "Error enabling write-ahead logging mode: " << error; Logger::log(stringStream.str()); sqlite3_free(error); return false; } bool create_metadata_table(sqlite3 *db) { std::string query = "CREATE TABLE IF NOT EXISTS metadata ( " "name TEXT UNIQUE PRIMARY KEY NOT NULL, " "data TEXT);"; return create_table(db, query, "metadata"); } void set_encryption_key(sqlite3 *db) { std::string set_encryption_key_query = "PRAGMA key = \"x'" + SQLiteQueryExecutor::encryptionKey + "'\";"; char *error_set_key; sqlite3_exec( db, set_encryption_key_query.c_str(), nullptr, nullptr, &error_set_key); if (error_set_key) { std::ostringstream error_message; error_message << "Failed to set encryption key: " << error_set_key; throw std::system_error( ECANCELED, std::generic_category(), error_message.str()); } } bool file_exists(const std::string &file_path) { std::ifstream file(file_path.c_str()); return file.good(); } void attempt_delete_file( const std::string &file_path, const char *error_message) { if (std::remove(file_path.c_str())) { throw std::system_error(errno, std::generic_category(), error_message); } } void attempt_rename_file( const std::string &old_path, const std::string &new_path, const char *error_message) { if (std::rename(old_path.c_str(), new_path.c_str())) { throw std::system_error(errno, std::generic_category(), error_message); } } void validate_encryption() { std::string temp_encrypted_db_path = SQLiteQueryExecutor::sqliteFilePath + "_temp_encrypted"; bool temp_encrypted_exists = file_exists(temp_encrypted_db_path); bool default_location_exists = file_exists(SQLiteQueryExecutor::sqliteFilePath); if (temp_encrypted_exists && default_location_exists) { Logger::log( "Previous encryption attempt failed. Repeating encryption process from " "the beginning."); attempt_delete_file( temp_encrypted_db_path, "Failed to delete corrupted encrypted database."); } else if (temp_encrypted_exists && !default_location_exists) { Logger::log( "Moving temporary encrypted database to default location failed in " "previous encryption attempt. Repeating rename step."); attempt_rename_file( temp_encrypted_db_path, SQLiteQueryExecutor::sqliteFilePath, "Failed to move encrypted database to default location."); return; } else if (!default_location_exists) { Logger::log( "Database not present yet. It will be created encrypted under default " "path."); return; } sqlite3 *db; sqlite3_open(SQLiteQueryExecutor::sqliteFilePath.c_str(), &db); set_encryption_key(db); char *key_validation_error; // According to SQLCipher documentation running some SELECT is the only way to // check for key validity sqlite3_exec( db, "SELECT COUNT(*) FROM sqlite_master;", nullptr, nullptr, &key_validation_error); sqlite3_close(db); if (!key_validation_error) { Logger::log( "Database exists under default path and it is correctly encrypted."); return; } Logger::log( "Validation of encryption key failed. Attempting encryption process."); sqlite3_open(SQLiteQueryExecutor::sqliteFilePath.c_str(), &db); std::string createEncryptedCopySQL = "ATTACH DATABASE '" + temp_encrypted_db_path + "' AS encrypted_comm " "KEY \"x'" + SQLiteQueryExecutor::encryptionKey + "'\";" "SELECT sqlcipher_export('encrypted_comm');" "DETACH DATABASE encrypted_comm;"; char *encryption_error; sqlite3_exec( db, createEncryptedCopySQL.c_str(), nullptr, nullptr, &encryption_error); if (encryption_error) { throw std::system_error( ECANCELED, std::generic_category(), "Failed to create encrypted copy of the original database."); } sqlite3_close(db); attempt_delete_file( SQLiteQueryExecutor::sqliteFilePath, "Failed to delete unencrypted database."); attempt_rename_file( temp_encrypted_db_path, SQLiteQueryExecutor::sqliteFilePath, "Failed to move encrypted database to default location."); Logger::log("Encryption completed successfully."); } typedef bool ShouldBeInTransaction; typedef std::pair, ShouldBeInTransaction> SQLiteMigration; std::vector> migrations{ {{1, {create_drafts_table, true}}, {2, {rename_threadID_to_key, true}}, {4, {create_persist_account_table, true}}, {5, {create_persist_sessions_table, true}}, {15, {create_media_table, true}}, {16, {drop_messages_table, true}}, {17, {recreate_messages_table, true}}, {18, {create_messages_idx_thread_time, true}}, {19, {create_media_idx_container, true}}, {20, {create_threads_table, true}}, {21, {update_threadID_for_pending_threads_in_drafts, true}}, {22, {enable_write_ahead_logging_mode, false}}, {23, {create_metadata_table, true}}}}; void SQLiteQueryExecutor::migrate() { validate_encryption(); sqlite3 *db; sqlite3_open(SQLiteQueryExecutor::sqliteFilePath.c_str(), &db); set_encryption_key(db); std::stringstream db_path; db_path << "db path: " << SQLiteQueryExecutor::sqliteFilePath.c_str() << std::endl; Logger::log(db_path.str()); sqlite3_stmt *user_version_stmt; sqlite3_prepare_v2( db, "PRAGMA user_version;", -1, &user_version_stmt, nullptr); sqlite3_step(user_version_stmt); int current_user_version = sqlite3_column_int(user_version_stmt, 0); sqlite3_finalize(user_version_stmt); std::stringstream version_msg; version_msg << "db version: " << current_user_version << std::endl; Logger::log(version_msg.str()); for (const auto &[idx, migration] : migrations) { if (idx <= current_user_version) { continue; } const auto &[applyMigration, shouldBeInTransaction] = migration; std::stringstream migration_msg; if (shouldBeInTransaction) { sqlite3_exec(db, "BEGIN TRANSACTION;", nullptr, nullptr, nullptr); } auto rc = applyMigration(db); if (!rc) { migration_msg << "migration " << idx << " failed." << std::endl; Logger::log(migration_msg.str()); break; } std::stringstream update_version; update_version << "PRAGMA user_version=" << idx << ";"; auto update_version_str = update_version.str(); sqlite3_exec(db, update_version_str.c_str(), nullptr, nullptr, nullptr); if (shouldBeInTransaction) { sqlite3_exec(db, "END TRANSACTION;", nullptr, nullptr, nullptr); } migration_msg << "migration " << idx << " succeeded." << std::endl; Logger::log(migration_msg.str()); } sqlite3_close(db); } auto &SQLiteQueryExecutor::getStorage() { static auto storage = make_storage( SQLiteQueryExecutor::sqliteFilePath, make_table( "drafts", make_column("key", &Draft::key, unique(), primary_key()), make_column("text", &Draft::text)), make_table( "messages", make_column("id", &Message::id, unique(), primary_key()), make_column("local_id", &Message::local_id), make_column("thread", &Message::thread), make_column("user", &Message::user), make_column("type", &Message::type), make_column("future_type", &Message::future_type), make_column("content", &Message::content), make_column("time", &Message::time)), make_table( "olm_persist_account", make_column("id", &OlmPersistAccount::id), make_column("account_data", &OlmPersistAccount::account_data)), make_table( "olm_persist_sessions", make_column("target_user_id", &OlmPersistSession::target_user_id), make_column("session_data", &OlmPersistSession::session_data)), make_table( "media", make_column("id", &Media::id, unique(), primary_key()), make_column("container", &Media::container), make_column("thread", &Media::thread), make_column("uri", &Media::uri), make_column("type", &Media::type), make_column("extras", &Media::extras)), make_table( "threads", make_column("id", &Thread::id, unique(), primary_key()), make_column("type", &Thread::type), make_column("name", &Thread::name), make_column("description", &Thread::description), make_column("color", &Thread::color), make_column("creation_time", &Thread::creation_time), make_column("parent_thread_id", &Thread::parent_thread_id), make_column("containing_thread_id", &Thread::containing_thread_id), make_column("community", &Thread::community), make_column("members", &Thread::members), make_column("roles", &Thread::roles), make_column("current_user", &Thread::current_user), make_column("source_message_id", &Thread::source_message_id), make_column("replies_count", &Thread::replies_count)), make_table( "metadata", make_column("name", &Metadata::name, unique(), primary_key()), make_column("data", &Metadata::data))); storage.on_open = set_encryption_key; return storage; } -void SQLiteQueryExecutor::initialize(std::string &databasePath) { - std::call_once(SQLiteQueryExecutor::initialized, [&databasePath]() { - SQLiteQueryExecutor::sqliteFilePath = databasePath; - CommSecureStore commSecureStore; - folly::Optional maybeEncryptionKey = - commSecureStore.get(SQLiteQueryExecutor::secureStoreEncryptionKeyID); - - if (maybeEncryptionKey) { - SQLiteQueryExecutor::encryptionKey = maybeEncryptionKey.value(); - return; - } - std::string encryptionKey = comm::crypto::Tools::generateRandomHexString( - SQLiteQueryExecutor::sqlcipherEncryptionKeySize); - commSecureStore.set( - SQLiteQueryExecutor::secureStoreEncryptionKeyID, encryptionKey); - SQLiteQueryExecutor::encryptionKey = encryptionKey; - }); -} - SQLiteQueryExecutor::SQLiteQueryExecutor() { this->migrate(); } std::string SQLiteQueryExecutor::getDraft(std::string key) const { std::unique_ptr draft = SQLiteQueryExecutor::getStorage().get_pointer(key); return (draft == nullptr) ? "" : draft->text; } void SQLiteQueryExecutor::updateDraft(std::string key, std::string text) const { Draft draft = {key, text}; SQLiteQueryExecutor::getStorage().replace(draft); } bool SQLiteQueryExecutor::moveDraft(std::string oldKey, std::string newKey) const { std::unique_ptr draft = SQLiteQueryExecutor::getStorage().get_pointer(oldKey); if (draft == nullptr) { return false; } draft->key = newKey; SQLiteQueryExecutor::getStorage().replace(*draft); SQLiteQueryExecutor::getStorage().remove(oldKey); return true; } std::vector SQLiteQueryExecutor::getAllDrafts() const { return SQLiteQueryExecutor::getStorage().get_all(); } void SQLiteQueryExecutor::removeAllDrafts() const { SQLiteQueryExecutor::getStorage().remove_all(); } void SQLiteQueryExecutor::removeAllMessages() const { SQLiteQueryExecutor::getStorage().remove_all(); } std::vector>> SQLiteQueryExecutor::getAllMessages() const { auto rows = SQLiteQueryExecutor::getStorage().select( columns( &Message::id, &Message::local_id, &Message::thread, &Message::user, &Message::type, &Message::future_type, &Message::content, &Message::time, &Media::id, &Media::container, &Media::thread, &Media::uri, &Media::type, &Media::extras), left_join(on(c(&Message::id) == &Media::container)), order_by(&Message::id)); std::vector>> allMessages; allMessages.reserve(rows.size()); std::string prev_msg_idx{}; for (auto &row : rows) { auto msg_id = std::get<0>(row); if (msg_id == prev_msg_idx) { allMessages.back().second.push_back(Media{ std::get<8>(row), std::move(std::get<9>(row)), std::move(std::get<10>(row)), std::move(std::get<11>(row)), std::move(std::get<12>(row)), std::move(std::get<13>(row)), }); } else { std::vector mediaForMsg; if (!std::get<8>(row).empty()) { mediaForMsg.push_back(Media{ std::get<8>(row), std::move(std::get<9>(row)), std::move(std::get<10>(row)), std::move(std::get<11>(row)), std::move(std::get<12>(row)), std::move(std::get<13>(row)), }); } allMessages.push_back(std::make_pair( Message{ msg_id, std::move(std::get<1>(row)), std::move(std::get<2>(row)), std::move(std::get<3>(row)), std::get<4>(row), std::move(std::get<5>(row)), std::move(std::get<6>(row)), std::get<7>(row)}, mediaForMsg)); prev_msg_idx = msg_id; } } return allMessages; } void SQLiteQueryExecutor::removeMessages( const std::vector &ids) const { SQLiteQueryExecutor::getStorage().remove_all( where(in(&Message::id, ids))); } void SQLiteQueryExecutor::removeMessagesForThreads( const std::vector &threadIDs) const { SQLiteQueryExecutor::getStorage().remove_all( where(in(&Message::thread, threadIDs))); } void SQLiteQueryExecutor::replaceMessage(const Message &message) const { SQLiteQueryExecutor::getStorage().replace(message); } void SQLiteQueryExecutor::rekeyMessage(std::string from, std::string to) const { auto msg = SQLiteQueryExecutor::getStorage().get(from); msg.id = to; SQLiteQueryExecutor::getStorage().replace(msg); SQLiteQueryExecutor::getStorage().remove(from); } void SQLiteQueryExecutor::removeAllMedia() const { SQLiteQueryExecutor::getStorage().remove_all(); } void SQLiteQueryExecutor::removeMediaForMessages( const std::vector &msg_ids) const { SQLiteQueryExecutor::getStorage().remove_all( where(in(&Media::container, msg_ids))); } void SQLiteQueryExecutor::removeMediaForMessage(std::string msg_id) const { SQLiteQueryExecutor::getStorage().remove_all( where(c(&Media::container) == msg_id)); } void SQLiteQueryExecutor::removeMediaForThreads( const std::vector &thread_ids) const { SQLiteQueryExecutor::getStorage().remove_all( where(in(&Media::thread, thread_ids))); } void SQLiteQueryExecutor::replaceMedia(const Media &media) const { SQLiteQueryExecutor::getStorage().replace(media); } void SQLiteQueryExecutor::rekeyMediaContainers(std::string from, std::string to) const { SQLiteQueryExecutor::getStorage().update_all( set(c(&Media::container) = to), where(c(&Media::container) == from)); } std::vector SQLiteQueryExecutor::getAllThreads() const { return SQLiteQueryExecutor::getStorage().get_all(); }; void SQLiteQueryExecutor::removeThreads(std::vector ids) const { SQLiteQueryExecutor::getStorage().remove_all( where(in(&Thread::id, ids))); }; void SQLiteQueryExecutor::replaceThread(const Thread &thread) const { SQLiteQueryExecutor::getStorage().replace(thread); }; void SQLiteQueryExecutor::removeAllThreads() const { SQLiteQueryExecutor::getStorage().remove_all(); }; void SQLiteQueryExecutor::beginTransaction() const { SQLiteQueryExecutor::getStorage().begin_transaction(); } void SQLiteQueryExecutor::commitTransaction() const { SQLiteQueryExecutor::getStorage().commit(); } void SQLiteQueryExecutor::rollbackTransaction() const { SQLiteQueryExecutor::getStorage().rollback(); } std::vector SQLiteQueryExecutor::getOlmPersistSessionsData() const { return SQLiteQueryExecutor::getStorage().get_all(); } folly::Optional SQLiteQueryExecutor::getOlmPersistAccountData() const { std::vector result = SQLiteQueryExecutor::getStorage().get_all(); if (result.size() > 1) { throw std::system_error( ECANCELED, std::generic_category(), "Multiple records found for the olm_persist_account table"); } return (result.size() == 0) ? folly::none : folly::Optional(result[0].account_data); } void SQLiteQueryExecutor::storeOlmPersistData(crypto::Persist persist) const { OlmPersistAccount persistAccount = { ACCOUNT_ID, std::string(persist.account.begin(), persist.account.end())}; SQLiteQueryExecutor::getStorage().replace(persistAccount); for (auto it = persist.sessions.begin(); it != persist.sessions.end(); it++) { OlmPersistSession persistSession = { it->first, std::string(it->second.begin(), it->second.end())}; SQLiteQueryExecutor::getStorage().replace(persistSession); } } void SQLiteQueryExecutor::setNotifyToken(std::string token) const { Metadata entry{ "notify_token", token, }; SQLiteQueryExecutor::getStorage().replace(entry); } void SQLiteQueryExecutor::clearNotifyToken() const { SQLiteQueryExecutor::getStorage().remove("notify_token"); } } // namespace comm diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h index 4d598f227..90804deed 100644 --- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h +++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h @@ -1,61 +1,55 @@ #pragma once #include "../CryptoTools/Persist.h" #include "DatabaseQueryExecutor.h" #include "entities/Draft.h" -#include #include namespace comm { class SQLiteQueryExecutor : public DatabaseQueryExecutor { void migrate(); static auto &getStorage(); - static std::once_flag initialized; - static int sqlcipherEncryptionKeySize; - static std::string secureStoreEncryptionKeyID; - public: static std::string sqliteFilePath; static std::string encryptionKey; SQLiteQueryExecutor(); - static void initialize(std::string &databasePath); std::string getDraft(std::string key) const override; void updateDraft(std::string key, std::string text) const override; bool moveDraft(std::string oldKey, std::string newKey) const override; std::vector getAllDrafts() const override; void removeAllDrafts() const override; void removeAllMessages() const override; std::vector>> getAllMessages() const override; void removeMessages(const std::vector &ids) const override; void removeMessagesForThreads( const std::vector &threadIDs) const override; void replaceMessage(const Message &message) const override; void rekeyMessage(std::string from, std::string to) const override; void removeAllMedia() const override; void removeMediaForMessages( const std::vector &msg_ids) const override; void removeMediaForMessage(std::string msg_id) const override; void removeMediaForThreads( const std::vector &thread_ids) const override; void replaceMedia(const Media &media) const override; void rekeyMediaContainers(std::string from, std::string to) const override; std::vector getAllThreads() const override; void removeThreads(std::vector ids) const override; void replaceThread(const Thread &thread) const override; void removeAllThreads() const override; void beginTransaction() const override; void commitTransaction() const override; void rollbackTransaction() const override; std::vector getOlmPersistSessionsData() const override; folly::Optional getOlmPersistAccountData() const override; void storeOlmPersistData(crypto::Persist persist) const override; void setNotifyToken(std::string token) const override; void clearNotifyToken() const override; }; } // namespace comm diff --git a/native/ios/Comm.xcodeproj/project.pbxproj b/native/ios/Comm.xcodeproj/project.pbxproj index 10f19e9c5..a0335f7bf 100644 --- a/native/ios/Comm.xcodeproj/project.pbxproj +++ b/native/ios/Comm.xcodeproj/project.pbxproj @@ -1,1351 +1,1192 @@ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 2DDA0AE067906E18B83A455C /* ClientGetReadReactor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DDA00CA889DFF0ECB7E338D /* ClientGetReadReactor.cpp */; }; 71009A7726FDCA67002C8453 /* tunnelbroker.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 71009A7326FDCA67002C8453 /* tunnelbroker.pb.cc */; }; 71009A7826FDCA67002C8453 /* tunnelbroker.grpc.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 71009A7526FDCA67002C8453 /* tunnelbroker.grpc.pb.cc */; }; 71009A7B26FDCD72002C8453 /* Client.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71009A7926FDCD71002C8453 /* Client.cpp */; }; 71142A7726C2650B0039DCBD /* CommSecureStoreIOSWrapper.mm in Sources */ = {isa = PBXBuildFile; fileRef = 71142A7626C2650A0039DCBD /* CommSecureStoreIOSWrapper.mm */; }; 711B408425DA97F9005F8F06 /* dummy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F26E81B24440D87004049C6 /* dummy.swift */; }; 713EE41126C66B80003D7C48 /* CryptoTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 713EE41026C66B80003D7C48 /* CryptoTest.mm */; }; 71762A75270D8AAE00F565ED /* PlatformSpecificTools.mm in Sources */ = {isa = PBXBuildFile; fileRef = 71762A74270D8AAE00F565ED /* PlatformSpecificTools.mm */; }; 718DE99E2653D41C00365824 /* WorkerThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 718DE99C2653D41C00365824 /* WorkerThread.cpp */; }; 71BE84492636A944002849D2 /* NativeModules.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71BE843B2636A944002849D2 /* NativeModules.cpp */; }; 71BE844A2636A944002849D2 /* CommCoreModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71BE843C2636A944002849D2 /* CommCoreModule.cpp */; }; 71BE844B2636A944002849D2 /* SQLiteQueryExecutor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71BE84412636A944002849D2 /* SQLiteQueryExecutor.cpp */; }; 71BF5B7126B3FF0900EDE27D /* Session.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71BF5B6F26B3FF0900EDE27D /* Session.cpp */; }; 71BF5B7526B401D300EDE27D /* Tools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71BF5B7326B401D300EDE27D /* Tools.cpp */; }; 71BF5B7F26BBDD7400EDE27D /* CryptoModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71BF5B7B26BBDA6100EDE27D /* CryptoModule.cpp */; }; 71CA4A64262DA8E500835C89 /* Logger.mm in Sources */ = {isa = PBXBuildFile; fileRef = 71CA4A63262DA8E500835C89 /* Logger.mm */; }; 71CA4AEC262F236100835C89 /* Tools.mm in Sources */ = {isa = PBXBuildFile; fileRef = 71CA4AEB262F236100835C89 /* Tools.mm */; }; 71D4D7CC26C50B1000FCDBCD /* CommSecureStore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 71D4D7CB26C50B1000FCDBCD /* CommSecureStore.mm */; }; 724995D527B4103A00323FCE /* NotificationService.mm in Sources */ = {isa = PBXBuildFile; fileRef = 724995D427B4103A00323FCE /* NotificationService.mm */; }; 724995D927B4103A00323FCE /* NotificationService.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 724995D127B4103A00323FCE /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 724995FB27BA9E8D00323FCE /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 724995FA27BA9E8C00323FCE /* UserNotifications.framework */; }; 726E5D752731A4790032361D /* NetworkModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 726E5D732731A4790032361D /* NetworkModule.cpp */; }; 726E5D782731A5E10032361D /* GlobalNetworkSingleton.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 726E5D762731A5E10032361D /* GlobalNetworkSingleton.cpp */; }; - 73F3F24A0D87D6EB04DD550E /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE22AD93CDB4FE5C1849A5AA /* ExpoModulesProvider.swift */; }; 7F761E602201141E001B6FB7 /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F761E292201141E001B6FB7 /* JavaScriptCore.framework */; }; 7F788C2C248AA2140098F071 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7F788C2B248AA2130098F071 /* SplashScreen.storyboard */; }; 7F8D602126535E060053CB29 /* OpenSans-Semibold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7F8D601E26535E060053CB29 /* OpenSans-Semibold.ttf */; }; 7F8D602226535E060053CB29 /* Anaheim-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7F8D601F26535E060053CB29 /* Anaheim-Regular.ttf */; }; 7F8D602326535E060053CB29 /* OpenSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7F8D602026535E060053CB29 /* OpenSans-Regular.ttf */; }; 7F8D602826535F240053CB29 /* IBMPlexSans-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7F8D602726535EEE0053CB29 /* IBMPlexSans-Bold.ttf */; }; 7F8D602926535F2A0053CB29 /* IBMPlexSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7F8D602626535EEE0053CB29 /* IBMPlexSans-Regular.ttf */; }; B71AFF1F265EDD8600B22352 /* IBMPlexSans-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B71AFF1E265EDD8600B22352 /* IBMPlexSans-Medium.ttf */; }; B723460726979250009A0709 /* swmansion.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B723460626979250009A0709 /* swmansion.ttf */; }; B7BEE749279B3FB6009CCA35 /* GRPCStreamHostObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B7BEE744279B3E20009CCA35 /* GRPCStreamHostObject.cpp */; }; - CB1648AD27CFBBBB00394D9D /* ClientGetReadReactor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DDA00CA889DFF0ECB7E338D /* ClientGetReadReactor.cpp */; }; - CB1648AF27CFBE6A00394D9D /* CryptoModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71BF5B7B26BBDA6100EDE27D /* CryptoModule.cpp */; }; - CB3C621127CE4A320054F24C /* Logger.mm in Sources */ = {isa = PBXBuildFile; fileRef = 71CA4A63262DA8E500835C89 /* Logger.mm */; }; - CB3C621227CE65030054F24C /* CommSecureStoreIOSWrapper.mm in Sources */ = {isa = PBXBuildFile; fileRef = 71142A7626C2650A0039DCBD /* CommSecureStoreIOSWrapper.mm */; }; - CB4821A527CF9F38001AB7E1 /* CommSecureStore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 71D4D7CB26C50B1000FCDBCD /* CommSecureStore.mm */; }; - CB4821A627CFB153001AB7E1 /* Client.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71009A7926FDCD71002C8453 /* Client.cpp */; }; - CB4821A827CFB153001AB7E1 /* tunnelbroker.grpc.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 71009A7526FDCA67002C8453 /* tunnelbroker.grpc.pb.cc */; }; - CB4821A927CFB153001AB7E1 /* WorkerThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 718DE99C2653D41C00365824 /* WorkerThread.cpp */; }; - CB4821AA27CFB153001AB7E1 /* Tools.mm in Sources */ = {isa = PBXBuildFile; fileRef = 71CA4AEB262F236100835C89 /* Tools.mm */; }; - CB4821AB27CFB17C001AB7E1 /* tunnelbroker.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 71009A7326FDCA67002C8453 /* tunnelbroker.pb.cc */; }; - CB4821AC27CFB17C001AB7E1 /* Session.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71BF5B6F26B3FF0900EDE27D /* Session.cpp */; }; - CB4821AD27CFB17C001AB7E1 /* NetworkModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 726E5D732731A4790032361D /* NetworkModule.cpp */; }; - CB4821AE27CFB187001AB7E1 /* Tools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71BF5B7326B401D300EDE27D /* Tools.cpp */; }; - CB4821AF27CFB19D001AB7E1 /* PlatformSpecificTools.mm in Sources */ = {isa = PBXBuildFile; fileRef = 71762A74270D8AAE00F565ED /* PlatformSpecificTools.mm */; }; - CB4821B127CFB1FA001AB7E1 /* GlobalNetworkSingleton.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 726E5D762731A5E10032361D /* GlobalNetworkSingleton.cpp */; }; - CB4821B227CFB20E001AB7E1 /* SQLiteQueryExecutor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 71BE84412636A944002849D2 /* SQLiteQueryExecutor.cpp */; }; D7DB6E0F85B2DBE15B01EC21 /* libPods-Comm.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 994BEBDD4E4959F69CEA0BC3 /* libPods-Comm.a */; }; - F02C296C528B51ADAB5AA19D /* libPods-NotificationService.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3EE4DCB430B05EC9DE7D7B01 /* libPods-NotificationService.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 713EE40B26C6676B003D7C48 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; proxyType = 1; remoteGlobalIDString = 13B07F861A680F5B00A75B9A; remoteInfo = Comm; }; 724995D727B4103A00323FCE /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; proxyType = 1; remoteGlobalIDString = 724995D027B4103A00323FCE; remoteInfo = NotificationService; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 724995DA27B4103A00323FCE /* Embed App Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 13; files = ( 724995D927B4103A00323FCE /* NotificationService.appex in Embed App Extensions */, ); name = "Embed App Extensions"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 13B07F961A680F5B00A75B9A /* Comm.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Comm.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Comm/AppDelegate.h; sourceTree = ""; }; 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = Comm/AppDelegate.mm; sourceTree = ""; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Comm/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.release.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.release.plist; path = Comm/Info.release.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Comm/main.m; sourceTree = ""; }; 2DDA00CA889DFF0ECB7E338D /* ClientGetReadReactor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClientGetReadReactor.cpp; sourceTree = ""; }; 2DDA05D6D8D20D885F22F82C /* SocketStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SocketStatus.h; sourceTree = ""; }; 2DDA0A22FECC9DAA5C19C35D /* Metadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Metadata.h; sourceTree = ""; }; - 3EE4DCB430B05EC9DE7D7B01 /* libPods-NotificationService.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NotificationService.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3EEB3E70587B0ADAD05237B0 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Comm/ExpoModulesProvider.swift"; sourceTree = ""; }; 71009A7326FDCA67002C8453 /* tunnelbroker.pb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tunnelbroker.pb.cc; sourceTree = ""; }; 71009A7426FDCA67002C8453 /* tunnelbroker.pb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tunnelbroker.pb.h; sourceTree = ""; }; 71009A7526FDCA67002C8453 /* tunnelbroker.grpc.pb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tunnelbroker.grpc.pb.cc; sourceTree = ""; }; 71009A7626FDCA67002C8453 /* tunnelbroker.grpc.pb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tunnelbroker.grpc.pb.h; sourceTree = ""; }; 71009A7926FDCD71002C8453 /* Client.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Client.cpp; sourceTree = ""; }; 71009A7A26FDCD71002C8453 /* Client.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Client.h; sourceTree = ""; }; 71142A7526C2650A0039DCBD /* CommSecureStoreIOSWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CommSecureStoreIOSWrapper.h; path = Comm/CommSecureStoreIOSWrapper.h; sourceTree = ""; }; 71142A7626C2650A0039DCBD /* CommSecureStoreIOSWrapper.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CommSecureStoreIOSWrapper.mm; path = Comm/CommSecureStoreIOSWrapper.mm; sourceTree = ""; }; 711CF80E25DC096000A00FBD /* libFolly.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libFolly.a; sourceTree = BUILT_PRODUCTS_DIR; }; 713EE40626C6676B003D7C48 /* CommTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CommTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 713EE40A26C6676B003D7C48 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 713EE41026C66B80003D7C48 /* CryptoTest.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CryptoTest.mm; sourceTree = ""; }; 71762A74270D8AAE00F565ED /* PlatformSpecificTools.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = PlatformSpecificTools.mm; path = Comm/PlatformSpecificTools.mm; sourceTree = ""; }; 718DE99C2653D41C00365824 /* WorkerThread.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = WorkerThread.cpp; sourceTree = ""; }; 718DE99D2653D41C00365824 /* WorkerThread.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WorkerThread.h; sourceTree = ""; }; 71B8CCBD26BD4DEB0040C0A2 /* CommSecureStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CommSecureStore.h; sourceTree = ""; }; 71BE84392636A944002849D2 /* Logger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logger.h; sourceTree = ""; }; 71BE843B2636A944002849D2 /* NativeModules.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NativeModules.cpp; sourceTree = ""; }; 71BE843C2636A944002849D2 /* CommCoreModule.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CommCoreModule.cpp; sourceTree = ""; }; 71BE843D2636A944002849D2 /* NativeModules.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NativeModules.h; sourceTree = ""; }; 71BE843E2636A944002849D2 /* CommCoreModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommCoreModule.h; sourceTree = ""; }; 71BE84402636A944002849D2 /* DatabaseQueryExecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DatabaseQueryExecutor.h; sourceTree = ""; }; 71BE84412636A944002849D2 /* SQLiteQueryExecutor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SQLiteQueryExecutor.cpp; sourceTree = ""; }; 71BE84422636A944002849D2 /* SQLiteQueryExecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQLiteQueryExecutor.h; sourceTree = ""; }; 71BE84432636A944002849D2 /* DatabaseManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DatabaseManager.h; sourceTree = ""; }; 71BE84452636A944002849D2 /* Draft.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Draft.h; sourceTree = ""; }; 71BE84482636A944002849D2 /* sqlite_orm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sqlite_orm.h; sourceTree = ""; }; 71BF5B6F26B3FF0900EDE27D /* Session.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Session.cpp; sourceTree = ""; }; 71BF5B7026B3FF0900EDE27D /* Session.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Session.h; sourceTree = ""; }; 71BF5B7226B3FFBC00EDE27D /* Persist.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Persist.h; sourceTree = ""; }; 71BF5B7326B401D300EDE27D /* Tools.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Tools.cpp; sourceTree = ""; }; 71BF5B7426B401D300EDE27D /* Tools.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Tools.h; sourceTree = ""; }; 71BF5B7A26BBDA6000EDE27D /* CryptoModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CryptoModule.h; sourceTree = ""; }; 71BF5B7B26BBDA6100EDE27D /* CryptoModule.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CryptoModule.cpp; sourceTree = ""; }; 71CA4A63262DA8E500835C89 /* Logger.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Logger.mm; path = Comm/Logger.mm; sourceTree = ""; }; 71CA4AEA262F230A00835C89 /* Tools.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Tools.h; path = Comm/Tools.h; sourceTree = ""; }; 71CA4AEB262F236100835C89 /* Tools.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = Tools.mm; path = Comm/Tools.mm; sourceTree = ""; }; 71D4D7CB26C50B1000FCDBCD /* CommSecureStore.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CommSecureStore.mm; path = Comm/CommSecureStore.mm; sourceTree = ""; }; 71DC160C270C43D300822863 /* PlatformSpecificTools.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlatformSpecificTools.h; sourceTree = ""; }; 724995D127B4103A00323FCE /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 724995D327B4103A00323FCE /* NotificationService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NotificationService.h; sourceTree = ""; }; 724995D427B4103A00323FCE /* NotificationService.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NotificationService.mm; sourceTree = ""; }; 724995D627B4103A00323FCE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 724995FA27BA9E8C00323FCE /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; }; 726E5D732731A4790032361D /* NetworkModule.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkModule.cpp; sourceTree = ""; }; 726E5D742731A4790032361D /* NetworkModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NetworkModule.h; sourceTree = ""; }; 726E5D762731A5E10032361D /* GlobalNetworkSingleton.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = GlobalNetworkSingleton.cpp; sourceTree = ""; }; 726E5D772731A5E10032361D /* GlobalNetworkSingleton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GlobalNetworkSingleton.h; sourceTree = ""; }; 7F26E81B24440D87004049C6 /* dummy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = dummy.swift; sourceTree = ""; }; 7F554F822332D58B007CB9F7 /* Info.debug.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.debug.plist; path = Comm/Info.debug.plist; sourceTree = ""; }; 7F761E292201141E001B6FB7 /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; 7F788C2B248AA2130098F071 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = SplashScreen.storyboard; sourceTree = ""; }; 7F8D601E26535E060053CB29 /* OpenSans-Semibold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-Semibold.ttf"; path = "Resources/OpenSans-Semibold.ttf"; sourceTree = ""; }; 7F8D601F26535E060053CB29 /* Anaheim-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "Anaheim-Regular.ttf"; path = "Resources/Anaheim-Regular.ttf"; sourceTree = ""; }; 7F8D602026535E060053CB29 /* OpenSans-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-Regular.ttf"; path = "Resources/OpenSans-Regular.ttf"; sourceTree = ""; }; 7F8D602626535EEE0053CB29 /* IBMPlexSans-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "IBMPlexSans-Regular.ttf"; path = "Resources/IBMPlexSans-Regular.ttf"; sourceTree = ""; }; 7F8D602726535EEE0053CB29 /* IBMPlexSans-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "IBMPlexSans-Bold.ttf"; path = "Resources/IBMPlexSans-Bold.ttf"; sourceTree = ""; }; 7FCEA2DC2444010B004017B1 /* Comm-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Comm-Bridging-Header.h"; sourceTree = ""; }; 7FCFD8BD1E81B8DF00629B0E /* Comm.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Comm.entitlements; path = Comm/Comm.entitlements; sourceTree = ""; }; - 891D1495EE1F375F3AF6C7ED /* Pods-NotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.debug.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.debug.xcconfig"; sourceTree = ""; }; - 913E5A7BDECB327E3DE11053 /* Pods-NotificationService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.release.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.release.xcconfig"; sourceTree = ""; }; 994BEBDD4E4959F69CEA0BC3 /* libPods-Comm.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Comm.a"; sourceTree = BUILT_PRODUCTS_DIR; }; B7055C6B26E477CF00BE0548 /* MessageStoreOperations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MessageStoreOperations.h; sourceTree = ""; }; B70FBC1226B047050040F480 /* Message.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Message.h; sourceTree = ""; }; B71AFF1E265EDD8600B22352 /* IBMPlexSans-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "IBMPlexSans-Medium.ttf"; path = "Resources/IBMPlexSans-Medium.ttf"; sourceTree = ""; }; B723460626979250009A0709 /* swmansion.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = swmansion.ttf; path = Resources/swmansion.ttf; sourceTree = ""; }; B72879B827A865EF008A04CC /* ClientGetReadReactor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ClientGetReadReactor.h; sourceTree = ""; }; B7906F692720905A009BBBF5 /* ThreadStoreOperations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ThreadStoreOperations.h; sourceTree = ""; }; B7906F6A27209091009BBBF5 /* OlmPersistAccount.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OlmPersistAccount.h; sourceTree = ""; }; B7906F6B27209091009BBBF5 /* OlmPersistSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OlmPersistSession.h; sourceTree = ""; }; B7906F6C27209091009BBBF5 /* Thread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Thread.h; sourceTree = ""; }; B7BEE744279B3E20009CCA35 /* GRPCStreamHostObject.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = GRPCStreamHostObject.cpp; sourceTree = ""; }; B7BEE748279B3F2E009CCA35 /* GRPCStreamHostObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GRPCStreamHostObject.h; sourceTree = ""; }; B7E937CA26F448E700022A7C /* Media.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Media.h; sourceTree = ""; }; C562A7004903539402D988CE /* Pods-Comm.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Comm.release.xcconfig"; path = "Target Support Files/Pods-Comm/Pods-Comm.release.xcconfig"; sourceTree = ""; }; - CB1648B027CFD07E00394D9D /* CommRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = CommRelease.entitlements; path = Comm/CommRelease.entitlements; sourceTree = ""; }; - CB30C12327D0ACF700FBE8DE /* NotificationService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationService.entitlements; sourceTree = ""; }; - CB3C621327CE66540054F24C /* libEXSecureStore.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libEXSecureStore.a; sourceTree = BUILT_PRODUCTS_DIR; }; - EE22AD93CDB4FE5C1849A5AA /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-NotificationService/ExpoModulesProvider.swift"; sourceTree = ""; }; F53DA7B3F26C2798DCE74A94 /* Pods-Comm.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Comm.debug.xcconfig"; path = "Target Support Files/Pods-Comm/Pods-Comm.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 7F761E602201141E001B6FB7 /* JavaScriptCore.framework in Frameworks */, D7DB6E0F85B2DBE15B01EC21 /* libPods-Comm.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 713EE40326C6676B003D7C48 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 724995CE27B4103A00323FCE /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 724995FB27BA9E8D00323FCE /* UserNotifications.framework in Frameworks */, - F02C296C528B51ADAB5AA19D /* libPods-NotificationService.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 13B07FAE1A68108700A75B9A /* Comm */ = { isa = PBXGroup; children = ( - CB1648B027CFD07E00394D9D /* CommRelease.entitlements */, 71B8CCB626BD30EC0040C0A2 /* CommCoreImplementations */, 7F788C2B248AA2130098F071 /* SplashScreen.storyboard */, 7FCFD8BD1E81B8DF00629B0E /* Comm.entitlements */, 13B07FAF1A68108700A75B9A /* AppDelegate.h */, 13B07FB01A68108700A75B9A /* AppDelegate.mm */, 13B07FB51A68108700A75B9A /* Images.xcassets */, 7F554F822332D58B007CB9F7 /* Info.debug.plist */, 13B07FB61A68108700A75B9A /* Info.release.plist */, 13B07FB71A68108700A75B9A /* main.m */, 7FCEA2DC2444010B004017B1 /* Comm-Bridging-Header.h */, 7F26E81B24440D87004049C6 /* dummy.swift */, ); name = Comm; sourceTree = ""; }; 6534411766BE4CA4B0AB0A78 /* Resources */ = { isa = PBXGroup; children = ( B723460626979250009A0709 /* swmansion.ttf */, 7F8D601F26535E060053CB29 /* Anaheim-Regular.ttf */, 7F8D602026535E060053CB29 /* OpenSans-Regular.ttf */, B71AFF1E265EDD8600B22352 /* IBMPlexSans-Medium.ttf */, 7F8D601E26535E060053CB29 /* OpenSans-Semibold.ttf */, 7F8D602726535EEE0053CB29 /* IBMPlexSans-Bold.ttf */, 7F8D602626535EEE0053CB29 /* IBMPlexSans-Regular.ttf */, ); name = Resources; sourceTree = ""; }; 713EE40726C6676B003D7C48 /* CommTests */ = { isa = PBXGroup; children = ( 713EE41026C66B80003D7C48 /* CryptoTest.mm */, 713EE40A26C6676B003D7C48 /* Info.plist */, ); path = CommTests; sourceTree = ""; }; 718A3C0426F22BD100F04A8D /* grpc */ = { isa = PBXGroup; children = ( 71009A7926FDCD71002C8453 /* Client.cpp */, 71009A7A26FDCD71002C8453 /* Client.h */, B7BEE744279B3E20009CCA35 /* GRPCStreamHostObject.cpp */, B7BEE748279B3F2E009CCA35 /* GRPCStreamHostObject.h */, 718A3C0626F22D0A00F04A8D /* _generated */, B72879B827A865EF008A04CC /* ClientGetReadReactor.h */, 2DDA00CA889DFF0ECB7E338D /* ClientGetReadReactor.cpp */, ); path = grpc; sourceTree = ""; }; 718A3C0626F22D0A00F04A8D /* _generated */ = { isa = PBXGroup; children = ( 71009A7526FDCA67002C8453 /* tunnelbroker.grpc.pb.cc */, 71009A7626FDCA67002C8453 /* tunnelbroker.grpc.pb.h */, 71009A7326FDCA67002C8453 /* tunnelbroker.pb.cc */, 71009A7426FDCA67002C8453 /* tunnelbroker.pb.h */, ); path = _generated; sourceTree = ""; }; 71B8CCB626BD30EC0040C0A2 /* CommCoreImplementations */ = { isa = PBXGroup; children = ( 71762A74270D8AAE00F565ED /* PlatformSpecificTools.mm */, 71D4D7CB26C50B1000FCDBCD /* CommSecureStore.mm */, 71142A7526C2650A0039DCBD /* CommSecureStoreIOSWrapper.h */, 71142A7626C2650A0039DCBD /* CommSecureStoreIOSWrapper.mm */, 71CA4AEA262F230A00835C89 /* Tools.h */, 71CA4AEB262F236100835C89 /* Tools.mm */, 71CA4A63262DA8E500835C89 /* Logger.mm */, ); name = CommCoreImplementations; sourceTree = ""; }; 71BE84362636A944002849D2 /* cpp */ = { isa = PBXGroup; children = ( 71BE84372636A944002849D2 /* CommonCpp */, 71BE84462636A944002849D2 /* lib */, ); name = cpp; path = ../cpp; sourceTree = ""; }; 71BE84372636A944002849D2 /* CommonCpp */ = { isa = PBXGroup; children = ( 71F971B4270726C000DDC5BF /* _generated */, 718A3C0426F22BD100F04A8D /* grpc */, 71BF5B6A26B3FCFF00EDE27D /* CryptoTools */, 71BE84382636A944002849D2 /* Tools */, 71BE843A2636A944002849D2 /* NativeModules */, 71BE843F2636A944002849D2 /* DatabaseManagers */, ); path = CommonCpp; sourceTree = ""; }; 71BE84382636A944002849D2 /* Tools */ = { isa = PBXGroup; children = ( 71B8CCBD26BD4DEB0040C0A2 /* CommSecureStore.h */, 718DE99C2653D41C00365824 /* WorkerThread.cpp */, 718DE99D2653D41C00365824 /* WorkerThread.h */, 71BE84392636A944002849D2 /* Logger.h */, 71DC160C270C43D300822863 /* PlatformSpecificTools.h */, ); path = Tools; sourceTree = ""; }; 71BE843A2636A944002849D2 /* NativeModules */ = { isa = PBXGroup; children = ( 726E5D722731A4240032361D /* InternalModules */, 71BE843C2636A944002849D2 /* CommCoreModule.cpp */, 71BE843E2636A944002849D2 /* CommCoreModule.h */, B7055C6B26E477CF00BE0548 /* MessageStoreOperations.h */, B7906F692720905A009BBBF5 /* ThreadStoreOperations.h */, ); path = NativeModules; sourceTree = ""; }; 71BE843F2636A944002849D2 /* DatabaseManagers */ = { isa = PBXGroup; children = ( 71BE84402636A944002849D2 /* DatabaseQueryExecutor.h */, 71BE84412636A944002849D2 /* SQLiteQueryExecutor.cpp */, 71BE84422636A944002849D2 /* SQLiteQueryExecutor.h */, 71BE84432636A944002849D2 /* DatabaseManager.h */, 71BE84442636A944002849D2 /* entities */, ); path = DatabaseManagers; sourceTree = ""; }; 71BE84442636A944002849D2 /* entities */ = { isa = PBXGroup; children = ( B7906F6A27209091009BBBF5 /* OlmPersistAccount.h */, B7906F6B27209091009BBBF5 /* OlmPersistSession.h */, B7906F6C27209091009BBBF5 /* Thread.h */, 71BE84452636A944002849D2 /* Draft.h */, B70FBC1226B047050040F480 /* Message.h */, B7E937CA26F448E700022A7C /* Media.h */, 2DDA0A22FECC9DAA5C19C35D /* Metadata.h */, ); path = entities; sourceTree = ""; }; 71BE84462636A944002849D2 /* lib */ = { isa = PBXGroup; children = ( 71BE84472636A944002849D2 /* sqlite_orm */, ); path = lib; sourceTree = ""; }; 71BE84472636A944002849D2 /* sqlite_orm */ = { isa = PBXGroup; children = ( 71BE84482636A944002849D2 /* sqlite_orm.h */, ); path = sqlite_orm; sourceTree = ""; }; 71BF5B6A26B3FCFF00EDE27D /* CryptoTools */ = { isa = PBXGroup; children = ( 71BF5B7B26BBDA6100EDE27D /* CryptoModule.cpp */, 71BF5B7A26BBDA6000EDE27D /* CryptoModule.h */, 71BF5B6F26B3FF0900EDE27D /* Session.cpp */, 71BF5B7026B3FF0900EDE27D /* Session.h */, 71BF5B7226B3FFBC00EDE27D /* Persist.h */, 71BF5B7326B401D300EDE27D /* Tools.cpp */, 71BF5B7426B401D300EDE27D /* Tools.h */, ); path = CryptoTools; sourceTree = ""; }; 71F971B4270726C000DDC5BF /* _generated */ = { isa = PBXGroup; children = ( 71BE843D2636A944002849D2 /* NativeModules.h */, 71BE843B2636A944002849D2 /* NativeModules.cpp */, ); path = _generated; sourceTree = ""; }; 724995D227B4103A00323FCE /* NotificationService */ = { isa = PBXGroup; children = ( - CB30C12327D0ACF700FBE8DE /* NotificationService.entitlements */, 724995D327B4103A00323FCE /* NotificationService.h */, 724995D427B4103A00323FCE /* NotificationService.mm */, 724995D627B4103A00323FCE /* Info.plist */, ); path = NotificationService; sourceTree = ""; }; 726E5D722731A4240032361D /* InternalModules */ = { isa = PBXGroup; children = ( 726E5D732731A4790032361D /* NetworkModule.cpp */, 726E5D742731A4790032361D /* NetworkModule.h */, 726E5D762731A5E10032361D /* GlobalNetworkSingleton.cpp */, 726E5D772731A5E10032361D /* GlobalNetworkSingleton.h */, 2DDA05D6D8D20D885F22F82C /* SocketStatus.h */, ); path = InternalModules; sourceTree = ""; }; - 72A0C808B17AE31F25B38C61 /* NotificationService */ = { - isa = PBXGroup; - children = ( - EE22AD93CDB4FE5C1849A5AA /* ExpoModulesProvider.swift */, - ); - name = NotificationService; - sourceTree = ""; - }; 7FF0870B1E833C3F000A1ACF /* Frameworks */ = { isa = PBXGroup; children = ( - CB3C621327CE66540054F24C /* libEXSecureStore.a */, 724995FA27BA9E8C00323FCE /* UserNotifications.framework */, 711CF80E25DC096000A00FBD /* libFolly.a */, 7F761E292201141E001B6FB7 /* JavaScriptCore.framework */, 994BEBDD4E4959F69CEA0BC3 /* libPods-Comm.a */, - 3EE4DCB430B05EC9DE7D7B01 /* libPods-NotificationService.a */, ); name = Frameworks; sourceTree = ""; }; 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( 71BE84362636A944002849D2 /* cpp */, 13B07FAE1A68108700A75B9A /* Comm */, 713EE40726C6676B003D7C48 /* CommTests */, 724995D227B4103A00323FCE /* NotificationService */, 83CBBA001A601CBA00E9B192 /* Products */, 6534411766BE4CA4B0AB0A78 /* Resources */, 7FF0870B1E833C3F000A1ACF /* Frameworks */, D533B93718E3B9684B508006 /* Pods */, AFF3F1F76178B42122C79BDE /* ExpoModulesProviders */, ); indentWidth = 2; sourceTree = ""; tabWidth = 2; }; 83CBBA001A601CBA00E9B192 /* Products */ = { isa = PBXGroup; children = ( 13B07F961A680F5B00A75B9A /* Comm.app */, 713EE40626C6676B003D7C48 /* CommTests.xctest */, 724995D127B4103A00323FCE /* NotificationService.appex */, ); name = Products; sourceTree = ""; }; AFF3F1F76178B42122C79BDE /* ExpoModulesProviders */ = { isa = PBXGroup; children = ( E75E6E4967CE9A8BBA89ED86 /* Comm */, - 72A0C808B17AE31F25B38C61 /* NotificationService */, ); name = ExpoModulesProviders; sourceTree = ""; }; D533B93718E3B9684B508006 /* Pods */ = { isa = PBXGroup; children = ( F53DA7B3F26C2798DCE74A94 /* Pods-Comm.debug.xcconfig */, C562A7004903539402D988CE /* Pods-Comm.release.xcconfig */, - 891D1495EE1F375F3AF6C7ED /* Pods-NotificationService.debug.xcconfig */, - 913E5A7BDECB327E3DE11053 /* Pods-NotificationService.release.xcconfig */, ); path = Pods; sourceTree = ""; }; E75E6E4967CE9A8BBA89ED86 /* Comm */ = { isa = PBXGroup; children = ( 3EEB3E70587B0ADAD05237B0 /* ExpoModulesProvider.swift */, ); name = Comm; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 13B07F861A680F5B00A75B9A /* Comm */ = { isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Comm" */; buildPhases = ( 02DE093B3C1DDF10C1FA3E9C /* [CP] Check Pods Manifest.lock */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, DB38BFA0686C805CE44F051F /* [CP] Copy Pods Resources */, EA2E8897D838D7F3E680EACE /* [CP] Embed Pods Frameworks */, 724995DA27B4103A00323FCE /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( 724995D827B4103A00323FCE /* PBXTargetDependency */, ); name = Comm; productName = "Hello World"; productReference = 13B07F961A680F5B00A75B9A /* Comm.app */; productType = "com.apple.product-type.application"; }; 713EE40526C6676B003D7C48 /* CommTests */ = { isa = PBXNativeTarget; buildConfigurationList = 713EE40F26C6676B003D7C48 /* Build configuration list for PBXNativeTarget "CommTests" */; buildPhases = ( 713EE40226C6676B003D7C48 /* Sources */, 713EE40326C6676B003D7C48 /* Frameworks */, 713EE40426C6676B003D7C48 /* Resources */, ); buildRules = ( ); dependencies = ( 713EE40C26C6676B003D7C48 /* PBXTargetDependency */, ); name = CommTests; productName = CommTests; productReference = 713EE40626C6676B003D7C48 /* CommTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; 724995D027B4103A00323FCE /* NotificationService */ = { isa = PBXNativeTarget; buildConfigurationList = 724995DD27B4103A00323FCE /* Build configuration list for PBXNativeTarget "NotificationService" */; buildPhases = ( - 6735FA74B2C82E3B27E18258 /* [CP] Check Pods Manifest.lock */, 724995CD27B4103A00323FCE /* Sources */, 724995CE27B4103A00323FCE /* Frameworks */, 724995CF27B4103A00323FCE /* Resources */, - E6221695BEF4548AF41DD8EB /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); name = NotificationService; productName = NotificationService; productReference = 724995D127B4103A00323FCE /* NotificationService.appex */; productType = "com.apple.product-type.app-extension"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 83CBB9F71A601CBA00E9B192 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 1150; ORGANIZATIONNAME = "Comm Technologies, Inc."; TargetAttributes = { 13B07F861A680F5B00A75B9A = { DevelopmentTeam = H98Y8MH53M; LastSwiftMigration = 1140; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.BackgroundModes = { enabled = 1; }; com.apple.GameCenter = { enabled = 0; }; com.apple.InAppPurchase = { enabled = 0; }; com.apple.Keychain = { enabled = 1; }; com.apple.Push = { enabled = 1; }; com.apple.SafariKeychain = { enabled = 1; }; }; }; 713EE40526C6676B003D7C48 = { CreatedOnToolsVersion = 12.5.1; ProvisioningStyle = Automatic; TestTargetID = 13B07F861A680F5B00A75B9A; }; 724995D027B4103A00323FCE = { CreatedOnToolsVersion = 13.0; DevelopmentTeam = H98Y8MH53M; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Comm" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( English, en, Base, ); mainGroup = 83CBB9F61A601CBA00E9B192; productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 13B07F861A680F5B00A75B9A /* Comm */, 713EE40526C6676B003D7C48 /* CommTests */, 724995D027B4103A00323FCE /* NotificationService */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 13B07F8E1A680F5B00A75B9A /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 7F8D602926535F2A0053CB29 /* IBMPlexSans-Regular.ttf in Resources */, 7F8D602826535F240053CB29 /* IBMPlexSans-Bold.ttf in Resources */, 7F8D602126535E060053CB29 /* OpenSans-Semibold.ttf in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 7F788C2C248AA2140098F071 /* SplashScreen.storyboard in Resources */, B723460726979250009A0709 /* swmansion.ttf in Resources */, 7F8D602226535E060053CB29 /* Anaheim-Regular.ttf in Resources */, B71AFF1F265EDD8600B22352 /* IBMPlexSans-Medium.ttf in Resources */, 7F8D602326535E060053CB29 /* OpenSans-Regular.ttf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 713EE40426C6676B003D7C48 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 724995CF27B4103A00323FCE /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "Bundle React Native code and images"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; }; 02DE093B3C1DDF10C1FA3E9C /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( "${PODS_PODFILE_DIR_PATH}/Podfile.lock", "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( ); outputPaths = ( "$(DERIVED_FILE_DIR)/Pods-Comm-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; }; - 6735FA74B2C82E3B27E18258 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-NotificationService-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; - }; DB38BFA0686C805CE44F051F /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Comm/Pods-Comm-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf", "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf", "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf", "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Feather.ttf", "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf", "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf", "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf", "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf", "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf", "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf", "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf", "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf", "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf", "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf", "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf", "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf", "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-C++/gRPCCertificates-Cpp.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Fontisto.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates-Cpp.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Comm/Pods-Comm-resources.sh\"\n"; showEnvVarsInLog = 0; }; - E6221695BEF4548AF41DD8EB /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-NotificationService/Pods-NotificationService-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", - "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf", - "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf", - "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf", - "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Feather.ttf", - "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf", - "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf", - "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf", - "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf", - "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf", - "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf", - "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf", - "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf", - "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf", - "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf", - "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf", - "${PODS_ROOT}/../../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-C++/gRPCCertificates-Cpp.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Fontisto.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates-Cpp.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NotificationService/Pods-NotificationService-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; EA2E8897D838D7F3E680EACE /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Comm/Pods-Comm-frameworks.sh", "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL", "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/hermes.framework/hermes", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Comm/Pods-Comm-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 13B07F871A680F5B00A75B9A /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 71009A7826FDCA67002C8453 /* tunnelbroker.grpc.pb.cc in Sources */, B7BEE749279B3FB6009CCA35 /* GRPCStreamHostObject.cpp in Sources */, 718DE99E2653D41C00365824 /* WorkerThread.cpp in Sources */, 71CA4AEC262F236100835C89 /* Tools.mm in Sources */, 71009A7B26FDCD72002C8453 /* Client.cpp in Sources */, 71762A75270D8AAE00F565ED /* PlatformSpecificTools.mm in Sources */, 71BF5B7126B3FF0900EDE27D /* Session.cpp in Sources */, 71009A7726FDCA67002C8453 /* tunnelbroker.pb.cc in Sources */, 726E5D752731A4790032361D /* NetworkModule.cpp in Sources */, 71BF5B7526B401D300EDE27D /* Tools.cpp in Sources */, 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, 71142A7726C2650B0039DCBD /* CommSecureStoreIOSWrapper.mm in Sources */, 71BE84492636A944002849D2 /* NativeModules.cpp in Sources */, 71CA4A64262DA8E500835C89 /* Logger.mm in Sources */, 71BF5B7F26BBDD7400EDE27D /* CryptoModule.cpp in Sources */, 71BE844A2636A944002849D2 /* CommCoreModule.cpp in Sources */, 71D4D7CC26C50B1000FCDBCD /* CommSecureStore.mm in Sources */, 711B408425DA97F9005F8F06 /* dummy.swift in Sources */, 726E5D782731A5E10032361D /* GlobalNetworkSingleton.cpp in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */, 71BE844B2636A944002849D2 /* SQLiteQueryExecutor.cpp in Sources */, 2DDA0AE067906E18B83A455C /* ClientGetReadReactor.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 713EE40226C6676B003D7C48 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 713EE41126C66B80003D7C48 /* CryptoTest.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 724995CD27B4103A00323FCE /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - CB1648AF27CFBE6A00394D9D /* CryptoModule.cpp in Sources */, - CB1648AD27CFBBBB00394D9D /* ClientGetReadReactor.cpp in Sources */, - CB4821B227CFB20E001AB7E1 /* SQLiteQueryExecutor.cpp in Sources */, - CB4821B127CFB1FA001AB7E1 /* GlobalNetworkSingleton.cpp in Sources */, - CB4821AE27CFB187001AB7E1 /* Tools.cpp in Sources */, - CB4821AB27CFB17C001AB7E1 /* tunnelbroker.pb.cc in Sources */, - CB4821AC27CFB17C001AB7E1 /* Session.cpp in Sources */, - CB4821AD27CFB17C001AB7E1 /* NetworkModule.cpp in Sources */, - CB4821A627CFB153001AB7E1 /* Client.cpp in Sources */, - CB4821A827CFB153001AB7E1 /* tunnelbroker.grpc.pb.cc in Sources */, - CB4821A927CFB153001AB7E1 /* WorkerThread.cpp in Sources */, - CB4821AA27CFB153001AB7E1 /* Tools.mm in Sources */, - CB4821A527CF9F38001AB7E1 /* CommSecureStore.mm in Sources */, - CB3C621227CE65030054F24C /* CommSecureStoreIOSWrapper.mm in Sources */, - CB3C621127CE4A320054F24C /* Logger.mm in Sources */, 724995D527B4103A00323FCE /* NotificationService.mm in Sources */, - CB4821AF27CFB19D001AB7E1 /* PlatformSpecificTools.mm in Sources */, - 73F3F24A0D87D6EB04DD550E /* ExpoModulesProvider.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 713EE40C26C6676B003D7C48 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 13B07F861A680F5B00A75B9A /* Comm */; targetProxy = 713EE40B26C6676B003D7C48 /* PBXContainerItemProxy */; }; 724995D827B4103A00323FCE /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 724995D027B4103A00323FCE /* NotificationService */; targetProxy = 724995D727B4103A00323FCE /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = F53DA7B3F26C2798DCE74A94 /* Pods-Comm.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES; CODE_SIGN_ENTITLEMENTS = Comm/Comm.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = H98Y8MH53M; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", "COCOAPODS=1", "FB_SONARKIT_ENABLED=1", "SD_WEBP=1", ); HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../node_modules/react-native/Libraries/LinkingIOS", "$(PODS_ROOT)/boost-for-react-native", ); INFOPLIST_FILE = Comm/Info.debug.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_CPLUSPLUSFLAGS = ( "-DFOLLY_MOBILE=1", "-DFOLLY_NO_CONFIG", "-DFOLLY_USE_LIBCPP=1", "-DRNVERSION=63", "-fcxx-modules", "-fmodules", ); OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", "-lc++", ); PRODUCT_BUNDLE_IDENTIFIER = app.comm; PRODUCT_NAME = Comm; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Comm-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; USE_HEADERMAP = YES; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = C562A7004903539402D988CE /* Pods-Comm.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES; CODE_SIGN_ENTITLEMENTS = Comm/Comm.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = H98Y8MH53M; ENABLE_BITCODE = YES; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../node_modules/react-native/Libraries/LinkingIOS", "$(PODS_ROOT)/boost-for-react-native", ); INFOPLIST_FILE = Comm/Info.release.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; ONLY_ACTIVE_ARCH = YES; OTHER_CPLUSPLUSFLAGS = ( "-DFOLLY_MOBILE=1", "-DFOLLY_NO_CONFIG", "-DFOLLY_USE_LIBCPP=1", "-DRNVERSION=63", "-fcxx-modules", "-fmodules", ); OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", "-lc++", ); PRODUCT_BUNDLE_IDENTIFIER = app.comm; PRODUCT_NAME = Comm; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Comm-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; USE_HEADERMAP = YES; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; 713EE40D26C6676B003D7C48 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; DEBUG_INFORMATION_FORMAT = dwarf; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); INFOPLIST_FILE = CommTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.5; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = swm.CommTests; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Comm.app/Comm"; USER_HEADER_SEARCH_PATHS = "../../node_modules/olm/include ../../node_modules/olm/lib ${PODS_ROOT}/OLMKit/include ${PODS_ROOT}/OLMKit/lib"; }; name = Debug; }; 713EE40E26C6676B003D7C48 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = CommTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.5; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = swm.CommTests; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Comm.app/Comm"; USER_HEADER_SEARCH_PATHS = "../../node_modules/olm/include ../../node_modules/olm/lib ${PODS_ROOT}/OLMKit/include ${PODS_ROOT}/OLMKit/lib"; }; name = Release; }; 724995DB27B4103A00323FCE /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 891D1495EE1F375F3AF6C7ED /* Pods-NotificationService.debug.xcconfig */; buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = H98Y8MH53M; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = NotificationService/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = NotificationService; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Comm Technologies, Inc. All rights reserved."; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - OTHER_CFLAGS = ( - "$(inherited)", - "-DSQLITE_HAS_CODEC", - "-DFOLLY_NO_CONFIG", - "-DSQLITE_TEMP_STORE=2", - "-DSQLCIPHER_CRYPTO_OPENSSL", - ); - OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; PRODUCT_BUNDLE_IDENTIFIER = app.comm.NotificationService; PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = NO; + SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; 724995DC27B4103A00323FCE /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 913E5A7BDECB327E3DE11053 /* Pods-NotificationService.release.xcconfig */; buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_ENTITLEMENTS = NotificationService/NotificationService.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = H98Y8MH53M; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = NotificationService/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = NotificationService; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Comm Technologies, Inc. All rights reserved."; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - OTHER_CFLAGS = ( - "$(inherited)", - "-DSQLITE_HAS_CODEC", - "-DFOLLY_NO_CONFIG", - "-DSQLITE_TEMP_STORE=2", - "-DSQLCIPHER_CRYPTO_OPENSSL", - ); PRODUCT_BUNDLE_IDENTIFIER = app.comm.NotificationService; PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = NO; + SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; 83CBBA201A601CBA00E9B192 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "FB_SONARKIT_ENABLED=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; name = Debug; }; 83CBBA211A601CBA00E9B192 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; VALIDATE_PRODUCT = YES; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Comm" */ = { isa = XCConfigurationList; buildConfigurations = ( 13B07F941A680F5B00A75B9A /* Debug */, 13B07F951A680F5B00A75B9A /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 713EE40F26C6676B003D7C48 /* Build configuration list for PBXNativeTarget "CommTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 713EE40D26C6676B003D7C48 /* Debug */, 713EE40E26C6676B003D7C48 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 724995DD27B4103A00323FCE /* Build configuration list for PBXNativeTarget "NotificationService" */ = { isa = XCConfigurationList; buildConfigurations = ( 724995DB27B4103A00323FCE /* Debug */, 724995DC27B4103A00323FCE /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Comm" */ = { isa = XCConfigurationList; buildConfigurations = ( 83CBBA201A601CBA00E9B192 /* Debug */, 83CBBA211A601CBA00E9B192 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; } diff --git a/native/ios/Comm/AppDelegate.mm b/native/ios/Comm/AppDelegate.mm index b9351c8cb..ec25e2834 100644 --- a/native/ios/Comm/AppDelegate.mm +++ b/native/ios/Comm/AppDelegate.mm @@ -1,261 +1,287 @@ #import "AppDelegate.h" #import "Orientation.h" #import "RNNotifications.h" #import #import #import #import +#import #import #import #import #import #import #import +#import + #import "CommCoreModule.h" +#import "CommSecureStore.h" +#import "CommSecureStoreIOSWrapper.h" #import "GlobalNetworkSingleton.h" #import "NetworkModule.h" #import "SQLiteQueryExecutor.h" #import "Tools.h" #import #ifdef FB_SONARKIT_ENABLED #import #import #import #import #import #import static void InitializeFlipper(UIApplication *application) { FlipperClient *client = [FlipperClient sharedClient]; SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; [client addPlugin:[FlipperKitReactPlugin new]]; [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; [client start]; } #endif #import #import #import NSString *const backgroundNotificationTypeKey = @"backgroundNotifType"; @interface AppDelegate () < RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> { } @end -@implementation AppDelegate +@interface CommSecureStoreIOSWrapper () +- (void)init:(EXSecureStore *)secureStore; +@end -- (BOOL)application:(UIApplication *)application - willFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [self attemptDatabaseInitialization]; - return YES; -} +@implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { #ifdef FB_SONARKIT_ENABLED InitializeFlipper(application); #endif RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"Comm" initialProperties:nil]; if (@available(iOS 13.0, *)) { rootView.backgroundColor = [UIColor systemBackgroundColor]; } else { rootView.backgroundColor = [UIColor whiteColor]; } self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [UIViewController new]; rootViewController.view = rootView; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; [super application:application didFinishLaunchingWithOptions:launchOptions]; // This prevents a very small flicker from occurring before expo-splash-screen // is able to display UIView *launchScreenView = [[UIStoryboard storyboardWithName:@"SplashScreen" bundle:nil] instantiateInitialViewController] .view; launchScreenView.frame = self.window.bounds; rootView.loadingView = launchScreenView; rootView.loadingViewFadeDelay = 0; rootView.loadingViewFadeDuration = 0.001; + EXModuleRegistryProvider *moduleRegistryProvider = + [[EXModuleRegistryProvider alloc] init]; + EXSecureStore *secureStore = + (EXSecureStore *)[[moduleRegistryProvider moduleRegistry] + getExportedModuleOfClass:EXSecureStore.class]; + [[CommSecureStoreIOSWrapper sharedInstance] init:secureStore]; + + // set sqlite file path + comm::SQLiteQueryExecutor::sqliteFilePath = + std::string([[Tools getSQLiteFilePath] UTF8String]); + + // set sqlcipher encryption key + comm::CommSecureStore commSecureStore; + folly::Optional maybeEncryptionKey; + try { + maybeEncryptionKey = commSecureStore.get("comm.encryptionKey"); + } catch (NSException *exception) { + maybeEncryptionKey = folly::none; + } + + if (maybeEncryptionKey) { + comm::SQLiteQueryExecutor::encryptionKey = maybeEncryptionKey.value(); + } else { + int sqlcipherEncryptionKeySize = 64; + std::string encryptionKey = comm::crypto::Tools::generateRandomHexString( + sqlcipherEncryptionKeySize); + commSecureStore.set("comm.encryptionKey", encryptionKey); + comm::SQLiteQueryExecutor::encryptionKey = encryptionKey; + } return YES; } - (NSArray> *)extraModulesForBridge:(RCTBridge *)bridge { // If you'd like to export some custom RCTBridgeModules that are not Expo // modules, add them here! return @[]; } - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { [RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; } - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { [RNNotifications didFailToRegisterForRemoteNotificationsWithError:error]; } // Required for the notification event. You must call the completion handler // after handling the remote notification. - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification fetchCompletionHandler: (void (^)(UIBackgroundFetchResult))completionHandler { BOOL handled = NO; if (notification[@"aps"][@"content-available"] && notification[backgroundNotificationTypeKey]) { handled = [self handleBackgroundNotification:notification fetchCompletionHandler:completionHandler]; } if (handled) { return; } [RNNotifications didReceiveRemoteNotification:notification fetchCompletionHandler:completionHandler]; } - (BOOL)handleBackgroundNotification:(NSDictionary *)notification fetchCompletionHandler: (void (^)(UIBackgroundFetchResult))completionHandler { if ([notification[backgroundNotificationTypeKey] isEqualToString:@"PING"]) { comm::GlobalNetworkSingleton::instance.scheduleOrRun( [=](comm::NetworkModule &networkModule) { networkModule.sendPong(); dispatch_async(dispatch_get_main_queue(), ^{ completionHandler(UIBackgroundFetchResultNewData); }); }); return YES; } else if ([notification[backgroundNotificationTypeKey] isEqualToString:@"CLEAR"]) { [[UNUserNotificationCenter currentNotificationCenter] getDeliveredNotificationsWithCompletionHandler:^( NSArray *notifications) { for (UNNotification *notif in notifications) { if ([notification[@"notificationId"] isEqual:notif.request.content.userInfo[@"id"]]) { NSArray *identifiers = [NSArray arrayWithObjects:notif.request.identifier, nil]; [[UNUserNotificationCenter currentNotificationCenter] removeDeliveredNotificationsWithIdentifiers:identifiers]; } } completionHandler(UIBackgroundFetchResultNewData); }]; return YES; } return NO; } // Required for the localNotification event. - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { [RNNotifications didReceiveLocalNotification:notification]; } - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window { return [Orientation getOrientation]; } - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { #if DEBUG return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; #else return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; #endif } using JSExecutorFactory = facebook::react::JSExecutorFactory; using HermesExecutorFactory = facebook::react::HermesExecutorFactory; using Runtime = facebook::jsi::Runtime; - (std::unique_ptr)jsExecutorFactoryForBridge: (RCTBridge *)bridge { __weak __typeof(self) weakSelf = self; const auto commRuntimeInstaller = [weakSelf, bridge](facebook::jsi::Runtime &rt) { if (!bridge) { return; } __typeof(self) strongSelf = weakSelf; if (strongSelf) { std::shared_ptr nativeModule = std::make_shared(bridge.jsCallInvoker); rt.global().setProperty( rt, facebook::jsi::PropNameID::forAscii(rt, "CommCoreModule"), facebook::jsi::Object::createFromHostObject(rt, nativeModule)); } }; const auto installer = reanimated::REAJSIExecutorRuntimeInstaller(bridge, commRuntimeInstaller); return std::make_unique( facebook::react::RCTJSIExecutorRuntimeInstaller(installer), JSIExecutor::defaultTimeoutInvoker, makeRuntimeConfig(3072)); } -- (void)attemptDatabaseInitialization { - std::string sqliteFilePath = - std::string([[Tools getSQLiteFilePath] UTF8String]); - comm::SQLiteQueryExecutor::initialize(sqliteFilePath); -} - // Copied from // ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/OnLoad.cpp static ::hermes::vm::RuntimeConfig makeRuntimeConfig(::hermes::vm::gcheapsize_t heapSizeMB) { namespace vm = ::hermes::vm; auto gcConfigBuilder = vm::GCConfig::Builder() .withName("RN") // For the next two arguments: avoid GC before TTI by initializing the // runtime to allocate directly in the old generation, but revert to // normal operation when we reach the (first) TTI point. .withAllocInYoung(false) .withRevertToYGAtTTI(true); if (heapSizeMB > 0) { gcConfigBuilder.withMaxHeapSize(heapSizeMB << 20); } return vm::RuntimeConfig::Builder() .withGCConfig(gcConfigBuilder.build()) .build(); } @end diff --git a/native/ios/Comm/CommSecureStoreIOSWrapper.mm b/native/ios/Comm/CommSecureStoreIOSWrapper.mm index c309fd23a..7d33ff124 100644 --- a/native/ios/Comm/CommSecureStoreIOSWrapper.mm +++ b/native/ios/Comm/CommSecureStoreIOSWrapper.mm @@ -1,67 +1,64 @@ #import "CommSecureStoreIOSWrapper.h" #import "CommSecureStoreIOSWrapper.h" -#import @interface CommSecureStoreIOSWrapper () -@property(nonatomic, strong) EXSecureStore *secureStore; +@property(nonatomic, assign) EXSecureStore *secureStore; @property(nonatomic, strong) NSDictionary *options; @end @interface EXSecureStore (CommEXSecureStore) - (BOOL)_setValue:(NSString *)value withKey:(NSString *)key withOptions:(NSDictionary *)options error:(NSError **)error; - (NSString *)_getValueWithKey:(NSString *)key withOptions:(NSDictionary *)options error:(NSError **)error; @end @implementation CommSecureStoreIOSWrapper #pragma mark Singleton Methods + (id)sharedInstance { static CommSecureStoreIOSWrapper *shared = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ shared = [[self alloc] init]; - EXModuleRegistryProvider *moduleRegistryProvider = - [[EXModuleRegistryProvider alloc] init]; - EXSecureStore *secureStore = - (EXSecureStore *)[[moduleRegistryProvider moduleRegistry] - getExportedModuleOfClass:EXSecureStore.class]; - shared.secureStore = secureStore; }); return shared; } +- (void)init:(EXSecureStore *)secureStore { + _secureStore = secureStore; +} + - (void)set:(NSString *)key value:(NSString *)value { if ([self secureStore] == nil) { [NSException raise:@"secure store error" format:@"secure store has not been initialized"]; } NSError *error; [[self secureStore] _setValue:value withKey:key withOptions:[self options] error:&error]; if (error != nil) { [NSException raise:@"secure store error" format:@"error occured when setting data"]; } } - (NSString *)get:(NSString *)key { if ([self secureStore] == nil) { [NSException raise:@"secure store error" format:@"secure store has not been initialized"]; } NSError *error; return [[self secureStore] _getValueWithKey:key withOptions:[self options] error:&error]; } @end diff --git a/native/ios/NotificationService/NotificationService.entitlements b/native/ios/NotificationService/NotificationService.entitlements deleted file mode 100644 index b2a03e8b0..000000000 --- a/native/ios/NotificationService/NotificationService.entitlements +++ /dev/null @@ -1,10 +0,0 @@ - - - - - keychain-access-groups - - $(AppIdentifierPrefix)app.comm - - - diff --git a/native/ios/Podfile b/native/ios/Podfile index 410a48227..75c5d953e 100644 --- a/native/ios/Podfile +++ b/native/ios/Podfile @@ -1,62 +1,46 @@ require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking") require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods") require File.join(File.dirname(`node --print "require.resolve('@react-native-community/cli-platform-ios/package.json')"`), "native_modules") platform :ios, '12.0' require 'json' +podfile_properties = JSON.parse(File.read('./Podfile.properties.json')) rescue {} -def common_comm_target_pods - podfile_properties = JSON.parse(File.read('./Podfile.properties.json')) rescue {} - +target 'Comm' do + pod 'ReactNativeKeyboardTrackingView', :path => '../../node_modules/react-native-keyboard-tracking-view' + pod 'ReactNativeKeyboardInput', :path => '../../node_modules/react-native-keyboard-input' pod 'react-native-ffmpeg/min-lts', :podspec => '../../node_modules/react-native-ffmpeg/react-native-ffmpeg.podspec' + pod 'react-native-video/VideoCaching', :podspec => '../../node_modules/react-native-video/react-native-video.podspec' pod 'SQLCipher-Amalgamation', :path => '../../node_modules/@commapp/sqlcipher-amalgamation' pod 'gRPC-C++', :podspec => './pod-patch/.patched/gRPC-C++/1.40.0/gRPC-C++.podspec.json' pod 'gRPC-C++/Protobuf', :podspec => './pod-patch/.patched/gRPC-C++/1.40.0/gRPC-C++.podspec.json' pod 'gRPC-Core', :podspec => './pod-patch/.patched/gRPC-Core/1.40.0/gRPC-Core.podspec.json' pod 'Protobuf-C++', '3.15.8' use_expo_modules! config = use_native_modules! use_react_native!( :path => config[:reactNativePath], # to enable hermes on iOS, change `false` to `true` and then install pods :hermes_enabled => podfile_properties['expo.jsEngine'] == 'hermes' ) # Enables Flipper. # # Note that if you have use_frameworks! enabled, Flipper will not work and # you should disable these next few lines. use_flipper!({ "Flipper-DoubleConversion" => "1.1.7" }) -end - -target 'Comm' do - pod 'ReactNativeKeyboardTrackingView', :path => '../../node_modules/react-native-keyboard-tracking-view' - pod 'ReactNativeKeyboardInput', :path => '../../node_modules/react-native-keyboard-input' - pod 'react-native-video/VideoCaching', :podspec => '../../node_modules/react-native-video/react-native-video.podspec' - common_comm_target_pods -end - -target 'NotificationService' do - common_comm_target_pods -end - -post_install do |installer| - react_native_post_install(installer) - __apply_Xcode_12_5_M1_post_install_workaround(installer) - - # Excluding arm64 because M1 simulator isn't supported yet - # Excluding 32-bit x86 because nobody uses it and it causes compilation issues - installer.pods_project.build_configurations.each do |config| - config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64 i386" - config.build_settings['OTHER_CPLUSPLUSFLAGS'] = '-DDONT_AUTOINSTALL_REANIMATED' - end - - installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'No' + post_install do |installer| + react_native_post_install(installer) + __apply_Xcode_12_5_M1_post_install_workaround(installer) + + # Excluding arm64 because M1 simulator isn't supported yet + # Excluding 32-bit x86 because nobody uses it and it causes compilation issues + installer.pods_project.build_configurations.each do |config| + config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64 i386" + config.build_settings['OTHER_CPLUSPLUSFLAGS'] = '-DDONT_AUTOINSTALL_REANIMATED' end - end + end end diff --git a/native/ios/Podfile.lock b/native/ios/Podfile.lock index 18b1583db..6a0eb45ee 100644 --- a/native/ios/Podfile.lock +++ b/native/ios/Podfile.lock @@ -1,1313 +1,1313 @@ PODS: - abseil/algorithm/algorithm (1.20210324.0): - abseil/base/config - abseil/algorithm/container (1.20210324.0): - abseil/algorithm/algorithm - abseil/base/core_headers - abseil/meta/type_traits - abseil/base/atomic_hook (1.20210324.0): - abseil/base/config - abseil/base/core_headers - abseil/base/base (1.20210324.0): - abseil/base/atomic_hook - abseil/base/base_internal - abseil/base/config - abseil/base/core_headers - abseil/base/dynamic_annotations - abseil/base/log_severity - abseil/base/raw_logging_internal - abseil/base/spinlock_wait - abseil/meta/type_traits - abseil/base/base_internal (1.20210324.0): - abseil/base/config - abseil/meta/type_traits - abseil/base/config (1.20210324.0) - abseil/base/core_headers (1.20210324.0): - abseil/base/config - abseil/base/dynamic_annotations (1.20210324.0): - abseil/base/config - abseil/base/core_headers - abseil/base/endian (1.20210324.0): - abseil/base/base - abseil/base/config - abseil/base/core_headers - abseil/base/errno_saver (1.20210324.0): - abseil/base/config - abseil/base/exponential_biased (1.20210324.0): - abseil/base/config - abseil/base/core_headers - abseil/base/log_severity (1.20210324.0): - abseil/base/config - abseil/base/core_headers - abseil/base/malloc_internal (1.20210324.0): - abseil/base/base - abseil/base/base_internal - abseil/base/config - abseil/base/core_headers - abseil/base/dynamic_annotations - abseil/base/raw_logging_internal - abseil/base/raw_logging_internal (1.20210324.0): - abseil/base/atomic_hook - abseil/base/config - abseil/base/core_headers - abseil/base/log_severity - abseil/base/spinlock_wait (1.20210324.0): - abseil/base/base_internal - abseil/base/core_headers - abseil/base/errno_saver - abseil/base/throw_delegate (1.20210324.0): - abseil/base/config - abseil/base/raw_logging_internal - abseil/container/common (1.20210324.0): - abseil/meta/type_traits - abseil/types/optional - abseil/container/compressed_tuple (1.20210324.0): - abseil/utility/utility - abseil/container/container_memory (1.20210324.0): - abseil/base/config - abseil/memory/memory - abseil/meta/type_traits - abseil/utility/utility - abseil/container/fixed_array (1.20210324.0): - abseil/algorithm/algorithm - abseil/base/config - abseil/base/core_headers - abseil/base/dynamic_annotations - abseil/base/throw_delegate - abseil/container/compressed_tuple - abseil/memory/memory - abseil/container/flat_hash_map (1.20210324.0): - abseil/algorithm/container - abseil/container/container_memory - abseil/container/hash_function_defaults - abseil/container/raw_hash_map - abseil/memory/memory - abseil/container/hash_function_defaults (1.20210324.0): - abseil/base/config - abseil/hash/hash - abseil/strings/cord - abseil/strings/strings - abseil/container/hash_policy_traits (1.20210324.0): - abseil/meta/type_traits - abseil/container/hashtable_debug_hooks (1.20210324.0): - abseil/base/config - abseil/container/hashtablez_sampler (1.20210324.0): - abseil/base/base - abseil/base/core_headers - abseil/base/exponential_biased - abseil/container/have_sse - abseil/debugging/stacktrace - abseil/memory/memory - abseil/synchronization/synchronization - abseil/utility/utility - abseil/container/have_sse (1.20210324.0) - abseil/container/inlined_vector (1.20210324.0): - abseil/algorithm/algorithm - abseil/base/core_headers - abseil/base/throw_delegate - abseil/container/inlined_vector_internal - abseil/memory/memory - abseil/container/inlined_vector_internal (1.20210324.0): - abseil/base/core_headers - abseil/container/compressed_tuple - abseil/memory/memory - abseil/meta/type_traits - abseil/types/span - abseil/container/layout (1.20210324.0): - abseil/base/config - abseil/base/core_headers - abseil/meta/type_traits - abseil/strings/strings - abseil/types/span - abseil/utility/utility - abseil/container/raw_hash_map (1.20210324.0): - abseil/base/throw_delegate - abseil/container/container_memory - abseil/container/raw_hash_set - abseil/container/raw_hash_set (1.20210324.0): - abseil/base/config - abseil/base/core_headers - abseil/base/endian - abseil/container/common - abseil/container/compressed_tuple - abseil/container/container_memory - abseil/container/hash_policy_traits - abseil/container/hashtable_debug_hooks - abseil/container/hashtablez_sampler - abseil/container/have_sse - abseil/container/layout - abseil/memory/memory - abseil/meta/type_traits - abseil/numeric/bits - abseil/utility/utility - abseil/debugging/debugging_internal (1.20210324.0): - abseil/base/config - abseil/base/core_headers - abseil/base/dynamic_annotations - abseil/base/errno_saver - abseil/base/raw_logging_internal - abseil/debugging/demangle_internal (1.20210324.0): - abseil/base/base - abseil/base/config - abseil/base/core_headers - abseil/debugging/stacktrace (1.20210324.0): - abseil/base/config - abseil/base/core_headers - abseil/debugging/debugging_internal - abseil/debugging/symbolize (1.20210324.0): - abseil/base/base - abseil/base/config - abseil/base/core_headers - abseil/base/dynamic_annotations - abseil/base/malloc_internal - abseil/base/raw_logging_internal - abseil/debugging/debugging_internal - abseil/debugging/demangle_internal - abseil/strings/strings - abseil/functional/bind_front (1.20210324.0): - abseil/base/base_internal - abseil/container/compressed_tuple - abseil/meta/type_traits - abseil/utility/utility - abseil/functional/function_ref (1.20210324.0): - abseil/base/base_internal - abseil/meta/type_traits - abseil/hash/city (1.20210324.0): - abseil/base/config - abseil/base/core_headers - abseil/base/endian - abseil/hash/hash (1.20210324.0): - abseil/base/config - abseil/base/core_headers - abseil/base/endian - abseil/container/fixed_array - abseil/hash/city - abseil/hash/wyhash - abseil/meta/type_traits - abseil/numeric/int128 - abseil/strings/strings - abseil/types/optional - abseil/types/variant - abseil/utility/utility - abseil/hash/wyhash (1.20210324.0): - abseil/base/config - abseil/base/endian - abseil/numeric/int128 - abseil/memory/memory (1.20210324.0): - abseil/base/core_headers - abseil/meta/type_traits - abseil/meta/type_traits (1.20210324.0): - abseil/base/config - abseil/numeric/bits (1.20210324.0): - abseil/base/config - abseil/base/core_headers - abseil/numeric/int128 (1.20210324.0): - abseil/base/config - abseil/base/core_headers - abseil/numeric/bits - abseil/numeric/representation (1.20210324.0): - abseil/base/config - abseil/status/status (1.20210324.0): - abseil/base/atomic_hook - abseil/base/config - abseil/base/core_headers - abseil/base/raw_logging_internal - abseil/container/inlined_vector - abseil/debugging/stacktrace - abseil/debugging/symbolize - abseil/strings/cord - abseil/strings/str_format - abseil/strings/strings - abseil/types/optional - abseil/status/statusor (1.20210324.0): - abseil/base/core_headers - abseil/base/raw_logging_internal - abseil/meta/type_traits - abseil/status/status - abseil/strings/strings - abseil/types/variant - abseil/utility/utility - abseil/strings/cord (1.20210324.0): - abseil/base/base - abseil/base/core_headers - abseil/base/endian - abseil/base/raw_logging_internal - abseil/container/fixed_array - abseil/container/inlined_vector - abseil/functional/function_ref - abseil/meta/type_traits - abseil/strings/cord_internal - abseil/strings/internal - abseil/strings/str_format - abseil/strings/strings - abseil/types/optional - abseil/strings/cord_internal (1.20210324.0): - abseil/base/base_internal - abseil/base/config - abseil/base/core_headers - abseil/base/endian - abseil/base/raw_logging_internal - abseil/base/throw_delegate - abseil/container/compressed_tuple - abseil/container/inlined_vector - abseil/container/layout - abseil/meta/type_traits - abseil/strings/strings - abseil/strings/internal (1.20210324.0): - abseil/base/config - abseil/base/core_headers - abseil/base/endian - abseil/base/raw_logging_internal - abseil/meta/type_traits - abseil/strings/str_format (1.20210324.0): - abseil/strings/str_format_internal - abseil/strings/str_format_internal (1.20210324.0): - abseil/base/config - abseil/base/core_headers - abseil/functional/function_ref - abseil/meta/type_traits - abseil/numeric/bits - abseil/numeric/int128 - abseil/numeric/representation - abseil/strings/strings - abseil/types/optional - abseil/types/span - abseil/strings/strings (1.20210324.0): - abseil/base/base - abseil/base/config - abseil/base/core_headers - abseil/base/endian - abseil/base/raw_logging_internal - abseil/base/throw_delegate - abseil/memory/memory - abseil/meta/type_traits - abseil/numeric/bits - abseil/numeric/int128 - abseil/strings/internal - abseil/synchronization/graphcycles_internal (1.20210324.0): - abseil/base/base - abseil/base/base_internal - abseil/base/config - abseil/base/core_headers - abseil/base/malloc_internal - abseil/base/raw_logging_internal - abseil/synchronization/kernel_timeout_internal (1.20210324.0): - abseil/base/core_headers - abseil/base/raw_logging_internal - abseil/time/time - abseil/synchronization/synchronization (1.20210324.0): - abseil/base/atomic_hook - abseil/base/base - abseil/base/base_internal - abseil/base/config - abseil/base/core_headers - abseil/base/dynamic_annotations - abseil/base/malloc_internal - abseil/base/raw_logging_internal - abseil/debugging/stacktrace - abseil/debugging/symbolize - abseil/synchronization/graphcycles_internal - abseil/synchronization/kernel_timeout_internal - abseil/time/time - abseil/time/internal/cctz/civil_time (1.20210324.0): - abseil/base/config - abseil/time/internal/cctz/time_zone (1.20210324.0): - abseil/base/config - abseil/time/internal/cctz/civil_time - abseil/time/time (1.20210324.0): - abseil/base/base - abseil/base/core_headers - abseil/base/raw_logging_internal - abseil/numeric/int128 - abseil/strings/strings - abseil/time/internal/cctz/civil_time - abseil/time/internal/cctz/time_zone - abseil/types/bad_optional_access (1.20210324.0): - abseil/base/config - abseil/base/raw_logging_internal - abseil/types/bad_variant_access (1.20210324.0): - abseil/base/config - abseil/base/raw_logging_internal - abseil/types/optional (1.20210324.0): - abseil/base/base_internal - abseil/base/config - abseil/base/core_headers - abseil/memory/memory - abseil/meta/type_traits - abseil/types/bad_optional_access - abseil/utility/utility - abseil/types/span (1.20210324.0): - abseil/algorithm/algorithm - abseil/base/core_headers - abseil/base/throw_delegate - abseil/meta/type_traits - abseil/types/variant (1.20210324.0): - abseil/base/base_internal - abseil/base/config - abseil/base/core_headers - abseil/meta/type_traits - abseil/types/bad_variant_access - abseil/utility/utility - abseil/utility/utility (1.20210324.0): - abseil/base/base_internal - abseil/base/config - abseil/meta/type_traits - boost (1.76.0) - CocoaAsyncSocket (7.6.5) - DoubleConversion (1.1.6) - DVAssetLoaderDelegate (0.3.3) - EXApplication (4.0.2): - ExpoModulesCore - EXConstants (12.1.3): - ExpoModulesCore - EXFileSystem (13.0.3): - ExpoModulesCore - EXFont (10.0.5): - ExpoModulesCore - EXHaptics (11.0.3): - ExpoModulesCore - EXImageLoader (3.0.0): - ExpoModulesCore - React-Core - EXImageManipulator (10.1.2): - EXImageLoader - ExpoModulesCore - EXKeepAwake (10.0.2): - ExpoModulesCore - EXMediaLibrary (13.0.3): - ExpoModulesCore - React-Core - Expo (43.0.0): - ExpoModulesCore - ExpoModulesCore (0.4.10): - React-Core - EXSecureStore (11.0.3): - ExpoModulesCore - EXSplashScreen (0.13.5): - ExpoModulesCore - React-Core - FBLazyVector (0.66.4) - FBReactNativeSpec (0.66.4): - RCT-Folly (= 2021.06.28.00-v2) - RCTRequired (= 0.66.4) - RCTTypeSafety (= 0.66.4) - React-Core (= 0.66.4) - React-jsi (= 0.66.4) - ReactCommon/turbomodule/core (= 0.66.4) - Flipper (0.99.0): - Flipper-Folly (~> 2.6) - Flipper-RSocket (~> 1.4) - Flipper-Boost-iOSX (1.76.0.1.11) - Flipper-DoubleConversion (1.1.7) - Flipper-Fmt (7.1.7) - Flipper-Folly (2.6.7): - Flipper-Boost-iOSX - Flipper-DoubleConversion - Flipper-Fmt (= 7.1.7) - Flipper-Glog - libevent (~> 2.1.12) - OpenSSL-Universal (= 1.1.180) - Flipper-Glog (0.3.6) - Flipper-PeerTalk (0.0.4) - Flipper-RSocket (1.4.3): - Flipper-Folly (~> 2.6) - FlipperKit (0.99.0): - FlipperKit/Core (= 0.99.0) - FlipperKit/Core (0.99.0): - Flipper (~> 0.99.0) - FlipperKit/CppBridge - FlipperKit/FBCxxFollyDynamicConvert - FlipperKit/FBDefines - FlipperKit/FKPortForwarding - FlipperKit/CppBridge (0.99.0): - Flipper (~> 0.99.0) - FlipperKit/FBCxxFollyDynamicConvert (0.99.0): - Flipper-Folly (~> 2.6) - FlipperKit/FBDefines (0.99.0) - FlipperKit/FKPortForwarding (0.99.0): - CocoaAsyncSocket (~> 7.6) - Flipper-PeerTalk (~> 0.0.4) - FlipperKit/FlipperKitHighlightOverlay (0.99.0) - FlipperKit/FlipperKitLayoutHelpers (0.99.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutTextSearchable - FlipperKit/FlipperKitLayoutIOSDescriptors (0.99.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutHelpers - YogaKit (~> 1.18) - FlipperKit/FlipperKitLayoutPlugin (0.99.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutHelpers - FlipperKit/FlipperKitLayoutIOSDescriptors - FlipperKit/FlipperKitLayoutTextSearchable - YogaKit (~> 1.18) - FlipperKit/FlipperKitLayoutTextSearchable (0.99.0) - FlipperKit/FlipperKitNetworkPlugin (0.99.0): - FlipperKit/Core - FlipperKit/FlipperKitReactPlugin (0.99.0): - FlipperKit/Core - FlipperKit/FlipperKitUserDefaultsPlugin (0.99.0): - FlipperKit/Core - FlipperKit/SKIOSNetworkPlugin (0.99.0): - FlipperKit/Core - FlipperKit/FlipperKitNetworkPlugin - fmt (6.2.1) - glog (0.3.5) - "gRPC-C++ (1.40.0)": - "gRPC-C++/Implementation (= 1.40.0)" - "gRPC-C++/Interface (= 1.40.0)" - "gRPC-C++/Implementation (1.40.0)": - abseil/base/base (= 1.20210324.0) - abseil/base/core_headers (= 1.20210324.0) - abseil/container/flat_hash_map (= 1.20210324.0) - abseil/container/inlined_vector (= 1.20210324.0) - abseil/functional/bind_front (= 1.20210324.0) - abseil/memory/memory (= 1.20210324.0) - abseil/status/status (= 1.20210324.0) - abseil/status/statusor (= 1.20210324.0) - abseil/strings/cord (= 1.20210324.0) - abseil/strings/str_format (= 1.20210324.0) - abseil/strings/strings (= 1.20210324.0) - abseil/synchronization/synchronization (= 1.20210324.0) - abseil/time/time (= 1.20210324.0) - abseil/types/optional (= 1.20210324.0) - "gRPC-C++/Interface (= 1.40.0)" - gRPC-Core (= 1.40.0) - "gRPC-C++/Interface (1.40.0)" - "gRPC-C++/Protobuf (1.40.0)": - "gRPC-C++/Interface (= 1.40.0)" - gRPC-Core (1.40.0): - gRPC-Core/Implementation (= 1.40.0) - gRPC-Core/Interface (= 1.40.0) - gRPC-Core/Implementation (1.40.0): - abseil/base/base (= 1.20210324.0) - abseil/base/core_headers (= 1.20210324.0) - abseil/container/flat_hash_map (= 1.20210324.0) - abseil/container/inlined_vector (= 1.20210324.0) - abseil/functional/bind_front (= 1.20210324.0) - abseil/memory/memory (= 1.20210324.0) - abseil/status/status (= 1.20210324.0) - abseil/status/statusor (= 1.20210324.0) - abseil/strings/cord (= 1.20210324.0) - abseil/strings/str_format (= 1.20210324.0) - abseil/strings/strings (= 1.20210324.0) - abseil/synchronization/synchronization (= 1.20210324.0) - abseil/time/time (= 1.20210324.0) - abseil/types/optional (= 1.20210324.0) - gRPC-Core/Interface (= 1.40.0) - OpenSSL-Universal - gRPC-Core/Interface (1.40.0) - hermes-engine (0.9.0) - libevent (2.1.12) - libwebp (1.2.1): - libwebp/demux (= 1.2.1) - libwebp/mux (= 1.2.1) - libwebp/webp (= 1.2.1) - libwebp/demux (1.2.1): - libwebp/webp - libwebp/mux (1.2.1): - libwebp/demux - libwebp/webp (1.2.1) - lottie-ios (3.1.8) - lottie-react-native (4.0.2): - lottie-ios (~> 3.1.8) - React-Core - mobile-ffmpeg-min (4.3.1.LTS) - OLMKit (3.2.4): - OLMKit/olmc (= 3.2.4) - OLMKit/olmcpp (= 3.2.4) - OLMKit/olmc (3.2.4) - OLMKit/olmcpp (3.2.4) - OpenSSL-Universal (1.1.180) - "Protobuf-C++ (3.15.8)" - RCT-Folly (2021.06.28.00-v2): - boost - DoubleConversion - fmt (~> 6.2.1) - glog - RCT-Folly/Default (= 2021.06.28.00-v2) - RCT-Folly/Default (2021.06.28.00-v2): - boost - DoubleConversion - fmt (~> 6.2.1) - glog - RCT-Folly/Futures (2021.06.28.00-v2): - boost - DoubleConversion - fmt (~> 6.2.1) - glog - libevent - RCTRequired (0.66.4) - RCTTypeSafety (0.66.4): - FBLazyVector (= 0.66.4) - RCT-Folly (= 2021.06.28.00-v2) - RCTRequired (= 0.66.4) - React-Core (= 0.66.4) - React (0.66.4): - React-Core (= 0.66.4) - React-Core/DevSupport (= 0.66.4) - React-Core/RCTWebSocket (= 0.66.4) - React-RCTActionSheet (= 0.66.4) - React-RCTAnimation (= 0.66.4) - React-RCTBlob (= 0.66.4) - React-RCTImage (= 0.66.4) - React-RCTLinking (= 0.66.4) - React-RCTNetwork (= 0.66.4) - React-RCTSettings (= 0.66.4) - React-RCTText (= 0.66.4) - React-RCTVibration (= 0.66.4) - React-callinvoker (0.66.4) - React-Core (0.66.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default (= 0.66.4) - React-cxxreact (= 0.66.4) - React-jsi (= 0.66.4) - React-jsiexecutor (= 0.66.4) - React-perflogger (= 0.66.4) - Yoga - React-Core/CoreModulesHeaders (0.66.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - React-cxxreact (= 0.66.4) - React-jsi (= 0.66.4) - React-jsiexecutor (= 0.66.4) - React-perflogger (= 0.66.4) - Yoga - React-Core/Default (0.66.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-cxxreact (= 0.66.4) - React-jsi (= 0.66.4) - React-jsiexecutor (= 0.66.4) - React-perflogger (= 0.66.4) - Yoga - React-Core/DevSupport (0.66.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default (= 0.66.4) - React-Core/RCTWebSocket (= 0.66.4) - React-cxxreact (= 0.66.4) - React-jsi (= 0.66.4) - React-jsiexecutor (= 0.66.4) - React-jsinspector (= 0.66.4) - React-perflogger (= 0.66.4) - Yoga - React-Core/RCTActionSheetHeaders (0.66.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - React-cxxreact (= 0.66.4) - React-jsi (= 0.66.4) - React-jsiexecutor (= 0.66.4) - React-perflogger (= 0.66.4) - Yoga - React-Core/RCTAnimationHeaders (0.66.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - React-cxxreact (= 0.66.4) - React-jsi (= 0.66.4) - React-jsiexecutor (= 0.66.4) - React-perflogger (= 0.66.4) - Yoga - React-Core/RCTBlobHeaders (0.66.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - React-cxxreact (= 0.66.4) - React-jsi (= 0.66.4) - React-jsiexecutor (= 0.66.4) - React-perflogger (= 0.66.4) - Yoga - React-Core/RCTImageHeaders (0.66.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - React-cxxreact (= 0.66.4) - React-jsi (= 0.66.4) - React-jsiexecutor (= 0.66.4) - React-perflogger (= 0.66.4) - Yoga - React-Core/RCTLinkingHeaders (0.66.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - React-cxxreact (= 0.66.4) - React-jsi (= 0.66.4) - React-jsiexecutor (= 0.66.4) - React-perflogger (= 0.66.4) - Yoga - React-Core/RCTNetworkHeaders (0.66.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - React-cxxreact (= 0.66.4) - React-jsi (= 0.66.4) - React-jsiexecutor (= 0.66.4) - React-perflogger (= 0.66.4) - Yoga - React-Core/RCTSettingsHeaders (0.66.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - React-cxxreact (= 0.66.4) - React-jsi (= 0.66.4) - React-jsiexecutor (= 0.66.4) - React-perflogger (= 0.66.4) - Yoga - React-Core/RCTTextHeaders (0.66.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - React-cxxreact (= 0.66.4) - React-jsi (= 0.66.4) - React-jsiexecutor (= 0.66.4) - React-perflogger (= 0.66.4) - Yoga - React-Core/RCTVibrationHeaders (0.66.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default - React-cxxreact (= 0.66.4) - React-jsi (= 0.66.4) - React-jsiexecutor (= 0.66.4) - React-perflogger (= 0.66.4) - Yoga - React-Core/RCTWebSocket (0.66.4): - glog - RCT-Folly (= 2021.06.28.00-v2) - React-Core/Default (= 0.66.4) - React-cxxreact (= 0.66.4) - React-jsi (= 0.66.4) - React-jsiexecutor (= 0.66.4) - React-perflogger (= 0.66.4) - Yoga - React-CoreModules (0.66.4): - FBReactNativeSpec (= 0.66.4) - RCT-Folly (= 2021.06.28.00-v2) - RCTTypeSafety (= 0.66.4) - React-Core/CoreModulesHeaders (= 0.66.4) - React-jsi (= 0.66.4) - React-RCTImage (= 0.66.4) - ReactCommon/turbomodule/core (= 0.66.4) - React-cxxreact (0.66.4): - boost (= 1.76.0) - DoubleConversion - glog - RCT-Folly (= 2021.06.28.00-v2) - React-callinvoker (= 0.66.4) - React-jsi (= 0.66.4) - React-jsinspector (= 0.66.4) - React-logger (= 0.66.4) - React-perflogger (= 0.66.4) - React-runtimeexecutor (= 0.66.4) - React-hermes (0.66.4): - DoubleConversion - glog - hermes-engine - RCT-Folly (= 2021.06.28.00-v2) - RCT-Folly/Futures (= 2021.06.28.00-v2) - React-cxxreact (= 0.66.4) - React-jsi (= 0.66.4) - React-jsiexecutor (= 0.66.4) - React-jsinspector (= 0.66.4) - React-perflogger (= 0.66.4) - React-jsi (0.66.4): - boost (= 1.76.0) - DoubleConversion - glog - RCT-Folly (= 2021.06.28.00-v2) - React-jsi/Default (= 0.66.4) - React-jsi/Default (0.66.4): - boost (= 1.76.0) - DoubleConversion - glog - RCT-Folly (= 2021.06.28.00-v2) - React-jsiexecutor (0.66.4): - DoubleConversion - glog - RCT-Folly (= 2021.06.28.00-v2) - React-cxxreact (= 0.66.4) - React-jsi (= 0.66.4) - React-perflogger (= 0.66.4) - React-jsinspector (0.66.4) - React-logger (0.66.4): - glog - react-native-background-upload (6.5.1): - React - react-native-camera (3.31.0): - React - react-native-camera/RCT (= 3.31.0) - react-native-camera/RN (= 3.31.0) - react-native-camera/RCT (3.31.0): - React - react-native-camera/RN (3.31.0): - React - react-native-ffmpeg/min-lts (0.4.4): - mobile-ffmpeg-min (= 4.3.1.LTS) - React - react-native-flipper (0.98.0): - React-Core - react-native-in-app-message (1.0.2): - React - react-native-netinfo (6.0.0): - React-Core - react-native-notifications (1.1.19): - React - react-native-orientation-locker (1.1.6): - React - react-native-safe-area-context (3.1.9): - React-Core - react-native-video/Video (5.1.1): - React-Core - react-native-video/VideoCaching (5.1.1): - DVAssetLoaderDelegate (~> 0.3.1) - React-Core - react-native-video/Video - SPTPersistentCache (~> 1.1.0) - React-perflogger (0.66.4) - React-RCTActionSheet (0.66.4): - React-Core/RCTActionSheetHeaders (= 0.66.4) - React-RCTAnimation (0.66.4): - FBReactNativeSpec (= 0.66.4) - RCT-Folly (= 2021.06.28.00-v2) - RCTTypeSafety (= 0.66.4) - React-Core/RCTAnimationHeaders (= 0.66.4) - React-jsi (= 0.66.4) - ReactCommon/turbomodule/core (= 0.66.4) - React-RCTBlob (0.66.4): - FBReactNativeSpec (= 0.66.4) - RCT-Folly (= 2021.06.28.00-v2) - React-Core/RCTBlobHeaders (= 0.66.4) - React-Core/RCTWebSocket (= 0.66.4) - React-jsi (= 0.66.4) - React-RCTNetwork (= 0.66.4) - ReactCommon/turbomodule/core (= 0.66.4) - React-RCTImage (0.66.4): - FBReactNativeSpec (= 0.66.4) - RCT-Folly (= 2021.06.28.00-v2) - RCTTypeSafety (= 0.66.4) - React-Core/RCTImageHeaders (= 0.66.4) - React-jsi (= 0.66.4) - React-RCTNetwork (= 0.66.4) - ReactCommon/turbomodule/core (= 0.66.4) - React-RCTLinking (0.66.4): - FBReactNativeSpec (= 0.66.4) - React-Core/RCTLinkingHeaders (= 0.66.4) - React-jsi (= 0.66.4) - ReactCommon/turbomodule/core (= 0.66.4) - React-RCTNetwork (0.66.4): - FBReactNativeSpec (= 0.66.4) - RCT-Folly (= 2021.06.28.00-v2) - RCTTypeSafety (= 0.66.4) - React-Core/RCTNetworkHeaders (= 0.66.4) - React-jsi (= 0.66.4) - ReactCommon/turbomodule/core (= 0.66.4) - React-RCTSettings (0.66.4): - FBReactNativeSpec (= 0.66.4) - RCT-Folly (= 2021.06.28.00-v2) - RCTTypeSafety (= 0.66.4) - React-Core/RCTSettingsHeaders (= 0.66.4) - React-jsi (= 0.66.4) - ReactCommon/turbomodule/core (= 0.66.4) - React-RCTText (0.66.4): - React-Core/RCTTextHeaders (= 0.66.4) - React-RCTVibration (0.66.4): - FBReactNativeSpec (= 0.66.4) - RCT-Folly (= 2021.06.28.00-v2) - React-Core/RCTVibrationHeaders (= 0.66.4) - React-jsi (= 0.66.4) - ReactCommon/turbomodule/core (= 0.66.4) - React-runtimeexecutor (0.66.4): - React-jsi (= 0.66.4) - ReactCommon/turbomodule/core (0.66.4): - DoubleConversion - glog - RCT-Folly (= 2021.06.28.00-v2) - React-callinvoker (= 0.66.4) - React-Core (= 0.66.4) - React-cxxreact (= 0.66.4) - React-jsi (= 0.66.4) - React-logger (= 0.66.4) - React-perflogger (= 0.66.4) - ReactNativeART (1.2.0): - React - ReactNativeDarkMode (0.2.0-rc.1): - React - ReactNativeKeyboardInput (6.0.1): - React - ReactNativeKeyboardTrackingView (5.7.0): - React - RNCAsyncStorage (1.6.2): - React - RNCClipboard (1.5.1): - React-Core - SDWebImage (~> 5.8) - RNCMaskedView (0.1.10): - React - RNDeviceInfo (8.0.7): - React-Core - RNExitApp (1.1.0): - React - RNFastImage (8.3.0): - React - SDWebImage (~> 5.8) - SDWebImageWebPCoder (~> 0.6.1) - RNFS (2.15.2): - React - RNGestureHandler (1.10.3): - React-Core - RNKeychain (8.0.0): - React-Core - RNReanimated (2.4.1): - DoubleConversion - FBLazyVector - FBReactNativeSpec - glog - RCT-Folly - RCTRequired - RCTTypeSafety - React - React-callinvoker - React-Core - React-Core/DevSupport - React-Core/RCTWebSocket - React-CoreModules - React-cxxreact - React-jsi - React-jsiexecutor - React-jsinspector - React-RCTActionSheet - React-RCTAnimation - React-RCTBlob - React-RCTImage - React-RCTLinking - React-RCTNetwork - React-RCTSettings - React-RCTText - ReactCommon/turbomodule/core - Yoga - RNScreens (3.8.0): - React-Core - React-RCTImage - RNSVG (12.1.1): - React - RNVectorIcons (6.6.0): - React - SDWebImage (5.12.3): - SDWebImage/Core (= 5.12.3) - SDWebImage/Core (5.12.3) - SDWebImageWebPCoder (0.6.1): - libwebp (~> 1.0) - SDWebImage/Core (~> 5.7) - SPTPersistentCache (1.1.0) - SQLCipher-Amalgamation (4.4.3): - OpenSSL-Universal - SQLCipher-Amalgamation/standard (= 4.4.3) - SQLCipher-Amalgamation/common (4.4.3): - OpenSSL-Universal - SQLCipher-Amalgamation/standard (4.4.3): - OpenSSL-Universal - SQLCipher-Amalgamation/common - Yoga (1.14.0) - YogaKit (1.18.1): - Yoga (~> 1.14) DEPENDENCIES: - boost (from `../../node_modules/react-native/third-party-podspecs/boost.podspec`) - DoubleConversion (from `../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - EXApplication (from `../../node_modules/expo-application/ios`) - EXConstants (from `../../node_modules/expo-constants/ios`) - EXFileSystem (from `../../node_modules/expo-file-system/ios`) - EXFont (from `../../node_modules/expo-font/ios`) - EXHaptics (from `../../node_modules/expo-haptics/ios`) - EXImageLoader (from `../../node_modules/expo-image-loader/ios`) - EXImageManipulator (from `../../node_modules/expo-image-manipulator/ios`) - EXKeepAwake (from `../../node_modules/expo-keep-awake/ios`) - EXMediaLibrary (from `../../node_modules/expo-media-library/ios`) - Expo (from `../../node_modules/expo/ios`) - ExpoModulesCore (from `../../node_modules/expo-modules-core/ios`) - EXSecureStore (from `../../node_modules/expo-secure-store/ios`) - EXSplashScreen (from `../../node_modules/expo-splash-screen/ios`) - FBLazyVector (from `../../node_modules/react-native/Libraries/FBLazyVector`) - FBReactNativeSpec (from `../../node_modules/react-native/React/FBReactNativeSpec`) - Flipper (= 0.99.0) - Flipper-Boost-iOSX (= 1.76.0.1.11) - Flipper-DoubleConversion (= 1.1.7) - Flipper-Fmt (= 7.1.7) - Flipper-Folly (= 2.6.7) - Flipper-Glog (= 0.3.6) - Flipper-PeerTalk (= 0.0.4) - Flipper-RSocket (= 1.4.3) - FlipperKit (= 0.99.0) - FlipperKit/Core (= 0.99.0) - FlipperKit/CppBridge (= 0.99.0) - FlipperKit/FBCxxFollyDynamicConvert (= 0.99.0) - FlipperKit/FBDefines (= 0.99.0) - FlipperKit/FKPortForwarding (= 0.99.0) - FlipperKit/FlipperKitHighlightOverlay (= 0.99.0) - FlipperKit/FlipperKitLayoutPlugin (= 0.99.0) - FlipperKit/FlipperKitLayoutTextSearchable (= 0.99.0) - FlipperKit/FlipperKitNetworkPlugin (= 0.99.0) - FlipperKit/FlipperKitReactPlugin (= 0.99.0) - FlipperKit/FlipperKitUserDefaultsPlugin (= 0.99.0) - FlipperKit/SKIOSNetworkPlugin (= 0.99.0) - glog (from `../../node_modules/react-native/third-party-podspecs/glog.podspec`) - "gRPC-C++ (from `./pod-patch/.patched/gRPC-C++/1.40.0/gRPC-C++.podspec.json`)" - "gRPC-C++/Protobuf (from `./pod-patch/.patched/gRPC-C++/1.40.0/gRPC-C++.podspec.json`)" - gRPC-Core (from `./pod-patch/.patched/gRPC-Core/1.40.0/gRPC-Core.podspec.json`) - hermes-engine (~> 0.9.0) - libevent (~> 2.1.12) - lottie-ios (from `../../node_modules/lottie-ios`) - lottie-react-native (from `../../node_modules/lottie-react-native`) - OLMKit (from `../../node_modules/olm`) - "Protobuf-C++ (= 3.15.8)" - RCT-Folly (from `../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - RCTRequired (from `../../node_modules/react-native/Libraries/RCTRequired`) - RCTTypeSafety (from `../../node_modules/react-native/Libraries/TypeSafety`) - React (from `../../node_modules/react-native/`) - React-callinvoker (from `../../node_modules/react-native/ReactCommon/callinvoker`) - React-Core (from `../../node_modules/react-native/`) - React-Core/DevSupport (from `../../node_modules/react-native/`) - React-Core/RCTWebSocket (from `../../node_modules/react-native/`) - React-CoreModules (from `../../node_modules/react-native/React/CoreModules`) - React-cxxreact (from `../../node_modules/react-native/ReactCommon/cxxreact`) - React-hermes (from `../../node_modules/react-native/ReactCommon/hermes`) - React-jsi (from `../../node_modules/react-native/ReactCommon/jsi`) - React-jsiexecutor (from `../../node_modules/react-native/ReactCommon/jsiexecutor`) - React-jsinspector (from `../../node_modules/react-native/ReactCommon/jsinspector`) - React-logger (from `../../node_modules/react-native/ReactCommon/logger`) - react-native-background-upload (from `../../node_modules/react-native-background-upload`) - react-native-camera (from `../../node_modules/react-native-camera`) - react-native-ffmpeg/min-lts (from `../../node_modules/react-native-ffmpeg/react-native-ffmpeg.podspec`) - react-native-flipper (from `../../node_modules/react-native-flipper`) - react-native-in-app-message (from `../../node_modules/react-native-in-app-message`) - "react-native-netinfo (from `../../node_modules/@react-native-community/netinfo`)" - react-native-notifications (from `../../node_modules/react-native-notifications`) - react-native-orientation-locker (from `../../node_modules/react-native-orientation-locker`) - react-native-safe-area-context (from `../../node_modules/react-native-safe-area-context`) - react-native-video/VideoCaching (from `../../node_modules/react-native-video/react-native-video.podspec`) - React-perflogger (from `../../node_modules/react-native/ReactCommon/reactperflogger`) - React-RCTActionSheet (from `../../node_modules/react-native/Libraries/ActionSheetIOS`) - React-RCTAnimation (from `../../node_modules/react-native/Libraries/NativeAnimation`) - React-RCTBlob (from `../../node_modules/react-native/Libraries/Blob`) - React-RCTImage (from `../../node_modules/react-native/Libraries/Image`) - React-RCTLinking (from `../../node_modules/react-native/Libraries/LinkingIOS`) - React-RCTNetwork (from `../../node_modules/react-native/Libraries/Network`) - React-RCTSettings (from `../../node_modules/react-native/Libraries/Settings`) - React-RCTText (from `../../node_modules/react-native/Libraries/Text`) - React-RCTVibration (from `../../node_modules/react-native/Libraries/Vibration`) - React-runtimeexecutor (from `../../node_modules/react-native/ReactCommon/runtimeexecutor`) - ReactCommon/turbomodule/core (from `../../node_modules/react-native/ReactCommon`) - "ReactNativeART (from `../../node_modules/@react-native-community/art`)" - ReactNativeDarkMode (from `../../node_modules/react-native-dark-mode`) - ReactNativeKeyboardInput (from `../../node_modules/react-native-keyboard-input`) - ReactNativeKeyboardTrackingView (from `../../node_modules/react-native-keyboard-tracking-view`) - "RNCAsyncStorage (from `../../node_modules/@react-native-community/async-storage`)" - "RNCClipboard (from `../../node_modules/@react-native-community/clipboard`)" - "RNCMaskedView (from `../../node_modules/@react-native-community/masked-view`)" - RNDeviceInfo (from `../../node_modules/react-native-device-info`) - RNExitApp (from `../../node_modules/react-native-exit-app`) - RNFastImage (from `../../node_modules/react-native-fast-image`) - RNFS (from `../../node_modules/react-native-fs`) - RNGestureHandler (from `../../node_modules/react-native-gesture-handler`) - RNKeychain (from `../../node_modules/react-native-keychain`) - RNReanimated (from `../../node_modules/react-native-reanimated`) - RNScreens (from `../../node_modules/react-native-screens`) - RNSVG (from `../../node_modules/react-native-svg`) - RNVectorIcons (from `../../node_modules/react-native-vector-icons`) - "SQLCipher-Amalgamation (from `../../node_modules/@commapp/sqlcipher-amalgamation`)" - Yoga (from `../../node_modules/react-native/ReactCommon/yoga`) SPEC REPOS: trunk: - abseil - CocoaAsyncSocket - DVAssetLoaderDelegate - Flipper - Flipper-Boost-iOSX - Flipper-DoubleConversion - Flipper-Fmt - Flipper-Folly - Flipper-Glog - Flipper-PeerTalk - Flipper-RSocket - FlipperKit - fmt - hermes-engine - libevent - libwebp - mobile-ffmpeg-min - OpenSSL-Universal - "Protobuf-C++" - SDWebImage - SDWebImageWebPCoder - SPTPersistentCache - YogaKit EXTERNAL SOURCES: boost: :podspec: "../../node_modules/react-native/third-party-podspecs/boost.podspec" DoubleConversion: :podspec: "../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" EXApplication: :path: "../../node_modules/expo-application/ios" EXConstants: :path: "../../node_modules/expo-constants/ios" EXFileSystem: :path: "../../node_modules/expo-file-system/ios" EXFont: :path: "../../node_modules/expo-font/ios" EXHaptics: :path: "../../node_modules/expo-haptics/ios" EXImageLoader: :path: "../../node_modules/expo-image-loader/ios" EXImageManipulator: :path: "../../node_modules/expo-image-manipulator/ios" EXKeepAwake: :path: "../../node_modules/expo-keep-awake/ios" EXMediaLibrary: :path: "../../node_modules/expo-media-library/ios" Expo: :path: "../../node_modules/expo/ios" ExpoModulesCore: :path: "../../node_modules/expo-modules-core/ios" EXSecureStore: :path: "../../node_modules/expo-secure-store/ios" EXSplashScreen: :path: "../../node_modules/expo-splash-screen/ios" FBLazyVector: :path: "../../node_modules/react-native/Libraries/FBLazyVector" FBReactNativeSpec: :path: "../../node_modules/react-native/React/FBReactNativeSpec" glog: :podspec: "../../node_modules/react-native/third-party-podspecs/glog.podspec" "gRPC-C++": :podspec: "./pod-patch/.patched/gRPC-C++/1.40.0/gRPC-C++.podspec.json" gRPC-Core: :podspec: "./pod-patch/.patched/gRPC-Core/1.40.0/gRPC-Core.podspec.json" lottie-ios: :path: "../../node_modules/lottie-ios" lottie-react-native: :path: "../../node_modules/lottie-react-native" OLMKit: :path: "../../node_modules/olm" RCT-Folly: :podspec: "../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" RCTRequired: :path: "../../node_modules/react-native/Libraries/RCTRequired" RCTTypeSafety: :path: "../../node_modules/react-native/Libraries/TypeSafety" React: :path: "../../node_modules/react-native/" React-callinvoker: :path: "../../node_modules/react-native/ReactCommon/callinvoker" React-Core: :path: "../../node_modules/react-native/" React-CoreModules: :path: "../../node_modules/react-native/React/CoreModules" React-cxxreact: :path: "../../node_modules/react-native/ReactCommon/cxxreact" React-hermes: :path: "../../node_modules/react-native/ReactCommon/hermes" React-jsi: :path: "../../node_modules/react-native/ReactCommon/jsi" React-jsiexecutor: :path: "../../node_modules/react-native/ReactCommon/jsiexecutor" React-jsinspector: :path: "../../node_modules/react-native/ReactCommon/jsinspector" React-logger: :path: "../../node_modules/react-native/ReactCommon/logger" react-native-background-upload: :path: "../../node_modules/react-native-background-upload" react-native-camera: :path: "../../node_modules/react-native-camera" react-native-ffmpeg: :podspec: "../../node_modules/react-native-ffmpeg/react-native-ffmpeg.podspec" react-native-flipper: :path: "../../node_modules/react-native-flipper" react-native-in-app-message: :path: "../../node_modules/react-native-in-app-message" react-native-netinfo: :path: "../../node_modules/@react-native-community/netinfo" react-native-notifications: :path: "../../node_modules/react-native-notifications" react-native-orientation-locker: :path: "../../node_modules/react-native-orientation-locker" react-native-safe-area-context: :path: "../../node_modules/react-native-safe-area-context" react-native-video: :podspec: "../../node_modules/react-native-video/react-native-video.podspec" React-perflogger: :path: "../../node_modules/react-native/ReactCommon/reactperflogger" React-RCTActionSheet: :path: "../../node_modules/react-native/Libraries/ActionSheetIOS" React-RCTAnimation: :path: "../../node_modules/react-native/Libraries/NativeAnimation" React-RCTBlob: :path: "../../node_modules/react-native/Libraries/Blob" React-RCTImage: :path: "../../node_modules/react-native/Libraries/Image" React-RCTLinking: :path: "../../node_modules/react-native/Libraries/LinkingIOS" React-RCTNetwork: :path: "../../node_modules/react-native/Libraries/Network" React-RCTSettings: :path: "../../node_modules/react-native/Libraries/Settings" React-RCTText: :path: "../../node_modules/react-native/Libraries/Text" React-RCTVibration: :path: "../../node_modules/react-native/Libraries/Vibration" React-runtimeexecutor: :path: "../../node_modules/react-native/ReactCommon/runtimeexecutor" ReactCommon: :path: "../../node_modules/react-native/ReactCommon" ReactNativeART: :path: "../../node_modules/@react-native-community/art" ReactNativeDarkMode: :path: "../../node_modules/react-native-dark-mode" ReactNativeKeyboardInput: :path: "../../node_modules/react-native-keyboard-input" ReactNativeKeyboardTrackingView: :path: "../../node_modules/react-native-keyboard-tracking-view" RNCAsyncStorage: :path: "../../node_modules/@react-native-community/async-storage" RNCClipboard: :path: "../../node_modules/@react-native-community/clipboard" RNCMaskedView: :path: "../../node_modules/@react-native-community/masked-view" RNDeviceInfo: :path: "../../node_modules/react-native-device-info" RNExitApp: :path: "../../node_modules/react-native-exit-app" RNFastImage: :path: "../../node_modules/react-native-fast-image" RNFS: :path: "../../node_modules/react-native-fs" RNGestureHandler: :path: "../../node_modules/react-native-gesture-handler" RNKeychain: :path: "../../node_modules/react-native-keychain" RNReanimated: :path: "../../node_modules/react-native-reanimated" RNScreens: :path: "../../node_modules/react-native-screens" RNSVG: :path: "../../node_modules/react-native-svg" RNVectorIcons: :path: "../../node_modules/react-native-vector-icons" SQLCipher-Amalgamation: :path: "../../node_modules/@commapp/sqlcipher-amalgamation" Yoga: :path: "../../node_modules/react-native/ReactCommon/yoga" SPEC CHECKSUMS: abseil: c12cac4b0b499c3335ac47ac46adf303f0618d13 boost: a7c83b31436843459a1961bfd74b96033dc77234 CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662 DVAssetLoaderDelegate: 0caec20e4e08b8560b691131539e9180024d4bce EXApplication: 54fe5bd6268d697771645e8f1aef8b806a65247a EXConstants: 6d585d93723b18d7a8c283591a335609e3bc153e EXFileSystem: 99aac7962c11c680681819dd9cbca24e20e5b1e7 EXFont: 2597c10ac85a69d348d44d7873eccf5a7576ef5e EXHaptics: f52df335f17192e440d9639c971ebcbb3fb91dec EXImageLoader: 939451be6f7b731aaa6588920b90743f20121a4d EXImageManipulator: 49bbcede2429523edfd95dc242cf7f8fdf271307 EXKeepAwake: bf48d7f740a5cd2befed6cf9a49911d385c6c47d EXMediaLibrary: 2a684cca228d6605a72f9995d3e7b8af2a05246c Expo: f3357f1f9796d0b4d396d0be84c49933671db4ad ExpoModulesCore: c9438f6add0fb7b04b7c64eb97a833d2752a7834 EXSecureStore: 919bf7c28472862020d2cd7b59b69aae160b5d40 EXSplashScreen: 57f329dbf25c5c12800feed79068a056453dc772 FBLazyVector: e5569e42a1c79ca00521846c223173a57aca1fe1 FBReactNativeSpec: fe08c1cd7e2e205718d77ad14b34957cce949b58 Flipper: 30e8eeeed6abdc98edaf32af0cda2f198be4b733 Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41 Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b Flipper-Folly: 83af37379faa69497529e414bd43fbfc7cae259a Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6 Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 Flipper-RSocket: d9d9ade67cbecf6ac10730304bf5607266dd2541 FlipperKit: d8d346844eca5d9120c17d441a2f38596e8ed2b9 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 5337263514dd6f09803962437687240c5dc39aa4 "gRPC-C++": 3a30fb847ea1af71bea38b5c85f8fddc0ea370ba gRPC-Core: cabfc7d9b2c6ef98b9dca7ff882ddfe762750225 hermes-engine: bf7577d12ac6ccf53ab8b5af3c6ccf0dd8458c5c libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 libwebp: 98a37e597e40bfdb4c911fc98f2c53d0b12d05fc lottie-ios: 48fac6be217c76937e36e340e2d09cf7b10b7f5f lottie-react-native: 4dff8fe8d10ddef9e7880e770080f4a56121397e mobile-ffmpeg-min: d5d22dcef5c8ec56f771258f1f5be245d914f193 OLMKit: a15f216aa14ba199b4fd827b3d7ef04629b56636 OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b "Protobuf-C++": 8833c36ca1a3b6422e75ec27534d63a6a762c843 RCT-Folly: 632694a4b94fb0f2cb090921861846beec77d8b8 RCTRequired: 4bf86c70714490bca4bf2696148638284622644b RCTTypeSafety: c475a7059eb77935fa53d2c17db299893f057d5d React: f64af14e3f2c50f6f2c91a5fd250e4ff1b3c3459 React-callinvoker: b74e4ae80287780dcdf0cab262bcb581eeef56e7 React-Core: 3eb7432bad96ff1d25aebc1defbae013fee2fd0e React-CoreModules: ad9e1fd5650e16666c57a08328df86fd7e480cb9 React-cxxreact: 02633ff398cf7e91a2c1e12590d323c4a4b8668a React-hermes: 7b4c6617b4d4c880d0f44e629651810bf3417440 React-jsi: 805c41a927d6499fb811772acb971467d9204633 React-jsiexecutor: 94ce921e1d8ce7023366873ec371f3441383b396 React-jsinspector: d0374f7509d407d2264168b6d0fad0b54e300b85 React-logger: 933f80c97c633ee8965d609876848148e3fef438 react-native-background-upload: c62f25610ffd49b1781fbdf1135b3b17f2f914b9 react-native-camera: b5c8c7a71feecfdd5b39f0dbbf6b64b957ed55f2 react-native-ffmpeg: f9a60452aaa5d478aac205b248224994f3bde416 react-native-flipper: 80b71629f5bf7b942dfae7af0c5f88526d19b3ca react-native-in-app-message: f91de5009620af01456531118264c93e249b83ec react-native-netinfo: e849fc21ca2f4128a5726c801a82fc6f4a6db50d react-native-notifications: bb042206ac7eab9323d528c780b3d6fe796c1f5e react-native-orientation-locker: 23918c400376a7043e752c639c122fcf6bce8f1c react-native-safe-area-context: b6e0e284002381d2ff29fa4fff42b4d8282e3c94 react-native-video: 0bb76b6d6b77da3009611586c7dbf817b947f30e React-perflogger: 93075d8931c32cd1fce8a98c15d2d5ccc4d891bd React-RCTActionSheet: 7d3041e6761b4f3044a37079ddcb156575fb6d89 React-RCTAnimation: 743e88b55ac62511ae5c2e22803d4f503f2a3a13 React-RCTBlob: bee3a2f98fa7fc25c957c8643494244f74bea0a0 React-RCTImage: 19fc9e29b06cc38611c553494f8d3040bf78c24e React-RCTLinking: dc799503979c8c711126d66328e7ce8f25c2848f React-RCTNetwork: 417e4e34cf3c19eaa5fd4e9eb20180d662a799ce React-RCTSettings: 4df89417265af26501a7e0e9192a34d3d9848dff React-RCTText: f8a21c3499ab322326290fa9b701ae29aa093aa5 React-RCTVibration: e3ffca672dd3772536cb844274094b0e2c31b187 React-runtimeexecutor: dec32ee6f2e2a26e13e58152271535fadff5455a ReactCommon: 57b69f6383eafcbd7da625bfa6003810332313c4 ReactNativeART: 78edc68dd4a1e675338cd0cd113319cf3a65f2ab ReactNativeDarkMode: 88317ff05ba95fd063dd347ad32f8c4cefd3166c ReactNativeKeyboardInput: 266ba27a2e9921f5bdc0b4cc30289b2a2f46b157 ReactNativeKeyboardTrackingView: 02137fac3b2ebd330d74fa54ead48b14750a2306 RNCAsyncStorage: 60a80e72d95bf02a01cace55d3697d9724f0d77f RNCClipboard: f470a4445c779f99c10201b038ab3f9e24e71dbc RNCMaskedView: 5a8ec07677aa885546a0d98da336457e2bea557f RNDeviceInfo: 55463fa6e252ca3f0e2ba6001a7b82f879914338 RNExitApp: c4e052df2568b43bec8a37c7cd61194d4cfee2c3 RNFastImage: 2ed80661d5ef384fb1b539f1f3c81a1733f92bc9 RNFS: 54da03c2b7d862c42ea3ca8c7f86f892760a535a RNGestureHandler: a479ebd5ed4221a810967000735517df0d2db211 RNKeychain: 4f63aada75ebafd26f4bc2c670199461eab85d94 RNReanimated: e8afbe2a9e08e9e778ea72ddb1a8533445812621 RNScreens: 6e1ea5787989f92b0671049b808aef64fa1ef98c RNSVG: 551acb6562324b1d52a4e0758f7ca0ec234e278f RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4 SDWebImage: 53179a2dba77246efa8a9b85f5c5b21f8f43e38f SDWebImageWebPCoder: d0dac55073088d24b2ac1b191a71a8f8d0adac21 SPTPersistentCache: df36ea46762d7cf026502bbb86a8b79d0080dff4 SQLCipher-Amalgamation: cbd36045fe7b458b8a442958a01aefdbc44c20f8 Yoga: e7dc4e71caba6472ff48ad7d234389b91dadc280 YogaKit: f782866e155069a2cca2517aafea43200b01fd5a -PODFILE CHECKSUM: 4f7e5eb9bdc9060cd55f3be240ddb30ce343a40f +PODFILE CHECKSUM: 29d35525f36d9b1828f8c1a0d1bb4d88f9715f02 COCOAPODS: 1.11.2 diff --git a/native/media/media-gallery-keyboard.react.js b/native/media/media-gallery-keyboard.react.js index ce0b8cae2..048ab029e 100644 --- a/native/media/media-gallery-keyboard.react.js +++ b/native/media/media-gallery-keyboard.react.js @@ -1,556 +1,556 @@ // @flow import * as MediaLibrary from 'expo-media-library'; import invariant from 'invariant'; import * as React from 'react'; import { View, Text, FlatList, ActivityIndicator, Animated, Easing, Platform, } from 'react-native'; import { KeyboardRegistry } from 'react-native-keyboard-input'; import { Provider } from 'react-redux'; import { extensionFromFilename } from 'lib/media/file-utils'; import { useIsAppForegrounded } from 'lib/shared/lifecycle-utils'; import type { MediaLibrarySelection } from 'lib/types/media-types'; import type { DimensionsInfo } from '../redux/dimensions-updater.react'; import { store } from '../redux/redux-setup'; import { useSelector } from '../redux/redux-utils'; import { type Colors, useColors, useStyles } from '../themes/colors'; import type { LayoutEvent, ViewableItemsChange } from '../types/react-native'; import type { ViewStyle } from '../types/styles'; import { getCompatibleMediaURI } from './identifier-utils'; import MediaGalleryMedia from './media-gallery-media.react'; import SendMediaButton from './send-media-button.react'; const animationSpec = { duration: 400, // $FlowFixMe[method-unbinding] easing: Easing.inOut(Easing.ease), useNativeDriver: true, }; type Props = { // Redux state +dimensions: DimensionsInfo, +foreground: boolean, +colors: Colors, +styles: typeof unboundStyles, }; type State = { +selections: ?$ReadOnlyArray, +error: ?string, +containerHeight: ?number, // null means end reached; undefined means no fetch yet +cursor: ?string, +queuedMediaURIs: ?Set, +focusedMediaURI: ?string, +dimensions: DimensionsInfo, }; class MediaGalleryKeyboard extends React.PureComponent { mounted = false; fetchingPhotos = false; flatList: ?FlatList; viewableIndices: number[] = []; queueModeProgress = new Animated.Value(0); sendButtonStyle: ViewStyle; mediaSelected = false; constructor(props: Props) { super(props); const sendButtonScale = this.queueModeProgress.interpolate({ inputRange: [0, 1], outputRange: ([1.3, 1]: number[]), // Flow... }); this.sendButtonStyle = { opacity: this.queueModeProgress, transform: [{ scale: sendButtonScale }], }; this.state = { selections: null, error: null, containerHeight: null, cursor: undefined, queuedMediaURIs: null, focusedMediaURI: null, dimensions: props.dimensions, }; } static getDerivedStateFromProps(props: Props) { // We keep this in state since we pass this.state as // FlatList's extraData prop return { dimensions: props.dimensions }; } componentDidMount() { this.mounted = true; return this.fetchPhotos(); } componentWillUnmount() { this.mounted = false; } componentDidUpdate(prevProps: Props, prevState: State) { const { queuedMediaURIs } = this.state; const prevQueuedMediaURIs = prevState.queuedMediaURIs; if (queuedMediaURIs && !prevQueuedMediaURIs) { Animated.timing(this.queueModeProgress, { ...animationSpec, toValue: 1, }).start(); } else if (!queuedMediaURIs && prevQueuedMediaURIs) { Animated.timing(this.queueModeProgress, { ...animationSpec, toValue: 0, }).start(); } const { flatList, viewableIndices } = this; const { selections, focusedMediaURI } = this.state; let scrollingSomewhere = false; if (flatList && selections) { let newURI; if (focusedMediaURI && focusedMediaURI !== prevState.focusedMediaURI) { newURI = focusedMediaURI; } else if ( queuedMediaURIs && (!prevQueuedMediaURIs || queuedMediaURIs.size > prevQueuedMediaURIs.size) ) { const flowMadeMeDoThis = queuedMediaURIs; for (const queuedMediaURI of flowMadeMeDoThis) { if (prevQueuedMediaURIs && prevQueuedMediaURIs.has(queuedMediaURI)) { continue; } newURI = queuedMediaURI; break; } } let index; if (newURI !== null && newURI !== undefined) { index = selections.findIndex(({ uri }) => uri === newURI); } if (index !== null && index !== undefined) { if (index === viewableIndices[0]) { scrollingSomewhere = true; flatList.scrollToIndex({ index }); } else if (index === viewableIndices[viewableIndices.length - 1]) { scrollingSomewhere = true; flatList.scrollToIndex({ index, viewPosition: 1 }); } } } if (this.props.foreground && !prevProps.foreground) { this.fetchPhotos(); } if ( !scrollingSomewhere && this.flatList && this.state.selections && prevState.selections && this.state.selections.length > 0 && prevState.selections.length > 0 && this.state.selections[0].uri !== prevState.selections[0].uri ) { this.flatList.scrollToIndex({ index: 0 }); } } guardedSetState(change) { if (this.mounted) { this.setState(change); } } async fetchPhotos(after?: ?string) { if (this.fetchingPhotos) { return; } this.fetchingPhotos = true; try { const hasPermission = await this.getPermissions(); if (!hasPermission) { return; } const { assets, endCursor, hasNextPage, } = await MediaLibrary.getAssetsAsync({ first: 20, after, - mediaType: [MediaLibrary.MediaType.photo], + mediaType: [MediaLibrary.MediaType.photo, MediaLibrary.MediaType.video], sortBy: [MediaLibrary.SortBy.modificationTime], }); let firstRemoved = false, lastRemoved = false; const mediaURIs = this.state.selections ? this.state.selections.map(({ uri }) => uri) : []; const existingURIs = new Set(mediaURIs); let first = true; const selections = assets .map(asset => { const { id, height, width, filename, mediaType, duration } = asset; const isVideo = mediaType === MediaLibrary.MediaType.video; const uri = getCompatibleMediaURI( asset.uri, extensionFromFilename(filename), ); if (existingURIs.has(uri)) { if (first) { firstRemoved = true; } lastRemoved = true; first = false; return null; } first = false; lastRemoved = false; existingURIs.add(uri); if (isVideo) { return { step: 'video_library', dimensions: { height, width }, uri, filename, mediaNativeID: id, duration, selectTime: 0, sendTime: 0, retries: 0, }; } else { return { step: 'photo_library', dimensions: { height, width }, uri, filename, mediaNativeID: id, selectTime: 0, sendTime: 0, retries: 0, }; } }) .filter(Boolean); let appendOrPrepend = after ? 'append' : 'prepend'; if (firstRemoved && !lastRemoved) { appendOrPrepend = 'append'; } else if (!firstRemoved && lastRemoved) { appendOrPrepend = 'prepend'; } let newSelections = selections; if (this.state.selections) { if (appendOrPrepend === 'prepend') { newSelections = [...newSelections, ...this.state.selections]; } else { newSelections = [...this.state.selections, ...newSelections]; } } this.guardedSetState({ selections: newSelections, error: null, cursor: hasNextPage ? endCursor : null, }); } catch (e) { this.guardedSetState({ selections: null, error: 'something went wrong :(', }); } this.fetchingPhotos = false; } async getPermissions(): Promise { const { granted } = await MediaLibrary.requestPermissionsAsync(); if (!granted) { this.guardedSetState({ error: "don't have permission :(" }); } return granted; } get queueModeActive() { return !!this.state.queuedMediaURIs; } renderItem = (row: { item: MediaLibrarySelection, ... }) => { const { containerHeight, queuedMediaURIs } = this.state; invariant(containerHeight, 'should be set'); const { uri } = row.item; const isQueued = !!(queuedMediaURIs && queuedMediaURIs.has(uri)); const { queueModeActive } = this; return ( ); }; ItemSeparator = () => { return ; }; static keyExtractor = (item: MediaLibrarySelection) => { return item.uri; }; render() { let content; const { selections, error, containerHeight } = this.state; const bottomOffsetStyle: ViewStyle = { marginBottom: this.props.dimensions.bottomInset, }; if (selections && selections.length > 0 && containerHeight) { content = ( ); } else if (selections && containerHeight) { content = ( no media was found! ); } else if (error) { content = ( {error} ); } else { content = ( ); } const { queuedMediaURIs } = this.state; const queueCount = queuedMediaURIs ? queuedMediaURIs.size : 0; const bottomInset = Platform.select({ ios: -1 * this.props.dimensions.bottomInset, default: 0, }); const containerStyle = { bottom: bottomInset }; return ( {content} ); } flatListRef = (flatList: ?FlatList) => { this.flatList = flatList; }; onContainerLayout = (event: LayoutEvent) => { this.guardedSetState({ containerHeight: event.nativeEvent.layout.height }); }; onEndReached = () => { const { cursor } = this.state; if (cursor !== null) { this.fetchPhotos(cursor); } }; onViewableItemsChanged = (info: ViewableItemsChange) => { const viewableIndices = []; for (const { index } of info.viewableItems) { if (index !== null && index !== undefined) { viewableIndices.push(index); } } this.viewableIndices = viewableIndices; }; setMediaQueued = (selection: MediaLibrarySelection, isQueued: boolean) => { this.setState((prevState: State) => { const prevQueuedMediaURIs = prevState.queuedMediaURIs ? [...prevState.queuedMediaURIs] : []; if (isQueued) { return { queuedMediaURIs: new Set([...prevQueuedMediaURIs, selection.uri]), focusedMediaURI: null, }; } const queuedMediaURIs = prevQueuedMediaURIs.filter( uri => uri !== selection.uri, ); if (queuedMediaURIs.length < prevQueuedMediaURIs.length) { return { queuedMediaURIs: new Set(queuedMediaURIs), focusedMediaURI: null, }; } return null; }); }; setFocus = (selection: MediaLibrarySelection, isFocused: boolean) => { const { uri } = selection; if (isFocused) { this.setState({ focusedMediaURI: uri }); } else if (this.state.focusedMediaURI === uri) { this.setState({ focusedMediaURI: null }); } }; sendSingleMedia = (selection: MediaLibrarySelection) => { this.sendMedia([selection]); }; sendQueuedMedia = () => { const { selections, queuedMediaURIs } = this.state; if (!selections || !queuedMediaURIs) { return; } const queuedSelections = []; for (const uri of queuedMediaURIs) { for (const selection of selections) { if (selection.uri === uri) { queuedSelections.push(selection); break; } } } this.sendMedia(queuedSelections); }; sendMedia(selections: $ReadOnlyArray) { if (this.mediaSelected) { return; } this.mediaSelected = true; const now = Date.now(); const timeProps = { selectTime: now, sendTime: now, }; const selectionsWithTime = selections.map(selection => ({ ...selection, ...timeProps, })); KeyboardRegistry.onItemSelected( mediaGalleryKeyboardName, selectionsWithTime, ); } } const mediaGalleryKeyboardName = 'MediaGalleryKeyboard'; const unboundStyles = { container: { alignItems: 'center', backgroundColor: 'listBackground', flexDirection: 'row', left: 0, position: 'absolute', right: 0, top: 0, }, error: { color: 'listBackgroundLabel', flex: 1, fontSize: 28, textAlign: 'center', }, loadingIndicator: { flex: 1, }, sendButtonContainer: { bottom: 20, position: 'absolute', right: 30, }, separator: { width: 2, }, }; function ConnectedMediaGalleryKeyboard() { const dimensions = useSelector(state => state.dimensions); const foreground = useIsAppForegrounded(); const colors = useColors(); const styles = useStyles(unboundStyles); return ( ); } function ReduxMediaGalleryKeyboard() { return ( ); } KeyboardRegistry.registerKeyboard( mediaGalleryKeyboardName, () => ReduxMediaGalleryKeyboard, ); export { mediaGalleryKeyboardName };