snapshot of https://github.com/jitsi/jitsi-meet.git @ fd1749dd28f4138aa80234fc7f311b19aaffa0e7 (2026-09-15)
Some checks failed
Prosody Plugin Tests / test (push) Failing after 10m11s
Close stale issues and PRs / stale (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-15 15:56:32 +05:00
commit 1d2bfcac0c
3159 changed files with 464524 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Text, View, ViewStyle } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import LoadingIndicator from '../../../base/react/components/native/LoadingIndicator';
import { TEXT_COLOR, navigationStyles } from './styles';
const ConnectingPage = () => {
const { t } = useTranslation();
return (
<JitsiScreen style = { navigationStyles.connectingScreenContainer }>
<View style = { navigationStyles.connectingScreenContent as ViewStyle }>
<SafeAreaView>
<LoadingIndicator
color = { TEXT_COLOR }
size = 'large' />
<Text style = { navigationStyles.connectingScreenText }>
{ t('connectingOverlay.joiningRoom') }
</Text>
</SafeAreaView>
</View>
</JitsiScreen>
);
};
export default ConnectingPage;

View File

@@ -0,0 +1,103 @@
import React from 'react';
import { GestureResponderEvent } from 'react-native';
import { StyleType } from '../../../base/styles/functions.native';
import Button from '../../../base/ui/components/native/Button';
import IconButton from '../../../base/ui/components/native/IconButton';
import { BUTTON_TYPES } from '../../../base/ui/constants.native';
import { navigationStyles } from './styles';
interface IProps {
/**
* Icon button color.
*/
color?: string;
/**
* Is the button disabled?
*/
disabled?: boolean;
/**
* ID of the header navigation button.
*/
id?: string;
/**
* Label of the button.
*/
label?: string;
/**
* Callback to invoke when the {@code HeaderNavigationButton} is clicked/pressed.
*/
onPress?: (e?: GestureResponderEvent | React.MouseEvent) => void;
/**
* The ImageSource to be rendered as image.
*/
src?: any;
/**
* Style of the button.
*/
style?: StyleType;
/**
* Header has two actions.
*/
twoActions?: boolean;
}
const HeaderNavigationButton = ({ color, id, disabled, label, onPress, src, style, twoActions }: IProps) => {
let btnStyle;
let labelStyle;
if (disabled) {
btnStyle = navigationStyles.headerNavigationButtonDisabled;
labelStyle = twoActions
? navigationStyles.headerNavigationButtonLabelBoldDisabled
: navigationStyles.headerNavigationButtonLabelDisabled;
} else {
btnStyle = navigationStyles.headerNavigationButton;
labelStyle = twoActions
? navigationStyles.headerNavigationButtonLabelBold
: navigationStyles.headerNavigationButtonLabel;
}
return (
<>
{
src ? (
<IconButton
color = { color }
id = { id }
onPress = { onPress }
size = { 24 }
src = { src }
style = { [
navigationStyles.headerNavigationButtonIcon,
style
] } />
) : (
<Button
disabled = { disabled }
id = { id }
labelKey = { label }
labelStyle = { labelStyle }
onClick = { onPress }
style = { [
btnStyle,
style
] }
type = { BUTTON_TYPES.TERTIARY } />
)}
</>
);
};
export default HeaderNavigationButton;

View File

@@ -0,0 +1,44 @@
import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { Platform } from 'react-native';
import { useDispatch } from 'react-redux';
import { appNavigate } from '../../../app/actions.native';
import { IconCloseLarge } from '../../../base/icons/svg';
import { cancelKnocking } from '../../../lobby/actions.native';
import HeaderNavigationButton from './HeaderNavigationButton';
/**
* Close icon/text button for the lobby screen based on platform.
*
* @returns {React.Component}
*/
const LobbyCloseButton = React.memo(() => {
const dispatch = useDispatch();
const { t } = useTranslation();
const goBack = useCallback(() => {
dispatch(cancelKnocking());
dispatch(appNavigate(undefined));
}, [ dispatch ]);
if (Platform.OS === 'ios') {
return (
<HeaderNavigationButton
id = { 'close-screen-button' }
label = { t('dialog.close') }
onPress = { goBack } />
);
}
return (
<HeaderNavigationButton
id = { 'close-screen-button' }
onPress = { goBack }
src = { IconCloseLarge } />
);
});
LobbyCloseButton.displayName = 'LobbyCloseButton';
export default LobbyCloseButton;

