feat: Add jitsi auth info (#5231)

* migrate JitsiMeetView to hooks

* create JitsiAuthModal component

* Update app/i18n/locales/en.json

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* Update JitsiAuthModal.tsx

* typo

* fix colors

---------

Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
Gleidson Daniel Silva 2023-09-26 13:31:03 -03:00 committed by GitHub
parent 4ccf815746
commit 9fa29047dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 189 additions and 109 deletions

View File

@ -743,6 +743,11 @@
"accept": "Accept", "accept": "Accept",
"Incoming_call_from": "Incoming call from", "Incoming_call_from": "Incoming call from",
"Call_started": "Call started", "Call_started": "Call started",
"Jitsi_may_require_authentication": "Jitsi may require authentication",
"Jitsi_authentication_before_making_calls_admin": "Jitsi may require authentication before making calls. To learn more about their policies, visit the Jitsi website. You can also update the default app for video calls in the preferences.",
"Jitsi_authentication_before_making_calls": "Jitsi may require authentication before making calls. To learn more about their policies, visit the Jitsi website.",
"Jitsi_authentication_before_making_calls_ask_admin": "If you believe there are problems with Jitsi and its authentication, ask a workspace administrator for help.",
"Continue": "Continue",
"Message_has_been_shared": "Message has been shared", "Message_has_been_shared": "Message has been shared",
"No_channels_in_team": "No Channels on this team" "No_channels_in_team": "No Channels on this team"
} }

View File

@ -726,10 +726,15 @@
"Select": "Selecionar", "Select": "Selecionar",
"Nickname": "Apelido", "Nickname": "Apelido",
"Bio": "Biografia", "Bio": "Biografia",
"Message_has_been_shared":"Menssagem foi compartilhada",
"No_channels_in_team": "Nenhum canal nesta equipe",
"Jitsi_may_requires_authentication": "Jitsi pode exigir autenticação",
"Jitsi_authentication_before_making_calls_admin": "Jitsi pode exigir autenticação antes de fazer chamadas. Para saber mais sobre as políticas deles, visite o site do Jitsi. Você também pode atualizar o aplicativo padrão para chamadas de vídeo nas preferências.",
"Jitsi_authentication_before_making_calls": "Jitsi pode exigir autenticação antes de fazer chamadas. Para saber mais sobre suas políticas, visite o site do Jitsi.",
"Jitsi_authentication_before_making_calls_ask_admin": "Se você acredita que há problemas com o Jitsi e sua autenticação, peça ajuda a um administrador do espaço de trabalho.",
"Continue": "Continuar",
"decline": "Recusar", "decline": "Recusar",
"accept": "Aceitar", "accept": "Aceitar",
"Incoming_call_from": "Chamada recebida de", "Incoming_call_from": "Chamada recebida de",
"Call_started": "Chamada Iniciada", "Call_started": "Chamada Iniciada"
"Message_has_been_shared": "Menssagem foi compartilhada",
"No_channels_in_team": "Nenhum canal nesta equipe"
} }

View File

