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,61 @@
import { connect } from 'react-redux';
import { IReduxState } from '../../../app/types';
import { translate } from '../../../base/i18n/functions';
import { IconTranslate } from '../../../base/icons/svg';
import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
import { navigate } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
import { screen } from '../../../mobile/navigation/routes';
import { isAudioTranslationAvailable } from '../../functions';
interface IProps extends AbstractButtonProps {
/**
* The target language remote audio is currently translated into, or null.
*/
_language: string | null;
}
/**
* An overflow-menu button that opens the AI audio-translation language selector screen.
*/
class AudioTranslationButton extends AbstractButton<IProps> {
override accessibilityLabel = 'toolbar.accessibilityLabel.audioTranslation';
override icon = IconTranslate;
override label = 'toolbar.audioTranslation';
/**
* Handles clicking the button by opening the language selector screen.
*
* @override
* @returns {void}
*/
override _handleClick() {
navigate(screen.conference.audioTranslation, { participantId: undefined });
}
/**
* Indicates whether this button is in the toggled (translation-on) state.
*
* @override
* @returns {boolean}
*/
override _isToggled() {
return Boolean(this.props._language);
}
}
/**
* Maps (parts of) the redux state to the button's props.
*
* @param {IReduxState} state - The redux state.
* @returns {Object}
*/
function _mapStateToProps(state: IReduxState) {
return {
_language: state['features/audio-translation'].language,
visible: isAudioTranslationAvailable(state)
};
}
export default translate(connect(_mapStateToProps)(AudioTranslationButton));

View File

@@ -0,0 +1,75 @@
import React, { useCallback } from 'react';
import { WithTranslation } from 'react-i18next';
import { ScrollView, StyleProp, ViewStyle } from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
import { IReduxState } from '../../../app/types';
import { translate } from '../../../base/i18n/functions';
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import { goBack } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
import { setAudioTranslationLanguage, setParticipantAudioTranslationLanguage } from '../../actions';
import { SUPPORTED_TRANSLATION_LANGUAGES } from '../../constants';
import AudioTranslationLanguageItem from './AudioTranslationLanguageItem';
import styles from './styles';
interface IProps extends WithTranslation {
/**
* Navigation route; params.participantId scopes the selector to one participant.
*/
route?: { params?: { participantId?: string; }; };
}
/**
* A screen for selecting the AI audio-translation target language (or off).
*
* @param {IProps} props - The component's props.
* @returns {ReactElement}
*/
const AudioTranslationDialog = ({ route, t }: IProps) => {
const participantId = route?.params?.participantId;
const dispatch = useDispatch();
const defaultLanguage = useSelector((state: IReduxState) => state['features/audio-translation'].language);
const participantLanguages
= useSelector((state: IReduxState) => state['features/audio-translation'].participantLanguages);
// Highlight the effective language for the target: the per-participant override if one is set, else the
// default. (For the conference-wide screen there is no participantId, so this is just the default.)
const language = participantId && participantId in participantLanguages
? participantLanguages[participantId]
: defaultLanguage;
const onSelect = useCallback((code: string | null) => {
if (participantId) {
dispatch(setParticipantAudioTranslationLanguage(participantId, code));
} else {
dispatch(setAudioTranslationLanguage(code));
}
goBack();
}, [ dispatch, participantId ]);
return (
<JitsiScreen style = { styles.container }>
<ScrollView
bounces = { false }
style = { styles.itemsContainer as StyleProp<ViewStyle> }>
<AudioTranslationLanguageItem
code = { null }
label = { t('toolbar.audioTranslationOff') }
onSelect = { onSelect }
selected = { !language } />
{ SUPPORTED_TRANSLATION_LANGUAGES.map(({ code, label }) => (
<AudioTranslationLanguageItem
code = { code }
key = { code }
label = { label }
onSelect = { onSelect }
selected = { language === code } />
)) }
</ScrollView>
</JitsiScreen>
);
};
export default translate(AudioTranslationDialog);

View File

