init
Some checks failed
Close stale issues and PRs / stale (push) Has been cancelled

This commit is contained in:
2025-09-02 14:49:16 +08:00
commit 38ba663466
2885 changed files with 391107 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
import React, { ReactElement } from 'react';
import { GestureResponderEvent, StyleProp } from 'react-native';
import { connect } from 'react-redux';
import Container from '../../../base/react/components/native/Container';
import Text from '../../../base/react/components/native/Text';
import {
AbstractCaptions,
type IAbstractCaptionsProps,
_abstractMapStateToProps
} from '../AbstractCaptions';
import styles from './styles';
/**
* The type of the React {@code Component} props of {@link Captions}.
*/
interface IProps extends IAbstractCaptionsProps {
onPress: (event: GestureResponderEvent) => void;
}
/**
* React {@code Component} which can display speech-to-text results from
* Jigasi as subtitles.
*/
class Captions extends AbstractCaptions<IProps> {
/**
* Renders the transcription text.
*
* @param {string} id - The ID of the transcript message from which the
* {@code text} has been created.
* @param {string} text - Subtitles text formatted with the participant's
* name.
* @protected
* @returns {ReactElement} - The React element which displays the text.
*/
_renderParagraph(id: string, text: string): ReactElement {
return (
<Text
key = { id }
onPress = { this.props.onPress }
style = { styles.captionsSubtitles as StyleProp<Object> } >
{ text }
</Text>
);
}
/**
* Renders the subtitles container.
*
* @param {Array<ReactElement>} paragraphs - An array of elements created
* for each subtitle using the {@link _renderParagraph} method.
* @protected
* @returns {ReactElement} - The subtitles container.
*/
_renderSubtitlesContainer(paragraphs: Array<ReactElement>): ReactElement {
return (
<Container style = { styles.captionsSubtitlesContainer } >
{ paragraphs }
</Container>
);
}
}
export default connect(_abstractMapStateToProps)(Captions);

View File

@@ -0,0 +1,59 @@
import { connect } from 'react-redux';
import { IReduxState } from '../../../app/types';
import { CLOSE_CAPTIONS_ENABLED } from '../../../base/flags/constants';
import { getFeatureFlag } from '../../../base/flags/functions';
import { translate } from '../../../base/i18n/functions';
import { IconSubtitles } from '../../../base/icons/svg';
import { navigate }
from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
import { screen } from '../../../mobile/navigation/routes';
import {
AbstractClosedCaptionButton,
_abstractMapStateToProps
} from '../AbstractClosedCaptionButton';
/**
* A button which starts/stops the transcriptions.
*/
class ClosedCaptionButton
extends AbstractClosedCaptionButton {
override accessibilityLabel = 'toolbar.accessibilityLabel.cc';
override icon = IconSubtitles;
override label = 'toolbar.startSubtitles';
labelProps = {
language: this.props.t(this.props._language ?? 'transcribing.subtitlesOff'),
languages: this.props.t(this.props.languages ?? ''),
languagesHead: this.props.t(this.props.languagesHead ?? '')
};
/**
* Toggle language selection dialog.
*
* @returns {void}
*/
_handleClickOpenLanguageSelector() {
navigate(screen.conference.subtitles);
}
}
/**
* Maps (parts of) the redux state to the associated props for this component.
*
* @param {Object} state - The redux state.
* @param {Object} ownProps - The properties explicitly passed to the component
* instance.
* @private
* @returns {Props}
*/
export function mapStateToProps(state: IReduxState, ownProps: any) {
const enabled = getFeatureFlag(state, CLOSE_CAPTIONS_ENABLED, true);
const abstractProps = _abstractMapStateToProps(state, ownProps);
return {
...abstractProps,
visible: abstractProps.visible && enabled
};
}
export default translate(connect(mapStateToProps)(ClosedCaptionButton));

View File

@@ -0,0 +1,43 @@
import React from 'react';
import { ScrollView } from 'react-native';
import LanguageListItem from './LanguageListItem';
import styles from './styles';
interface ILanguageListProps {
items: Array<ILanguageItem>;
onLanguageSelected: (lang: string) => void;
selectedLanguage: string;
}
interface ILanguageItem {
id: string;
lang: string;
selected: boolean;
}
/**
* Component that renders the security options dialog.
*
* @returns {React$Element<any>}
*/
const LanguageList = ({ items, onLanguageSelected }: ILanguageListProps) => {
const listItems = items?.map(item => (
<LanguageListItem
key = { item.id }
lang = { item.lang }
onLanguageSelected = { onLanguageSelected }
selected = { item.selected } />
));
return (
<ScrollView
bounces = { false }
style = { styles.itemsContainer }>
{ listItems }
</ScrollView>
);
};
export default LanguageList;