@ -1,105 +0,0 @@
import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake';
import React from 'react';
import { BackHandler, Linking, NativeEventSubscription, SafeAreaView } from 'react-native';
import WebView from 'react-native-webview';
import { WebViewNavigation } from 'react-native-webview/lib/WebViewTypes';
import { IBaseScreen } from '../definitions';
import { userAgent } from '../lib/constants';
import { isIOS } from '../lib/methods/helpers';
import { getRoomIdFromJitsiCallUrl } from '../lib/methods/helpers/getRoomIdFromJitsiCall';
import { events, logEvent } from '../lib/methods/helpers/log';
import { endVideoConfTimer, initVideoConfTimer } from '../lib/methods/videoConfTimer';
import { ChatsStackParamList } from '../stacks/types';
import { withTheme } from '../theme';
type TJitsiMeetViewProps = IBaseScreen<ChatsStackParamList, 'JitsiMeetView'>;
class JitsiMeetView extends React.Component<TJitsiMeetViewProps> {
private rid: string;
private url: string;
private videoConf: boolean;
private backHandler!: NativeEventSubscription;
constructor(props: TJitsiMeetViewProps) {
super(props);
this.rid = props.route.params?.rid;
this.url = props.route.params?.url;
this.videoConf = !!props.route.params?.videoConf;
}
componentDidMount() {
this.handleJitsiApp();
this.onConferenceJoined();
activateKeepAwake();
}
componentWillUnmount() {
logEvent(this.videoConf ? events.LIVECHAT_VIDEOCONF_TERMINATE : events.JM_CONFERENCE_TERMINATE);
if (!this.videoConf) {
endVideoConfTimer();
}
if (this.backHandler) {
this.backHandler.remove();
}
deactivateKeepAwake();
}
handleJitsiApp = async () => {
const { route, navigation } = this.props;
const callUrl = route.params.url.replace(/^https?:\/\//, '');
try {
await Linking.openURL(`org.jitsi.meet://${callUrl}`);
navigation.pop();
} catch (error) {
// As the jitsi app was not opened disable the backhandler on android
this.backHandler = BackHandler.addEventListener('hardwareBackPress', () => true);
}
};
// Jitsi Update Timeout needs to be called every 10 seconds to make sure
// call is not ended and is available to web users.
onConferenceJoined = () => {
logEvent(this.videoConf ? events.LIVECHAT_VIDEOCONF_JOIN : events.JM_CONFERENCE_JOIN);
if (this.rid && !this.videoConf) {
initVideoConfTimer(this.rid);
}
};
onNavigationStateChange = (webViewState: WebViewNavigation) => {
const { navigation, route } = this.props;
const roomId = getRoomIdFromJitsiCallUrl(route.params.url);
if ((roomId && !webViewState.url.includes(roomId)) || webViewState.url.includes('close')) {
if (isIOS) {
if (webViewState.navigationType) {
navigation.pop();
}
} else {
navigation.pop();
}
}
};
render() {
const uri = `${this.url}${this.url.includes('#config') ? '&' : '#'}config.disableDeepLinking=true`;
return (
<SafeAreaView style={{ flex: 1 }}>
<WebView
source={{ uri: uri.replace(/"/g, "'") }}
onNavigationStateChange={this.onNavigationStateChange}
// Jitsi default background color
style={{ flex: 1, backgroundColor: 'rgb(62,62,62)' }}
userAgent={userAgent}
javaScriptEnabled
domStorageEnabled
allowsInlineMediaPlayback
mediaCapturePermissionGrantType={'grant'}
/>
</SafeAreaView>
);
}
}
export default withTheme(JitsiMeetView);

View File

@ -0,0 +1,81 @@
import { useNavigation } from '@react-navigation/native';
import React from 'react';
import { Linking, StyleSheet, Text, View } from 'react-native';
import Modal from 'react-native-modal';
import sharedStyles from '../Styles';
import Button from '../../containers/Button';
import { useTheme } from '../../theme';
import { useAppSelector } from '../../lib/hooks';
import { getUserSelector } from '../../selectors/login';
import i18n from '../../i18n';
const styles = StyleSheet.create({
title: {
...sharedStyles.textBold,
fontSize: 24,
marginBottom: 24
},
regular: {
...sharedStyles.textRegular,
fontSize: 16,
marginBottom: 24
},
min: {
...sharedStyles.textRegular,
fontSize: 12,
marginBottom: 24
},
container: { padding: 24, borderRadius: 8 },
buttonContainer: {
flexDirection: 'row',
justifyContent: 'space-between'
}
});
const JitsiAuthModal = ({
setAuthModal,
callUrl
}: {
setAuthModal: React.Dispatch<React.SetStateAction<boolean>>;
callUrl: string;
}): React.ReactElement => {
const { goBack } = useNavigation();
const { colors } = useTheme();
const user = useAppSelector(state => getUserSelector(state));
const isAdmin = !!user.roles?.includes('admin');
return (
<Modal isVisible>
<View style={[styles.container, { backgroundColor: colors.backgroundColor }]}>
<Text style={[styles.title, { color: colors.titleText }]}>{i18n.t('Jitsi_may_require_authentication')}</Text>
{isAdmin ? (
<Text style={[styles.regular, { color: colors.titleText }]}>
{i18n.t('Jitsi_authentication_before_making_calls_admin')}
</Text>
) : (
<Text style={[styles.regular, { color: colors.titleText }]}>{i18n.t('Jitsi_authentication_before_making_calls')}</Text>
)}
{!isAdmin ? (
<Text style={[styles.min, { color: colors.auxiliaryText }]}>
{i18n.t('Jitsi_authentication_before_making_calls_ask_admin')}
</Text>
) : null}
<View style={styles.buttonContainer}>
<Button title={i18n.t('Cancel')} type='secondary' onPress={() => setAuthModal(false)} />
<Button
title={i18n.t('Continue')}
onPress={() => {
setAuthModal(false);
goBack();
Linking.openURL(callUrl);
}}
/>
</View>
</View>
</Modal>
);
};
export default JitsiAuthModal;

View File

@ -0,0 +1,94 @@
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake';
import React, { useCallback, useEffect, useState } from 'react';
import { BackHandler, Linking, SafeAreaView } from 'react-native';
import WebView from 'react-native-webview';
import { userAgent } from '../../lib/constants';
import { isIOS } from '../../lib/methods/helpers';
import { getRoomIdFromJitsiCallUrl } from '../../lib/methods/helpers/getRoomIdFromJitsiCall';
import { events, logEvent } from '../../lib/methods/helpers/log';
import { endVideoConfTimer, initVideoConfTimer } from '../../lib/methods/videoConfTimer';
import { ChatsStackParamList } from '../../stacks/types';
import JitsiAuthModal from './JitsiAuthModal';
const JitsiMeetView = (): React.ReactElement => {
const {
params: { rid, url, videoConf }
} = useRoute<RouteProp<ChatsStackParamList, 'JitsiMeetView'>>();
const { goBack } = useNavigation();
const [authModal, setAuthModal] = useState(false);
const handleJitsiApp = useCallback(async () => {
const callUrl = url.replace(/^https?:\/\//, '');
try {
await Linking.openURL(`org.jitsi.meet://${callUrl}`);
goBack();
} catch (error) {
// As the jitsi app was not opened, disable the backhandler on android
BackHandler.addEventListener('hardwareBackPress', () => true);
}
}, [goBack, url]);
const onConferenceJoined = useCallback(() => {
logEvent(videoConf ? events.LIVECHAT_VIDEOCONF_JOIN : events.JM_CONFERENCE_JOIN);
if (rid && !videoConf) {
initVideoConfTimer(rid);
}
}, [rid, videoConf]);
const onNavigationStateChange = useCallback(
webViewState => {
const roomId = getRoomIdFromJitsiCallUrl(url);
if (webViewState.url.includes('auth-static')) {
setAuthModal(true);
return false;
}
if ((roomId && !webViewState.url.includes(roomId)) || webViewState.url.includes('close')) {
if (isIOS) {
if (webViewState.navigationType) {
goBack();
}
} else {
goBack();
}
}
return true;
},
[goBack, url]
);
useEffect(() => {
handleJitsiApp();
onConferenceJoined();
activateKeepAwake();
return () => {
logEvent(videoConf ? events.LIVECHAT_VIDEOCONF_TERMINATE : events.JM_CONFERENCE_TERMINATE);
if (!videoConf) endVideoConfTimer();
deactivateKeepAwake();
};
}, [handleJitsiApp, onConferenceJoined, videoConf]);
const callUrl = `${url}${url.includes('#config') ? '&' : '#'}config.disableDeepLinking=true`;
return (
<SafeAreaView style={{ flex: 1 }}>
{authModal && <JitsiAuthModal setAuthModal={setAuthModal} callUrl={callUrl} />}
<WebView
source={{ uri: callUrl.replace(/"/g, "'") }}
onNavigationStateChange={onNavigationStateChange}
onShouldStartLoadWithRequest={onNavigationStateChange}
style={{ flex: 1, backgroundColor: 'rgb(62,62,62)' }}
userAgent={userAgent}
javaScriptEnabled
domStorageEnabled
allowsInlineMediaPlayback
mediaCapturePermissionGrantType={'grant'}
/>
</SafeAreaView>
);
};
export default JitsiMeetView;