View File

@@ -0,0 +1,114 @@
import { NavigationContainer, NavigationIndependentTree } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import React, { useCallback } from 'react';
import { connect } from 'react-redux';
import { IReduxState, IStore } from '../../../app/types';
import DialInSummary from '../../../invite/components/dial-in-summary/native/DialInSummary';
import Prejoin from '../../../prejoin/components/native/Prejoin';
import UnsafeRoomWarning from '../../../prejoin/components/native/UnsafeRoomWarning';
import VisitorsQueue from '../../../visitors/components/native/VisitorsQueue';
// eslint-disable-next-line
// @ts-ignore
import WelcomePage from '../../../welcome/components/WelcomePage';
import { isWelcomePageEnabled } from '../../../welcome/functions';
import { _ROOT_NAVIGATION_READY } from '../actionTypes';
import { rootNavigationRef } from '../rootNavigationContainerRef';
import { screen } from '../routes';
import {
conferenceNavigationContainerScreenOptions,
connectingScreenOptions,
dialInSummaryScreenOptions,
navigationContainerTheme,
preJoinScreenOptions,
unsafeMeetingScreenOptions,
visitorsScreenOptions,
welcomeScreenOptions
} from '../screenOptions';
import ConnectingPage from './ConnectingPage';
import ConferenceNavigationContainer
from './conference/components/ConferenceNavigationContainer';
const RootStack = createStackNavigator();
interface IProps {
/**
* Redux dispatch function.
*/
dispatch: IStore['dispatch'];
/**
* Is welcome page available?
*/
isWelcomePageAvailable: boolean;
}
const RootNavigationContainer = ({ dispatch, isWelcomePageAvailable }: IProps) => {
const initialRouteName = isWelcomePageAvailable
? screen.welcome.main : screen.connecting;
const onReady = useCallback(() => {
dispatch({
type: _ROOT_NAVIGATION_READY,
ready: true
});
}, [ dispatch ]);
return (
<NavigationIndependentTree>
<NavigationContainer
onReady = { onReady }
ref = { rootNavigationRef }
theme = { navigationContainerTheme }>
<RootStack.Navigator
initialRouteName = { initialRouteName }>
<RootStack.Screen // @ts-ignore
component = { WelcomePage }
name = { screen.welcome.main }
options = { welcomeScreenOptions } />
<RootStack.Screen // @ts-ignore
component = { DialInSummary }
name = { screen.dialInSummary }
options = { dialInSummaryScreenOptions } />
<RootStack.Screen
component = { ConnectingPage }
name = { screen.connecting }
options = { connectingScreenOptions } />
<RootStack.Screen
component = { Prejoin }
name = { screen.preJoin }
options = { preJoinScreenOptions } />
<RootStack.Screen
component = { UnsafeRoomWarning }
name = { screen.unsafeRoomWarning }
options = { unsafeMeetingScreenOptions } />
<RootStack.Screen
component = { VisitorsQueue }
name = { screen.visitorsQueue }
options = { visitorsScreenOptions } />
<RootStack.Screen
component = { ConferenceNavigationContainer }
name = { screen.conference.root }
options = { conferenceNavigationContainerScreenOptions } />
</RootStack.Navigator>
</NavigationContainer>
</NavigationIndependentTree>
);
};
/**
* Maps part of the Redux store to the props of this component.
*
* @param {Object} state - The Redux state.
* @returns {IProps}
*/
function mapStateToProps(state: IReduxState) {
return {
isWelcomePageAvailable: isWelcomePageEnabled(state)
};
}
export default connect(mapStateToProps)(RootNavigationContainer);

View File

