Merge branch 'develop' into chore.try-flashlist
# Conflicts: # app/containers/MessageActions/Header.tsx
This commit is contained in:
commit
d860991c87
|
@ -2,7 +2,7 @@ module.exports = {
|
||||||
settings: {
|
settings: {
|
||||||
'import/resolver': {
|
'import/resolver': {
|
||||||
node: {
|
node: {
|
||||||
extensions: ['.ts', '.tsx', '.js', '.ios.js', '.android.js', '.native.js']
|
extensions: ['.ts', '.tsx', '.js', '.ios.js', '.android.js', '.native.js', '.ios.tsx', '.android.tsx']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -147,7 +147,7 @@ android {
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode VERSIONCODE as Integer
|
versionCode VERSIONCODE as Integer
|
||||||
versionName "4.33.0"
|
versionName "4.34.0"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
if (!isFoss) {
|
if (!isFoss) {
|
||||||
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
|
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
|
||||||
|
@ -357,9 +357,6 @@ dependencies {
|
||||||
playImplementation project(':@react-native-firebase_app')
|
playImplementation project(':@react-native-firebase_app')
|
||||||
playImplementation project(':@react-native-firebase_analytics')
|
playImplementation project(':@react-native-firebase_analytics')
|
||||||
playImplementation project(':@react-native-firebase_crashlytics')
|
playImplementation project(':@react-native-firebase_crashlytics')
|
||||||
implementation(project(':react-native-jitsi-meet')) { // https://github.com/skrafft/react-native-jitsi-meet#side-note
|
|
||||||
exclude group: 'com.facebook.react',module:'react-native-svg'
|
|
||||||
}
|
|
||||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||||
|
|
||||||
//noinspection GradleDynamicVersion
|
//noinspection GradleDynamicVersion
|
||||||
|
|
|
@ -5,6 +5,12 @@
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||||
|
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||||
|
<uses-permission android:name="android.permission.VIDEO_CAPTURE" />
|
||||||
|
<uses-permission android:name="android.permission.AUDIO_CAPTURE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="chat.rocket.reactnative.MainApplication"
|
android:name="chat.rocket.reactnative.MainApplication"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
|
@ -14,6 +20,7 @@
|
||||||
android:requestLegacyExternalStorage="true"
|
android:requestLegacyExternalStorage="true"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/BootTheme"
|
android:theme="@style/BootTheme"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
tools:replace="android:allowBackup">
|
tools:replace="android:allowBackup">
|
||||||
<activity
|
<activity
|
||||||
android:name="chat.rocket.reactnative.MainActivity"
|
android:name="chat.rocket.reactnative.MainActivity"
|
||||||
|
@ -69,5 +76,10 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
<queries>
|
||||||
|
<package android:name="org.jitsi.meet" />
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
</intent>
|
||||||
|
</queries>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -26,8 +26,6 @@ buildscript {
|
||||||
kotlinVersion = '1.6.10'
|
kotlinVersion = '1.6.10'
|
||||||
supportLibVersion = "28.0.0"
|
supportLibVersion = "28.0.0"
|
||||||
libre_build = !(isPlay.toBoolean())
|
libre_build = !(isPlay.toBoolean())
|
||||||
jitsi_url = "https://github.com/RocketChat/jitsi-maven-repository/raw/master/releases"
|
|
||||||
jitsi_version = "3.7.0"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -68,9 +66,6 @@ allprojects {
|
||||||
url "$rootDir/../node_modules/detox/Detox-android"
|
url "$rootDir/../node_modules/detox/Detox-android"
|
||||||
}
|
}
|
||||||
|
|
||||||
maven {
|
|
||||||
url jitsi_url
|
|
||||||
}
|
|
||||||
mavenCentral {
|
mavenCentral {
|
||||||
content {
|
content {
|
||||||
excludeGroup "com.facebook.react"
|
excludeGroup "com.facebook.react"
|
||||||
|
|
|
@ -17,7 +17,9 @@ const OrderedList = ({ value }: IOrderedListProps): React.ReactElement => {
|
||||||
{value.map(item => (
|
{value.map(item => (
|
||||||
<View style={styles.row} key={item.number?.toString()}>
|
<View style={styles.row} key={item.number?.toString()}>
|
||||||
<Text style={[styles.text, { color: colors.bodyText }]}>{item.number}. </Text>
|
<Text style={[styles.text, { color: colors.bodyText }]}>{item.number}. </Text>
|
||||||
<Inline value={item.value} />
|
<Text style={{ color: colors.bodyText }}>
|
||||||
|
<Inline value={item.value} />
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -18,7 +18,9 @@ const UnorderedList = ({ value }: IUnorderedListProps) => {
|
||||||
{value.map(item => (
|
{value.map(item => (
|
||||||
<View style={styles.row}>
|
<View style={styles.row}>
|
||||||
<Text style={[styles.text, { color: themes[theme].bodyText }]}>- </Text>
|
<Text style={[styles.text, { color: themes[theme].bodyText }]}>- </Text>
|
||||||
<Inline value={item.value} />
|
<Text style={{ color: themes[theme].bodyText }}>
|
||||||
|
<Inline value={item.value} />
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -565,6 +565,8 @@
|
||||||
"Unsupported_system_message": "Unsupported system message",
|
"Unsupported_system_message": "Unsupported system message",
|
||||||
"Updating": "Updating...",
|
"Updating": "Updating...",
|
||||||
"Uploading": "Uploading",
|
"Uploading": "Uploading",
|
||||||
|
"FileUpload_Error": "File Upload Error",
|
||||||
|
"Upload_in_progress": "Upload in progress",
|
||||||
"Upload_file_question_mark": "Upload file?",
|
"Upload_file_question_mark": "Upload file?",
|
||||||
"User": "User",
|
"User": "User",
|
||||||
"Users": "Users",
|
"Users": "Users",
|
||||||
|
|
|
@ -513,6 +513,8 @@
|
||||||
"Unsupported_system_message": "Mensagem de sistema não suportada",
|
"Unsupported_system_message": "Mensagem de sistema não suportada",
|
||||||
"Updating": "Atualizando...",
|
"Updating": "Atualizando...",
|
||||||
"Uploading": "Subindo arquivo",
|
"Uploading": "Subindo arquivo",
|
||||||
|
"FileUpload_Error": "Erro de upload de arquivo",
|
||||||
|
"Upload_in_progress": "Carregamento em andamento",
|
||||||
"Upload_file_question_mark": "Enviar arquivo?",
|
"Upload_file_question_mark": "Enviar arquivo?",
|
||||||
"User": "Usuário",
|
"User": "Usuário",
|
||||||
"Users": "Usuários",
|
"Users": "Usuários",
|
||||||
|
|
|
@ -1,27 +1,35 @@
|
||||||
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
|
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
|
||||||
import { settings as RocketChatSettings } from '@rocket.chat/sdk';
|
import { settings as RocketChatSettings } from '@rocket.chat/sdk';
|
||||||
import { FetchBlobResponse, StatefulPromise } from 'rn-fetch-blob';
|
|
||||||
import isEmpty from 'lodash/isEmpty';
|
import isEmpty from 'lodash/isEmpty';
|
||||||
|
import { FetchBlobResponse, StatefulPromise } from 'rn-fetch-blob';
|
||||||
|
import { Alert } from 'react-native';
|
||||||
|
|
||||||
import FileUpload from './helpers/fileUpload';
|
|
||||||
import database from '../database';
|
|
||||||
import log from './helpers/log';
|
|
||||||
import { IUpload, IUser, TUploadModel } from '../../definitions';
|
import { IUpload, IUser, TUploadModel } from '../../definitions';
|
||||||
|
import i18n from '../../i18n';
|
||||||
|
import database from '../database';
|
||||||
|
import FileUpload from './helpers/fileUpload';
|
||||||
import { IFileUpload } from './helpers/fileUpload/interfaces';
|
import { IFileUpload } from './helpers/fileUpload/interfaces';
|
||||||
|
import log from './helpers/log';
|
||||||
|
|
||||||
const uploadQueue: { [index: string]: StatefulPromise<FetchBlobResponse> } = {};
|
const uploadQueue: { [index: string]: StatefulPromise<FetchBlobResponse> } = {};
|
||||||
|
|
||||||
export function isUploadActive(path: string): boolean {
|
const getUploadPath = (path: string, rid: string) => `${path}-${rid}`;
|
||||||
return !!uploadQueue[path];
|
|
||||||
|
export function isUploadActive(path: string, rid: string): boolean {
|
||||||
|
return !!uploadQueue[getUploadPath(path, rid)];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function cancelUpload(item: TUploadModel): Promise<void> {
|
export async function cancelUpload(item: TUploadModel, rid: string): Promise<void> {
|
||||||
if (!isEmpty(uploadQueue[item.path])) {
|
const uploadPath = getUploadPath(item.path, rid);
|
||||||
|
if (!isEmpty(uploadQueue[uploadPath])) {
|
||||||
try {
|
try {
|
||||||
await uploadQueue[item.path].cancel();
|
await uploadQueue[uploadPath].cancel();
|
||||||
} catch {
|
} catch {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
delete uploadQueue[uploadPath];
|
||||||
|
}
|
||||||
|
if (item.id) {
|
||||||
try {
|
try {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
await db.write(async () => {
|
await db.write(async () => {
|
||||||
|
@ -30,7 +38,6 @@ export async function cancelUpload(item: TUploadModel): Promise<void> {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
}
|
}
|
||||||
delete uploadQueue[item.path];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,14 +58,18 @@ export function sendFileMessage(
|
||||||
|
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const uploadsCollection = db.get('uploads');
|
const uploadsCollection = db.get('uploads');
|
||||||
|
const uploadPath = getUploadPath(fileInfo.path, rid);
|
||||||
let uploadRecord: TUploadModel;
|
let uploadRecord: TUploadModel;
|
||||||
try {
|
try {
|
||||||
uploadRecord = await uploadsCollection.find(fileInfo.path);
|
uploadRecord = await uploadsCollection.find(uploadPath);
|
||||||
|
if (uploadRecord.id) {
|
||||||
|
return Alert.alert(i18n.t('FileUpload_Error'), i18n.t('Upload_in_progress'));
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
try {
|
try {
|
||||||
await db.write(async () => {
|
await db.write(async () => {
|
||||||
uploadRecord = await uploadsCollection.create(u => {
|
uploadRecord = await uploadsCollection.create(u => {
|
||||||
u._raw = sanitizedRaw({ id: fileInfo.path }, uploadsCollection.schema);
|
u._raw = sanitizedRaw({ id: uploadPath }, uploadsCollection.schema);
|
||||||
Object.assign(u, fileInfo);
|
Object.assign(u, fileInfo);
|
||||||
if (u.subscription) {
|
if (u.subscription) {
|
||||||
u.subscription.id = rid;
|
u.subscription.id = rid;
|
||||||
|
@ -99,9 +110,9 @@ export function sendFileMessage(
|
||||||
'X-User-Id': id
|
'X-User-Id': id
|
||||||
};
|
};
|
||||||
|
|
||||||
uploadQueue[fileInfo.path] = FileUpload.fetch('POST', uploadUrl, headers, formData);
|
uploadQueue[uploadPath] = FileUpload.fetch('POST', uploadUrl, headers, formData);
|
||||||
|
|
||||||
uploadQueue[fileInfo.path].uploadProgress(async (loaded: number, total: number) => {
|
uploadQueue[uploadPath].uploadProgress(async (loaded: number, total: number) => {
|
||||||
try {
|
try {
|
||||||
await db.write(async () => {
|
await db.write(async () => {
|
||||||
await uploadRecord.update(u => {
|
await uploadRecord.update(u => {
|
||||||
|
@ -113,7 +124,7 @@ export function sendFileMessage(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
uploadQueue[fileInfo.path].then(async response => {
|
uploadQueue[uploadPath].then(async response => {
|
||||||
if (response.respInfo.status >= 200 && response.respInfo.status < 400) {
|
if (response.respInfo.status >= 200 && response.respInfo.status < 400) {
|
||||||
// If response is all good...
|
// If response is all good...
|
||||||
try {
|
try {
|
||||||
|
@ -142,7 +153,7 @@ export function sendFileMessage(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
uploadQueue[fileInfo.path].catch(async error => {
|
uploadQueue[uploadPath].catch(async error => {
|
||||||
try {
|
try {
|
||||||
await db.write(async () => {
|
await db.write(async () => {
|
||||||
await uploadRecord.update(u => {
|
await uploadRecord.update(u => {
|
||||||
|
|
|
@ -1,14 +1,22 @@
|
||||||
import navigation from '../navigation/appNavigation';
|
import { PermissionsAndroid } from 'react-native';
|
||||||
import openLink from './helpers/openLink';
|
|
||||||
import { Services } from '../services';
|
|
||||||
import log from './helpers/log';
|
|
||||||
import { showErrorAlert } from './helpers';
|
|
||||||
import i18n from '../../i18n';
|
import i18n from '../../i18n';
|
||||||
|
import navigation from '../navigation/appNavigation';
|
||||||
|
import { Services } from '../services';
|
||||||
|
import { isAndroid, showErrorAlert } from './helpers';
|
||||||
|
import log from './helpers/log';
|
||||||
|
import openLink from './helpers/openLink';
|
||||||
|
|
||||||
export const videoConfJoin = async (callId: string, cam: boolean) => {
|
export const videoConfJoin = async (callId: string, cam: boolean) => {
|
||||||
try {
|
try {
|
||||||
const result = await Services.videoConferenceJoin(callId, cam);
|
const result = await Services.videoConferenceJoin(callId, cam);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
|
if (isAndroid) {
|
||||||
|
await PermissionsAndroid.requestMultiple([
|
||||||
|
PermissionsAndroid.PERMISSIONS.CAMERA,
|
||||||
|
PermissionsAndroid.PERMISSIONS.RECORD_AUDIO
|
||||||
|
]);
|
||||||
|
}
|
||||||
const { url, providerName } = result;
|
const { url, providerName } = result;
|
||||||
if (providerName === 'jitsi') {
|
if (providerName === 'jitsi') {
|
||||||
navigation.navigate('JitsiMeetView', { url, onlyAudio: !cam, videoConf: true });
|
navigation.navigate('JitsiMeetView', { url, onlyAudio: !cam, videoConf: true });
|
||||||
|
|
|
@ -80,6 +80,7 @@ import {
|
||||||
ProfileStackParamList,
|
ProfileStackParamList,
|
||||||
SettingsStackParamList
|
SettingsStackParamList
|
||||||
} from './types';
|
} from './types';
|
||||||
|
import { isIOS } from '../lib/methods/helpers';
|
||||||
|
|
||||||
// ChatsStackNavigator
|
// ChatsStackNavigator
|
||||||
const ChatsStack = createStackNavigator<ChatsStackParamList>();
|
const ChatsStack = createStackNavigator<ChatsStackParamList>();
|
||||||
|
@ -135,7 +136,11 @@ const ChatsStackNavigator = () => {
|
||||||
<ChatsStack.Screen name='QueueListView' component={QueueListView} />
|
<ChatsStack.Screen name='QueueListView' component={QueueListView} />
|
||||||
<ChatsStack.Screen name='CannedResponsesListView' component={CannedResponsesListView} />
|
<ChatsStack.Screen name='CannedResponsesListView' component={CannedResponsesListView} />
|
||||||
<ChatsStack.Screen name='CannedResponseDetail' component={CannedResponseDetail} />
|
<ChatsStack.Screen name='CannedResponseDetail' component={CannedResponseDetail} />
|
||||||
<ChatsStack.Screen name='JitsiMeetView' component={JitsiMeetView} options={{ headerShown: false }} />
|
<ChatsStack.Screen
|
||||||
|
name='JitsiMeetView'
|
||||||
|
component={JitsiMeetView}
|
||||||
|
options={{ headerShown: false, animationEnabled: isIOS }}
|
||||||
|
/>
|
||||||
</ChatsStack.Navigator>
|
</ChatsStack.Navigator>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -73,6 +73,7 @@ import {
|
||||||
MasterDetailInsideStackParamList,
|
MasterDetailInsideStackParamList,
|
||||||
ModalStackParamList
|
ModalStackParamList
|
||||||
} from './types';
|
} from './types';
|
||||||
|
import { isIOS } from '../../lib/methods/helpers';
|
||||||
|
|
||||||
// ChatsStackNavigator
|
// ChatsStackNavigator
|
||||||
const ChatsStack = createStackNavigator<MasterDetailChatsStackParamList>();
|
const ChatsStack = createStackNavigator<MasterDetailChatsStackParamList>();
|
||||||
|
@ -223,7 +224,11 @@ const InsideStackNavigator = React.memo(() => {
|
||||||
<InsideStack.Screen name='ModalStackNavigator' component={ModalStackNavigator} options={{ headerShown: false }} />
|
<InsideStack.Screen name='ModalStackNavigator' component={ModalStackNavigator} options={{ headerShown: false }} />
|
||||||
<InsideStack.Screen name='AttachmentView' component={AttachmentView} />
|
<InsideStack.Screen name='AttachmentView' component={AttachmentView} />
|
||||||
<InsideStack.Screen name='ModalBlockView' component={ModalBlockView} options={ModalBlockView.navigationOptions} />
|
<InsideStack.Screen name='ModalBlockView' component={ModalBlockView} options={ModalBlockView.navigationOptions} />
|
||||||
<InsideStack.Screen name='JitsiMeetView' component={JitsiMeetView} options={{ headerShown: false }} />
|
<InsideStack.Screen
|
||||||
|
name='JitsiMeetView'
|
||||||
|
component={JitsiMeetView}
|
||||||
|
options={{ headerShown: false, animationEnabled: isIOS }}
|
||||||
|
/>
|
||||||
<InsideStack.Screen name='ShareView' component={ShareView} />
|
<InsideStack.Screen name='ShareView' component={ShareView} />
|
||||||
</InsideStack.Navigator>
|
</InsideStack.Navigator>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { BackHandler, NativeEventSubscription } from 'react-native';
|
||||||
|
import BackgroundTimer from 'react-native-background-timer';
|
||||||
|
import { isAppInstalled, openAppWithUri } from 'react-native-send-intent';
|
||||||
|
import WebView from 'react-native-webview';
|
||||||
|
import { WebViewMessage, WebViewNavigation } from 'react-native-webview/lib/WebViewTypes';
|
||||||
|
|
||||||
|
import { IBaseScreen } from '../definitions';
|
||||||
|
import { events, logEvent } from '../lib/methods/helpers/log';
|
||||||
|
import { Services } from '../lib/services';
|
||||||
|
import { ChatsStackParamList } from '../stacks/types';
|
||||||
|
import { withTheme } from '../theme';
|
||||||
|
|
||||||
|
const JITSI_INTENT = 'org.jitsi.meet';
|
||||||
|
|
||||||
|
type TJitsiMeetViewProps = IBaseScreen<ChatsStackParamList, 'JitsiMeetView'>;
|
||||||
|
|
||||||
|
class JitsiMeetView extends React.Component<TJitsiMeetViewProps> {
|
||||||
|
private rid: string;
|
||||||
|
private url: string;
|
||||||
|
private videoConf: boolean;
|
||||||
|
private jitsiTimeout: number | null;
|
||||||
|
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;
|
||||||
|
this.jitsiTimeout = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { route, navigation } = this.props;
|
||||||
|
isAppInstalled(JITSI_INTENT)
|
||||||
|
.then(function (isInstalled) {
|
||||||
|
if (isInstalled) {
|
||||||
|
const callUrl = route.params.url.replace(/^https?:\/\//, '').split('#')[0];
|
||||||
|
openAppWithUri(`intent://${callUrl}#Intent;scheme=${JITSI_INTENT};package=${JITSI_INTENT};end`)
|
||||||
|
.then(() => navigation.pop())
|
||||||
|
.catch(() => {});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
this.onConferenceJoined();
|
||||||
|
this.backHandler = BackHandler.addEventListener('hardwareBackPress', () => true);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
logEvent(this.videoConf ? events.LIVECHAT_VIDEOCONF_TERMINATE : events.JM_CONFERENCE_TERMINATE);
|
||||||
|
if (this.jitsiTimeout && !this.videoConf) {
|
||||||
|
BackgroundTimer.clearInterval(this.jitsiTimeout);
|
||||||
|
this.jitsiTimeout = null;
|
||||||
|
BackgroundTimer.stopBackgroundTimer();
|
||||||
|
}
|
||||||
|
this.backHandler.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
Services.updateJitsiTimeout(this.rid).catch((e: unknown) => console.log(e));
|
||||||
|
if (this.jitsiTimeout) {
|
||||||
|
BackgroundTimer.clearInterval(this.jitsiTimeout);
|
||||||
|
BackgroundTimer.stopBackgroundTimer();
|
||||||
|
this.jitsiTimeout = null;
|
||||||
|
}
|
||||||
|
this.jitsiTimeout = BackgroundTimer.setInterval(() => {
|
||||||
|
Services.updateJitsiTimeout(this.rid).catch((e: unknown) => console.log(e));
|
||||||
|
}, 10000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onNavigationStateChange = (webViewState: WebViewNavigation | WebViewMessage) => {
|
||||||
|
const { navigation, route } = this.props;
|
||||||
|
const jitsiRoomId = route.params.url
|
||||||
|
?.split(/^https?:\/\//)[1]
|
||||||
|
?.split('#')[0]
|
||||||
|
?.split('/')[1];
|
||||||
|
if ((jitsiRoomId && !webViewState.url.includes(jitsiRoomId)) || webViewState.url.includes('close')) {
|
||||||
|
navigation.pop();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<WebView
|
||||||
|
source={{ uri: `${this.url}&config.disableDeepLinking=true` }}
|
||||||
|
onMessage={({ nativeEvent }) => this.onNavigationStateChange(nativeEvent)}
|
||||||
|
onNavigationStateChange={this.onNavigationStateChange}
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
javaScriptEnabled
|
||||||
|
domStorageEnabled
|
||||||
|
mediaPlaybackRequiresUserAction={false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withTheme(JitsiMeetView);
|
|
@ -0,0 +1,5 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
declare const JitsiMeetView: React.SFC<>;
|
||||||
|
|
||||||
|
export default JitsiMeetView;
|
|
@ -1,17 +1,16 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { BackHandler, StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
import JitsiMeet, { JitsiMeetView as RNJitsiMeetView } from 'react-native-jitsi-meet';
|
|
||||||
import BackgroundTimer from 'react-native-background-timer';
|
import BackgroundTimer from 'react-native-background-timer';
|
||||||
|
import JitsiMeet, { JitsiMeetView as RNJitsiMeetView } from 'react-native-jitsi-meet';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { getUserSelector } from '../selectors/login';
|
import RCActivityIndicator from '../containers/ActivityIndicator';
|
||||||
import ActivityIndicator from '../containers/ActivityIndicator';
|
import { IApplicationState, IBaseScreen, IUser } from '../definitions';
|
||||||
import { events, logEvent } from '../lib/methods/helpers/log';
|
import { events, logEvent } from '../lib/methods/helpers/log';
|
||||||
import { isAndroid, isIOS } from '../lib/methods/helpers';
|
|
||||||
import { withTheme } from '../theme';
|
|
||||||
import { ChatsStackParamList } from '../stacks/types';
|
|
||||||
import { IApplicationState, IUser, IBaseScreen } from '../definitions';
|
|
||||||
import { Services } from '../lib/services';
|
import { Services } from '../lib/services';
|
||||||
|
import { getUserSelector } from '../selectors/login';
|
||||||
|
import { ChatsStackParamList } from '../stacks/types';
|
||||||
|
import { withTheme } from '../theme';
|
||||||
|
|
||||||
const formatUrl = (url: string, baseUrl: string, uriSize: number, avatarAuthURLFragment: string) =>
|
const formatUrl = (url: string, baseUrl: string, uriSize: number, avatarAuthURLFragment: string) =>
|
||||||
`${baseUrl}/avatar/${url}?format=png&width=${uriSize}&height=${uriSize}${avatarAuthURLFragment}`;
|
`${baseUrl}/avatar/${url}?format=png&width=${uriSize}&height=${uriSize}${avatarAuthURLFragment}`;
|
||||||
|
@ -60,20 +59,14 @@ class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewS
|
||||||
const { userInfo } = this.state;
|
const { userInfo } = this.state;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
const onlyAudio = route.params?.onlyAudio ?? false;
|
||||||
|
if (onlyAudio) {
|
||||||
|
JitsiMeet.audioCall(this.url, userInfo);
|
||||||
|
} else {
|
||||||
|
JitsiMeet.call(this.url, userInfo);
|
||||||
|
}
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
if (isIOS) {
|
|
||||||
setTimeout(() => {
|
|
||||||
const onlyAudio = route.params?.onlyAudio ?? false;
|
|
||||||
if (onlyAudio) {
|
|
||||||
JitsiMeet.audioCall(this.url, userInfo);
|
|
||||||
} else {
|
|
||||||
JitsiMeet.call(this.url, userInfo);
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
BackHandler.addEventListener('hardwareBackPress', () => null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -83,16 +76,8 @@ class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewS
|
||||||
this.jitsiTimeout = null;
|
this.jitsiTimeout = null;
|
||||||
BackgroundTimer.stopBackgroundTimer();
|
BackgroundTimer.stopBackgroundTimer();
|
||||||
}
|
}
|
||||||
BackHandler.removeEventListener('hardwareBackPress', () => null);
|
|
||||||
if (isIOS) {
|
|
||||||
JitsiMeet.endCall();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
endCall = () => {
|
|
||||||
JitsiMeet.endCall();
|
JitsiMeet.endCall();
|
||||||
return null;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
onConferenceWillJoin = () => {
|
onConferenceWillJoin = () => {
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
|
@ -117,8 +102,8 @@ class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewS
|
||||||
};
|
};
|
||||||
|
|
||||||
onConferenceTerminated = () => {
|
onConferenceTerminated = () => {
|
||||||
logEvent(this.videoConf ? events.LIVECHAT_VIDEOCONF_TERMINATE : events.JM_CONFERENCE_TERMINATE);
|
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
|
logEvent(this.videoConf ? events.LIVECHAT_VIDEOCONF_TERMINATE : events.JM_CONFERENCE_TERMINATE);
|
||||||
// fix to go back when the call ends
|
// fix to go back when the call ends
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
JitsiMeet.endCall();
|
JitsiMeet.endCall();
|
||||||
|
@ -127,10 +112,8 @@ class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewS
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { userInfo, loading } = this.state;
|
const { loading } = this.state;
|
||||||
const { route } = this.props;
|
|
||||||
const onlyAudio = route.params?.onlyAudio ?? false;
|
|
||||||
const options = isAndroid ? { url: this.url, userInfo, audioOnly: onlyAudio } : null;
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<RNJitsiMeetView
|
<RNJitsiMeetView
|
||||||
|
@ -138,9 +121,9 @@ class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewS
|
||||||
onConferenceTerminated={this.onConferenceTerminated}
|
onConferenceTerminated={this.onConferenceTerminated}
|
||||||
onConferenceJoined={this.onConferenceJoined}
|
onConferenceJoined={this.onConferenceJoined}
|
||||||
style={StyleSheet.absoluteFill}
|
style={StyleSheet.absoluteFill}
|
||||||
options={options}
|
options={null}
|
||||||
/>
|
/>
|
||||||
{loading ? <ActivityIndicator /> : null}
|
{loading ? <RCActivityIndicator absolute size='large' /> : null}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -112,9 +112,10 @@ class UploadProgress extends Component<IUploadProgressProps, IUploadProgressStat
|
||||||
|
|
||||||
uploadCheck = () => {
|
uploadCheck = () => {
|
||||||
this.ranInitialUploadCheck = true;
|
this.ranInitialUploadCheck = true;
|
||||||
|
const { rid } = this.props;
|
||||||
const { uploads } = this.state;
|
const { uploads } = this.state;
|
||||||
uploads.forEach(async u => {
|
uploads.forEach(async u => {
|
||||||
if (!isUploadActive(u.path)) {
|
if (!isUploadActive(u.path, rid)) {
|
||||||
try {
|
try {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
await db.write(async () => {
|
await db.write(async () => {
|
||||||
|
@ -141,8 +142,9 @@ class UploadProgress extends Component<IUploadProgressProps, IUploadProgressStat
|
||||||
};
|
};
|
||||||
|
|
||||||
handleCancelUpload = async (item: TUploadModel) => {
|
handleCancelUpload = async (item: TUploadModel) => {
|
||||||
|
const { rid } = this.props;
|
||||||
try {
|
try {
|
||||||
await cancelUpload(item);
|
await cancelUpload(item, rid);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
}
|
}
|
||||||
|
@ -210,7 +212,7 @@ class UploadProgress extends Component<IUploadProgressProps, IUploadProgressStat
|
||||||
key={item.path}
|
key={item.path}
|
||||||
style={[
|
style={[
|
||||||
styles.item,
|
styles.item,
|
||||||
index !== 0 ? { marginTop: 10 } : {},
|
index !== 0 ? { marginTop: 4 } : {},
|
||||||
{
|
{
|
||||||
backgroundColor: themes[theme!].chatComponentBackground,
|
backgroundColor: themes[theme!].chatComponentBackground,
|
||||||
borderColor: themes[theme!].borderColor
|
borderColor: themes[theme!].borderColor
|
||||||
|
|
|
@ -142,7 +142,7 @@ class ThreadMessagesView extends React.Component<IThreadMessagesViewProps, IThre
|
||||||
),
|
),
|
||||||
headerRight: () => (
|
headerRight: () => (
|
||||||
<HeaderButton.Container>
|
<HeaderButton.Container>
|
||||||
<HeaderButton.Item iconName='search' onPress={this.onSearchPress} />
|
<HeaderButton.Item iconName='search' onPress={this.onSearchPress} testID='thread-messages-view-search-icon' />
|
||||||
</HeaderButton.Container>
|
</HeaderButton.Container>
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -242,7 +242,7 @@ class ThreadMessagesView extends React.Component<IThreadMessagesViewProps, IThre
|
||||||
const { subscription } = this.state;
|
const { subscription } = this.state;
|
||||||
// if there's no subscription, manage data on this.state.messages
|
// if there's no subscription, manage data on this.state.messages
|
||||||
// note: sync will never be called without subscription
|
// note: sync will never be called without subscription
|
||||||
if (!subscription) {
|
if (!subscription._id) {
|
||||||
this.setState(({ messages }) => ({ messages: [...messages, ...update] }));
|
this.setState(({ messages }) => ({ messages: [...messages, ...update] }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import data from '../../data';
|
import data from '../../data';
|
||||||
import { navigateToLogin, login, tapBack, sleep } from '../../helpers/app';
|
import { navigateToLogin, login, tapBack, sleep } from '../../helpers/app';
|
||||||
|
import { sendMessage } from '../../helpers/data_setup';
|
||||||
|
|
||||||
const testuser = data.users.regular;
|
const testuser = data.users.regular;
|
||||||
|
|
||||||
|
@ -26,6 +27,13 @@ describe('Join room from directory', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Usage', () => {
|
describe('Usage', () => {
|
||||||
|
const threadMessage = `thread-${data.random}`;
|
||||||
|
before(async () => {
|
||||||
|
const result = await sendMessage(data.users.alternate, data.channels.detoxpublic.name, threadMessage);
|
||||||
|
const threadId = result.message._id;
|
||||||
|
await sendMessage(data.users.alternate, result.message.rid, data.random, threadId);
|
||||||
|
});
|
||||||
|
|
||||||
it('should tap directory', async () => {
|
it('should tap directory', async () => {
|
||||||
await element(by.id('rooms-list-view-directory')).tap();
|
await element(by.id('rooms-list-view-directory')).tap();
|
||||||
await waitFor(element(by.id('directory-view')))
|
await waitFor(element(by.id('directory-view')))
|
||||||
|
@ -37,6 +45,20 @@ describe('Join room from directory', () => {
|
||||||
await navigateToRoom(data.channels.detoxpublic.name);
|
await navigateToRoom(data.channels.detoxpublic.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should navigate to thread messages view and load messages', async () => {
|
||||||
|
await waitFor(element(by.id('room-view-header-threads')))
|
||||||
|
.toBeVisible()
|
||||||
|
.withTimeout(2000);
|
||||||
|
await element(by.id('room-view-header-threads')).tap();
|
||||||
|
await waitFor(element(by.id(`thread-messages-view-${threadMessage}`)))
|
||||||
|
.toBeVisible()
|
||||||
|
.withTimeout(2000);
|
||||||
|
await tapBack();
|
||||||
|
await waitFor(element(by.id('room-view-header-threads')))
|
||||||
|
.toBeVisible()
|
||||||
|
.withTimeout(2000);
|
||||||
|
});
|
||||||
|
|
||||||
it('should search user and navigate', async () => {
|
it('should search user and navigate', async () => {
|
||||||
await tapBack();
|
await tapBack();
|
||||||
await element(by.id('rooms-list-view-directory')).tap();
|
await element(by.id('rooms-list-view-directory')).tap();
|
||||||
|
|
|
@ -1767,7 +1767,7 @@
|
||||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||||
MARKETING_VERSION = 4.33.0;
|
MARKETING_VERSION = 4.34.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
||||||
|
@ -1806,7 +1806,7 @@
|
||||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||||
MARKETING_VERSION = 4.33.0;
|
MARKETING_VERSION = 4.34.0;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService;
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>4.33.0</string>
|
<string>4.34.0</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>XPC!</string>
|
<string>XPC!</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>4.33.0</string>
|
<string>4.34.0</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
<key>KeychainGroup</key>
|
<key>KeychainGroup</key>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "rocket-chat-reactnative",
|
"name": "rocket-chat-reactnative",
|
||||||
"version": "4.33.0",
|
"version": "4.34.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-native start",
|
"start": "react-native start",
|
||||||
|
@ -120,6 +120,7 @@
|
||||||
"react-native-safe-area-context": "3.2.0",
|
"react-native-safe-area-context": "3.2.0",
|
||||||
"react-native-screens": "3.13.1",
|
"react-native-screens": "3.13.1",
|
||||||
"react-native-scrollable-tab-view": "ptomasroos/react-native-scrollable-tab-view",
|
"react-native-scrollable-tab-view": "ptomasroos/react-native-scrollable-tab-view",
|
||||||
|
"react-native-send-intent": "^1.3.0",
|
||||||
"react-native-simple-crypto": "RocketChat/react-native-simple-crypto#0.5.1",
|
"react-native-simple-crypto": "RocketChat/react-native-simple-crypto#0.5.1",
|
||||||
"react-native-skeleton-placeholder": "^5.2.3",
|
"react-native-skeleton-placeholder": "^5.2.3",
|
||||||
"react-native-slowlog": "^1.0.2",
|
"react-native-slowlog": "^1.0.2",
|
||||||
|
|
|
@ -14,6 +14,11 @@ module.exports = {
|
||||||
platforms: {
|
platforms: {
|
||||||
android: null
|
android: null
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'react-native-jitsi-meet': {
|
||||||
|
platforms: {
|
||||||
|
android: null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -17821,6 +17821,11 @@ react-native-scrollable-tab-view@ptomasroos/react-native-scrollable-tab-view:
|
||||||
prop-types "^15.6.0"
|
prop-types "^15.6.0"
|
||||||
react-timer-mixin "^0.13.3"
|
react-timer-mixin "^0.13.3"
|
||||||
|
|
||||||
|
react-native-send-intent@^1.3.0:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-native-send-intent/-/react-native-send-intent-1.3.0.tgz#d8c7898827da1b8b10e25a645ce6802d1a0b440c"
|
||||||
|
integrity sha512-ODTX7BHITFxdcAL0K2iHfa3qVYnqG8GPcv1NbLBNC1DyCaOSJiiGtVH6Kc5YBqzQ8+1pV9uN5nfQ5wyFgiq74g==
|
||||||
|
|
||||||
react-native-simple-crypto@RocketChat/react-native-simple-crypto#0.5.1:
|
react-native-simple-crypto@RocketChat/react-native-simple-crypto#0.5.1:
|
||||||
version "0.5.1"
|
version "0.5.1"
|
||||||
resolved "https://codeload.github.com/RocketChat/react-native-simple-crypto/tar.gz/dcf6eef5359c739d521371918e13a73f2ea6cb42"
|
resolved "https://codeload.github.com/RocketChat/react-native-simple-crypto/tar.gz/dcf6eef5359c739d521371918e13a73f2ea6cb42"
|
||||||
|
@ -17865,7 +17870,7 @@ react-native-text-size@4.0.0-rc.1:
|
||||||
|
|
||||||
react-native-ui-lib@RocketChat/react-native-ui-lib:
|
react-native-ui-lib@RocketChat/react-native-ui-lib:
|
||||||
version "4.2.0"
|
version "4.2.0"
|
||||||
resolved "https://codeload.github.com/RocketChat/react-native-ui-lib/tar.gz/d20c1bcd09b694fc5133fc2232fd510f5f4ba581"
|
resolved "https://codeload.github.com/RocketChat/react-native-ui-lib/tar.gz/fd5869e493b5b9cf888cec4a252c9ef292364b02"
|
||||||
dependencies:
|
dependencies:
|
||||||
babel-plugin-transform-inline-environment-variables "^0.0.2"
|
babel-plugin-transform-inline-environment-variables "^0.0.2"
|
||||||
color "^3.1.0"
|
color "^3.1.0"
|
||||||
|
|
Loading…
Reference in New Issue