View File

@@ -0,0 +1,67 @@
import React, { useCallback } from 'react';
import { WithTranslation } from 'react-i18next';
import { StyleProp, TouchableHighlight, View, ViewStyle } from 'react-native';
import { Text } from 'react-native-paper';
import { translate } from '../../../base/i18n/functions';
import Icon from '../../../base/icons/components/Icon';
import { IconCheck } from '../../../base/icons/svg';
import styles from './styles';
interface ILanguageListItemProps extends WithTranslation {
/**
* Language string.
*/
lang: string;
/**
* Callback for language selection.
*/
onLanguageSelected: (lang: string) => void;
/**
* If language item is selected or not.
*/
selected?: boolean;
}
/**
* Component that renders the language list item.
*
* @returns {React$Element<any>}
*/
const LanguageListItem = ({ t, lang, selected, onLanguageSelected
}: ILanguageListItemProps) => {
const onLanguageSelectedWrapper
= useCallback(() => onLanguageSelected(lang), [ lang ]);
return (
<View style = { styles.languageItemWrapper as StyleProp<ViewStyle> }>
<View style = { styles.iconWrapper }>
{
selected
&& <Icon
size = { 20 }
src = { IconCheck } />
}
</View>
<TouchableHighlight
onPress = { onLanguageSelectedWrapper }
underlayColor = { 'transparent' } >
<Text
style = { [
styles.languageItemText,
selected && styles.activeLanguageItemText ] }>
{ t(lang) }
</Text>
</TouchableHighlight>
</View>
);
};
export default translate(LanguageListItem);

View File

@@ -0,0 +1,38 @@
import React, { useCallback } from 'react';
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import { goBack }
from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
import AbstractLanguageSelectorDialog, {
IAbstractLanguageSelectorDialogProps
} from '../AbstractLanguageSelectorDialog';
import LanguageList from './LanguageList';
import styles from './styles';
const LanguageSelectorDialog = (props: IAbstractLanguageSelectorDialogProps) => {
const { language, listItems, onLanguageSelected, subtitles } = props;
const onSelected = useCallback((e: string) => {
onLanguageSelected(e);
goBack();
}, [ language ]);
return (
<JitsiScreen
disableForcedKeyboardDismiss = { true }
style = { styles.subtitlesContainer }>
<LanguageList
items = { listItems }
onLanguageSelected = { onSelected }
selectedLanguage = { subtitles } />
</JitsiScreen>
);
};
/*
* We apply AbstractLanguageSelector to fill in the AbstractProps common
* to both the web and native implementations.
*/
// eslint-disable-next-line new-cap
export default AbstractLanguageSelectorDialog(LanguageSelectorDialog);

View File

@@ -0,0 +1,63 @@
import { BoxModel } from '../../../base/styles/components/styles/BoxModel';
import {
ColorPalette
} from '../../../base/styles/components/styles/ColorPalette';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
/**
* The styles of the React {@code Component}s of the feature subtitles.
*/
export default {
languageItemWrapper: {
alignItems: 'center',
display: 'flex',
flexDirection: 'row'
},
iconWrapper: {
width: 32
},
activeLanguageItemText: {
...BaseTheme.typography.bodyShortBoldLarge
},
languageItemText: {
...BaseTheme.typography.bodyShortRegularLarge,
color: BaseTheme.palette.text01,
marginLeft: BaseTheme.spacing[2],
marginVertical: BaseTheme.spacing[2]
},
subtitlesContainer: {
backgroundColor: BaseTheme.palette.ui01,
flex: 1
},
/**
* Style for subtitle paragraph.
*/
captionsSubtitles: {
backgroundColor: ColorPalette.black,
borderRadius: BoxModel.margin / 4,
color: ColorPalette.white,
marginBottom: BoxModel.margin,
padding: BoxModel.padding / 2
},
/**
* Style for the subtitles container.
*/
captionsSubtitlesContainer: {
alignItems: 'center',
flexDirection: 'column',
flexGrow: 0,
justifyContent: 'flex-end',
margin: BoxModel.margin
},
itemsContainer: {
marginHorizontal: BaseTheme.spacing[4],
marginVertical: BaseTheme.spacing[4]
}
};