@@ -0,0 +1,44 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { GestureResponderEvent, Platform } from 'react-native';
import { IconCloseLarge } from '../../../base/icons/svg';
import HeaderNavigationButton from './HeaderNavigationButton';
interface IProps {
/**
* Goes back to the previous screen function.
*/
goBack: (e?: GestureResponderEvent | React.MouseEvent) => void;
}
/**
* Close icon/text button based on platform.
*
* @returns {React.Component}
*/
const ScreenCloseButton = React.memo(({ goBack }: IProps) => {
const { t } = useTranslation();
if (Platform.OS === 'ios') {
return (
<HeaderNavigationButton
id = { 'close-screen-button' }
label = { t('dialog.close') }
onPress = { goBack } />
);
}
return (
<HeaderNavigationButton
id = { 'close-screen-button' }
onPress = { goBack }
src = { IconCloseLarge } />
);
});
ScreenCloseButton.displayName = 'ScreenCloseButton';
export default ScreenCloseButton;

View File

@@ -0,0 +1,39 @@
import React from 'react';
import { StyleProp, Text, TextStyle, View } from 'react-native';
import { navigationStyles } from './styles';
interface ITabBarLabelCounterProps {
activeUnreadNr?: boolean;
isFocused: boolean;
label: string;
unreadCount?: number;
}
export const TabBarLabelCounter = ({ activeUnreadNr, isFocused, label, unreadCount }: ITabBarLabelCounterProps) => {
const labelStyles = isFocused
? navigationStyles.unreadCounterDescriptionFocused
: navigationStyles.unreadCounterDescription;
return (
<View
style = {
navigationStyles.unreadCounterContainer as StyleProp<TextStyle> }>
<Text
style = { labelStyles }>
{ label && label }
</Text>
{
activeUnreadNr && (
<View
style = { navigationStyles.unreadCounterCircle as StyleProp<TextStyle> }>
<Text
style = { navigationStyles.unreadCounter as StyleProp<TextStyle> }>
{ unreadCount }
</Text>
</View>
)
}
</View>
);
};

View File

@@ -0,0 +1,91 @@
/* eslint-disable lines-around-comment */
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
getClientHeight,
getClientWidth
} from '../../../../../base/modal/components/functions.native';
import { setFocusedTab } from '../../../../../chat/actions.native';
import Chat from '../../../../../chat/components/native/Chat';
import ClosedCaptions from '../../../../../chat/components/native/ClosedCaptions';
import { ChatTabs } from '../../../../../chat/constants';
import { getFocusedTab, isChatDisabled } from '../../../../../chat/functions';
import { arePollsDisabled } from '../../../../../conference/functions.native';
import { resetUnreadPollsCount } from '../../../../../polls/actions';
import PollsPane from '../../../../../polls/components/native/PollsPane';
import { isCCTabEnabled } from '../../../../../subtitles/functions.any';
import { screen } from '../../../routes';
import { chatTabBarOptions } from '../../../screenOptions';
import ChatTabBar from './ChatTabBar';
const ChatTab = createMaterialTopTabNavigator();
const ChatNavigator = () => {
const dispatch = useDispatch();
const clientHeight = useSelector(getClientHeight);
const clientWidth = useSelector(getClientWidth);
const currentFocusedTab = useSelector(getFocusedTab);
const isPollsTabDisabled = useSelector(arePollsDisabled);
const isChatTabDisabled = useSelector(isChatDisabled);
const isCCTabDisabled = !useSelector(isCCTabEnabled);
const initialRouteName
= currentFocusedTab === ChatTabs.POLLS ? screen.conference.chatTabs.tab.polls
: currentFocusedTab === ChatTabs.CLOSED_CAPTIONS ? screen.conference.chatTabs.tab.closedCaptions
: screen.conference.chatTabs.tab.chat;
return (
// @ts-ignore
<ChatTab.Navigator
backBehavior = 'none'
initialLayout = {{
height: clientHeight,
width: clientWidth
}}
initialRouteName = { initialRouteName }
screenOptions = { chatTabBarOptions }
tabBar = { ChatTabBar }>
{
!isChatTabDisabled
&& <ChatTab.Screen
component = { Chat }
listeners = {{
tabPress: () => {
dispatch(setFocusedTab(ChatTabs.CHAT));
}
}}
name = { screen.conference.chatTabs.tab.chat } />
}
{
!isPollsTabDisabled
&& <ChatTab.Screen
component = { PollsPane }
listeners = {{
tabPress: () => {
dispatch(setFocusedTab(ChatTabs.POLLS));
dispatch(resetUnreadPollsCount);
}
}}
name = { screen.conference.chatTabs.tab.polls } />
}
{
!isCCTabDisabled
&& <ChatTab.Screen
component = { ClosedCaptions }
listeners = {{
tabPress: () => {
dispatch(setFocusedTab(ChatTabs.CLOSED_CAPTIONS));
}
}}
name = { screen.conference.chatTabs.tab.closedCaptions } />
}
</ChatTab.Navigator>
);
};
export default ChatNavigator;

