diff --git a/app/stacks/BottomTabNavigator.tsx b/app/stacks/BottomTabNavigator.tsx new file mode 100644 index 00000000000..4e09df7c506 --- /dev/null +++ b/app/stacks/BottomTabNavigator.tsx @@ -0,0 +1,275 @@ +import React from 'react'; +import { createNativeStackNavigator } from '@react-navigation/native-stack'; +import { createNativeBottomTabNavigator } from '@bottom-tabs/react-navigation'; + +import { ThemeContext } from '../theme'; +import { defaultHeader, themedHeader } from '../lib/methods/helpers/navigation'; +// Chat screens +import RoomsListView from '../views/RoomsListView'; +import RoomView from '../views/RoomView'; +import RoomActionsView from '../views/RoomActionsView'; +import RoomInfoView from '../views/RoomInfoView'; +import ReportUserView from '../views/ReportUserView'; +import RoomInfoEditView from '../views/RoomInfoEditView'; +import RoomMembersView from '../views/RoomMembersView'; +import SearchMessagesView from '../views/SearchMessagesView'; +import SelectedUsersView from '../views/SelectedUsersView'; +import InviteUsersView from '../views/InviteUsersView'; +import InviteUsersEditView from '../views/InviteUsersEditView'; +import MessagesView from '../views/MessagesView'; +import AutoTranslateView from '../views/AutoTranslateView'; +import DirectoryView from '../views/DirectoryView'; +import NotificationPrefView from '../views/NotificationPreferencesView'; +import E2EEToggleRoomView from '../views/E2EEToggleRoomView'; +import ForwardLivechatView from '../views/ForwardLivechatView'; +import CloseLivechatView from '../views/CloseLivechatView'; +import LivechatEditView from '../views/LivechatEditView'; +import PickerView from '../views/PickerView'; +import ThreadMessagesView from '../views/ThreadMessagesView'; +import TeamChannelsView from '../views/TeamChannelsView'; +import ReadReceiptsView from '../views/ReadReceiptView'; +import CannedResponsesListView from '../views/CannedResponsesListView'; +import CannedResponseDetail from '../views/CannedResponseDetail'; +import DiscussionsView from '../views/DiscussionsView'; +import ChangeAvatarView from '../views/ChangeAvatarView'; +import AddChannelTeamView from '../views/AddChannelTeamView'; +import AddExistingChannelView from '../views/AddExistingChannelView'; +import SelectListView from '../views/SelectListView'; +import QueueListView from '../ee/omnichannel/views/QueueListView'; +import JitsiMeetView from '../views/JitsiMeetView'; +import CreateChannelView from '../views/CreateChannelView'; +import PushTroubleshootView from '../views/PushTroubleshootView'; +// Search +import SearchView from '../views/SearchView'; +// More tab screens +import MoreView from '../views/MoreView'; +import ProfileView from '../views/ProfileView'; +import UserPreferencesView from '../views/UserPreferencesView'; +import UserNotificationPrefView from '../views/UserNotificationPreferencesView'; +import ChangePasswordView from '../views/ChangePasswordView'; +import SettingsView from '../views/SettingsView'; +import SecurityPrivacyView from '../views/SecurityPrivacyView'; +import GetHelpView from '../views/GetHelpView'; +import E2EEncryptionSecurityView from '../views/E2EEncryptionSecurityView'; +import LanguageView from '../views/LanguageView'; +import ThemeView from '../views/ThemeView'; +import DefaultBrowserView from '../views/DefaultBrowserView'; +import ScreenLockConfigView from '../views/ScreenLockConfigView'; +import MediaAutoDownloadView from '../views/MediaAutoDownloadView'; +import AdminPanelView from '../views/AdminPanelView'; +import AccessibilityAndAppearanceView from '../views/AccessibilityAndAppearanceView'; +import DisplayPrefsView from '../views/DisplayPrefsView'; +import LegalView from '../views/LegalView'; +import { isIOS } from '../lib/methods/helpers'; +import { type TNavigation } from './stackType'; +import i18n from '../i18n'; +import { type BottomTabParamList, type ChatsStackParamList, type MoreStackParamList, type SearchStackParamList } from './types'; + +const renderChatScreens = (Stack: any) => ( + <> + + + {/* @ts-ignore */} + + + + {/* @ts-ignore */} + + + + {/* @ts-ignore */} + + + + {/* @ts-ignore */} + + + + + {/* @ts-ignore */} + + + + + + {/* @ts-ignore */} + + {/* @ts-ignore */} + + + {/* @ts-ignore */} + + + + + + {/* @ts-ignore */} + + + + + + +); + +const HomeStack = createNativeStackNavigator(); +const HomeStackScreen = () => { + 'use memo'; + + const { theme } = React.useContext(ThemeContext); + return ( + + + {renderChatScreens(HomeStack)} + + ); +}; + +const DiscussionsStack = createNativeStackNavigator(); +const DiscussionsStackScreen = () => { + 'use memo'; + + const { theme } = React.useContext(ThemeContext); + return ( + + + {renderChatScreens(DiscussionsStack)} + + ); +}; + +const DMsStack = createNativeStackNavigator(); +const DMsStackScreen = () => { + 'use memo'; + + const { theme } = React.useContext(ThemeContext); + return ( + + + {renderChatScreens(DMsStack)} + + ); +}; + +const SearchStackNav = createNativeStackNavigator(); +const SearchStackScreen = () => { + 'use memo'; + + const { theme } = React.useContext(ThemeContext); + return ( + + + + ); +}; + +const MoreStack = createNativeStackNavigator(); +const MoreStackScreen = () => { + 'use memo'; + + const { theme } = React.useContext(ThemeContext); + return ( + + + + + + + + + + + + + + + + + {/* @ts-ignore */} + + + + + + + + ); +}; + +const Tab = createNativeBottomTabNavigator(); +const BottomTabNavigator = () => { + 'use memo'; + + return ( + + ({ sfSymbol: 'house.fill' }) + }} + /> + ({ sfSymbol: 'bubble.left.and.bubble.right.fill' }) + }} + /> + ({ sfSymbol: 'envelope.fill' }) + }} + /> + ({ sfSymbol: 'magnifyingglass' }) + }} + /> + ({ sfSymbol: 'ellipsis' }) + }} + /> + + ); +}; + +export default BottomTabNavigator; diff --git a/app/stacks/InsideStack.tsx b/app/stacks/InsideStack.tsx index a314c801885..f3237f09da5 100644 --- a/app/stacks/InsideStack.tsx +++ b/app/stacks/InsideStack.tsx @@ -6,6 +6,7 @@ import { createDrawerNavigator } from '@react-navigation/drawer'; import { ThemeContext } from '../theme'; import { defaultHeader, themedHeader } from '../lib/methods/helpers/navigation'; import Sidebar from '../views/SidebarView'; +import BottomTabNavigator from './BottomTabNavigator'; // Chats Stack import RoomView from '../views/RoomView'; import RoomsListView from '../views/RoomsListView'; @@ -235,9 +236,8 @@ const AccessibilityStackNavigator = () => { ); }; -// DrawerNavigator const Drawer = createDrawerNavigator(); -const DrawerNavigator = () => { +export const DrawerNavigator = () => { 'use memo'; const { colors } = React.useContext(ThemeContext); @@ -321,7 +321,7 @@ const InsideStackNavigator = () => { return ( - + ; E2ESaveYourPasswordStackNavigator: NavigatorScreenParams; @@ -26,7 +28,7 @@ export type ChatsStackParamList = { SettingsView: any; NewMessageStackNavigator: any; NewMessageStack: undefined; - RoomsListView: undefined; + RoomsListView: { roomFilter?: RoomFilterType } | undefined; RoomView: | { rid: string; @@ -239,6 +241,25 @@ export type DrawerParamList = { AccessibilityStackNavigator: NavigatorScreenParams; }; +export type SearchStackParamList = { + SearchView: undefined; +}; + +export type MoreStackParamList = { + MoreView: undefined; +} & ProfileStackParamList & + SettingsStackParamList & + AdminPanelStackParamList & + AccessibilityStackParamList; + +export type BottomTabParamList = { + HomeStack: NavigatorScreenParams; + DiscussionsStack: NavigatorScreenParams; + DMsStack: NavigatorScreenParams; + SearchStack: NavigatorScreenParams; + MoreStack: NavigatorScreenParams; +}; + export type NewMessageStackParamList = { NewMessageView: undefined; SelectedUsersView: { @@ -277,6 +298,7 @@ export type E2EEnterYourPasswordStackParamList = { export type InsideStackParamList = { DrawerNavigator: NavigatorScreenParams; + BottomTabNavigator: NavigatorScreenParams; NewMessageStackNavigator: NavigatorScreenParams; E2ESaveYourPasswordStackNavigator: NavigatorScreenParams; E2EEnterYourPasswordStackNavigator: NavigatorScreenParams; diff --git a/app/views/MoreView/index.tsx b/app/views/MoreView/index.tsx new file mode 100644 index 00000000000..ec28881d581 --- /dev/null +++ b/app/views/MoreView/index.tsx @@ -0,0 +1,70 @@ +import React, { memo, useCallback } from 'react'; +import { ScrollView } from 'react-native'; +import { useNavigation } from '@react-navigation/native'; + +import SafeAreaView from '../../containers/SafeAreaView'; +import * as List from '../../containers/List'; +import i18n from '../../i18n'; +import { usePermissions } from '../../lib/hooks/usePermissions'; + +const MoreView = memo(function MoreView() { + 'use memo'; + + const navigation = useNavigation(); + const [viewRoomAdministrationPermission, viewStatisticsPermission, viewPrivilegedSettingPermission] = usePermissions([ + 'view-room-administration', + 'view-statistics', + 'view-privileged-setting' + ]); + const showAdmin = viewRoomAdministrationPermission || viewStatisticsPermission || viewPrivilegedSettingPermission; + + const navigateTo = useCallback( + (screen: string) => { + navigation.navigate(screen); + }, + [navigation] + ); + + return ( + + + + } + onPress={() => navigateTo('ProfileView')} + testID='more-view-profile' + /> + + } + onPress={() => navigateTo('AccessibilityAndAppearanceView')} + testID='more-view-accessibility' + /> + + } + onPress={() => navigateTo('SettingsView')} + testID='more-view-settings' + /> + + {showAdmin ? ( + <> + } + onPress={() => navigateTo('AdminPanelView')} + testID='more-view-admin' + /> + + + ) : null} + + + + ); +}); + +export default MoreView; diff --git a/app/views/RoomsListView/hooks/useHeader.tsx b/app/views/RoomsListView/hooks/useHeader.tsx index cccc32456fa..cc592212ab6 100644 --- a/app/views/RoomsListView/hooks/useHeader.tsx +++ b/app/views/RoomsListView/hooks/useHeader.tsx @@ -11,10 +11,18 @@ import { getUserSelector } from '../../../selectors/login'; import { useTheme } from '../../../theme'; import RoomsListHeaderView from '../components/Header'; import { RoomsSearchContext } from '../contexts/RoomsSearchProvider'; +import { type RoomFilterType } from '../../../stacks/types'; -export const useHeader = () => { +const TAB_TITLES: Record = { + home: 'Chats', + discussions: 'Discussions', + dms: 'Direct_Messages' +}; + +export const useHeader = (roomFilter?: RoomFilterType) => { 'use memo'; + const isTabLayout = !!roomFilter; const { searchEnabled, search, startSearch, stopSearch } = useContext(RoomsSearchContext); const [options, setOptions] = useState(null); const supportedVersionsStatus = useAppSelector(state => state.supportedVersions.status); @@ -80,7 +88,8 @@ export const useHeader = () => { }, [isMasterDetail, navigation]); useLayoutEffect(() => { - if (searchEnabled) { + // Search mode header (only for drawer layout - tabs use the Search tab instead) + if (!isTabLayout && searchEnabled) { const searchOptions = { headerLeft: () => ( @@ -97,6 +106,48 @@ export const useHeader = () => { return; } + // Tab layout header: no drawer toggle, no search button, tab-specific title + if (isTabLayout) { + const tabOptions = { + headerLeft: () => null, + title: i18n.t(TAB_TITLES[roomFilter] || 'Home'), + headerRight: () => ( + + {issuesWithNotifications ? ( + + ) : null} + {canCreateRoom ? ( + + ) : null} + + + ) + }; + navigation.setOptions(tabOptions); + if (isTablet) { + setOptions(tabOptions); + } + return; + } + + // Default drawer layout header const options = { headerLeft: () => ( { goToNewMessage, startSearch, stopSearch, - search + search, + isTabLayout, + roomFilter ]); return { options }; diff --git a/app/views/RoomsListView/hooks/useSubscriptions.ts b/app/views/RoomsListView/hooks/useSubscriptions.ts index 485649f35c4..7ebfe44231a 100644 --- a/app/views/RoomsListView/hooks/useSubscriptions.ts +++ b/app/views/RoomsListView/hooks/useSubscriptions.ts @@ -8,6 +8,7 @@ import { SortBy } from '../../../lib/constants/constantDisplayMode'; import database from '../../../lib/database'; import { useAppSelector } from '../../../lib/hooks/useAppSelector'; import { getUserSelector } from '../../../selectors/login'; +import { type RoomFilterType } from '../../../stacks/types'; const CHATS_HEADER = 'Chats'; const UNREAD_HEADER = 'Unread'; @@ -35,7 +36,7 @@ const addRoomsGroup = (data: TSubscriptionModel[], header: string, allData: TSub return allData; }; -export const useSubscriptions = () => { +export const useSubscriptions = (roomFilter?: RoomFilterType) => { 'use memo'; const useRealName = useAppSelector(state => state.settings.UI_Use_Real_Name); @@ -68,7 +69,19 @@ export const useSubscriptions = () => { subscriptionRef.current = observable.subscribe(data => { let tempChats = [] as TSubscriptionModel[]; + + // Apply tab-level filter before grouping let chats = data; + if (roomFilter === 'home') { + // Home: everything except DMs and discussions + chats = data.filter(s => s.t !== 'd' && !s.prid); + } else if (roomFilter === 'discussions') { + // Discussions only + chats = data.filter(s => !!s.prid); + } else if (roomFilter === 'dms') { + // DMs only + chats = data.filter(s => s.t === 'd' && !s.prid); + } // let omnichannelsUpdate: string[] = []; const isOmnichannelAgent = roles?.includes('livechat-agent'); @@ -124,7 +137,7 @@ export const useSubscriptions = () => { return () => { subscriptionRef.current?.unsubscribe(); }; - }, [isGrouping, sortBy, useRealName, showUnread, showFavorites, groupByType, roles, server]); + }, [isGrouping, sortBy, useRealName, showUnread, showFavorites, groupByType, roles, server, roomFilter]); return { subscriptions, diff --git a/app/views/RoomsListView/index.tsx b/app/views/RoomsListView/index.tsx index 0ec091befb3..700de7196b1 100644 --- a/app/views/RoomsListView/index.tsx +++ b/app/views/RoomsListView/index.tsx @@ -1,9 +1,10 @@ -import { useNavigation } from '@react-navigation/native'; +import { useNavigation, useRoute, type RouteProp } from '@react-navigation/native'; import React, { memo, useContext, useEffect } from 'react'; import { BackHandler, FlatList, RefreshControl } from 'react-native'; import { useSafeAreaFrame } from 'react-native-safe-area-context'; import { shallowEqual } from 'react-redux'; +import { type ChatsStackParamList } from '../../stacks/types'; import ActivityIndicator from '../../containers/ActivityIndicator'; import BackgroundContainer from '../../containers/BackgroundContainer'; import { ChangePasswordRequired } from '../../containers/ChangePasswordRequired'; @@ -33,7 +34,10 @@ const INITIAL_NUM_TO_RENDER = isTablet ? 20 : 12; const RoomsListView = memo(function RoomsListView() { 'use memo'; - useHeader(); + const route = useRoute>(); + const roomFilter = route.params?.roomFilter; + + useHeader(roomFilter); const { searching, searchEnabled, searchResults, stopSearch } = useContext(RoomsSearchContext); const { colors } = useTheme(); const username = useAppSelector(state => getUserSelector(state).username); @@ -45,7 +49,7 @@ const RoomsListView = memo(function RoomsListView() { const navigation = useNavigation(); const { width } = useSafeAreaFrame(); const getItemLayout = useGetItemLayout(); - const { subscriptions, loading } = useSubscriptions(); + const { subscriptions, loading } = useSubscriptions(roomFilter); const subscribedRoom = useAppSelector(state => state.room.subscribedRoom); const changingServer = useAppSelector(state => state.server.changingServer); const { refreshing, onRefresh } = useRefresh({ searching }); diff --git a/app/views/SearchView/index.tsx b/app/views/SearchView/index.tsx new file mode 100644 index 00000000000..02cf1398719 --- /dev/null +++ b/app/views/SearchView/index.tsx @@ -0,0 +1,120 @@ +import React, { memo, useCallback, useLayoutEffect, useState } from 'react'; +import { FlatList } from 'react-native'; +import { useNavigation } from '@react-navigation/native'; +import { useSafeAreaFrame } from 'react-native-safe-area-context'; +import { shallowEqual } from 'react-redux'; + +import ActivityIndicator from '../../containers/ActivityIndicator'; +import BackgroundContainer from '../../containers/BackgroundContainer'; +import RoomItem from '../../containers/RoomItem'; +import { type IRoomItem } from '../../containers/RoomItem/interfaces'; +import i18n from '../../i18n'; +import { MAX_SIDEBAR_WIDTH } from '../../lib/constants/tablet'; +import { useAppSelector } from '../../lib/hooks/useAppSelector'; +import { getRoomAvatar, getRoomTitle, getUidDirectMessage, isIOS } from '../../lib/methods/helpers'; +import { goRoom } from '../../lib/methods/helpers/goRoom'; +import { useDebounce } from '../../lib/methods/helpers/debounce'; +import { search as searchMethod } from '../../lib/methods/search'; +import { getUserSelector } from '../../selectors/login'; +import { useTheme } from '../../theme'; + +const SearchView = memo(function SearchView() { + 'use memo'; + + const [results, setResults] = useState([]); + const [searching, setSearching] = useState(false); + const [hasSearched, setHasSearched] = useState(false); + const navigation = useNavigation(); + const { colors } = useTheme(); + const username = useAppSelector(state => getUserSelector(state).username); + const isMasterDetail = useAppSelector(state => state.app.isMasterDetail); + const useRealName = useAppSelector(state => state.settings.UI_Use_Real_Name) as boolean; + const showLastMessage = useAppSelector(state => state.settings.Store_Last_Message) as boolean; + const { displayMode, showAvatar } = useAppSelector(state => state.sortPreferences, shallowEqual); + const { width } = useSafeAreaFrame(); + const subscribedRoom = useAppSelector(state => state.room.subscribedRoom); + + const handleSearch = useDebounce(async (text: string) => { + if (!text.trim()) { + setResults([]); + setSearching(false); + setHasSearched(false); + return; + } + setSearching(true); + setHasSearched(true); + const result = await searchMethod({ text }); + setResults(result as IRoomItem[]); + setSearching(false); + }, 300); + + useLayoutEffect(() => { + navigation.setOptions({ + headerSearchBarOptions: { + onChangeText: (e: { nativeEvent: { text: string } }) => { + handleSearch(e.nativeEvent.text); + }, + placeholder: i18n.t('Search'), + autoCapitalize: 'none', + hideWhenScrolling: false + } + }); + }, [navigation, handleSearch]); + + const onPressItem = useCallback( + (item = {} as IRoomItem) => { + goRoom({ item, isMasterDetail }); + }, + [isMasterDetail] + ); + + const renderItem = useCallback( + ({ item }: { item: IRoomItem }) => { + const id = item.t === 'd' ? item._id : getUidDirectMessage(item); + return ( + true} + isFocused={subscribedRoom === item.rid} + swipeEnabled={false} + showAvatar={showAvatar} + displayMode={displayMode} + /> + ); + }, + [username, showLastMessage, onPressItem, isMasterDetail, width, useRealName, subscribedRoom, showAvatar, displayMode] + ); + + if (searching) { + return ; + } + + if (hasSearched && results.length === 0) { + return ; + } + + if (!hasSearched) { + return ; + } + + return ( + item.rid || item._id} + renderItem={renderItem} + style={{ backgroundColor: colors.surfaceRoom }} + keyboardShouldPersistTaps='always' + keyboardDismissMode={isIOS ? 'on-drag' : 'none'} + /> + ); +}); + +export default SearchView; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 8122f18684a..fd2ae18f6d8 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -164,7 +164,7 @@ PODS: - GoogleUtilities/Environment (~> 8.0) - GoogleUtilities/UserDefaults (~> 8.0) - PromisesObjC (~> 2.4) - - FirebaseRemoteConfigInterop (11.10.0) + - FirebaseRemoteConfigInterop (11.15.0) - FirebaseSessions (11.10.0): - FirebaseCore (~> 11.10.0) - FirebaseCoreExtension (~> 11.10.0) @@ -185,38 +185,38 @@ PODS: - GoogleDataTransport (10.1.0): - nanopb (~> 3.30910.0) - PromisesObjC (~> 2.4) - - GoogleUtilities/AppDelegateSwizzler (8.0.2): + - GoogleUtilities/AppDelegateSwizzler (8.1.0): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - GoogleUtilities/Privacy - - GoogleUtilities/Environment (8.0.2): + - GoogleUtilities/Environment (8.1.0): - GoogleUtilities/Privacy - - GoogleUtilities/Logger (8.0.2): + - GoogleUtilities/Logger (8.1.0): - GoogleUtilities/Environment - GoogleUtilities/Privacy - - GoogleUtilities/MethodSwizzler (8.0.2): + - GoogleUtilities/MethodSwizzler (8.1.0): - GoogleUtilities/Logger - GoogleUtilities/Privacy - - GoogleUtilities/Network (8.0.2): + - GoogleUtilities/Network (8.1.0): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Privacy - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (8.0.2)": + - "GoogleUtilities/NSData+zlib (8.1.0)": - GoogleUtilities/Privacy - - GoogleUtilities/Privacy (8.0.2) - - GoogleUtilities/Reachability (8.0.2): + - GoogleUtilities/Privacy (8.1.0) + - GoogleUtilities/Reachability (8.1.0): - GoogleUtilities/Logger - GoogleUtilities/Privacy - - GoogleUtilities/UserDefaults (8.0.2): + - GoogleUtilities/UserDefaults (8.1.0): - GoogleUtilities/Logger - GoogleUtilities/Privacy - hermes-engine (0.79.4): - hermes-engine/Pre-built (= 0.79.4) - hermes-engine/Pre-built (0.79.4) - - libavif/core (0.11.1) - - libavif/libdav1d (0.11.1): + - libavif/core (1.0.0) + - libavif/libdav1d (1.0.0): - libavif/core - libdav1d (>= 0.6.0) - libdav1d (1.2.0) @@ -1614,6 +1614,57 @@ PODS: - Yoga - react-native-background-timer (2.4.1): - React-Core + - react-native-bottom-tabs (1.1.0): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - react-native-bottom-tabs/common (= 1.1.0) + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SwiftUIIntrospect (~> 1.0) + - Yoga + - react-native-bottom-tabs/common (1.1.0): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SwiftUIIntrospect (~> 1.0) + - Yoga - react-native-cameraroll (7.10.0): - DoubleConversion - glog @@ -2506,7 +2557,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNScreens (4.13.1): + - RNScreens (4.16.0): - DoubleConversion - glog - hermes-engine @@ -2530,9 +2581,9 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNScreens/common (= 4.13.1) + - RNScreens/common (= 4.16.0) - Yoga - - RNScreens/common (4.13.1): + - RNScreens/common (4.16.0): - DoubleConversion - glog - hermes-engine @@ -2630,10 +2681,10 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - SDWebImage (5.21.0): - - SDWebImage/Core (= 5.21.0) - - SDWebImage/Core (5.21.0) - - SDWebImageAVIFCoder (0.11.0): + - SDWebImage (5.21.7): + - SDWebImage/Core (= 5.21.7) + - SDWebImage/Core (5.21.7) + - SDWebImageAVIFCoder (0.11.1): - libavif/core (>= 0.11.0) - SDWebImage (~> 5.10) - SDWebImageSVGCoder (1.7.0): @@ -2643,6 +2694,7 @@ PODS: - SDWebImage/Core (~> 5.17) - simdjson (3.9.4) - SocketRocket (0.7.1) + - SwiftUIIntrospect (1.3.0) - TOCropViewController (2.7.4) - WatermelonDB (0.28.1-0): - React @@ -2720,6 +2772,7 @@ DEPENDENCIES: - React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`) - react-native-a11y-order (from `../node_modules/react-native-a11y-order`) - react-native-background-timer (from `../node_modules/react-native-background-timer`) + - react-native-bottom-tabs (from `../node_modules/react-native-bottom-tabs`) - "react-native-cameraroll (from `../node_modules/@react-native-camera-roll/camera-roll`)" - "react-native-cookies (from `../node_modules/@react-native-cookies/cookies`)" - react-native-keyboard-controller (from `../node_modules/react-native-keyboard-controller`) @@ -2808,6 +2861,7 @@ SPEC REPOS: - SDWebImageSVGCoder - SDWebImageWebPCoder - SocketRocket + - SwiftUIIntrospect - TOCropViewController - ZXingObjC @@ -2939,6 +2993,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-a11y-order" react-native-background-timer: :path: "../node_modules/react-native-background-timer" + react-native-bottom-tabs: + :path: "../node_modules/react-native-bottom-tabs" react-native-cameraroll: :path: "../node_modules/@react-native-camera-roll/camera-roll" react-native-cookies: @@ -3098,15 +3154,15 @@ SPEC CHECKSUMS: FirebaseCoreInternal: ef4505d2afb1d0ebbc33162cb3795382904b5679 FirebaseCrashlytics: 84b073c997235740e6a951b7ee49608932877e5c FirebaseInstallations: 9980995bdd06ec8081dfb6ab364162bdd64245c3 - FirebaseRemoteConfigInterop: 7c9a9c65eff32cbb0f7bf8d18140612ad57dfcc6 + FirebaseRemoteConfigInterop: 1c6135e8a094cc6368949f5faeeca7ee8948b8aa FirebaseSessions: 9b3b30947b97a15370e0902ee7a90f50ef60ead6 fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 GoogleAppMeasurement: 36684bfb3ee034e2b42b4321eb19da3a1b81e65d GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 - GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d + GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 hermes-engine: 8b5a5eb386b990287d072fd7b6f6ebd9544dd251 - libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7 + libavif: 5f8e715bea24debec477006f21ef9e95432e254d libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 MobileCrypto: badd466d89e6fb166575c8a5cb74606ee3eec0fe @@ -3146,6 +3202,7 @@ SPEC CHECKSUMS: React-microtasksnativemodule: 44180d1c009908666733364261fabf0613ac77e7 react-native-a11y-order: 419a0c2849125d884e1c2a5db3a6ed06c8d45b92 react-native-background-timer: 4638ae3bee00320753647900b21260b10587b6f7 + react-native-bottom-tabs: 33ebd0e1518501a03b2c80872005a42e47f21ed3 react-native-cameraroll: 23d28040c32ca8b20661e0c41b56ab041779244b react-native-cookies: d648ab7025833b977c0b19e142503034f5f29411 react-native-keyboard-controller: 9ec7ee23328c30251a399cffd8b54324a00343bf @@ -3202,20 +3259,21 @@ SPEC CHECKSUMS: RNImageCropPicker: c78ee80ea778f90cb850f4d26696fd9c1a7bcfbb RNLocalize: ca86348d88b9a89da0e700af58d428ab3f343c4e RNReanimated: f52ccd5ceea2bae48d7421eec89b3f0c10d7b642 - RNScreens: b13e4c45f0406f33986a39c0d8da0324bff94435 + RNScreens: eb3800639b236501d2d6487d930b35fe3db68cb0 RNSVG: 680e961f640e381aab730a04b2371969686ed9f7 RNTrueSheet: e9ee7ff82a7854295eac6fc225e54f1539e6cd7b - SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868 - SDWebImageAVIFCoder: 00310d246aab3232ce77f1d8f0076f8c4b021d90 + SDWebImage: e9fc87c1aab89a8ab1bbd74eba378c6f53be8abf + SDWebImageAVIFCoder: afe194a084e851f70228e4be35ef651df0fc5c57 SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 simdjson: 7bb9e33d87737cec966e7b427773c67baa4458fe SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 + SwiftUIIntrospect: fee9aa07293ee280373a591e1824e8ddc869ba5d TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654 WatermelonDB: 4c846c8cb94eef3cba90fa034d15310163226703 - Yoga: dfabf1234ccd5ac41d1b1d43179f024366ae9831 + Yoga: 2a3a4c38a8441b6359d5e5914d35db7b2b67aebd ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5 PODFILE CHECKSUM: 199f6fbbe6fb415c822cca992e6152000ac55b3e -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj index 5a928d6ed48..c498845b783 100644 --- a/ios/RocketChatRN.xcodeproj/project.pbxproj +++ b/ios/RocketChatRN.xcodeproj/project.pbxproj @@ -1825,7 +1825,7 @@ inputFileListPaths = ( ); inputPaths = ( - "$TARGET_BUILD_DIR/$INFOPLIST_PATH", + $TARGET_BUILD_DIR/$INFOPLIST_PATH, ); name = "Upload source maps to Bugsnag"; outputFileListPaths = ( @@ -1845,7 +1845,7 @@ inputFileListPaths = ( ); inputPaths = ( - "$TARGET_BUILD_DIR/$INFOPLIST_PATH", + $TARGET_BUILD_DIR/$INFOPLIST_PATH, ); name = "Upload source maps to Bugsnag"; outputFileListPaths = ( @@ -2593,7 +2593,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 +2669,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; diff --git a/ios/RocketChatRN/Info.plist b/ios/RocketChatRN/Info.plist index d5f34d83938..6184a45ee1a 100644 --- a/ios/RocketChatRN/Info.plist +++ b/ios/RocketChatRN/Info.plist @@ -109,7 +109,7 @@ UIViewControllerBasedStatusBarAppearance UIDesignRequiresCompatibility - + NSUserActivityTypes INSendMessageIntent diff --git a/package.json b/package.json index 4b3e53b0dd1..8819e690e38 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "bugsnag:upload-android": "bugsnag-cli upload react-native-android" }, "dependencies": { + "@bottom-tabs/react-navigation": "^1.1.0", "@bugsnag/react-native": "8.4.0", "@expo/vector-icons": "^14.1.0", "@hookform/resolvers": "^2.9.10", @@ -89,6 +90,7 @@ "react-native-animatable": "1.3.3", "react-native-background-timer": "2.4.1", "react-native-bootsplash": "^6.3.8", + "react-native-bottom-tabs": "^1.1.0", "react-native-config-reader": "4.1.1", "react-native-console-time-polyfill": "1.2.3", "react-native-device-info": "11.1.0", @@ -111,7 +113,7 @@ "react-native-reanimated": "^3.17.1", "react-native-restart": "0.0.22", "react-native-safe-area-context": "^5.4.0", - "react-native-screens": "^4.13.1", + "react-native-screens": "4.16.0", "react-native-skeleton-placeholder": "5.2.4", "react-native-svg": "^15.12.1", "react-native-url-polyfill": "2.0.0", diff --git a/patches/react-native-bottom-tabs+1.1.0.patch b/patches/react-native-bottom-tabs+1.1.0.patch new file mode 100644 index 00000000000..9fa3bf33fd7 --- /dev/null +++ b/patches/react-native-bottom-tabs+1.1.0.patch @@ -0,0 +1,20 @@ +diff --git a/node_modules/react-native-bottom-tabs/ios/TabViewImpl.swift b/node_modules/react-native-bottom-tabs/ios/TabViewImpl.swift +index 72938be..9799e4b 100644 +--- a/node_modules/react-native-bottom-tabs/ios/TabViewImpl.swift ++++ b/node_modules/react-native-bottom-tabs/ios/TabViewImpl.swift +@@ -117,6 +117,15 @@ struct TabViewImpl: View { + + tabBar.isHidden = props.tabBarHidden + ++ if #available(iOS 26.0, *) { ++ if props.barTintColor == nil && props.scrollEdgeAppearance != "opaque" { ++ if let inactiveTintColor = props.inactiveTintColor { ++ tabBar.unselectedItemTintColor = inactiveTintColor ++ } ++ return ++ } ++ } ++ + if props.scrollEdgeAppearance == "transparent" { + configureTransparentAppearance(tabBar: tabBar, props: props) + return diff --git a/yarn.lock b/yarn.lock index 6d8d740fa76..50988ea4f9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2251,6 +2251,13 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@bottom-tabs/react-navigation@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@bottom-tabs/react-navigation/-/react-navigation-1.1.0.tgz#83e1931e47de2edf6660f3fdf6b06518104edea1" + integrity sha512-+4YppCodABcSNIgJiq95QUQ+3ClVBG+rLG3WmYI0+/nbxqKbCz6luFBep4KFOj98Iplj1JY2Ki6ix8CcOZVQ/Q== + dependencies: + color "^5.0.0" + "@bugsnag/cli@^3.2.1": version "3.2.1" resolved "https://registry.yarnpkg.com/@bugsnag/cli/-/cli-3.2.1.tgz#0c0149920e940c1a05f4492ef991bbe83741f2de" @@ -6526,6 +6533,13 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" +color-convert@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-3.1.3.tgz#db6627b97181cb8facdfce755ae26f97ab0711f1" + integrity sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg== + dependencies: + color-name "^2.0.0" + color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" @@ -6536,6 +6550,11 @@ color-name@^1.0.0, color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-name@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-2.1.0.tgz#0b677385c1c4b4edfdeaf77e38fa338e3a40b693" + integrity sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg== + color-string@^1.9.0: version "1.9.1" resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" @@ -6544,6 +6563,13 @@ color-string@^1.9.0: color-name "^1.0.0" simple-swizzle "^0.2.2" +color-string@^2.1.3: + version "2.1.4" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-2.1.4.tgz#9dcf566ff976e23368c8bd673f5c35103ab41058" + integrity sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg== + dependencies: + color-name "^2.0.0" + color2k@1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/color2k/-/color2k-1.2.4.tgz#af34950ac58e23cf224a01cb8dd0c9911a79605e" @@ -6557,6 +6583,14 @@ color@^4.2.3: color-convert "^2.0.1" color-string "^1.9.0" +color@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/color/-/color-5.0.3.tgz#f79390b1b778e222ffbb54304d3dbeaef633f97f" + integrity sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA== + dependencies: + color-convert "^3.1.3" + color-string "^2.1.3" + colorette@^1.0.7: version "1.4.0" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" @@ -12013,6 +12047,15 @@ react-native-bootsplash@^6.3.8: ts-dedent "^2.2.0" xml-formatter "^3.6.5" +react-native-bottom-tabs@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/react-native-bottom-tabs/-/react-native-bottom-tabs-1.1.0.tgz#8b63340fc8d3bff479d12c49df656b551a63aa74" + integrity sha512-Uu1gvM3i1Hb4DjVvR/38J1QVQEs0RkPc7K6yon99HgvRWWOyLs7kjPDhUswtb8ije4pKW712skIXWJ0lgKzbyQ== + dependencies: + react-freeze "^1.0.0" + sf-symbols-typescript "^2.0.0" + use-latest-callback "^0.2.1" + react-native-config-reader@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/react-native-config-reader/-/react-native-config-reader-4.1.1.tgz#478b69e32adcc2e9a14f6ef5fa2cbbe012b9a27e" @@ -12091,9 +12134,9 @@ react-native-is-edge-to-edge@^1.1.6, react-native-is-edge-to-edge@^1.1.7: integrity sha512-EH6i7E8epJGIcu7KpfXYXiV2JFIYITtq+rVS8uEb+92naMRBdxhTuS8Wn2Q7j9sqyO0B+Xbaaf9VdipIAmGW4w== react-native-is-edge-to-edge@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.2.1.tgz#64e10851abd9d176cbf2b40562f751622bde3358" - integrity sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q== + version "1.3.1" + resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.3.1.tgz#feb9a6a8faf0874298947edd556e5af22044e139" + integrity sha512-NIXU/iT5+ORyCc7p0z2nnlkouYKX425vuU1OEm6bMMtWWR9yvb+Xg5AZmImTKoF9abxCPqrKC3rOZsKzUYgYZA== "react-native-katex@git+https://github.com/RocketChat/react-native-katex.git": version "1.3.0" @@ -12194,10 +12237,10 @@ react-native-safe-area-context@^5.4.0: resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-5.4.0.tgz#04b51940408c114f75628a12a93569d30c525454" integrity sha512-JaEThVyJcLhA+vU0NU8bZ0a1ih6GiF4faZ+ArZLqpYbL6j7R3caRqj+mE3lEtKCuHgwjLg3bCxLL1GPUJZVqUA== -react-native-screens@^4.13.1: - version "4.13.1" - resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.13.1.tgz#bf19f09b76dee90b5f7bd8aab3c951a565dd111d" - integrity sha512-EESsMAtyzYcL3gpAI2NKKiIo+Ew0fnX4P4b3Zy/+MTc6SJIo3foJbZwdIWd/SUBswOf7IYCvWBppg+D8tbwnsw== +react-native-screens@4.16.0: + version "4.16.0" + resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.16.0.tgz#efa42e77a092aa0b5277c9ae41391ea0240e0870" + integrity sha512-yIAyh7F/9uWkOzCi1/2FqvNvK6Wb9Y1+Kzn16SuGfN9YFJDTbwlzGRvePCNTOX0recpLQF3kc2FmvMUhyTCH1Q== dependencies: react-freeze "^1.0.0" react-native-is-edge-to-edge "^1.2.1" @@ -12969,6 +13012,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.0.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" @@ -14292,6 +14340,11 @@ use-deep-compare-effect@1.6.1: "@types/react" "^17.0.0" dequal "^2.0.2" +use-latest-callback@^0.2.1: + version "0.2.6" + resolved "https://registry.yarnpkg.com/use-latest-callback/-/use-latest-callback-0.2.6.tgz#e5ea752808c86219acc179ace0ae3c1203255e77" + integrity sha512-FvRG9i1HSo0wagmX63Vrm8SnlUU3LMM3WyZkQ76RnslpBrX694AdG4A0zQBx2B3ZifFA0yv/BaEHGBnEax5rZg== + use-latest-callback@^0.2.4: version "0.2.4" resolved "https://registry.yarnpkg.com/use-latest-callback/-/use-latest-callback-0.2.4.tgz#35c0f028f85a3f4cf025b06011110e87cc18f57e"