diff --git a/android/app/src/main/java/chat/rocket/reactnative/MainActivity.kt b/android/app/src/main/java/chat/rocket/reactnative/MainActivity.kt index e4ab65ceba1..da574e2767d 100644 --- a/android/app/src/main/java/chat/rocket/reactnative/MainActivity.kt +++ b/android/app/src/main/java/chat/rocket/reactnative/MainActivity.kt @@ -1,24 +1,28 @@ package chat.rocket.reactnative - + import com.facebook.react.ReactActivity import com.facebook.react.ReactActivityDelegate import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled import com.facebook.react.defaults.DefaultReactActivityDelegate import android.os.Bundle +import android.os.Build +import android.os.PersistableBundle import com.zoontek.rnbootsplash.RNBootSplash import android.content.Intent import android.content.res.Configuration import chat.rocket.reactnative.notification.NotificationIntentHandler - +import androidx.core.net.toUri +import java.util.Locale + class MainActivity : ReactActivity() { - + /** * Returns the name of the main component registered from JavaScript. This is used to schedule * rendering of the component. */ override fun getMainComponentName(): String = "RocketChatRN" - + /** * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] @@ -27,17 +31,30 @@ class MainActivity : ReactActivity() { DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) override fun onCreate(savedInstanceState: Bundle?) { + // quick actions handling + intent?.let { + embedQuickActionIntoLinking(it) + } + RNBootSplash.init(this, R.style.BootTheme) super.onCreate(null) - + // Handle notification intents intent?.let { NotificationIntentHandler.handleIntent(this, it) } } - + public override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) setIntent(intent) - + + /* Keep this after super: + - onNewIntent is required for background quick actions + - Expo handles background shortcuts via JS listeners + - Normalizing before super breaks that flow + */ + // quick actions handling + embedQuickActionIntoLinking(intent) + // Handle notification intents when activity is already running NotificationIntentHandler.handleIntent(this, intent) } @@ -45,4 +62,82 @@ class MainActivity : ReactActivity() { override fun invokeDefaultOnBackPressed() { moveTaskToBack(true) } + + private fun embedQuickActionIntoLinking(intent: Intent) { + val action = intent.action ?: return + + if (BuildConfig.DEBUG){ + android.util.Log.d("RocketChat.QuickAction", "Original action: $action") + } + + // Handle Expo quick actions + if (action == "expo.modules.quickactions.SHORTCUT") { + // Get the PersistableBundle + val shortcutData: PersistableBundle? = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + intent.getParcelableExtra( + "shortcut_data", + PersistableBundle::class.java + ) + } else { + @Suppress("DEPRECATION") + intent.getParcelableExtra("shortcut_data") + } + + if (shortcutData != null) { + + // Try to get the shortcut ID from various possible keys + val shortcutId = shortcutData.getString("id") + ?: shortcutData.getString("shortcutId") + ?: shortcutData.getString("android.intent.extra.shortcut.ID") + + if (shortcutId != null) { + val uri = "rocketchat://quick-action/$shortcutId".toUri() + + if (BuildConfig.DEBUG) { + android.util.Log.d("RocketChat.QuickAction", "Converted to: $uri") + } + + intent.action = Intent.ACTION_VIEW + intent.data = uri + setIntent(intent) + + if (BuildConfig.DEBUG) { + android.util.Log.d( + "RocketChat.QuickAction", + "Intent set with data: ${getIntent().data}" + ) + } + } + } else { + if (BuildConfig.DEBUG){ + android.util.Log.d("RocketChat.QuickAction", "No shortcut_data bundle found") + } + } + return + } + + // skip for non-Expo quick actions (app launches) + if (!action.startsWith("chat.rocket.reactnative.")) { + if (BuildConfig.DEBUG) { + android.util.Log.d("RocketChat.QuickAction", "Not a quick action, skipping") + } + return + } + + val quickAction = action + .removePrefix("chat.rocket.reactnative.") + .lowercase(Locale.ROOT) + .replace('_', '-') + + val uri = "rocketchat://quick-action/$quickAction".toUri() + + intent.action = Intent.ACTION_VIEW + intent.data = uri + setIntent(intent) + + if (BuildConfig.DEBUG){ + android.util.Log.d("RocketChat.QuickAction", "Intent set with data: ${getIntent().data}") + } + } } \ No newline at end of file diff --git a/android/app/src/main/res/drawable/ic_quickaction_add.xml b/android/app/src/main/res/drawable/ic_quickaction_add.xml new file mode 100644 index 00000000000..ab03689d18d --- /dev/null +++ b/android/app/src/main/res/drawable/ic_quickaction_add.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/android/app/src/main/res/drawable/ic_quickaction_contact.xml b/android/app/src/main/res/drawable/ic_quickaction_contact.xml new file mode 100644 index 00000000000..2e92649f3bc --- /dev/null +++ b/android/app/src/main/res/drawable/ic_quickaction_contact.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/android/app/src/main/res/drawable/ic_quickaction_find.xml b/android/app/src/main/res/drawable/ic_quickaction_find.xml new file mode 100644 index 00000000000..a0e11aa0f02 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_quickaction_find.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/android/app/src/main/res/drawable/ic_quickaction_recent.xml b/android/app/src/main/res/drawable/ic_quickaction_recent.xml new file mode 100644 index 00000000000..f18f24197e2 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_quickaction_recent.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/AppContainer.tsx b/app/AppContainer.tsx index 001b9d33a0d..8eaabb90f8d 100644 --- a/app/AppContainer.tsx +++ b/app/AppContainer.tsx @@ -1,12 +1,13 @@ import React, { useContext, memo, useEffect } from 'react'; import { NavigationContainer } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; -import { connect } from 'react-redux'; +import { connect, useDispatch } from 'react-redux'; +import { registerQuickActions, unregisterQuickActions, updateQuickActions } from './lib/quickActions'; import type { SetUsernameStackParamList, StackParamList } from './definitions/navigationTypes'; import Navigation from './lib/navigation/appNavigation'; import { defaultHeader, getActiveRouteName, navigationTheme } from './lib/methods/helpers/navigation'; -import { RootEnum } from './definitions'; +import { type IApplicationState, RootEnum } from './definitions'; // Stacks import AuthLoadingView from './views/AuthLoadingView'; // SetUsername Stack @@ -19,6 +20,8 @@ import { ThemeContext } from './theme'; import { setCurrentScreen } from './lib/methods/helpers/log'; import { themes } from './lib/constants/colors'; import { emitter } from './lib/methods/helpers'; +import { useAppSelector } from './lib/hooks/useAppSelector'; +import { NAVIGATION } from './actions/actionsTypes'; const createStackNavigator = createNativeStackNavigator; @@ -34,6 +37,17 @@ const SetUsernameStack = () => ( const Stack = createStackNavigator(); const App = memo(({ root, isMasterDetail }: { root: string; isMasterDetail: boolean }) => { const { theme } = useContext(ThemeContext); + const dispatch = useDispatch(); + const recentRooms = useAppSelector((state: IApplicationState) => state.rooms.recentRooms); + + useEffect(() => { + registerQuickActions(); + + return () => { + unregisterQuickActions(); + }; + }, []); + useEffect(() => { if (root) { const state = Navigation.navigationRef.current?.getRootState(); @@ -43,6 +57,10 @@ const App = memo(({ root, isMasterDetail }: { root: string; isMasterDetail: bool } }, [root]); + useEffect(() => { + updateQuickActions({ recentRooms }); + }, [recentRooms]); + if (!root) { return null; } @@ -55,6 +73,7 @@ const App = memo(({ root, isMasterDetail }: { root: string; isMasterDetail: bool ref={Navigation.navigationRef} onReady={() => { emitter.emit('navigationReady'); + dispatch({ type: NAVIGATION.NAVIGATION_READY }); }} onStateChange={state => { const previousRouteName = Navigation.routeNameRef.current; diff --git a/app/actions/actionsTypes.ts b/app/actions/actionsTypes.ts index 938ff7b9d5d..3b7bb7acac1 100644 --- a/app/actions/actionsTypes.ts +++ b/app/actions/actionsTypes.ts @@ -12,7 +12,7 @@ function createRequestTypes(base = {}, types = defaultTypes): Record; +} +export function quickActionHandle(params: Partial): IQuickAction { + return { + type: QUICK_ACTIONS.QUICK_ACTION_HANDLE, + params + }; +} diff --git a/app/actions/rooms.ts b/app/actions/rooms.ts index b2034080cc0..ac1fa60d416 100644 --- a/app/actions/rooms.ts +++ b/app/actions/rooms.ts @@ -1,6 +1,7 @@ import { type Action } from 'redux'; import { ROOMS } from './actionsTypes'; +import { type IRecentRoomsStore } from '../reducers/rooms'; export interface IRoomsRequest extends Action { params: any; @@ -14,7 +15,16 @@ export interface IRoomsFailure extends Action { err: Record | string; } -export type IRoomsAction = IRoomsRequest & ISetSearch & IRoomsFailure; +export interface IRoomsLastVisited extends Action { + lastVisitedRoomId: string; + lastVisitedRoomName: string; +} + +export interface IRecentRooms extends Action { + recentRooms: IRecentRoomsStore[]; +} + +export type IRoomsAction = IRoomsRequest & ISetSearch & IRoomsFailure & IRoomsLastVisited & IRecentRooms; export function roomsRequest( params: { @@ -45,3 +55,18 @@ export function roomsRefresh(): Action { type: ROOMS.REFRESH }; } + +export function roomsStoreLastVisited(rid: string, name: string): IRoomsLastVisited { + return { + type: ROOMS.STORE_LAST_VISITED, + lastVisitedRoomId: rid, + lastVisitedRoomName: name + }; +} + +export function roomsStoreRecentRooms(recentRooms: IRecentRoomsStore[]): IRecentRooms { + return { + type: ROOMS.STORE_RECENT_ROOMS, + recentRooms + }; +} diff --git a/app/containers/Avatar/__snapshots__/Avatar.test.tsx.snap b/app/containers/Avatar/__snapshots__/Avatar.test.tsx.snap index 97d9de5ea35..2e201a58eb5 100644 --- a/app/containers/Avatar/__snapshots__/Avatar.test.tsx.snap +++ b/app/containers/Avatar/__snapshots__/Avatar.test.tsx.snap @@ -37,7 +37,9 @@ exports[`Story Snapshots: AvatarExternalProviderUrl should match snapshot 1`] = source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://hips.hearstapps.com/hmg-prod/images/dog-puppy-on-garden-royalty-free-image-1586966191.jpg?crop=0.752xw:1.00xh;0.175xw,0&resize=1200:*&format=png&size=112", }, ] @@ -92,7 +94,9 @@ exports[`Story Snapshots: AvatarPath should match snapshot 1`] = ` source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://open.rocket.chat/avatar/diego.mello?format=png&size=112", }, ] @@ -147,7 +151,9 @@ exports[`Story Snapshots: AvatarRoomId should match snapshot 1`] = ` source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://open.rocket.chat/avatar/room/devWBbYr7inwupPqK?format=png&size=112", }, ] @@ -202,7 +208,9 @@ exports[`Story Snapshots: AvatarText should match snapshot 1`] = ` source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://open.rocket.chat/avatar/Avatar?format=png&size=112", }, ] @@ -257,7 +265,9 @@ exports[`Story Snapshots: AvatarUrl should match snapshot 1`] = ` source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://user-images.githubusercontent.com/29778115/89444446-14738480-d728-11ea-9412-75fd978d95fb.jpg", }, ] @@ -312,7 +322,9 @@ exports[`Story Snapshots: Channel should match snapshot 1`] = ` source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://open.rocket.chat/avatar/@general?format=png&size=112", }, ] @@ -367,7 +379,9 @@ exports[`Story Snapshots: Children should match snapshot 1`] = ` source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://open.rocket.chat/avatar/Avatar?format=png&size=112", }, ] @@ -462,7 +476,9 @@ exports[`Story Snapshots: CustomBorderRadius should match snapshot 1`] = ` source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://open.rocket.chat/avatar/Avatar?format=png&size=112", }, ] @@ -519,7 +535,9 @@ exports[`Story Snapshots: CustomStyle should match snapshot 1`] = ` source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://open.rocket.chat/avatar/Avatar?format=png&size=112", }, ] @@ -574,7 +592,9 @@ exports[`Story Snapshots: Direct should match snapshot 1`] = ` source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://open.rocket.chat/avatar/diego.mello?format=png&size=112", }, ] @@ -696,7 +716,9 @@ exports[`Story Snapshots: RoomAvatarExternalProviderUrl should match snapshot 1` source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://cdn.pensador.com/img/authors/ho/me/homer-simpson-l.jpg?format=png&size=112", }, ] @@ -751,7 +773,9 @@ exports[`Story Snapshots: Static should match snapshot 1`] = ` source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://user-images.githubusercontent.com/29778115/89444446-14738480-d728-11ea-9412-75fd978d95fb.jpg", }, ] @@ -840,7 +864,9 @@ exports[`Story Snapshots: Touchable should match snapshot 1`] = ` source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://open.rocket.chat/avatar/Avatar?format=png&size=112", }, ] @@ -896,7 +922,9 @@ exports[`Story Snapshots: WithETag should match snapshot 1`] = ` source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://open.rocket.chat/avatar/djorkaeff.alexandre?format=png&size=112&etag=5ag8KffJcZj9m5rCv", }, ] @@ -951,7 +979,9 @@ exports[`Story Snapshots: WithoutETag should match snapshot 1`] = ` source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://open.rocket.chat/avatar/djorkaeff.alexandre?format=png&size=112", }, ] @@ -1006,7 +1036,9 @@ exports[`Story Snapshots: WrongServer should match snapshot 1`] = ` source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://google.com/avatar/Avatar?format=png&size=112", }, ] diff --git a/app/containers/Chip/__snapshots__/Chip.test.tsx.snap b/app/containers/Chip/__snapshots__/Chip.test.tsx.snap index 58de1a6452d..8a26f96352e 100644 --- a/app/containers/Chip/__snapshots__/Chip.test.tsx.snap +++ b/app/containers/Chip/__snapshots__/Chip.test.tsx.snap @@ -207,7 +207,9 @@ exports[`Story Snapshots: ChipText should match snapshot 1`] = ` source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://open.rocket.chat/avatar/rocket.cat?format=png&size=56", }, ] @@ -389,7 +391,9 @@ exports[`Story Snapshots: ChipWithShortText should match snapshot 1`] = ` source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://open.rocket.chat/avatar/rocket.cat?format=png&size=56", }, ] @@ -799,7 +803,9 @@ exports[`Story Snapshots: ChipWithoutIcon should match snapshot 1`] = ` source={ [ { - "headers": undefined, + "headers": { + "User-Agent": "RC Mobile; ios unknown; vunknown (unknown)", + }, "uri": "https://open.rocket.chat/avatar/rocket.cat?format=png&size=56", }, ] diff --git a/app/definitions/redux/index.ts b/app/definitions/redux/index.ts index f32d7041e69..002699268a2 100644 --- a/app/definitions/redux/index.ts +++ b/app/definitions/redux/index.ts @@ -48,6 +48,7 @@ import { type IRooms } from '../../reducers/rooms'; import { type IPreferences } from '../IPreferences'; import { type ICustomEmojis } from '../IEmoji'; import { type IUsersTyping } from '../../reducers/usersTyping'; +import { type IUIState } from '../../reducers/ui'; export interface IApplicationState { settings: TSettingsState; @@ -76,6 +77,7 @@ export interface IApplicationState { troubleshootingNotification: ITroubleshootingNotification; supportedVersions: ISupportedVersionsState; inAppFeedback: IInAppFeedbackState; + ui: IUIState; } export type TApplicationActions = TActionActiveUsers & diff --git a/app/i18n/locales/en.json b/app/i18n/locales/en.json index 20adc12eb90..772163ce9f2 100644 --- a/app/i18n/locales/en.json +++ b/app/i18n/locales/en.json @@ -328,6 +328,7 @@ "Enter_manually": "Enter manually", "Enter_the_code": "Enter the code we just emailed you.", "Error_Download_file": "Error while downloading file", + "Error_finding_room": "Error finding room in this server\ntry switching server", "Error_incorrect_password": "Incorrect password", "Error_play_video": "There was an error while playing this video", "Error_prefix": "Error: {{message}}", @@ -453,6 +454,7 @@ "Last_updated": "Last updated", "Last_updated_at": "Last updated at", "Last_updated_on": "Last updated on", + "Last_visited_room": "Last visited", "last-owner-can-not-be-removed": "Last owner cannot be removed", "Leader": "Leader", "Learn_more": "Learn more", @@ -693,6 +695,7 @@ "Receive_Group_Mentions_Info": "Receive @all and @here mentions", "Receive_Notification": "Receive notification", "Receive_notifications_from": "Receive notifications from {{name}}", + "Recent_Rooms": "Recent Rooms", "Recently_used": "Recently used", "Record_audio_message": "Record audio message", "Recording_audio_in_progress": "Recording audio message", @@ -840,6 +843,7 @@ "Slash_Topic_Description": "Set topic", "Slash_Topic_Params": "Topic message", "Smileys_and_people": "Smileys and people", + "Something_Wrong?": "Something Wrong?", "Sort_by": "Sort by", "Sound": "Sound", "Star": "Star", @@ -977,6 +981,7 @@ "Waiting_for_answer": "Waiting for answer", "Waiting_for_network": "Waiting for network...", "Waiting_for_server_connection": "Waiting for server connection", + "We_are_here_to_help": "We're here to help", "Websocket_disabled": "Websocket is disabled for this workspace.\n{{contact}}", "What_are_you_doing_right_now": "What are you doing right now?", "Whats_the_password_for_your_certificate": "What's the password for your certificate?", diff --git a/app/i18n/locales/pt-BR.json b/app/i18n/locales/pt-BR.json index cbd4dfb4eae..b1a16ba5cb5 100644 --- a/app/i18n/locales/pt-BR.json +++ b/app/i18n/locales/pt-BR.json @@ -319,6 +319,7 @@ "Enter_manually": "Inserir manualmente", "Enter_the_code": "Insira o código que acabamos de enviar por e-mail.", "Error_Download_file": "Erro ao baixar o arquivo", + "Error_finding_room": "Erro ao encontrar uma sala neste servidor\ntente mudar de servidor.", "Error_incorrect_password": "Senha incorreta", "Error_play_video": "Houve um erro ao reproduzir esse vídeo", "Error_prefix": "Erro: {{message}}", @@ -443,6 +444,7 @@ "Last_updated": "Última atualização", "Last_updated_at": "Última atualização em", "Last_updated_on": "Última atualização em", + "Last_visited_room": "Última visita", "last-owner-can-not-be-removed": "O último dono não pode ser removido", "Leader": "Líder", "Learn_more": "Saiba mais", @@ -681,6 +683,7 @@ "Receive_Group_Mentions_Info": "Receber menções @all e @here", "Receive_Notification": "Receber notificação", "Receive_notifications_from": "Receber notificação de {{name}}", + "Recent_Rooms": "Quartos recentes", "Recently_used": "Usados recentemente", "Record_audio_message": "Gravar mensagem de áudio", "Register": "Registrar", @@ -821,6 +824,7 @@ "Slash_Topic_Description": "Definir tópico", "Slash_Topic_Params": "Mensagem do tópico", "Smileys_and_people": "Carinhas e pessoas", + "Something_Wrong?": "Algo errado?", "Sort_by": "Ordenar por", "Sound": "Som da notificação", "Star": "Favorito", @@ -959,6 +963,7 @@ "Waiting_for_answer": "Esperando por resposta", "Waiting_for_network": "Aguardando rede...", "Waiting_for_server_connection": "Aguardando conexão com o servidor", + "We_are_here_to_help": "Estamos aqui para ajudar.", "Websocket_disabled": "Websocket está desativado para essa workspace.\n{{contact}}", "What_are_you_doing_right_now": "O que você está fazendo agora?", "Whats_the_password_for_your_certificate": "Qual é a senha para o seu certificado?", diff --git a/app/index.tsx b/app/index.tsx index 88f8347445f..20eed013ddb 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -9,7 +9,9 @@ import { KeyboardProvider } from 'react-native-keyboard-controller'; import ResponsiveLayoutProvider from './lib/hooks/useResponsiveLayout/useResponsiveLayout'; import AppContainer from './AppContainer'; import { appInit, appInitLocalSettings, setMasterDetail as setMasterDetailAction } from './actions/app'; +import { getRecentQuickAction } from './lib/quickActions/getRecentQuickAction'; import { deepLinkingOpen } from './actions/deepLinking'; +import { quickActionHandle } from './actions/quickActions'; import { ActionSheetProvider } from './containers/ActionSheet'; import InAppNotification from './containers/InAppNotification'; import Loading from './containers/Loading'; @@ -61,6 +63,18 @@ interface IState { const parseDeepLinking = (url: string) => { if (url) { + /** + * android only + * restores quick action from intent / Linking + */ + if (url.startsWith('rocketchat://quick-action/')) { + const action = url.replace('rocketchat://quick-action/', ''); + return { + type: 'quick-action', + action + }; + } + url = url.replace(/rocketchat:\/\/|https:\/\/go.rocket.chat\//, ''); const regex = /^(room|auth|invite|shareextension)\?/; const match = url.match(regex); @@ -151,6 +165,24 @@ export default class Root extends React.Component<{}, IState> { // Open app from deep linking const deepLinking = await Linking.getInitialURL(); const parsedDeepLinkingURL = parseDeepLinking(deepLinking!); + + /** + * ios only + * fetched recent quick action stored on native side + * automatically cleared after we call to `getRecentQuickAction()` + */ + const quickAction = await getRecentQuickAction(); + if (quickAction) { + store.dispatch(quickActionHandle({ action: quickAction })); + return; + } + + // android quick action handling via intent + if (parsedDeepLinkingURL?.type === 'quick-action') { + store.dispatch(quickActionHandle({ action: parsedDeepLinkingURL.action })); + return; + } + if (parsedDeepLinkingURL) { store.dispatch(deepLinkingOpen(parsedDeepLinkingURL)); return; diff --git a/app/lib/constants/keys.ts b/app/lib/constants/keys.ts index 8c6dc1da7d2..e0c73d356d1 100644 --- a/app/lib/constants/keys.ts +++ b/app/lib/constants/keys.ts @@ -25,3 +25,6 @@ export const ANALYTICS_EVENTS_KEY = 'RC_ANALYTICS_EVENTS_KEY'; export const TOKEN_KEY = 'reactnativemeteor_usertoken'; export const CURRENT_SERVER = 'currentServer'; export const CERTIFICATE_KEY = 'RC_CERTIFICATE_KEY'; +export const LAST_VISITED_ROOM_ID_KEY = 'RC_LAST_VISITED_ROOM_ID_KEY'; +export const LAST_VISITED_ROOM_NAME_KEY = 'RC_LAST_VISITED_ROOM_NAME_KEY'; +export const RECENT_VISITED_ROOMS_KEY = 'RC_RECENT_VISITED_ROOMS_KEY'; diff --git a/app/lib/methods/getServerList.ts b/app/lib/methods/getServerList.ts new file mode 100644 index 00000000000..de053a8f90e --- /dev/null +++ b/app/lib/methods/getServerList.ts @@ -0,0 +1,10 @@ +import { type TServerModel } from '../../definitions'; +import database from '../database'; + +export const getServersList = async (): Promise => { + const serversDB = database.servers; + + const servers: TServerModel[] = await serversDB.get('servers').query().fetch(); + + return servers; +}; diff --git a/app/lib/methods/helpers/emitter.ts b/app/lib/methods/helpers/emitter.ts index 268ca8f0a96..6516246c6f2 100644 --- a/app/lib/methods/helpers/emitter.ts +++ b/app/lib/methods/helpers/emitter.ts @@ -17,6 +17,10 @@ export type TEmitterEvents = TDynamicMediaDownloadEvents & { setComposerHeightThread: number; audioFocused: string; navigationReady: undefined; + roomVisited: { + rid: string; + name: string; + }; }; export type TKeyEmitterEvent = keyof TEmitterEvents; diff --git a/app/lib/methods/helpers/goRoom.ts b/app/lib/methods/helpers/goRoom.ts index 6119420898f..ae83ee335bc 100644 --- a/app/lib/methods/helpers/goRoom.ts +++ b/app/lib/methods/helpers/goRoom.ts @@ -12,6 +12,8 @@ import { import { getRoomTitle, getUidDirectMessage } from './helpers'; import { createDirectMessage } from '../../services/restApi'; import { emitErrorCreateDirectMessage } from './emitErrorCreateDirectMessage'; +import { getRoom } from '../getRoom'; +import { emitter } from './emitter'; interface IGoRoomItem { search?: boolean; // comes from spotlight @@ -98,6 +100,22 @@ export const goRoom = async ({ const { username } = item; const result = await createDirectMessage(username as string); if (result.success && result?.room?._id) { + try { + // storing last visited room + const room = await getRoom(result?.room?.rid || ''); + + /** + * store.dispatch causing dependency cycle error here + * using emitter based flow to prevent it + */ + emitter.emit('roomVisited', { + rid: result.room._id, + name: room.name + }); + } catch { + // do nothing + } + return navigate({ item: { rid: result.room._id, @@ -120,6 +138,18 @@ export const goRoom = async ({ */ let _item = item; if (item.rid) { + try { + const room = await getRoom(item.rid); + + // storing last visited room + emitter.emit('roomVisited', { + rid: room.rid, + name: room.name + }); + } catch { + // do nothing + } + const sub = await getSubscriptionByRoomId(item.rid); if (sub) { _item = sub; diff --git a/app/lib/methods/logout.ts b/app/lib/methods/logout.ts index 69a9643ec2d..080d038549e 100644 --- a/app/lib/methods/logout.ts +++ b/app/lib/methods/logout.ts @@ -2,7 +2,7 @@ import { Rocketchat as RocketchatClient } from '@rocket.chat/sdk'; import type Model from '@nozbe/watermelondb/Model'; import { getDeviceToken } from '../notifications'; -import { isSsl } from './helpers'; +import { emitter, isSsl } from './helpers'; import { BASIC_AUTH_KEY } from './helpers/fetch'; import database, { getDatabase } from '../database'; import log from './helpers/log'; @@ -24,6 +24,14 @@ function removeServerKeys({ server, userId }: { server: string; userId?: string UserPreferences.removeItem(`${server}-${E2E_RANDOM_PASSWORD_KEY}`); } +function removeLastVisitedRoomKeys() { + // clearing the state + emitter.emit('roomVisited', { + rid: '', + name: '' + }); +} + export async function removeServerData({ server }: { server: string }): Promise { try { const batch: Model[] = []; @@ -117,4 +125,5 @@ export async function logout({ server }: { server: string }): Promise { await removeServerData({ server }); await removeCurrentServer(); await removeServerDatabase({ server }); + removeLastVisitedRoomKeys(); } diff --git a/app/lib/quickActions/getRecentQuickAction.ts b/app/lib/quickActions/getRecentQuickAction.ts new file mode 100644 index 00000000000..90a194bec32 --- /dev/null +++ b/app/lib/quickActions/getRecentQuickAction.ts @@ -0,0 +1,20 @@ +import { NativeModules } from 'react-native'; + +const { QuickActionsConnector } = NativeModules; + +/** + * Reads the initial iOS quick action (cold / background launch). + * Returns the action type once, then clears it on native side. + */ +export async function getRecentQuickAction(): Promise { + if (!QuickActionsConnector?.getInitialQuickAction) { + return null; + } + + try { + const action = await QuickActionsConnector.getInitialQuickAction(); + return action ?? null; + } catch { + return null; + } +} diff --git a/app/lib/quickActions/index.ts b/app/lib/quickActions/index.ts new file mode 100644 index 00000000000..6abab0275b6 --- /dev/null +++ b/app/lib/quickActions/index.ts @@ -0,0 +1,93 @@ +import * as QuickActions from 'expo-quick-actions'; +import { InteractionManager, AppState, Platform } from 'react-native'; + +import store from '../store'; +import { getRecentQuickAction } from './getRecentQuickAction'; +import { quickActionHandle } from '../../actions/quickActions'; +import { type IRecentRoomsStore } from '../../reducers/rooms'; +import { getServersList } from '../methods/getServerList'; +import I18n from '../../i18n'; + +let registered = false; + +let quickActionSubscription: { remove(): void } | null = null; +let appStateSubscription: { remove(): void } | null = null; + +export async function updateQuickActions({ recentRooms }: { recentRooms: IRecentRoomsStore[] } = { recentRooms: [] }) { + const serverList = (await getServersList()).map(server => server._raw.id); + const recentRoomQuickActions: QuickActions.Action[] = recentRooms + .map(room => { + if (!room.server) return null; + if (!serverList.includes(room.server)) return null; + + return { + id: `recent/${encodeURIComponent(room.server)}/${room.rid}`, + title: room.name, + subtitle: room.server, + icon: Platform.select({ + ios: 'symbol:clock.arrow.trianglehead.counterclockwise.rotate.90', + android: 'ic_quickaction_recent', + default: undefined + }) + } as QuickActions.Action; + }) + .filter(item => item !== null) + .reverse(); + + const quickActionItems = [ + ...recentRoomQuickActions, + { + id: 'contact', + title: Platform.select({ android: I18n.t('Contact_us'), ios: I18n.t('Something_Wrong?') }) ?? I18n.t('Contact_us'), + subtitle: I18n.t('We_are_here_to_help'), + icon: Platform.select({ + ios: 'symbol:envelope', + android: 'ic_quickaction_contact' + }) + } + ]; + + await QuickActions.setItems(quickActionItems); +} + +export async function registerQuickActions() { + if (registered) { + return; + } + registered = true; + + await updateQuickActions(); + + quickActionSubscription = QuickActions.addListener(action => { + if (!action?.id) { + return; + } + handleQuickAction(action.id); + }); + + appStateSubscription = AppState.addEventListener('change', async nextState => { + if (nextState === 'active') { + const nativeAction = await getRecentQuickAction(); + if (nativeAction) { + InteractionManager.runAfterInteractions(() => { + handleQuickAction(nativeAction); + }); + } + } + }); +} + +export function unregisterQuickActions() { + quickActionSubscription?.remove(); + appStateSubscription?.remove(); + quickActionSubscription = null; + registered = false; +} + +function handleQuickAction(id: string) { + store.dispatch( + quickActionHandle({ + action: id + }) + ); +} diff --git a/app/lib/store/index.ts b/app/lib/store/index.ts index 3ae917484ac..cfbc18a12e1 100644 --- a/app/lib/store/index.ts +++ b/app/lib/store/index.ts @@ -27,6 +27,7 @@ if (__DEV__) { } const store = createStore(reducers, enhancers); +export type AppDispatch = typeof store.dispatch; sagaMiddleware.run(sagas); export default store; diff --git a/app/reducers/index.js b/app/reducers/index.js index 4258685f94f..3653d31f0b1 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -26,6 +26,8 @@ import usersRoles from './usersRoles'; import troubleshootingNotification from './troubleshootingNotification'; import supportedVersions from './supportedVersions'; import inAppFeedback from './inAppFeedback'; +import quickActions from './quickActions'; +import ui from './ui'; export default combineReducers({ settings, @@ -35,8 +37,10 @@ export default combineReducers({ selectedUsers, createChannel, app, + ui, room, rooms, + quickActions, sortPreferences, share, customEmojis, diff --git a/app/reducers/quickActions.ts b/app/reducers/quickActions.ts new file mode 100644 index 00000000000..74a839fb02f --- /dev/null +++ b/app/reducers/quickActions.ts @@ -0,0 +1,44 @@ +import { type Action } from 'redux'; + +import { QUICK_ACTIONS } from '../actions/actionsTypes'; + +interface IQuickActionsState { + lastAction: string | null; + handled: boolean; +} + +export const initialState: IQuickActionsState = { + lastAction: null, + handled: false +}; + +interface IQuickActionPayloadAction extends Action { + payload: { + action: string; + }; +} + +type QuickActionsAction = IQuickActionPayloadAction | Action; + +export default function quickActions(state = initialState, action: QuickActionsAction): IQuickActionsState { + switch (action.type) { + case QUICK_ACTIONS.QUICK_ACTION_HANDLE: + if (!('payload' in action) || !action.payload?.action) { + return state; + } + return { + ...state, + lastAction: (action as IQuickActionPayloadAction).payload.action, + handled: false + }; + + case QUICK_ACTIONS.QUICK_ACTION_HANDLED: + return { + ...state, + handled: true + }; + + default: + return state; + } +} diff --git a/app/reducers/quickactions.test.ts b/app/reducers/quickactions.test.ts new file mode 100644 index 00000000000..f004dcb3b42 --- /dev/null +++ b/app/reducers/quickactions.test.ts @@ -0,0 +1,67 @@ +import { mockedStore } from './mockedStore'; +import { initialState } from './quickActions'; +import { QUICK_ACTIONS } from '../actions/actionsTypes'; + +describe('test quickActions reducer', () => { + it('should return initial state', () => { + const state = mockedStore.getState().quickActions; + expect(state).toEqual(initialState); + }); + + it('should set lastAction and handled=false on QUICK_ACTION_HANDLE', () => { + mockedStore.dispatch({ + type: QUICK_ACTIONS.QUICK_ACTION_HANDLE, + payload: { + action: 'NEW_MESSAGE' + } + }); + + const state = mockedStore.getState().quickActions; + + expect(state.lastAction).toEqual('NEW_MESSAGE'); + expect(state.handled).toEqual(false); + }); + + it('should not modify state if QUICK_ACTION_HANDLE has no payload', () => { + const prevState = mockedStore.getState().quickActions; + + mockedStore.dispatch({ + type: QUICK_ACTIONS.QUICK_ACTION_HANDLE + }); + + const state = mockedStore.getState().quickActions; + expect(state).toEqual(prevState); + }); + + it('should not modify state if QUICK_ACTION_HANDLE payload has no action', () => { + const prevState = mockedStore.getState().quickActions; + + mockedStore.dispatch({ + type: QUICK_ACTIONS.QUICK_ACTION_HANDLE, + payload: {} + }); + + const state = mockedStore.getState().quickActions; + expect(state).toEqual(prevState); + }); + + it('should set handled=true on QUICK_ACTION_HANDLED', () => { + mockedStore.dispatch({ + type: QUICK_ACTIONS.QUICK_ACTION_HANDLED + }); + + const state = mockedStore.getState().quickActions; + expect(state.handled).toEqual(true); + }); + + it('should return same state for unknown action', () => { + const prevState = mockedStore.getState().quickActions; + + mockedStore.dispatch({ + type: 'UNKNOWN_ACTION' + }); + + const state = mockedStore.getState().quickActions; + expect(state).toEqual(prevState); + }); +}); diff --git a/app/reducers/rooms.test.ts b/app/reducers/rooms.test.ts index 1f84137c701..a1d0718adc8 100644 --- a/app/reducers/rooms.test.ts +++ b/app/reducers/rooms.test.ts @@ -1,4 +1,4 @@ -import { roomsFailure, roomsRefresh, roomsRequest, roomsSuccess } from '../actions/rooms'; +import { roomsFailure, roomsRefresh, roomsRequest, roomsStoreLastVisited, roomsSuccess } from '../actions/rooms'; import { mockedStore } from './mockedStore'; import { initialState } from './rooms'; @@ -34,4 +34,13 @@ describe('test selectedUsers reducer', () => { const state = mockedStore.getState().rooms; expect(state.errorMessage).toEqual('error'); }); + + it('should handle storeLastVisited', () => { + mockedStore.dispatch(roomsStoreLastVisited('room-id-123', 'general')); + + const state = mockedStore.getState().rooms; + + expect(state.lastVisitedRid).toEqual('room-id-123'); + expect(state.lastVisitedName).toEqual('general'); + }); }); diff --git a/app/reducers/rooms.ts b/app/reducers/rooms.ts index de2ef250c35..17dcb8e23bf 100644 --- a/app/reducers/rooms.ts +++ b/app/reducers/rooms.ts @@ -1,18 +1,32 @@ import { type IRoomsAction } from '../actions/rooms'; import { ROOMS } from '../actions/actionsTypes'; +import UserPreferences from '../lib/methods/userPreferences'; +import { CURRENT_SERVER } from '../lib/constants/keys'; + +export interface IRecentRoomsStore { + rid: string; + name: string; + server: string | null; +} export interface IRooms { isFetching: boolean; refreshing: boolean; failure: boolean; errorMessage: Record | string; + lastVisitedRid: string; + lastVisitedName: string; + recentRooms: IRecentRoomsStore[]; } export const initialState: IRooms = { isFetching: false, refreshing: false, failure: false, - errorMessage: {} + errorMessage: {}, + lastVisitedRid: '', + lastVisitedName: '', + recentRooms: [] }; export default function rooms(state = initialState, action: IRoomsAction): IRooms { @@ -44,6 +58,41 @@ export default function rooms(state = initialState, action: IRoomsAction): IRoom isFetching: true, refreshing: true }; + case ROOMS.STORE_LAST_VISITED: + const server = UserPreferences.getString(CURRENT_SERVER); + const newRoom = { rid: action.lastVisitedRoomId, name: action.lastVisitedRoomName, server }; + + const existingIndex = state.recentRooms.findIndex(room => room.rid === newRoom.rid && room.server === newRoom.server); + let updatedRecentRooms: IRecentRoomsStore[]; + + if (existingIndex !== -1) { + // Move existing room to end + updatedRecentRooms = [ + ...state.recentRooms.slice(0, existingIndex), + ...state.recentRooms.slice(existingIndex + 1), + newRoom + ]; + } else { + // Add new room + updatedRecentRooms = [...state.recentRooms, newRoom]; + } + + if (updatedRecentRooms.length > 3) { + updatedRecentRooms = updatedRecentRooms.slice(-3); + } + + return { + ...state, + lastVisitedRid: action.lastVisitedRoomId, + lastVisitedName: action.lastVisitedRoomName, + recentRooms: updatedRecentRooms + }; + case ROOMS.STORE_RECENT_ROOMS: + return { + ...state, + recentRooms: action.recentRooms + }; + default: return state; } diff --git a/app/reducers/ui.ts b/app/reducers/ui.ts new file mode 100644 index 00000000000..b305accfee7 --- /dev/null +++ b/app/reducers/ui.ts @@ -0,0 +1,28 @@ +import { type Action } from 'redux'; + +import { UI } from '../actions/actionsTypes'; + +export interface IUIState { + triggerSearch: boolean; +} + +export const initialState: IUIState = { + triggerSearch: false +}; + +export default function ui(state = initialState, action: Action): IUIState { + switch (action.type) { + case UI.TRIGGER_SEARCH: + return { + ...state, + triggerSearch: true + }; + case UI.CLEAR_TRIGGERED_SEARCH: + return { + ...state, + triggerSearch: false + }; + default: + return state; + } +} diff --git a/app/sagas/index.js b/app/sagas/index.js index b60cc211837..ebdbbe175ff 100644 --- a/app/sagas/index.js +++ b/app/sagas/index.js @@ -15,10 +15,14 @@ import createDiscussion from './createDiscussion'; import encryption from './encryption'; import videoConf from './videoConf'; import troubleshootingNotification from './troubleshootingNotification'; +import quickActions from './quickActions'; +import roomVisited from './roomVisited'; const root = function* root() { yield all([ init(), + quickActions(), + roomVisited(), createChannel(), rooms(), room(), diff --git a/app/sagas/init.js b/app/sagas/init.js index d9d6024abe8..8f587531355 100644 --- a/app/sagas/init.js +++ b/app/sagas/init.js @@ -2,7 +2,13 @@ import { call, put, select, takeLatest } from 'redux-saga/effects'; import RNBootSplash from 'react-native-bootsplash'; import AsyncStorage from '@react-native-async-storage/async-storage'; -import { CURRENT_SERVER, TOKEN_KEY } from '../lib/constants/keys'; +import { + CURRENT_SERVER, + LAST_VISITED_ROOM_ID_KEY, + LAST_VISITED_ROOM_NAME_KEY, + RECENT_VISITED_ROOMS_KEY, + TOKEN_KEY +} from '../lib/constants/keys'; import UserPreferences from '../lib/methods/userPreferences'; import { selectServerRequest } from '../actions/server'; import { setAllPreferences } from '../actions/sortPreferences'; @@ -15,6 +21,7 @@ import { RootEnum } from '../definitions'; import { getSortPreferences } from '../lib/methods/userPreferencesMethods'; import { deepLinkingClickCallPush } from '../actions/deepLinking'; import { getServerById } from '../lib/database/services/Server'; +import { roomsStoreLastVisited, roomsStoreRecentRooms } from '../actions/rooms'; export const initLocalSettings = function* initLocalSettings() { const sortPreferences = getSortPreferences(); @@ -51,6 +58,17 @@ const restore = function* restore() { return; } yield put(selectServerRequest(server, serverRecord.version)); + const lastVisitedRid = UserPreferences.getString(LAST_VISITED_ROOM_ID_KEY); + const lastVisitedRoomName = UserPreferences.getString(LAST_VISITED_ROOM_NAME_KEY); + const recentRooms = JSON.parse(UserPreferences.getString(RECENT_VISITED_ROOMS_KEY)); + + if (recentRooms) { + yield put(roomsStoreRecentRooms(recentRooms)); + } + + if (lastVisitedRid && lastVisitedRoomName) { + yield put(roomsStoreLastVisited(lastVisitedRid, lastVisitedRoomName)); + } } yield put(appReady({})); diff --git a/app/sagas/quickActions.ts b/app/sagas/quickActions.ts new file mode 100644 index 00000000000..91955aa08a8 --- /dev/null +++ b/app/sagas/quickActions.ts @@ -0,0 +1,142 @@ +import { select, takeEvery, put, take, type Effect, call, race, delay } from 'redux-saga/effects'; +import { Alert } from 'react-native'; +import { type Action } from 'redux'; + +import { QUICK_ACTIONS, APP, UI, NAVIGATION } from '../actions/actionsTypes'; +import { appStart, appInit } from '../actions/app'; +import { selectServerRequest, serverInitAdd } from '../actions/server'; +import { type IApplicationState, RootEnum, type TServerModel, type TSubscriptionModel } from '../definitions'; +import UserPreferences from '../lib/methods/userPreferences'; +import { CURRENT_SERVER } from '../lib/constants/keys'; +import Navigation from '../lib/navigation/appNavigation'; +import { sendEmail } from '../views/SettingsView'; +import { goRoom } from '../lib/methods/helpers/goRoom'; +import { getRoom } from '../lib/methods/getRoom'; +import I18n from '../i18n'; +import { getServerById } from '../lib/database/services/Server'; + +interface IQuickActionOpen extends Action { + params?: { + action?: string; + }; + payload?: { + action?: string; + }; +} + +function* waitForAppReady(): Generator { + const isReady: boolean = yield select((state: IApplicationState) => state.app.ready); + + if (!isReady) { + yield put(appInit()); + yield take(APP.READY); + } +} + +function* waitForRoomInDB(rid: string): Generator { + try { + const room = (yield call(getRoom, rid)) as TSubscriptionModel; + return room; + } catch { + // Wait for APP.START OR timeout + const { timeout } = (yield race({ + started: take(APP.START), + timeout: delay(3000) + })) as { started?: unknown; timeout?: true }; + + if (timeout) { + throw new Error('Timed out waiting for APP.START'); + } + } + + return yield call(getRoom, rid); +} + +function* waitForNavigationReady(): Generator { + if (Navigation.navigationRef.current) { + return; + } + + yield take(NAVIGATION.NAVIGATION_READY); +} + +function* switchServer(targetServer: string): Generator { + const server = (yield call(getServerById, targetServer)) as TServerModel; + yield put(selectServerRequest(server._raw.id, server.version, true, true)); +} + +function* handleQuickActionOpen(action: IQuickActionOpen): Generator { + const quickActionFromParams = action.params?.action ?? action.payload?.action; + + if (!quickActionFromParams) { + return; + } + + const actionWithId = quickActionFromParams.split('/'); + + const quickAction = actionWithId[0]; + + switch (quickAction) { + case 'add-server': { + const server = UserPreferences.getString(CURRENT_SERVER); + + yield put(appStart({ root: RootEnum.ROOT_OUTSIDE })); + yield put(serverInitAdd(server || '')); + break; + } + case 'search': { + yield waitForAppReady(); + const currentRoute = Navigation.getCurrentRoute(); + + if (currentRoute?.name !== 'RoomsListView') { + Navigation.navigate('RoomsListView'); + } + yield put({ type: UI.TRIGGER_SEARCH }); + break; + } + case 'contact': { + yield call(sendEmail); + yield waitForAppReady(); // if user navigates back to app just init it + break; + } + case 'recent': { + yield waitForAppReady(); + + const targetServer = decodeURIComponent(actionWithId[1]); + const currentServer: string = yield select((state: IApplicationState) => state.server.server); + + const rid = actionWithId[2]; + + if (!rid) { + showRoomNotFoundError(); + break; + } + + if (currentServer !== targetServer) { + yield call(switchServer, targetServer); + } + + try { + const room = (yield call(waitForRoomInDB, rid)) as TSubscriptionModel; + yield waitForNavigationReady(); + const isMasterDetail = yield select((state: IApplicationState) => state.app.isMasterDetail); + yield call(goRoom, { item: { rid: room.rid }, isMasterDetail }); + } catch (e) { + console.log(e); + showRoomNotFoundError(); + } + + break; + } + } + + yield put({ type: QUICK_ACTIONS.QUICK_ACTION_HANDLED }); +} + +const showRoomNotFoundError = () => { + Alert.alert(I18n.t('Room_not_found'), I18n.t('Error_finding_room')); +}; + +export default function* root(): Generator { + yield takeEvery(QUICK_ACTIONS.QUICK_ACTION_HANDLE, handleQuickActionOpen); +} diff --git a/app/sagas/roomVisited.ts b/app/sagas/roomVisited.ts new file mode 100644 index 00000000000..fcb772c3da7 --- /dev/null +++ b/app/sagas/roomVisited.ts @@ -0,0 +1,43 @@ +import { eventChannel } from 'redux-saga'; +import { call, put, take, fork, select } from 'redux-saga/effects'; +import type { SagaIterator } from 'redux-saga'; + +import { emitter } from '../lib/methods/helpers'; +import { roomsStoreLastVisited } from '../actions/rooms'; +import UserPreferences from '../lib/methods/userPreferences'; +import { LAST_VISITED_ROOM_ID_KEY, LAST_VISITED_ROOM_NAME_KEY, RECENT_VISITED_ROOMS_KEY } from '../lib/constants/keys'; +import { type IApplicationState } from '../definitions'; +import { type IRooms } from '../reducers/rooms'; + +const getRecentRooms = (state: IApplicationState) => state.rooms.recentRooms; + +function createRoomVisitedChannel() { + return eventChannel<{ rid: string; name: string }>(emit => { + const handler = ({ rid, name }: { rid: string; name: string }) => { + emit({ rid, name }); + }; + + emitter.on('roomVisited', handler); + return () => emitter.off('roomVisited', handler); + }); +} + +function* watchRoomVisited(): SagaIterator { + const channel = yield call(createRoomVisitedChannel); + + while (true) { + const { rid, name } = yield take(channel); + yield put(roomsStoreLastVisited(rid, name)); + + const recentRooms: IRooms['recentRooms'] = yield select(getRecentRooms); + + UserPreferences.setString(RECENT_VISITED_ROOMS_KEY, JSON.stringify(recentRooms)); + + UserPreferences.setString(LAST_VISITED_ROOM_ID_KEY, rid); + UserPreferences.setString(LAST_VISITED_ROOM_NAME_KEY, name); + } +} + +export default function* roomVisited(): SagaIterator { + yield fork(watchRoomVisited); +} diff --git a/app/sagas/rooms.js b/app/sagas/rooms.js index 39058a03228..174325121f7 100644 --- a/app/sagas/rooms.js +++ b/app/sagas/rooms.js @@ -18,7 +18,7 @@ const updateRooms = function* updateRooms({ server, newRoomsUpdatedAt }) { const serverRecord = yield serversCollection.find(server); return serversDB.write(async () => { - await serverRecord.update((record) => { + await serverRecord.update(record => { record.roomsUpdatedAt = newRoomsUpdatedAt; }); }); @@ -75,12 +75,12 @@ const handleRoomsRequest = function* handleRoomsRequest({ params }) { const allRecords = [ ...subsToCreate.map(subscription => - subCollection.prepareCreate((s) => { + subCollection.prepareCreate(s => { s._raw = sanitizedRaw({ id: subscription.rid }, subCollection.schema); return Object.assign(s, subscription); }) ), - ...subsToUpdate.map((subscription) => { + ...subsToUpdate.map(subscription => { try { const newSub = subscriptions.find(s => s._id === subscription._id); if (!newSub) { @@ -99,7 +99,7 @@ const handleRoomsRequest = function* handleRoomsRequest({ params }) { return null; } }), - ...subsToDelete.map((subscription) => { + ...subsToDelete.map(subscription => { try { return subscription.prepareDestroyPermanently(); } catch (e) { @@ -108,13 +108,13 @@ const handleRoomsRequest = function* handleRoomsRequest({ params }) { } }), ...messagesToCreate.map(message => - messagesCollection.prepareCreate((m) => { + messagesCollection.prepareCreate(m => { m._raw = sanitizedRaw({ id: message._id }, messagesCollection.schema); m.subscription.id = message.rid; return Object.assign(m, message); }) ), - ...messagesToUpdate.map((message) => { + ...messagesToUpdate.map(message => { const newMessage = lastMessages.find(m => m._id === message.id); return message.prepareUpdate(() => { try { diff --git a/app/views/RoomsListView/hooks/useSearch.ts b/app/views/RoomsListView/hooks/useSearch.ts index 97c7a0d2a12..5f239121ada 100644 --- a/app/views/RoomsListView/hooks/useSearch.ts +++ b/app/views/RoomsListView/hooks/useSearch.ts @@ -1,10 +1,13 @@ -import { useCallback, useReducer } from 'react'; +import { useCallback, useEffect, useReducer } from 'react'; import { AccessibilityInfo } from 'react-native'; +import { useDispatch } from 'react-redux'; import { type IRoomItem } from '../../../containers/RoomItem/interfaces'; import { search as searchLib } from '../../../lib/methods/search'; import { useDebounce } from '../../../lib/methods/helpers/debounce'; import i18n from '../../../i18n'; +import { useAppSelector } from '../../../lib/hooks/useAppSelector'; +import { UI } from '../../../actions/actionsTypes'; interface SearchState { searchEnabled: boolean; @@ -59,7 +62,8 @@ export const useSearch = () => { 'use memo'; const [state, dispatch] = useReducer(searchReducer, initialState); - + const triggerSearch = useAppSelector(state => state.ui?.triggerSearch); + const storeDispatch = useDispatch(); const announceSearchResultsForAccessibility = (count: number) => { if (count < 1) { AccessibilityInfo.announceForAccessibility(i18n.t('No_results_found')); @@ -87,6 +91,13 @@ export const useSearch = () => { dispatch({ type: 'STOP_SEARCH' }); }, []); + useEffect(() => { + if (triggerSearch) { + startSearch(); + storeDispatch({ type: UI.CLEAR_TRIGGERED_SEARCH }); + } + }, [startSearch, storeDispatch, triggerSearch]); + return { searching: state.searching, searchEnabled: state.searchEnabled, diff --git a/app/views/SettingsView/index.tsx b/app/views/SettingsView/index.tsx index 169f7d6da02..5abc1b957a7 100644 --- a/app/views/SettingsView/index.tsx +++ b/app/views/SettingsView/index.tsx @@ -33,6 +33,22 @@ import SidebarView from '../SidebarView'; type TLogScreenName = 'SE_GO_LANGUAGE' | 'SE_GO_DEFAULTBROWSER' | 'SE_GO_THEME' | 'SE_GO_PROFILE' | 'SE_GO_SECURITYPRIVACY'; +export const sendEmail = async () => { + logEvent(events.SE_CONTACT_US); + const subject = encodeURI('Rocket.Chat Mobile App Support'); + const email = encodeURI('support@rocket.chat'); + const description = encodeURI(` + version: ${getReadableVersion} + device: ${getDeviceModel} + `); + try { + await Linking.openURL(`mailto:${email}?subject=${subject}&body=${description}`); + } catch (e) { + logEvent(events.SE_CONTACT_US_F); + showErrorAlert(I18n.t('error-email-send-failed', { message: 'support@rocket.chat' })); + } +}; + const SettingsView = (): React.ReactElement => { 'use memo'; @@ -86,22 +102,6 @@ const SettingsView = (): React.ReactElement => { navigation.navigate(screen); }; - const sendEmail = async () => { - logEvent(events.SE_CONTACT_US); - const subject = encodeURI('Rocket.Chat Mobile App Support'); - const email = encodeURI('support@rocket.chat'); - const description = encodeURI(` - version: ${getReadableVersion} - device: ${getDeviceModel} - `); - try { - await Linking.openURL(`mailto:${email}?subject=${subject}&body=${description}`); - } catch (e) { - logEvent(events.SE_CONTACT_US_F); - showErrorAlert(I18n.t('error-email-send-failed', { message: 'support@rocket.chat' })); - } - }; - const shareApp = () => { let message; if (isAndroid) { diff --git a/ios/AppDelegate.swift b/ios/AppDelegate.swift index b0aa0e2851f..c683dcdbb4f 100644 --- a/ios/AppDelegate.swift +++ b/ios/AppDelegate.swift @@ -4,6 +4,7 @@ import ReactAppDependencyProvider import Firebase import Bugsnag import WatchConnectivity +import Foundation @UIApplicationMain public class AppDelegate: ExpoAppDelegate { @@ -42,6 +43,15 @@ public class AppDelegate: ExpoAppDelegate { launchOptions: launchOptions) #endif + if let shortcutItem = + launchOptions?[UIApplication.LaunchOptionsKey.shortcutItem] as? UIApplicationShortcutItem { + + RCSetPendingQuickActionType(shortcutItem.type) + + // Return false so iOS does NOT call performActionFor again + return false + } + let result = super.application(application, didFinishLaunchingWithOptions: launchOptions) // Initialize boot splash @@ -76,6 +86,15 @@ public class AppDelegate: ExpoAppDelegate { let result = RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler) return super.application(application, continue: userActivity, restorationHandler: restorationHandler) || result } + + public override func application( + _ application: UIApplication, + performActionFor shortcutItem: UIApplicationShortcutItem, + completionHandler: @escaping (Bool) -> Void + ) { + RCSetPendingQuickActionType(shortcutItem.type) + completionHandler(true) + } } class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate { diff --git a/ios/Podfile b/ios/Podfile index 4d852a82cd3..61ffa429e1d 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -9,6 +9,7 @@ prepare_react_native_project! def all_pods pod 'simdjson', path: '../node_modules/@nozbe/simdjson', modular_headers: true pod 'react-native-mmkv', path: '../node_modules/react-native-mmkv', modular_headers: true + pod 'ExpoQuickActions', path: '../node_modules/expo-quick-actions/ios' $RNFirebaseAnalyticsWithoutAdIdSupport = true use_expo_modules! diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 99a6053f60c..c94a12a53a7 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -117,6 +117,8 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - ExpoQuickActions (5.0.0): + - ExpoModulesCore - ExpoSystemUI (5.0.7): - ExpoModulesCore - ExpoVideoThumbnails (9.1.2): @@ -2652,6 +2654,7 @@ DEPENDENCIES: - ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`) - ExpoLocalAuthentication (from `../node_modules/expo-local-authentication/ios`) - ExpoModulesCore (from `../node_modules/expo-modules-core`) + - ExpoQuickActions (from `../node_modules/expo-quick-actions/ios`) - ExpoSystemUI (from `../node_modules/expo-system-ui/ios`) - ExpoVideoThumbnails (from `../node_modules/expo-video-thumbnails/ios`) - ExpoWebBrowser (from `../node_modules/expo-web-browser/ios`) @@ -2829,6 +2832,8 @@ EXTERNAL SOURCES: :path: "../node_modules/expo-local-authentication/ios" ExpoModulesCore: :path: "../node_modules/expo-modules-core" + ExpoQuickActions: + :path: "../node_modules/expo-quick-actions/ios" ExpoSystemUI: :path: "../node_modules/expo-system-ui/ios" ExpoVideoThumbnails: @@ -3059,6 +3064,7 @@ SPEC CHECKSUMS: ExpoKeepAwake: 213acedecafb6fda8c0ffedad22ee9e2903400c5 ExpoLocalAuthentication: f69863a1822e42db67a311ce839ecbac70e7fa65 ExpoModulesCore: b4fdeaceca6a4360d4a75fbae335907427c1df6b + ExpoQuickActions: fdbda7f5874aed3dd2b1d891ec00ab3300dc7541 ExpoSystemUI: 82c970cf8495449698e7343b4f78a0d04bcec9ee ExpoVideoThumbnails: 2a448a23eb4eeb860d92ded372fec6e6a7a0cdcb ExpoWebBrowser: d81a92137919312afc5628d47951dc7a35554f90 @@ -3185,9 +3191,9 @@ SPEC CHECKSUMS: SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654 WatermelonDB: 4c846c8cb94eef3cba90fa034d15310163226703 - Yoga: dfabf1234ccd5ac41d1b1d43179f024366ae9831 + Yoga: 2a3a4c38a8441b6359d5e5914d35db7b2b67aebd ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5 -PODFILE CHECKSUM: 199f6fbbe6fb415c822cca992e6152000ac55b3e +PODFILE CHECKSUM: dd876c3ef913a49cdfbdb46236ad1b6a54b56724 COCOAPODS: 1.15.2 diff --git a/ios/QuickActionsConnector.h b/ios/QuickActionsConnector.h new file mode 100644 index 00000000000..9f6bc2446ad --- /dev/null +++ b/ios/QuickActionsConnector.h @@ -0,0 +1,4 @@ +#import + +@interface QuickActionsConnector : NSObject +@end diff --git a/ios/QuickActionsConnector.mm b/ios/QuickActionsConnector.mm new file mode 100644 index 00000000000..085aff3f34a --- /dev/null +++ b/ios/QuickActionsConnector.mm @@ -0,0 +1,17 @@ +#import "QuickActionsConnector.h" +#import "QuickActionsStore.h" + +@implementation QuickActionsConnector + +RCT_EXPORT_MODULE(); + +RCT_EXPORT_METHOD( + getInitialQuickAction: + (RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject +) { + NSString *action = RCConsumePendingQuickActionType(); + resolve(action); +} + +@end diff --git a/ios/QuickActionsStore.h b/ios/QuickActionsStore.h new file mode 100644 index 00000000000..34764e99c73 --- /dev/null +++ b/ios/QuickActionsStore.h @@ -0,0 +1,8 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +FOUNDATION_EXPORT void RCSetPendingQuickActionType(NSString * _Nullable type); +FOUNDATION_EXPORT NSString * _Nullable RCConsumePendingQuickActionType(void); + +NS_ASSUME_NONNULL_END diff --git a/ios/QuickActionsStore.mm b/ios/QuickActionsStore.mm new file mode 100644 index 00000000000..c3183db1858 --- /dev/null +++ b/ios/QuickActionsStore.mm @@ -0,0 +1,27 @@ +#import "QuickActionsStore.h" + +static NSString * _Nullable RCPendingQuickActionType = nil; +static dispatch_queue_t rcQuickActionQueue; + +__attribute__((constructor)) +static void RCInitQuickActionQueue(void) { + rcQuickActionQueue = dispatch_queue_create( + "chat.rocket.quickactions.queue", + DISPATCH_QUEUE_SERIAL + ); +} + +void RCSetPendingQuickActionType(NSString * _Nullable type) { + dispatch_sync(rcQuickActionQueue, ^{ + RCPendingQuickActionType = type; + }); +} + +NSString * _Nullable RCConsumePendingQuickActionType(void) { + __block NSString *type = nil; + dispatch_sync(rcQuickActionQueue, ^{ + type = RCPendingQuickActionType; + RCPendingQuickActionType = nil; + }); + return type; +} diff --git a/ios/RocketChatRN-Bridging-Header.h b/ios/RocketChatRN-Bridging-Header.h index 40e9146f471..20a4b23ff93 100644 --- a/ios/RocketChatRN-Bridging-Header.h +++ b/ios/RocketChatRN-Bridging-Header.h @@ -10,3 +10,4 @@ #import #import #import +#import "QuickActionsStore.h" diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj index 29cc9563241..a0b2806e2a1 100644 --- a/ios/RocketChatRN.xcodeproj/project.pbxproj +++ b/ios/RocketChatRN.xcodeproj/project.pbxproj @@ -7,10 +7,13 @@ objects = { /* Begin PBXBuildFile section */ - 020BAAEF21339D1538B55D15 /* Pods_defaults_NotificationService.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 537F37501C643FE86F92916E /* Pods_defaults_NotificationService.framework */; }; - 05F9D701BF644C25192B8E79 /* Pods_defaults_RocketChatRN.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A46AABC73B7E9703E69AF850 /* Pods_defaults_RocketChatRN.framework */; }; + 059517F8FF85756835127879 /* Pods_defaults_Rocket_Chat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BABABB66676C60622674D15E /* Pods_defaults_Rocket_Chat.framework */; }; 0C6E2DE448364EA896869ADF /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = B37C79D9BD0742CE936B6982 /* libc++.tbd */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 167D56822F104B7D00880439 /* QuickActionsConnector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 167D56812F104B7D00880439 /* QuickActionsConnector.mm */; }; + 167D56832F104B7D00880439 /* QuickActionsConnector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 167D56812F104B7D00880439 /* QuickActionsConnector.mm */; }; + 16A6D54E2F0D95FC000FCD09 /* QuickActionsStore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16A6D54D2F0D95FC000FCD09 /* QuickActionsStore.mm */; }; + 16A6D54F2F0D95FC000FCD09 /* QuickActionsStore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16A6D54D2F0D95FC000FCD09 /* QuickActionsStore.mm */; }; 1E01C81C2511208400FEF824 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E01C81B2511208400FEF824 /* URL+Extensions.swift */; }; 1E01C8212511301400FEF824 /* PushResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E01C8202511301400FEF824 /* PushResponse.swift */; }; 1E01C8252511303100FEF824 /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E01C8242511303100FEF824 /* Notification.swift */; }; @@ -364,9 +367,10 @@ A2C6E2DD38F8BEE19BFB2E1D /* SecureStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B215A42CFB843397273C7EA /* SecureStorage.m */; }; A48B46D92D3FFBD200945489 /* A11yFlowModule.m in Sources */ = {isa = PBXBuildFile; fileRef = A48B46D82D3FFBD200945489 /* A11yFlowModule.m */; }; A48B46DA2D3FFBD200945489 /* A11yFlowModule.m in Sources */ = {isa = PBXBuildFile; fileRef = A48B46D82D3FFBD200945489 /* A11yFlowModule.m */; }; - ACCF5C382F186B5D43B2C952 /* Pods_defaults_Rocket_Chat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 85094D3182EA9331CC444733 /* Pods_defaults_Rocket_Chat.framework */; }; + AFE1A0329E79D5FDE4B09ECF /* Pods_defaults_RocketChatRN.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C409D947C9DBB25E0204FD /* Pods_defaults_RocketChatRN.framework */; }; BC404914E86821389EEB543D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 391C4F7AA7023CD41EEBD106 /* ExpoModulesProvider.swift */; }; DD2BA30A89E64F189C2C24AC /* libWatermelonDB.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BA7E862283664608B3894E34 /* libWatermelonDB.a */; }; + FF53AD18205526A976C47AA5 /* Pods_defaults_NotificationService.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A10597851D3193C3E990372A /* Pods_defaults_NotificationService.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -465,10 +469,15 @@ /* Begin PBXFileReference section */ 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; + 0547DC0CF78D9F082A8B0BB5 /* Pods-defaults-Rocket.Chat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-Rocket.Chat.debug.xcconfig"; path = "Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat.debug.xcconfig"; sourceTree = ""; }; 06BB44DD4855498082A744AD /* libz.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 13B07F961A680F5B00A75B9A /* Rocket.Chat Experimental.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Rocket.Chat Experimental.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = RocketChatRN/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = RocketChatRN/Info.plist; sourceTree = ""; }; + 167D56812F104B7D00880439 /* QuickActionsConnector.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = QuickActionsConnector.mm; sourceTree = ""; }; + 16A6D54C2F0D95E3000FCD09 /* QuickActionsStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = QuickActionsStore.h; sourceTree = ""; }; + 16A6D54D2F0D95FC000FCD09 /* QuickActionsStore.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = QuickActionsStore.mm; sourceTree = ""; }; + 16A6D5502F0D9674000FCD09 /* QuickActionsConnector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = QuickActionsConnector.h; sourceTree = ""; }; 194D9A8897F4A486C2C6F89A /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-defaults-NotificationService/ExpoModulesProvider.swift"; sourceTree = ""; }; 1E01C81B2511208400FEF824 /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = ""; }; 1E01C8202511301400FEF824 /* PushResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushResponse.swift; sourceTree = ""; }; @@ -612,11 +621,10 @@ 1EFEB5972493B6640072EDC0 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; 1EFEB5992493B6640072EDC0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 1EFEB5A12493B67D0072EDC0 /* NotificationService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationService.entitlements; sourceTree = ""; }; + 25B6129FD3765EC5B5D0F3F3 /* Pods-defaults-RocketChatRN.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-RocketChatRN.debug.xcconfig"; path = "Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN.debug.xcconfig"; sourceTree = ""; }; 391C4F7AA7023CD41EEBD106 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-defaults-Rocket.Chat/ExpoModulesProvider.swift"; sourceTree = ""; }; - 44A0BDC6158008BF9FF04F0A /* Pods-defaults-NotificationService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-NotificationService.release.xcconfig"; path = "Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService.release.xcconfig"; sourceTree = ""; }; + 42C409D947C9DBB25E0204FD /* Pods_defaults_RocketChatRN.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_defaults_RocketChatRN.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 45D5C142B655F8EFD006792C /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-defaults-RocketChatRN/ExpoModulesProvider.swift"; sourceTree = ""; }; - 482E711ACFA5E2C4281835BF /* Pods-defaults-RocketChatRN.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-RocketChatRN.debug.xcconfig"; path = "Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN.debug.xcconfig"; sourceTree = ""; }; - 537F37501C643FE86F92916E /* Pods_defaults_NotificationService.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_defaults_NotificationService.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 60B2A6A31FC4588700BD58E5 /* RocketChatRN.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = RocketChatRN.entitlements; path = RocketChatRN/RocketChatRN.entitlements; sourceTree = ""; }; 65AD38362BFBDF4A00271B39 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 65B9A7192AFC24190088956F /* ringtone.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = ringtone.mp3; sourceTree = ""; }; @@ -624,7 +632,7 @@ 66C2701A2EBBCB570062725F /* MMKVKeyManager.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MMKVKeyManager.mm; sourceTree = ""; }; 66C2701E2EBBCB780062725F /* SecureStorage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SecureStorage.h; sourceTree = ""; }; 66C2701F2EBBCB780062725F /* SecureStorage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SecureStorage.m; sourceTree = ""; }; - 6CE8C7A54627937DB698E839 /* Pods-defaults-NotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-NotificationService.debug.xcconfig"; path = "Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService.debug.xcconfig"; sourceTree = ""; }; + 6B6D45FCB4C3A2DAC625E0C5 /* Pods-defaults-Rocket.Chat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-Rocket.Chat.release.xcconfig"; path = "Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat.release.xcconfig"; sourceTree = ""; }; 7A006F13229C83B600803143 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 7A0129D22C6E8B5900F84A97 /* ShareRocketChatRN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareRocketChatRN.swift; sourceTree = ""; }; 7A0D62D1242AB187006D5C06 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; @@ -640,18 +648,18 @@ 7ACD4853222860DE00442C55 /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; 7ACFE7D82DDE48760090D9BC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AE10C0528A59530003593CB /* Inter.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Inter.ttf; sourceTree = ""; }; - 85094D3182EA9331CC444733 /* Pods_defaults_Rocket_Chat.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_defaults_Rocket_Chat.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 92B416D2ADF070E0E0C782A0 /* Pods-defaults-Rocket.Chat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-Rocket.Chat.debug.xcconfig"; path = "Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat.debug.xcconfig"; sourceTree = ""; }; 9B215A42CFB843397273C7EA /* SecureStorage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = SecureStorage.m; sourceTree = ""; }; 9B215A44CFB843397273C7EC /* MMKVBridge.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = MMKVBridge.mm; path = Shared/RocketChat/MMKVBridge.mm; sourceTree = ""; }; - A46AABC73B7E9703E69AF850 /* Pods_defaults_RocketChatRN.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_defaults_RocketChatRN.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A10597851D3193C3E990372A /* Pods_defaults_NotificationService.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_defaults_NotificationService.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A48B46D72D3FFBD200945489 /* A11yFlowModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = A11yFlowModule.h; sourceTree = ""; }; A48B46D82D3FFBD200945489 /* A11yFlowModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = A11yFlowModule.m; sourceTree = ""; }; + A775A4A535C3A4DF9E009CB5 /* Pods-defaults-NotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-NotificationService.debug.xcconfig"; path = "Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService.debug.xcconfig"; sourceTree = ""; }; B179038FDD7AAF285047814B /* SecureStorage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = SecureStorage.h; sourceTree = ""; }; B37C79D9BD0742CE936B6982 /* libc++.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; BA7E862283664608B3894E34 /* libWatermelonDB.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libWatermelonDB.a; sourceTree = ""; }; - CD1F8BBE6FE382D33AB37935 /* Pods-defaults-Rocket.Chat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-Rocket.Chat.release.xcconfig"; path = "Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat.release.xcconfig"; sourceTree = ""; }; - F6BFACDCE2AB06F4936B3E03 /* Pods-defaults-RocketChatRN.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-RocketChatRN.release.xcconfig"; path = "Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN.release.xcconfig"; sourceTree = ""; }; + BABABB66676C60622674D15E /* Pods_defaults_Rocket_Chat.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_defaults_Rocket_Chat.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D37FD1CB4DF877266A49B72B /* Pods-defaults-RocketChatRN.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-RocketChatRN.release.xcconfig"; path = "Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN.release.xcconfig"; sourceTree = ""; }; + D97BDAB3F63F70EF45D2FD6B /* Pods-defaults-NotificationService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-NotificationService.release.xcconfig"; path = "Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -672,7 +680,7 @@ 7ACD4897222860DE00442C55 /* JavaScriptCore.framework in Frameworks */, 24A2AEF2383D44B586D31C01 /* libz.tbd in Frameworks */, DD2BA30A89E64F189C2C24AC /* libWatermelonDB.a in Frameworks */, - 05F9D701BF644C25192B8E79 /* Pods_defaults_RocketChatRN.framework in Frameworks */, + AFE1A0329E79D5FDE4B09ECF /* Pods_defaults_RocketChatRN.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -694,7 +702,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 020BAAEF21339D1538B55D15 /* Pods_defaults_NotificationService.framework in Frameworks */, + FF53AD18205526A976C47AA5 /* Pods_defaults_NotificationService.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -715,7 +723,7 @@ 7AAB3E3D257E6A6E00707CF6 /* JavaScriptCore.framework in Frameworks */, 7AAB3E3E257E6A6E00707CF6 /* libz.tbd in Frameworks */, 7AAB3E3F257E6A6E00707CF6 /* libWatermelonDB.a in Frameworks */, - ACCF5C382F186B5D43B2C952 /* Pods_defaults_Rocket_Chat.framework in Frameworks */, + 059517F8FF85756835127879 /* Pods_defaults_Rocket_Chat.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -728,6 +736,9 @@ 66C2701E2EBBCB780062725F /* SecureStorage.h */, 66C2701F2EBBCB780062725F /* SecureStorage.m */, 7ACFE7D82DDE48760090D9BC /* AppDelegate.swift */, + 16A6D54C2F0D95E3000FCD09 /* QuickActionsStore.h */, + 16A6D54D2F0D95FC000FCD09 /* QuickActionsStore.mm */, + 16A6D5502F0D9674000FCD09 /* QuickActionsConnector.h */, A48B46D72D3FFBD200945489 /* A11yFlowModule.h */, A48B46D82D3FFBD200945489 /* A11yFlowModule.m */, 7A8B30742BCD9D3F00146A40 /* SSLPinning.h */, @@ -746,6 +757,7 @@ 65AD38362BFBDF4A00271B39 /* PrivacyInfo.xcprivacy */, 66C270192EBBCB570062725F /* MMKVKeyManager.h */, 66C2701A2EBBCB570062725F /* MMKVKeyManager.mm */, + 167D56812F104B7D00880439 /* QuickActionsConnector.mm */, ); name = RocketChatRN; sourceTree = ""; @@ -1143,12 +1155,12 @@ 7AC2B09613AA7C3FEBAC9F57 /* Pods */ = { isa = PBXGroup; children = ( - 6CE8C7A54627937DB698E839 /* Pods-defaults-NotificationService.debug.xcconfig */, - 44A0BDC6158008BF9FF04F0A /* Pods-defaults-NotificationService.release.xcconfig */, - 92B416D2ADF070E0E0C782A0 /* Pods-defaults-Rocket.Chat.debug.xcconfig */, - CD1F8BBE6FE382D33AB37935 /* Pods-defaults-Rocket.Chat.release.xcconfig */, - 482E711ACFA5E2C4281835BF /* Pods-defaults-RocketChatRN.debug.xcconfig */, - F6BFACDCE2AB06F4936B3E03 /* Pods-defaults-RocketChatRN.release.xcconfig */, + A775A4A535C3A4DF9E009CB5 /* Pods-defaults-NotificationService.debug.xcconfig */, + D97BDAB3F63F70EF45D2FD6B /* Pods-defaults-NotificationService.release.xcconfig */, + 0547DC0CF78D9F082A8B0BB5 /* Pods-defaults-Rocket.Chat.debug.xcconfig */, + 6B6D45FCB4C3A2DAC625E0C5 /* Pods-defaults-Rocket.Chat.release.xcconfig */, + 25B6129FD3765EC5B5D0F3F3 /* Pods-defaults-RocketChatRN.debug.xcconfig */, + D37FD1CB4DF877266A49B72B /* Pods-defaults-RocketChatRN.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -1247,9 +1259,9 @@ 7ACD4853222860DE00442C55 /* JavaScriptCore.framework */, B37C79D9BD0742CE936B6982 /* libc++.tbd */, 06BB44DD4855498082A744AD /* libz.tbd */, - 537F37501C643FE86F92916E /* Pods_defaults_NotificationService.framework */, - 85094D3182EA9331CC444733 /* Pods_defaults_Rocket_Chat.framework */, - A46AABC73B7E9703E69AF850 /* Pods_defaults_RocketChatRN.framework */, + A10597851D3193C3E990372A /* Pods_defaults_NotificationService.framework */, + BABABB66676C60622674D15E /* Pods_defaults_Rocket_Chat.framework */, + 42C409D947C9DBB25E0204FD /* Pods_defaults_RocketChatRN.framework */, ); name = Frameworks; sourceTree = ""; @@ -1269,8 +1281,8 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "RocketChatRN" */; buildPhases = ( - 8A4915EBB9B9EA919C35752B /* [CP] Check Pods Manifest.lock */, - 06C10D4F29CD7532492AD29E /* [Expo] Configure project */, + D17D219AF77F48D35A0D7171 /* [CP] Check Pods Manifest.lock */, + 589729E8381BA997CD19EF19 /* [Expo] Configure project */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, @@ -1279,8 +1291,10 @@ 1E1EA8082326CCE300E22452 /* ShellScript */, 1ED0389C2B507B4F00C007D4 /* Embed Watch Content */, 7AAE9EB32891A0D20024F559 /* Upload source maps to Bugsnag */, - BCA373A96A91C30B09231381 /* [CP] Embed Pods Frameworks */, - E57DE3ACCEF8E313DFF4D411 /* [CP] Copy Pods Resources */, + 407D3EDE3DABEE15D27BD87D /* ShellScript */, + 9C104B12BEE385F7555E641F /* [Expo] Configure project */, + 4CCE5B7235CA003F286BD050 /* [CP] Embed Pods Frameworks */, + 69520DF942793F987B1AA05B /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1348,12 +1362,12 @@ isa = PBXNativeTarget; buildConfigurationList = 1EFEB5A02493B6640072EDC0 /* Build configuration list for PBXNativeTarget "NotificationService" */; buildPhases = ( - 664B079CA244E1ABB144A1C9 /* [CP] Check Pods Manifest.lock */, + 23A5F6CB83957B93A7EA1C97 /* [CP] Check Pods Manifest.lock */, 86A998705576AFA7CE938617 /* [Expo] Configure project */, 1EFEB5912493B6640072EDC0 /* Sources */, 1EFEB5922493B6640072EDC0 /* Frameworks */, 1EFEB5932493B6640072EDC0 /* Resources */, - 6BC31F4F1CF6E74A7683D2D4 /* [CP] Copy Pods Resources */, + B4801301A00C50FA3AD72CF9 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1368,18 +1382,15 @@ isa = PBXNativeTarget; buildConfigurationList = 7AAB3E4F257E6A6E00707CF6 /* Build configuration list for PBXNativeTarget "Rocket.Chat" */; buildPhases = ( - 7E2F5D729E377A4A5D1E96B4 /* [CP] Check Pods Manifest.lock */, - 6319FBDD06EF0030B48AD389 /* [Expo] Configure project */, + AEF2010F70C9B50729A6B50C /* [CP] Check Pods Manifest.lock */, + 84028E94C77DEBDD5200728D /* [Expo] Configure project */, 7AAB3E14257E6A6E00707CF6 /* Sources */, 7AAB3E32257E6A6E00707CF6 /* Frameworks */, 7AAB3E41257E6A6E00707CF6 /* Resources */, - 7A55BE3B2F11316900D8744D /* Bundle React Native code and images */, 7AAB3E48257E6A6E00707CF6 /* Embed App Extensions */, - 7A55BE3C2F1131C000D8744D /* ShellScript */, 1ED1ECE32B8699DD00F6620C /* Embed Watch Content */, - 7A55BE3D2F11320C00D8744D /* Upload source maps to Bugsnag */, - F1B660D44BA706C4AA9FC60C /* [CP] Embed Pods Frameworks */, - 441DA981C582EB474E37526F /* [CP] Copy Pods Resources */, + 6E2B7753E1396AE4448B35AE /* [CP] Embed Pods Frameworks */, + 6931B3875D0CA69F22785138 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1553,9 +1564,8 @@ shellPath = /bin/sh; shellScript = "export EXTRA_PACKAGER_ARGS=\"--sourcemap-output $TMPDIR/$(md5 -qs \"$CONFIGURATION_BUILD_DIR\")-main.jsbundle.map\"\nexport NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; }; - 06C10D4F29CD7532492AD29E /* [Expo] Configure project */ = { + 1E1EA8082326CCE300E22452 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -1563,16 +1573,15 @@ ); inputPaths = ( ); - name = "[Expo] Configure project"; outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-defaults-RocketChatRN/expo-configure-project.sh\"\n"; + shellScript = "echo \"Target architectures: $ARCHS\"\n\nAPP_PATH=\"${TARGET_BUILD_DIR}/${WRAPPER_NAME}\"\n\nfind \"$APP_PATH\" -name '*.framework' -type d | while read -r FRAMEWORK\ndo\nFRAMEWORK_EXECUTABLE_NAME=$(defaults read \"$FRAMEWORK/Info.plist\" CFBundleExecutable)\nFRAMEWORK_EXECUTABLE_PATH=\"$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME\"\necho \"Executable is $FRAMEWORK_EXECUTABLE_PATH\"\necho $(lipo -info \"$FRAMEWORK_EXECUTABLE_PATH\")\n\nFRAMEWORK_TMP_PATH=\"$FRAMEWORK_EXECUTABLE_PATH-tmp\"\n\n# remove simulator's archs if location is not simulator's directory\ncase \"${TARGET_BUILD_DIR}\" in\n*\"iphonesimulator\")\necho \"No need to remove archs\"\n;;\n*)\nif $(lipo \"$FRAMEWORK_EXECUTABLE_PATH\" -verify_arch \"i386\") ; then\nlipo -output \"$FRAMEWORK_TMP_PATH\" -remove \"i386\" \"$FRAMEWORK_EXECUTABLE_PATH\"\necho \"i386 architecture removed\"\nrm \"$FRAMEWORK_EXECUTABLE_PATH\"\nmv \"$FRAMEWORK_TMP_PATH\" \"$FRAMEWORK_EXECUTABLE_PATH\"\nfi\nif $(lipo \"$FRAMEWORK_EXECUTABLE_PATH\" -verify_arch \"x86_64\") ; then\nlipo -output \"$FRAMEWORK_TMP_PATH\" -remove \"x86_64\" \"$FRAMEWORK_EXECUTABLE_PATH\"\necho \"x86_64 architecture removed\"\nrm \"$FRAMEWORK_EXECUTABLE_PATH\"\nmv \"$FRAMEWORK_TMP_PATH\" \"$FRAMEWORK_EXECUTABLE_PATH\"\nfi\n;;\nesac\n\necho \"Completed for executable $FRAMEWORK_EXECUTABLE_PATH\"\necho $\n\ndone\n"; }; - 1E1EA8082326CCE300E22452 /* ShellScript */ = { + 23A5F6CB83957B93A7EA1C97 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1580,16 +1589,76 @@ inputFileListPaths = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( ); outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-defaults-NotificationService-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "echo \"Target architectures: $ARCHS\"\n\nAPP_PATH=\"${TARGET_BUILD_DIR}/${WRAPPER_NAME}\"\n\nfind \"$APP_PATH\" -name '*.framework' -type d | while read -r FRAMEWORK\ndo\nFRAMEWORK_EXECUTABLE_NAME=$(defaults read \"$FRAMEWORK/Info.plist\" CFBundleExecutable)\nFRAMEWORK_EXECUTABLE_PATH=\"$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME\"\necho \"Executable is $FRAMEWORK_EXECUTABLE_PATH\"\necho $(lipo -info \"$FRAMEWORK_EXECUTABLE_PATH\")\n\nFRAMEWORK_TMP_PATH=\"$FRAMEWORK_EXECUTABLE_PATH-tmp\"\n\n# remove simulator's archs if location is not simulator's directory\ncase \"${TARGET_BUILD_DIR}\" in\n*\"iphonesimulator\")\necho \"No need to remove archs\"\n;;\n*)\nif $(lipo \"$FRAMEWORK_EXECUTABLE_PATH\" -verify_arch \"i386\") ; then\nlipo -output \"$FRAMEWORK_TMP_PATH\" -remove \"i386\" \"$FRAMEWORK_EXECUTABLE_PATH\"\necho \"i386 architecture removed\"\nrm \"$FRAMEWORK_EXECUTABLE_PATH\"\nmv \"$FRAMEWORK_TMP_PATH\" \"$FRAMEWORK_EXECUTABLE_PATH\"\nfi\nif $(lipo \"$FRAMEWORK_EXECUTABLE_PATH\" -verify_arch \"x86_64\") ; then\nlipo -output \"$FRAMEWORK_TMP_PATH\" -remove \"x86_64\" \"$FRAMEWORK_EXECUTABLE_PATH\"\necho \"x86_64 architecture removed\"\nrm \"$FRAMEWORK_EXECUTABLE_PATH\"\nmv \"$FRAMEWORK_TMP_PATH\" \"$FRAMEWORK_EXECUTABLE_PATH\"\nfi\n;;\nesac\n\necho \"Completed for executable $FRAMEWORK_EXECUTABLE_PATH\"\necho $\n\ndone\n"; + shellScript = "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; + }; + 407D3EDE3DABEE15D27BD87D /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-frameworks.sh", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-defaults-NotificationService-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 4CCE5B7235CA003F286BD050 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-frameworks.sh", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 589729E8381BA997CD19EF19 /* [Expo] Configure project */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "[Expo] Configure project"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-defaults-RocketChatRN/expo-configure-project.sh\"\n"; }; - 441DA981C582EB474E37526F /* [CP] Copy Pods Resources */ = { + 6931B3875D0CA69F22785138 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1665,54 +1734,13 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 6319FBDD06EF0030B48AD389 /* [Expo] Configure project */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "[Expo] Configure project"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-defaults-Rocket.Chat/expo-configure-project.sh\"\n"; - }; - 664B079CA244E1ABB144A1C9 /* [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-defaults-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; - }; - 6BC31F4F1CF6E74A7683D2D4 /* [CP] Copy Pods Resources */ = { + 69520DF942793F987B1AA05B /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService-resources.sh", + "${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/BugsnagReactNative/Bugsnag.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", @@ -1779,45 +1807,28 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 7A55BE3B2F11316900D8744D /* Bundle React Native code and images */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Bundle React Native code and images"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "export EXTRA_PACKAGER_ARGS=\"--sourcemap-output $TMPDIR/$(md5 -qs \"$CONFIGURATION_BUILD_DIR\")-main.jsbundle.map\"\nexport NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; - }; - 7A55BE3C2F1131C000D8744D /* ShellScript */ = { + 6E2B7753E1396AE4448B35AE /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat-frameworks.sh", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", ); - outputFileListPaths = ( - ); + name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "echo \"Target architectures: $ARCHS\"\n\nAPP_PATH=\"${TARGET_BUILD_DIR}/${WRAPPER_NAME}\"\n\nfind \"$APP_PATH\" -name '*.framework' -type d | while read -r FRAMEWORK\ndo\nFRAMEWORK_EXECUTABLE_NAME=$(defaults read \"$FRAMEWORK/Info.plist\" CFBundleExecutable)\nFRAMEWORK_EXECUTABLE_PATH=\"$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME\"\necho \"Executable is $FRAMEWORK_EXECUTABLE_PATH\"\necho $(lipo -info \"$FRAMEWORK_EXECUTABLE_PATH\")\n\nFRAMEWORK_TMP_PATH=\"$FRAMEWORK_EXECUTABLE_PATH-tmp\"\n\n# remove simulator's archs if location is not simulator's directory\ncase \"${TARGET_BUILD_DIR}\" in\n*\"iphonesimulator\")\necho \"No need to remove archs\"\n;;\n*)\nif $(lipo \"$FRAMEWORK_EXECUTABLE_PATH\" -verify_arch \"i386\") ; then\nlipo -output \"$FRAMEWORK_TMP_PATH\" -remove \"i386\" \"$FRAMEWORK_EXECUTABLE_PATH\"\necho \"i386 architecture removed\"\nrm \"$FRAMEWORK_EXECUTABLE_PATH\"\nmv \"$FRAMEWORK_TMP_PATH\" \"$FRAMEWORK_EXECUTABLE_PATH\"\nfi\nif $(lipo \"$FRAMEWORK_EXECUTABLE_PATH\" -verify_arch \"x86_64\") ; then\nlipo -output \"$FRAMEWORK_TMP_PATH\" -remove \"x86_64\" \"$FRAMEWORK_EXECUTABLE_PATH\"\necho \"x86_64 architecture removed\"\nrm \"$FRAMEWORK_EXECUTABLE_PATH\"\nmv \"$FRAMEWORK_TMP_PATH\" \"$FRAMEWORK_EXECUTABLE_PATH\"\nfi\n;;\nesac\n\necho \"Completed for executable $FRAMEWORK_EXECUTABLE_PATH\"\necho $\n\ndone\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat-frameworks.sh\"\n"; + showEnvVarsInLog = 0; }; - 7A55BE3D2F11320C00D8744D /* Upload source maps to Bugsnag */ = { + 7AAE9EB32891A0D20024F559 /* Upload source maps to Bugsnag */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1825,7 +1836,7 @@ inputFileListPaths = ( ); inputPaths = ( - "$TARGET_BUILD_DIR/$INFOPLIST_PATH", + $TARGET_BUILD_DIR/$INFOPLIST_PATH, ); name = "Upload source maps to Bugsnag"; outputFileListPaths = ( @@ -1837,49 +1848,45 @@ shellScript = "SOURCE_MAP=\"$TMPDIR/$(md5 -qs \"$CONFIGURATION_BUILD_DIR\")-main.jsbundle.map\" ../node_modules/@bugsnag/react-native/bugsnag-react-native-xcode.sh\n"; showEnvVarsInLog = 0; }; - 7AAE9EB32891A0D20024F559 /* Upload source maps to Bugsnag */ = { + 84028E94C77DEBDD5200728D /* [Expo] Configure project */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( - "$TARGET_BUILD_DIR/$INFOPLIST_PATH", ); - name = "Upload source maps to Bugsnag"; + name = "[Expo] Configure project"; outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "SOURCE_MAP=\"$TMPDIR/$(md5 -qs \"$CONFIGURATION_BUILD_DIR\")-main.jsbundle.map\" ../node_modules/@bugsnag/react-native/bugsnag-react-native-xcode.sh\n"; - showEnvVarsInLog = 0; + shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-defaults-Rocket.Chat/expo-configure-project.sh\"\n"; }; - 7E2F5D729E377A4A5D1E96B4 /* [CP] Check Pods Manifest.lock */ = { + 86A998705576AFA7CE938617 /* [Expo] Configure project */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", ); - name = "[CP] Check Pods Manifest.lock"; + name = "[Expo] Configure project"; outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-defaults-Rocket.Chat-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; + shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-defaults-NotificationService/expo-configure-project.sh\"\n"; }; - 86A998705576AFA7CE938617 /* [Expo] Configure project */ = { + 9C104B12BEE385F7555E641F /* [Expo] Configure project */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; buildActionMask = 2147483647; @@ -1898,7 +1905,7 @@ shellPath = /bin/sh; shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-defaults-NotificationService/expo-configure-project.sh\"\n"; }; - 8A4915EBB9B9EA919C35752B /* [CP] Check Pods Manifest.lock */ = { + AEF2010F70C9B50729A6B50C /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1913,38 +1920,20 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-defaults-RocketChatRN-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-defaults-Rocket.Chat-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - BCA373A96A91C30B09231381 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - E57DE3ACCEF8E313DFF4D411 /* [CP] Copy Pods Resources */ = { + B4801301A00C50FA3AD72CF9 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-resources.sh", + "${PODS_ROOT}/Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/BugsnagReactNative/Bugsnag.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", @@ -2011,25 +2000,29 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService-resources.sh\"\n"; showEnvVarsInLog = 0; }; - F1B660D44BA706C4AA9FC60C /* [CP] Embed Pods Frameworks */ = { + D17D219AF77F48D35A0D7171 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", + "$(DERIVED_FILE_DIR)/Pods-defaults-RocketChatRN-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -2047,6 +2040,7 @@ 66C2701B2EBBCB570062725F /* MMKVKeyManager.mm in Sources */, 1ED00BB12513E04400A1331F /* ReplyNotification.swift in Sources */, 1E76CBC2251529560067298C /* Storage.swift in Sources */, + 16A6D54F2F0D95FC000FCD09 /* QuickActionsStore.mm in Sources */, 1E76CBD925152C8C0067298C /* Push.swift in Sources */, 1E0426E6251A5467008F022C /* RoomType.swift in Sources */, 1E76CBCD25152C2C0067298C /* MessageType.swift in Sources */, @@ -2066,6 +2060,7 @@ 1ED038A52B50900800C007D4 /* Bundle+Extensions.swift in Sources */, 1E76CBC325152A460067298C /* String+Extensions.swift in Sources */, 1ED038BA2B50A1B800C007D4 /* WatchConnection.swift in Sources */, + 167D56822F104B7D00880439 /* QuickActionsConnector.mm in Sources */, 7AACF8A82C94AB8B0082844E /* EncryptedContent.swift in Sources */, 1ED038A12B508FE700C007D4 /* FileManager+Extensions.swift in Sources */, 1E76CBCA25152C220067298C /* Notification.swift in Sources */, @@ -2322,6 +2317,7 @@ 66C2701C2EBBCB570062725F /* MMKVKeyManager.mm in Sources */, 7AAB3E17257E6A6E00707CF6 /* ReplyNotification.swift in Sources */, 7AAB3E18257E6A6E00707CF6 /* Storage.swift in Sources */, + 16A6D54E2F0D95FC000FCD09 /* QuickActionsStore.mm in Sources */, 7AAB3E19257E6A6E00707CF6 /* Push.swift in Sources */, 7AAB3E1A257E6A6E00707CF6 /* RoomType.swift in Sources */, 7AAB3E1B257E6A6E00707CF6 /* MessageType.swift in Sources */, @@ -2341,6 +2337,7 @@ 1ED038A72B50900800C007D4 /* Bundle+Extensions.swift in Sources */, 7AAB3E26257E6A6E00707CF6 /* String+Extensions.swift in Sources */, 1ED038BB2B50A1B800C007D4 /* WatchConnection.swift in Sources */, + 167D56832F104B7D00880439 /* QuickActionsConnector.mm in Sources */, 7AACF8AA2C94AB8B0082844E /* EncryptedContent.swift in Sources */, 1ED038A32B508FE700C007D4 /* FileManager+Extensions.swift in Sources */, 7AAB3E27257E6A6E00707CF6 /* Notification.swift in Sources */, @@ -2421,7 +2418,7 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 482E711ACFA5E2C4281835BF /* Pods-defaults-RocketChatRN.debug.xcconfig */; + baseConfigurationReference = 25B6129FD3765EC5B5D0F3F3 /* Pods-defaults-RocketChatRN.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; APPLICATION_EXTENSION_API_ONLY = NO; @@ -2486,7 +2483,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F6BFACDCE2AB06F4936B3E03 /* Pods-defaults-RocketChatRN.release.xcconfig */; + baseConfigurationReference = D37FD1CB4DF877266A49B72B /* Pods-defaults-RocketChatRN.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; APPLICATION_EXTENSION_API_ONLY = NO; @@ -2593,7 +2590,7 @@ "$(inherited)", "$(SRCROOT)/../node_modules/rn-extensions-share/ios/**", "$(SRCROOT)/../node_modules/react-native-firebase/ios/RNFirebase/**", - "$PODS_CONFIGURATION_BUILD_DIR/Firebase", + $PODS_CONFIGURATION_BUILD_DIR/Firebase, ); INFOPLIST_FILE = ShareRocketChatRN/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; @@ -2669,7 +2666,7 @@ "$(inherited)", "$(SRCROOT)/../node_modules/rn-extensions-share/ios/**", "$(SRCROOT)/../node_modules/react-native-firebase/ios/RNFirebase/**", - "$PODS_CONFIGURATION_BUILD_DIR/Firebase", + $PODS_CONFIGURATION_BUILD_DIR/Firebase, ); INFOPLIST_FILE = ShareRocketChatRN/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; @@ -2901,7 +2898,7 @@ }; 1EFEB59D2493B6640072EDC0 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 6CE8C7A54627937DB698E839 /* Pods-defaults-NotificationService.debug.xcconfig */; + baseConfigurationReference = A775A4A535C3A4DF9E009CB5 /* Pods-defaults-NotificationService.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(EMBEDDED_CONTENT_CONTAINS_SWIFT)"; CLANG_ANALYZER_NONNULL = YES; @@ -2953,7 +2950,7 @@ }; 1EFEB59E2493B6640072EDC0 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 44A0BDC6158008BF9FF04F0A /* Pods-defaults-NotificationService.release.xcconfig */; + baseConfigurationReference = D97BDAB3F63F70EF45D2FD6B /* Pods-defaults-NotificationService.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(EMBEDDED_CONTENT_CONTAINS_SWIFT)"; CLANG_ANALYZER_NONNULL = YES; @@ -3004,7 +3001,7 @@ }; 7AAB3E50257E6A6E00707CF6 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 92B416D2ADF070E0E0C782A0 /* Pods-defaults-Rocket.Chat.debug.xcconfig */; + baseConfigurationReference = 0547DC0CF78D9F082A8B0BB5 /* Pods-defaults-Rocket.Chat.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; APPLICATION_EXTENSION_API_ONLY = NO; @@ -3019,7 +3016,7 @@ DEVELOPMENT_TEAM = S6UPZG7ZR3; ENABLE_BITCODE = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO; - EXCLUDED_ARCHS = ""; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -3069,7 +3066,7 @@ }; 7AAB3E51257E6A6E00707CF6 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = CD1F8BBE6FE382D33AB37935 /* Pods-defaults-Rocket.Chat.release.xcconfig */; + baseConfigurationReference = 6B6D45FCB4C3A2DAC625E0C5 /* Pods-defaults-Rocket.Chat.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; APPLICATION_EXTENSION_API_ONLY = NO; diff --git a/ios/RocketChatRN/Info.plist b/ios/RocketChatRN/Info.plist index b2d1a400cba..57d83fd56d1 100644 --- a/ios/RocketChatRN/Info.plist +++ b/ios/RocketChatRN/Info.plist @@ -11,8 +11,8 @@ apiKey apiKeyValue - CADisableMinimumFrameDurationOnPhone - + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion en CFBundleDisplayName @@ -60,13 +60,13 @@ LSRequiresIPhoneOS - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - NSAllowsLocalNetworking - - + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSAllowsLocalNetworking + + NSCameraUsageDescription Take photos to share with other users NSFaceIDUsageDescription diff --git a/package.json b/package.json index 45c7b5e50e1..9d3f1465ebb 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "expo-local-authentication": "16.0.3", "expo-navigation-bar": "^4.2.4", "expo-notifications": "0.32.11", + "expo-quick-actions": "^5.0.0", "expo-status-bar": "^2.2.3", "expo-system-ui": "^5.0.7", "expo-video-thumbnails": "9.1.2", diff --git a/yarn.lock b/yarn.lock index 5f334cbad37..2afa4b1d100 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2934,6 +2934,21 @@ temp-dir "~2.0.0" unique-string "~2.0.0" +"@expo/image-utils@~0.7.4": + version "0.7.6" + resolved "https://registry.yarnpkg.com/@expo/image-utils/-/image-utils-0.7.6.tgz#b8442bef770e1c7b39997d57f666bffeeced0a7a" + integrity sha512-GKnMqC79+mo/1AFrmAcUcGfbsXXTRqOMNS1umebuevl3aaw+ztsYEFEiuNhHZW7PQ3Xs3URNT513ZxKhznDscw== + dependencies: + "@expo/spawn-async" "^1.7.2" + chalk "^4.0.0" + getenv "^2.0.0" + jimp-compact "0.16.1" + parse-png "^2.1.0" + resolve-from "^5.0.0" + semver "^7.6.0" + temp-dir "~2.0.0" + unique-string "~2.0.0" + "@expo/json-file@^10.0.8", "@expo/json-file@~10.0.7": version "10.0.8" resolved "https://registry.yarnpkg.com/@expo/json-file/-/json-file-10.0.8.tgz#05e524d1ecc0011db0a6d66b525ea2f58cfe6d43" @@ -8578,6 +8593,15 @@ expo-notifications@0.32.11: expo-application "~7.0.7" expo-constants "~18.0.8" +expo-quick-actions@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/expo-quick-actions/-/expo-quick-actions-5.0.0.tgz#6b3c5c21190607f779be1588ebaf119577a3c120" + integrity sha512-NSsDhfbal11gXsHkJbvYVw7x0QUCKrEth2kBBKZUv03dX4J7ZPADSV89LyEpOVYXCkrw6LuanlEtKavg/BFaRA== + dependencies: + "@expo/image-utils" "~0.7.4" + schema-utils "^4.2.0" + sf-symbols-typescript "^2.1.0" + expo-status-bar@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/expo-status-bar/-/expo-status-bar-2.2.3.tgz#09385a866732328e0af3b4588c4f349a15fd7cd0" @@ -13505,6 +13529,16 @@ schema-utils@^4.0.0: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" +schema-utils@^4.2.0: + version "4.3.3" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.3.tgz#5b1850912fa31df90716963d45d9121fdfc09f46" + integrity sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + semver-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" @@ -13668,6 +13702,11 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== +sf-symbols-typescript@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/sf-symbols-typescript/-/sf-symbols-typescript-2.2.0.tgz#926d6e0715e3d8784cadf7658431e36581254208" + integrity sha512-TPbeg0b7ylrswdGCji8FRGFAKuqbpQlLbL8SOle3j1iHSs5Ob5mhvMAxWN2UItOjgALAB5Zp3fmMfj8mbWvXKw== + shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"