View File

@@ -0,0 +1,8 @@
import { MaterialTopTabBar, MaterialTopTabBarProps } from '@react-navigation/material-top-tabs';
import React from 'react';
const ChatTabBar = (props: MaterialTopTabBarProps) => (
<MaterialTopTabBar { ...props } />
);
export default ChatTabBar;

View File

@@ -0,0 +1,45 @@
import { NavigationContainerRef, StackActions } from '@react-navigation/native';
import React from 'react';
export const conferenceNavigationRef = React.createRef<NavigationContainerRef<any>>();
/**
* User defined navigation action included inside the reference to the container.
*
* @param {string} name - Destination name of the route that has been defined somewhere.
* @param {Object} params - Params to pass to the destination route.
* @returns {Function}
*/
export function navigate(name: string, params?: Object) {
return conferenceNavigationRef.current?.navigate(name, params);
}
/**
* User defined navigation action included inside the reference to the container.
*
* @returns {Function}
*/
export function goBack() {
return conferenceNavigationRef.current?.goBack();
}
/**
* User defined navigation action included inside the reference to the container.
*
* @param {string} name - Destination name of the route to pop back to.
* @returns {Function}
*/
export function popTo(name: string) {
return conferenceNavigationRef.current?.dispatch(StackActions.popTo(name));
}
/**
* User defined navigation action included inside the reference to the container.
*
* @param {Object} params - Params to pass to the destination route.
* @returns {Function}
*/
export function setParams(params: Object) {
return conferenceNavigationRef.current?.setParams(params);
}

View File