@@ -0,0 +1,60 @@
import React, { useCallback } from 'react';
import { StyleProp, TouchableHighlight, View, ViewStyle } from 'react-native';
import { Text } from 'react-native-paper';
import Icon from '../../../base/icons/components/Icon';
import { IconCheck } from '../../../base/icons/svg';
import styles from './styles';
interface IProps {
/**
* The language code this item selects, or null for the "off" item.
*/
code: string | null;
/**
* The label shown for the item.
*/
label: string;
/**
* Invoked with the item's code when the item is pressed.
*/
onSelect: (code: string | null) => void;
/**
* Whether this item is the current selection.
*/
selected: boolean;
}
/**
* A row in the native audio-translation language selector.
*
* @param {IProps} props - The component's props.
* @returns {ReactElement}
*/
const AudioTranslationLanguageItem = ({ code, label, onSelect, selected }: IProps) => {
const onPress = useCallback(() => onSelect(code), [ code, onSelect ]);
return (
<View style = { styles.languageItemWrapper as StyleProp<ViewStyle> }>
<View style = { styles.iconWrapper as StyleProp<ViewStyle> }>
{ selected && <Icon
size = { 20 }
src = { IconCheck } /> }
</View>
<TouchableHighlight
onPress = { onPress }
underlayColor = { 'transparent' }>
<Text style = { [ styles.languageItemText, selected && styles.selectedLanguageItemText ] }>
{ label }
</Text>
</TouchableHighlight>
</View>
);
};
export default AudioTranslationLanguageItem;

View File

@@ -0,0 +1,23 @@
import { WithTranslation } from 'react-i18next';
import { translate } from '../../../base/i18n/functions';
import ExpandedLabel, { IProps as AbstractProps } from '../../../base/label/components/native/ExpandedLabel';
type Props = AbstractProps & WithTranslation;
/**
* A tooltip-like expanded label explaining the meaning of the audio-translation conference label.
*/
class TranslationExpandedLabel extends ExpandedLabel<Props> {
/**
* Returns the label specific text of this {@code ExpandedLabel}.
*
* @returns {string}
*/
_getLabel() {
return this.props.t('audioTranslation.labelTooltip');
}
}
export default translate(TranslationExpandedLabel);

View File

@@ -0,0 +1,37 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { IconTranslate } from '../../../base/icons/svg';
import Label from '../../../base/label/components/native/Label';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
import { isAudioTranslationActiveInMeeting, isAudioTranslationAvailable } from '../../functions';
const styles = {
translationLabel: {
alignItems: 'center',
backgroundColor: BaseTheme.palette.action01,
borderRadius: BaseTheme.shape.borderRadius,
flexDirection: 'row',
marginBottom: BaseTheme.spacing[0],
marginLeft: BaseTheme.spacing[0]
}
};
/**
* Conference-header label shown while audio translation is active in the meeting.
*
* @returns {ReactElement|null}
*/
const TranslationLabel = () => {
const available = useSelector(isAudioTranslationAvailable);
const active = useSelector(isAudioTranslationActiveInMeeting);
return available && active ? (
<Label
icon = { IconTranslate }
iconColor = { BaseTheme.palette.icon01 }
style = { styles.translationLabel } />
) : null;
};
export default TranslationLabel;

View File

@@ -0,0 +1,37 @@
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
/**
* The styles of the native audio-translation components.
*/
export default {
container: {
backgroundColor: BaseTheme.palette.ui01,
flex: 1
},
iconWrapper: {
width: 32
},
itemsContainer: {
marginHorizontal: BaseTheme.spacing[4],
marginVertical: BaseTheme.spacing[4]
},
languageItemText: {
...BaseTheme.typography.bodyShortRegularLarge,
color: BaseTheme.palette.text01,
marginLeft: BaseTheme.spacing[2],
marginVertical: BaseTheme.spacing[2]
},
languageItemWrapper: {
alignItems: 'center',
display: 'flex',
flexDirection: 'row'
},
selectedLanguageItemText: {
...BaseTheme.typography.bodyShortBoldLarge
}
};