@@ -0,0 +1,256 @@
/* eslint-disable lines-around-comment */
import { NavigationContainer, NavigationIndependentTree } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import AudioTranslationDialog
from '../../../../../audio-translation/components/native/AudioTranslationDialog';
import BreakoutRooms
// @ts-ignore
from '../../../../../breakout-rooms/components/native/BreakoutRooms';
// @ts-ignore
import Chat from '../../../../../chat/components/native/Chat';
// @ts-ignore
import Conference from '../../../../../conference/components/native/Conference';
// @ts-ignore
import CarMode from '../../../../../conference/components/native/carmode/CarMode';
// @ts-ignore
import { arePollsDisabled } from '../../../../../conference/functions';
// @ts-ignore
import SharedDocument from '../../../../../etherpad/components/native/SharedDocument';
// @ts-ignore
import GifsMenu from '../../../../../gifs/components/native/GifsMenu';
import AddPeopleDialog
// @ts-ignore
from '../../../../../invite/components/add-people-dialog/native/AddPeopleDialog';
// @ts-ignore
import ParticipantsPane from '../../../../../participants-pane/components/native/ParticipantsPane';
// @ts-ignore
import StartLiveStreamDialog from '../../../../../recording/components/LiveStream/native/StartLiveStreamDialog';
import RecordingTranscriptionDialog
// @ts-ignore
from '../../../../../recording/components/Recording/native/RecordingTranscriptionDialog';
import SalesforceLinkDialog
// @ts-ignore
from '../../../../../salesforce/components/native/SalesforceLinkDialog';
import SecurityDialog
// @ts-ignore
from '../../../../../security/components/security-dialog/native/SecurityDialog';
import SpeakerStats
// @ts-ignore
from '../../../../../speaker-stats/components/native/SpeakerStats';
import LanguageSelectorDialog
// @ts-ignore
from '../../../../../subtitles/components/native/LanguageSelectorDialog';
import { isCCTabEnabled } from '../../../../../subtitles/functions.any';
import Whiteboard from '../../../../../whiteboard/components/native/Whiteboard';
// @ts-ignore
import { screen } from '../../../routes';
import {
audioTranslationScreenOptions,
breakoutRoomsScreenOptions,
carmodeScreenOptions,
chatScreenOptions,
conferenceScreenOptions,
gifsMenuOptions,
inviteScreenOptions,
liveStreamScreenOptions,
lobbyNavigationContainerScreenOptions,
navigationContainerTheme,
participantsScreenOptions,
recordingScreenOptions,
salesforceScreenOptions,
securityScreenOptions,
settingsNavigationContainerScreenOptions,
sharedDocumentScreenOptions,
speakerStatsScreenOptions,
subtitlesScreenOptions,
whiteboardScreenOptions
// @ts-ignore
} from '../../../screenOptions';
// @ts-ignore
import ChatNavigator from '../../chat/components/ChatNavigator';
// @ts-ignore
import LobbyNavigationContainer from '../../lobby/components/LobbyNavigationContainer';
// @ts-ignore
import SettingsNavigationContainer from '../../settings/components/SettingsNavigationContainer';
import {
conferenceNavigationRef
// @ts-ignore
} from '../ConferenceNavigationContainerRef';
const ConferenceStack = createStackNavigator();
const ConferenceNavigationContainer = () => {
const isPollsDisabled = useSelector(arePollsDisabled);
const _isPollsEnabled = !isPollsDisabled;
const _isCCTabEnabled = useSelector(isCCTabEnabled);
const { t } = useTranslation();
let ChatScreen;
let chatScreenName;
let chatTitle;
if (isPollsDisabled) {
ChatScreen = Chat;
chatScreenName = screen.conference.chat;
} else {
ChatScreen = ChatNavigator;
chatScreenName = screen.conference.chatTabs.main;
}
if (_isPollsEnabled || _isCCTabEnabled) {
const features = [
_isPollsEnabled ? t('chat.titleWithPolls') : '',
_isCCTabEnabled ? t('chat.titleWithCC') : ''
].filter(Boolean);
chatTitle = `${t('chat.titleWithFeatures')} ${features.join(' and ')}`;
} else {
chatTitle = t('chat.title');
}
return (
<NavigationIndependentTree>
<NavigationContainer
ref = { conferenceNavigationRef }
theme = { navigationContainerTheme }>
<ConferenceStack.Navigator
screenOptions = {{
presentation: 'modal'
}}>
<ConferenceStack.Screen
component = { Conference }
name = { screen.conference.main }
options = { conferenceScreenOptions } />
<ConferenceStack.Screen
component = { ChatScreen }
name = { chatScreenName }
options = {{
...chatScreenOptions,
title: chatTitle
}} />
<ConferenceStack.Screen
component = { ParticipantsPane }
name = { screen.conference.participants }
options = {{
...participantsScreenOptions,
title: t('participantsPane.title')
}} />
<ConferenceStack.Screen
component = { SecurityDialog }
name = { screen.conference.security }
options = {{
...securityScreenOptions,
title: t('security.title')
}} />
<ConferenceStack.Screen
component = { RecordingTranscriptionDialog }
name = { screen.conference.recording }
options = {{
...recordingScreenOptions,
title: t('dialog.recordAndTranscribe')
}} />
<ConferenceStack.Screen
component = { StartLiveStreamDialog }
name = { screen.conference.liveStream }
options = {{
...liveStreamScreenOptions,
title: t('liveStreaming.title')
}} />
<ConferenceStack.Screen
component = { SpeakerStats }
name = { screen.conference.speakerStats }
options = {{
...speakerStatsScreenOptions,
title: t('speakerStats.speakerStats')
}} />
<ConferenceStack.Screen
component = { SalesforceLinkDialog }
name = { screen.conference.salesforce }
options = {{
...salesforceScreenOptions,
title: t('notify.linkToSalesforce')
}} />
<ConferenceStack.Screen
component = { GifsMenu }
name = { screen.conference.gifsMenu }
options = {{
...gifsMenuOptions,
title: t('notify.gifsMenu')
}} />
<ConferenceStack.Screen
component = { LobbyNavigationContainer }
name = { screen.lobby.root }
options = {{
...lobbyNavigationContainerScreenOptions,
title: t('lobby.title')
}} />
<ConferenceStack.Screen
component = { AddPeopleDialog }
name = { screen.conference.invite }
options = {{
...inviteScreenOptions,
title: t('addPeople.add')
}} />
<ConferenceStack.Screen
component = { SharedDocument }
name = { screen.conference.sharedDocument }
options = {{
...sharedDocumentScreenOptions,
title: t('documentSharing.title')
}} />
<ConferenceStack.Screen
// @ts-ignore
component = { SettingsNavigationContainer }
name = { screen.settings.main }
options = { settingsNavigationContainerScreenOptions } />
<ConferenceStack.Screen
// @ts-ignore
component = { CarMode }
name = { screen.conference.carmode }
options = {{
...carmodeScreenOptions,
title: t('carmode.labels.title')
}} />
<ConferenceStack.Screen
component = { LanguageSelectorDialog }
name = { screen.conference.subtitles }
options = {{
...subtitlesScreenOptions,
title: t('transcribing.subtitles')
}} />
<ConferenceStack.Screen
component = { AudioTranslationDialog }
name = { screen.conference.audioTranslation }
options = {{
...audioTranslationScreenOptions,
title: t('toolbar.audioTranslation')
}} />
<ConferenceStack.Screen
component = { BreakoutRooms }
name = { screen.conference.breakoutRooms }
options = {{
...breakoutRoomsScreenOptions,
title: t('breakoutRooms.title')
}} />
<ConferenceStack.Screen
// @ts-ignore
component = { Whiteboard }
name = { screen.conference.whiteboard }
options = {{
...whiteboardScreenOptions,
title: t('whiteboard.screenTitle')
}} />
</ConferenceStack.Navigator>
</NavigationContainer>
</NavigationIndependentTree>
);
};
export default ConferenceNavigationContainer;

View File

@@ -0,0 +1,24 @@
import { NavigationContainerRef } from '@react-navigation/native';
import React from 'react';
export const lobbyNavigationContainerRef = React.createRef<NavigationContainerRef<any>>();
/**
* User defined navigation action included inside the reference to the container.
*
* @param {string} name - Destination name of the route that has been defined somewhere.
* @param {Object} params - Params to pass to the destination route.
* @returns {Function}
*/
export function navigate(name: string, params?: Object) {
return lobbyNavigationContainerRef.current?.navigate(name, params);
}
/**
* User defined navigation action included inside the reference to the container.
*
* @returns {Function}
*/
export function goBack() {
return lobbyNavigationContainerRef.current?.goBack();
}

View File

@@ -0,0 +1,51 @@
import { NavigationContainer, NavigationIndependentTree } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import React from 'react';
import { useSelector } from 'react-redux';
import { IReduxState } from '../../../../../app/types';
import LobbyChatScreen from '../../../../../lobby/components/native/LobbyChatScreen';
import LobbyScreen from '../../../../../lobby/components/native/LobbyScreen';
import { screen } from '../../../routes';
import {
lobbyChatScreenOptions,
lobbyScreenOptions,
navigationContainerTheme
} from '../../../screenOptions';
import { lobbyNavigationContainerRef } from '../LobbyNavigationContainerRef';
const LobbyStack = createStackNavigator();
const LobbyNavigationContainer = () => {
const { isLobbyChatActive }
= useSelector((state: IReduxState) => state['features/chat']);
return (
<NavigationIndependentTree>
<NavigationContainer
ref = { lobbyNavigationContainerRef }
theme = { navigationContainerTheme }>
<LobbyStack.Navigator
screenOptions = {{
presentation: 'modal'
}}>
<LobbyStack.Screen
component = { LobbyScreen }
name = { screen.lobby.main }
options = { lobbyScreenOptions } />
{
isLobbyChatActive
&& <LobbyStack.Screen
component = { LobbyChatScreen }
name = { screen.lobby.chat }
options = { lobbyChatScreenOptions } />
}
</LobbyStack.Navigator>
</NavigationContainer>
</NavigationIndependentTree>
);
};
export default LobbyNavigationContainer;

View File

@@ -0,0 +1,24 @@
import { NavigationContainerRef } from '@react-navigation/native';
import React from 'react';
export const settingsNavigationContainerRef = React.createRef<NavigationContainerRef<any>>();
/**
* User defined navigation action included inside the reference to the container.
*
* @param {string} name - Destination name of the route that has been defined somewhere.
* @param {Object} params - Params to pass to the destination route.
* @returns {Function}
*/
export function navigate(name: string, params?: Object) {
return settingsNavigationContainerRef.current?.navigate(name, params);
}
/**
* User defined navigation action included inside the reference to the container.
*
* @returns {Function}
*/
export function goBack() {
return settingsNavigationContainerRef.current?.goBack();
}

View File

@@ -0,0 +1,94 @@
import { NavigationContainer, NavigationIndependentTree } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import LanguageSelectView from '../../../../../settings/components/native/LanguageSelectView';
import ProfileView from '../../../../../settings/components/native/ProfileView';
import SettingsView
from '../../../../../settings/components/native/SettingsView';
import { screen } from '../../../routes';
import {
languageSelectScreenOptions,
navigationContainerTheme,
profileSettingsScreenOptions,
settingsScreenOptions,
welcomeScreenOptions
} from '../../../screenOptions';
import {
goBack,
settingsNavigationContainerRef
} from '../SettingsNavigationContainerRef';
const SettingsStack = createStackNavigator();
/**
* The type of the React {@code Component} props of {@link SettingsNavigationContainer}.
*/
interface IProps {
/**
* Is the navigator part of Welcome page?
*/
isInWelcomePage?: boolean | undefined;
}
const SettingsNavigationContainer = ({ isInWelcomePage }: IProps) => {
const baseSettingsScreenOptions = isInWelcomePage ? welcomeScreenOptions : settingsScreenOptions;
const { t } = useTranslation();
const SettingsScreen = useCallback(() =>
(
<SettingsView
isInWelcomePage = { isInWelcomePage } />
), []);
const ProfileScreen = useCallback(() =>
(<ProfileView
isInWelcomePage = { isInWelcomePage } />)
, []);
const LanguageSelectScreen = useCallback(() =>
(<LanguageSelectView
goBack = { goBack }
isInWelcomePage = { isInWelcomePage } />)
, []);
return (
<NavigationIndependentTree>
<NavigationContainer
ref = { settingsNavigationContainerRef }
theme = { navigationContainerTheme }>
<SettingsStack.Navigator
initialRouteName = { screen.settings.main }>
<SettingsStack.Screen
name = { screen.settings.main }
options = {{
...baseSettingsScreenOptions,
title: t('settings.title')
}}>
{ SettingsScreen }
</SettingsStack.Screen>
<SettingsStack.Screen
component = { ProfileScreen }
name = { screen.settings.profile }
options = {{
...profileSettingsScreenOptions,
title: t('settingsView.profileSection')
}} />
<SettingsStack.Screen
component = { LanguageSelectScreen }
name = { screen.settings.language }
options = {{
...languageSelectScreenOptions,
title: t('settings.language')
}} />
</SettingsStack.Navigator>
</NavigationContainer>
</NavigationIndependentTree>
);
};
export default SettingsNavigationContainer;

View File

@@ -0,0 +1,114 @@
import { BoxModel } from '../../../base/styles/components/styles/BoxModel';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
export const TEXT_COLOR = BaseTheme.palette.text01;
const unreadCounterDescription = {
...BaseTheme.typography.bodyShortBoldLarge,
color: BaseTheme.palette.text03
};
const HEADER_ACTION_BUTTON_SIZE = 16;
const headerNavigationButtonLabel = {
color: BaseTheme.palette.link01,
fontSize: HEADER_ACTION_BUTTON_SIZE,
lineHeight: BaseTheme.spacing[3]
};
const headerNavigationButton = {
borderRadius: BaseTheme.shape.borderRadius,
height: BaseTheme.spacing[6],
marginLeft: BaseTheme.spacing[3]
};
/**
* Styles of the navigation feature.
*/
export const navigationStyles = {
connectingScreenContainer: {
backgroundColor: BaseTheme.palette.uiBackground,
flex: 1
},
connectingScreenContent: {
alignItems: 'center',
flex: 1,
flexDirection: 'column',
justifyContent: 'center'
},
connectingScreenIndicator: {
margin: BoxModel.margin
},
connectingScreenText: {
color: TEXT_COLOR
},
headerNavigationButton: {
...headerNavigationButton
},
headerNavigationButtonIcon: {
...headerNavigationButton,
padding: BaseTheme.spacing[2]
},
headerNavigationButtonDisabled: {
backgroundColor: 'transparent',
marginLeft: BaseTheme.spacing[2]
},
headerNavigationButtonLabel: {
...headerNavigationButtonLabel
},
headerNavigationButtonLabelDisabled: {
...headerNavigationButtonLabel,
color: BaseTheme.palette.text03
},
headerNavigationButtonLabelBold: {
...headerNavigationButtonLabel,
...BaseTheme.typography.bodyShortRegularLarge
},
headerNavigationButtonLabelBoldDisabled: {
...headerNavigationButtonLabel,
...BaseTheme.typography.bodyShortRegularLarge,
color: BaseTheme.palette.text03
},
unreadCounterContainer: {
alignItems: 'center',
display: 'flex',
flexDirection: 'row'
},
unreadCounterDescription: {
...unreadCounterDescription
},
unreadCounterDescriptionFocused: {
...unreadCounterDescription,
color: BaseTheme.palette.text01
},
unreadCounterCircle: {
backgroundColor: BaseTheme.palette.warning01,
borderRadius: BaseTheme.spacing[4] / 2,
height: BaseTheme.spacing[4],
justifyContent: 'center',
marginLeft: BaseTheme.spacing[2],
width: BaseTheme.spacing[4]
},
unreadCounter: {
...BaseTheme.typography.bodyShortBold,
alignSelf: 'center',
color: BaseTheme.palette.text04
}
};

View File

@@ -0,0 +1,120 @@
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { GestureResponderEvent } from 'react-native';
import { useSelector } from 'react-redux';
import CalendarList from '../../../../../calendar-sync/components/CalendarList.native';
import { isCalendarEnabled } from '../../../../../calendar-sync/functions.native';
import RecentList from '../../../../../recent-list/components/RecentList.native';
import {
calendarListTabBarOptions,
recentListTabBarOptions,
settingsTabBarOptions,
tabBarOptions
} from '../../../../../welcome/constants';
import { screen } from '../../../routes';
import SettingsNavigationContainer
from '../../settings/components/SettingsNavigationContainer';
const WelcomePage = createBottomTabNavigator();
/**
* The type of the React {@code Component} props of {@link WelcomePageTabs}.
*/
interface IProps {
/**
* Renders the lists disabled.
*/
disabled: boolean;
/**
* Callback to be invoked when pressing the list container.
*/
onListContainerPress?: (e?: GestureResponderEvent) => void;
/**
* Callback to be invoked when settings screen is focused.
*/
onSettingsScreenFocused: Function;
}
const WelcomePageTabs = ({ disabled, onListContainerPress, onSettingsScreenFocused }: IProps) => {
const { t } = useTranslation();
const RecentListScreen = useCallback(() =>
(
<RecentList
disabled = { disabled }
onListContainerPress = { onListContainerPress } />
), []);
const calendarEnabled = useSelector(isCalendarEnabled);
const CalendarListScreen = useCallback(() =>
(
<CalendarList
disabled = { disabled } />
), []);
const SettingsScreen = useCallback(() =>
(
<SettingsNavigationContainer
isInWelcomePage = { true } />
), []);
return (
<WelcomePage.Navigator
backBehavior = { 'none' }
screenOptions = {{
...tabBarOptions,
headerShown: false
}}>
<WelcomePage.Screen
listeners = {{
tabPress: () => {
onSettingsScreenFocused(false);
}
}}
name = { screen.welcome.tabs.recent }
options = {{
...recentListTabBarOptions,
title: t('welcomepage.recentList')
}}>
{ RecentListScreen }
</WelcomePage.Screen>
{
calendarEnabled
&& <WelcomePage.Screen
listeners = {{
tabPress: () => {
onSettingsScreenFocused(false);
}
}}
name = { screen.welcome.tabs.calendar }
options = {{
...calendarListTabBarOptions,
title: t('welcomepage.calendar')
}}>
{ CalendarListScreen }
</WelcomePage.Screen>
}
<WelcomePage.Screen
listeners = {{
tabPress: () => {
onSettingsScreenFocused(true);
}
}}
name = { screen.settings.main }
options = {{
...settingsTabBarOptions,
title: t('welcomepage.settings')
}}>
{ SettingsScreen }
</WelcomePage.Screen>
</WelcomePage.Navigator>
);
};
export default WelcomePageTabs;