Merge branch 'expo-image-picker' of github.com:RocketChat/Rocket.Chat.ReactNative into expo-image-picker
This commit is contained in:
commit
a6df0c4357
|
@ -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
|
@ -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>
|
||||||
|
|
Binary file not shown.
|
@ -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"
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { ViewStyle } from 'react-native';
|
||||||
|
|
||||||
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
export interface IAvatar {
|
export interface IAvatar {
|
||||||
server?: string;
|
server?: string;
|
||||||
style?: any;
|
style?: ViewStyle;
|
||||||
text?: string;
|
text?: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
emoji?: string;
|
emoji?: string;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
export const mappedIcons = {
|
export const mappedIcons = {
|
||||||
'lamp-bulb': 59812,
|
'lamp-bulb': 59836,
|
||||||
|
'phone-in': 59835,
|
||||||
'basketball': 59776,
|
'basketball': 59776,
|
||||||
'percentage': 59777,
|
'percentage': 59777,
|
||||||
|
'glasses': 59812,
|
||||||
'burger': 59813,
|
'burger': 59813,
|
||||||
'leaf': 59814,
|
'leaf': 59814,
|
||||||
'airplane': 59815,
|
'airplane': 59815,
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -12,7 +12,6 @@ import { themes } from '../../lib/constants';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { ROW_HEIGHT } from '../RoomItem';
|
import { ROW_HEIGHT } from '../RoomItem';
|
||||||
import { goRoom } from '../../lib/methods/helpers/goRoom';
|
import { goRoom } from '../../lib/methods/helpers/goRoom';
|
||||||
import Navigation from '../../lib/navigation/appNavigation';
|
|
||||||
import { useOrientation } from '../../dimensions';
|
import { useOrientation } from '../../dimensions';
|
||||||
import { IApplicationState, ISubscription, SubscriptionType } from '../../definitions';
|
import { IApplicationState, ISubscription, SubscriptionType } from '../../definitions';
|
||||||
|
|
||||||
|
@ -98,12 +97,7 @@ const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifie
|
||||||
prid
|
prid
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isMasterDetail) {
|
goRoom({ item, isMasterDetail, jumpToMessageId: _id, popToRoot: true });
|
||||||
Navigation.navigate('DrawerNavigator');
|
|
||||||
} else {
|
|
||||||
Navigation.navigate('RoomsListView');
|
|
||||||
}
|
|
||||||
goRoom({ item, isMasterDetail, jumpToMessageId: _id });
|
|
||||||
hideNotification();
|
hideNotification();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -124,6 +118,7 @@ const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifie
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
hitSlop={BUTTON_HIT_SLOP}
|
hitSlop={BUTTON_HIT_SLOP}
|
||||||
background={Touchable.SelectableBackgroundBorderless()}
|
background={Touchable.SelectableBackgroundBorderless()}
|
||||||
|
testID={`in-app-notification-${text}`}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
<Avatar text={avatar} size={AVATAR_SIZE} type={type} rid={rid} style={styles.avatar} />
|
<Avatar text={avatar} size={AVATAR_SIZE} type={type} rid={rid} style={styles.avatar} />
|
||||||
|
|
|
@ -1,56 +1,50 @@
|
||||||
import React, { memo, useEffect } from 'react';
|
import React, { memo, useEffect } from 'react';
|
||||||
import { Easing, Notifier, NotifierRoot } from 'react-native-notifier';
|
import { Easing, Notifier, NotifierRoot } from 'react-native-notifier';
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { dequal } from 'dequal';
|
|
||||||
|
|
||||||
import NotifierComponent, { INotifierComponent } from './NotifierComponent';
|
import NotifierComponent, { INotifierComponent } from './NotifierComponent';
|
||||||
import EventEmitter from '../../lib/methods/helpers/events';
|
import EventEmitter from '../../lib/methods/helpers/events';
|
||||||
import Navigation from '../../lib/navigation/appNavigation';
|
import Navigation from '../../lib/navigation/appNavigation';
|
||||||
import { getActiveRoute } from '../../lib/methods/helpers/navigation';
|
import { getActiveRoute } from '../../lib/methods/helpers/navigation';
|
||||||
import { IApplicationState } from '../../definitions';
|
import { useAppSelector } from '../../lib/hooks';
|
||||||
import { IRoom } from '../../reducers/room';
|
|
||||||
|
|
||||||
export const INAPP_NOTIFICATION_EMITTER = 'NotificationInApp';
|
export const INAPP_NOTIFICATION_EMITTER = 'NotificationInApp';
|
||||||
|
|
||||||
const InAppNotification = memo(
|
const InAppNotification = memo(() => {
|
||||||
({ rooms, appState }: { rooms: IRoom['rooms']; appState: string }) => {
|
const { appState, subscribedRoom } = useAppSelector(state => ({
|
||||||
const show = (notification: INotifierComponent['notification']) => {
|
subscribedRoom: state.room.subscribedRoom,
|
||||||
if (appState !== 'foreground') {
|
appState: state.app.ready && state.app.foreground ? 'foreground' : 'background'
|
||||||
|
}));
|
||||||
|
|
||||||
|
const show = (notification: INotifierComponent['notification']) => {
|
||||||
|
if (appState !== 'foreground') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { payload } = notification;
|
||||||
|
const state = Navigation.navigationRef.current?.getRootState();
|
||||||
|
const route = getActiveRoute(state);
|
||||||
|
if (payload.rid) {
|
||||||
|
if (payload.rid === subscribedRoom || route?.name === 'JitsiMeetView') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Notifier.showNotification({
|
||||||
const { payload } = notification;
|
showEasing: Easing.inOut(Easing.quad),
|
||||||
const state = Navigation.navigationRef.current?.getRootState();
|
Component: NotifierComponent,
|
||||||
const route = getActiveRoute(state);
|
componentProps: {
|
||||||
if (payload.rid) {
|
notification
|
||||||
if (rooms.includes(payload.rid) || route?.name === 'JitsiMeetView') {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
Notifier.showNotification({
|
});
|
||||||
showEasing: Easing.inOut(Easing.quad),
|
}
|
||||||
Component: NotifierComponent,
|
};
|
||||||
componentProps: {
|
|
||||||
notification
|
useEffect(() => {
|
||||||
}
|
const listener = EventEmitter.addEventListener(INAPP_NOTIFICATION_EMITTER, show);
|
||||||
});
|
return () => {
|
||||||
}
|
EventEmitter.removeListener(INAPP_NOTIFICATION_EMITTER, listener);
|
||||||
};
|
};
|
||||||
|
}, [subscribedRoom, appState]);
|
||||||
|
|
||||||
useEffect(() => {
|
return <NotifierRoot />;
|
||||||
const listener = EventEmitter.addEventListener(INAPP_NOTIFICATION_EMITTER, show);
|
|
||||||
return () => {
|
|
||||||
EventEmitter.removeListener(INAPP_NOTIFICATION_EMITTER, listener);
|
|
||||||
};
|
|
||||||
}, [rooms]);
|
|
||||||
|
|
||||||
return <NotifierRoot />;
|
|
||||||
},
|
|
||||||
(prevProps, nextProps) => dequal(prevProps.rooms, nextProps.rooms)
|
|
||||||
);
|
|
||||||
|
|
||||||
const mapStateToProps = (state: IApplicationState) => ({
|
|
||||||
rooms: state.room.rooms,
|
|
||||||
appState: state.app.ready && state.app.foreground ? 'foreground' : 'background'
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(InAppNotification);
|
export default InAppNotification;
|
||||||
|
|
|
@ -353,9 +353,10 @@ const MessageActions = React.memo(
|
||||||
|
|
||||||
const getOptions = (message: TAnyMessageModel) => {
|
const getOptions = (message: TAnyMessageModel) => {
|
||||||
const options: TActionSheetOptionsItem[] = [];
|
const options: TActionSheetOptionsItem[] = [];
|
||||||
|
const videoConfBlock = message.t === 'videoconf';
|
||||||
|
|
||||||
// Quote
|
// Quote
|
||||||
if (!isReadOnly) {
|
if (!isReadOnly && !videoConfBlock) {
|
||||||
options.push({
|
options.push({
|
||||||
title: I18n.t('Quote'),
|
title: I18n.t('Quote'),
|
||||||
icon: 'quote',
|
icon: 'quote',
|
||||||
|
@ -373,7 +374,7 @@ const MessageActions = React.memo(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reply in DM
|
// Reply in DM
|
||||||
if (room.t !== 'd' && room.t !== 'l' && createDirectMessagePermission) {
|
if (room.t !== 'd' && room.t !== 'l' && createDirectMessagePermission && !videoConfBlock) {
|
||||||
options.push({
|
options.push({
|
||||||
title: I18n.t('Reply_in_direct_message'),
|
title: I18n.t('Reply_in_direct_message'),
|
||||||
icon: 'arrow-back',
|
icon: 'arrow-back',
|
||||||
|
@ -396,11 +397,13 @@ const MessageActions = React.memo(
|
||||||
});
|
});
|
||||||
|
|
||||||
// Copy
|
// Copy
|
||||||
options.push({
|
if (!videoConfBlock) {
|
||||||
title: I18n.t('Copy'),
|
options.push({
|
||||||
icon: 'copy',
|
title: I18n.t('Copy'),
|
||||||
onPress: () => handleCopy(message)
|
icon: 'copy',
|
||||||
});
|
onPress: () => handleCopy(message)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Share
|
// Share
|
||||||
options.push({
|
options.push({
|
||||||
|
@ -410,7 +413,7 @@ const MessageActions = React.memo(
|
||||||
});
|
});
|
||||||
|
|
||||||
// Edit
|
// Edit
|
||||||
if (allowEdit(message)) {
|
if (allowEdit(message) && !videoConfBlock) {
|
||||||
options.push({
|
options.push({
|
||||||
title: I18n.t('Edit'),
|
title: I18n.t('Edit'),
|
||||||
icon: 'edit',
|
icon: 'edit',
|
||||||
|
@ -419,7 +422,7 @@ const MessageActions = React.memo(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pin
|
// Pin
|
||||||
if (Message_AllowPinning && permissions?.hasPinPermission) {
|
if (Message_AllowPinning && permissions?.hasPinPermission && !videoConfBlock) {
|
||||||
options.push({
|
options.push({
|
||||||
title: I18n.t(message.pinned ? 'Unpin' : 'Pin'),
|
title: I18n.t(message.pinned ? 'Unpin' : 'Pin'),
|
||||||
icon: 'pin',
|
icon: 'pin',
|
||||||
|
@ -428,7 +431,7 @@ const MessageActions = React.memo(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Star
|
// Star
|
||||||
if (Message_AllowStarring) {
|
if (Message_AllowStarring && !videoConfBlock) {
|
||||||
options.push({
|
options.push({
|
||||||
title: I18n.t(message.starred ? 'Unstar' : 'Star'),
|
title: I18n.t(message.starred ? 'Unstar' : 'Star'),
|
||||||
icon: message.starred ? 'star-filled' : 'star',
|
icon: message.starred ? 'star-filled' : 'star',
|
||||||
|
|
|
@ -189,7 +189,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const { rid, tmid, navigation, sharing, usedCannedResponse, isMasterDetail } = this.props;
|
const { rid, tmid, navigation, sharing, usedCannedResponse } = this.props;
|
||||||
let msg;
|
let msg;
|
||||||
try {
|
try {
|
||||||
const threadsCollection = db.get('threads');
|
const threadsCollection = db.get('threads');
|
||||||
|
@ -224,7 +224,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
EventEmiter.addEventListener(KEY_COMMAND, this.handleCommands);
|
EventEmiter.addEventListener(KEY_COMMAND, this.handleCommands);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMasterDetail && usedCannedResponse) {
|
if (usedCannedResponse) {
|
||||||
this.onChangeText(usedCannedResponse);
|
this.onChangeText(usedCannedResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { Text, View } from 'react-native';
|
||||||
|
import Touchable from 'react-native-platform-touchable';
|
||||||
|
|
||||||
|
import i18n from '../../../../i18n';
|
||||||
|
import { getSubscriptionByRoomId } from '../../../../lib/database/services/Subscription';
|
||||||
|
import { useAppSelector } from '../../../../lib/hooks';
|
||||||
|
import { getRoomAvatar, getUidDirectMessage } from '../../../../lib/methods/helpers';
|
||||||
|
import { videoConfStartAndJoin } from '../../../../lib/methods/videoConf';
|
||||||
|
import { useTheme } from '../../../../theme';
|
||||||
|
import { useActionSheet } from '../../../ActionSheet';
|
||||||
|
import AvatarContainer from '../../../Avatar';
|
||||||
|
import Button from '../../../Button';
|
||||||
|
import { CustomIcon } from '../../../CustomIcon';
|
||||||
|
import { BUTTON_HIT_SLOP } from '../../../message/utils';
|
||||||
|
import StatusContainer from '../../../Status';
|
||||||
|
import useStyle from './styles';
|
||||||
|
|
||||||
|
export default function CallAgainActionSheet({ rid }: { rid: string }): React.ReactElement {
|
||||||
|
const style = useStyle();
|
||||||
|
const { colors } = useTheme();
|
||||||
|
const [user, setUser] = useState({ username: '', avatar: '', uid: '', rid: '' });
|
||||||
|
const [phone, setPhone] = useState(true);
|
||||||
|
const [camera, setCamera] = useState(false);
|
||||||
|
const username = useAppSelector(state => state.login.user.username);
|
||||||
|
|
||||||
|
const { hideActionSheet } = useActionSheet();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const room = await getSubscriptionByRoomId(rid);
|
||||||
|
const uid = (await getUidDirectMessage(room)) as string;
|
||||||
|
const avt = getRoomAvatar(room);
|
||||||
|
setUser({ uid, username: room?.name || '', avatar: avt, rid: room?.id || '' });
|
||||||
|
})();
|
||||||
|
}, [rid]);
|
||||||
|
|
||||||
|
const handleColor = (enabled: boolean) => (enabled ? colors.conferenceCallEnabledIcon : colors.conferenceCallDisabledIcon);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={style.actionSheetContainer}>
|
||||||
|
<View style={style.actionSheetHeader}>
|
||||||
|
<Text style={style.actionSheetHeaderTitle}>{i18n.t('Start_a_call')}</Text>
|
||||||
|
<View style={style.actionSheetHeaderButtons}>
|
||||||
|
<Touchable
|
||||||
|
onPress={() => setCamera(!camera)}
|
||||||
|
style={[style.iconCallContainer, camera && style.enabledBackground, { marginRight: 6 }]}
|
||||||
|
hitSlop={BUTTON_HIT_SLOP}
|
||||||
|
>
|
||||||
|
<CustomIcon name={camera ? 'camera' : 'camera-disabled'} size={16} color={handleColor(camera)} />
|
||||||
|
</Touchable>
|
||||||
|
<Touchable
|
||||||
|
onPress={() => setPhone(!phone)}
|
||||||
|
style={[style.iconCallContainer, phone && style.enabledBackground]}
|
||||||
|
hitSlop={BUTTON_HIT_SLOP}
|
||||||
|
>
|
||||||
|
<CustomIcon name={phone ? 'microphone' : 'microphone-disabled'} size={16} color={handleColor(phone)} />
|
||||||
|
</Touchable>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={style.actionSheetUsernameContainer}>
|
||||||
|
<AvatarContainer text={user.avatar} size={36} />
|
||||||
|
<StatusContainer size={16} id={user.uid} style={{ marginLeft: 8, marginRight: 6 }} />
|
||||||
|
<Text style={style.actionSheetUsername}>{user.username}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={style.actionSheetPhotoContainer}>
|
||||||
|
<AvatarContainer size={62} text={username} />
|
||||||
|
</View>
|
||||||
|
<Button
|
||||||
|
onPress={() => {
|
||||||
|
hideActionSheet();
|
||||||
|
setTimeout(() => {
|
||||||
|
videoConfStartAndJoin(user.rid, camera);
|
||||||
|
}, 100);
|
||||||
|
}}
|
||||||
|
title={i18n.t('Call')}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Text, View } from 'react-native';
|
||||||
|
|
||||||
|
import i18n from '../../../../i18n';
|
||||||
|
import useStyle from './styles';
|
||||||
|
import AvatarContainer from '../../../Avatar';
|
||||||
|
|
||||||
|
const MAX_USERS = 3;
|
||||||
|
|
||||||
|
export type TCallUsers = { _id: string; username: string; name: string; avatarETag: string }[];
|
||||||
|
|
||||||
|
export const CallParticipants = ({ users }: { users: TCallUsers }): React.ReactElement => {
|
||||||
|
const style = useStyle();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{users.map(({ username }, index) =>
|
||||||
|
index < MAX_USERS ? <AvatarContainer style={{ marginRight: 4 }} key={index} size={28} text={username} /> : null
|
||||||
|
)}
|
||||||
|
{users.length > MAX_USERS ? (
|
||||||
|
<View style={style.plusUsers}>
|
||||||
|
<Text style={style.plusUsersText}>{users.length > 9 ? '+9' : `+${users.length}`}</Text>
|
||||||
|
</View>
|
||||||
|
) : null}
|
||||||
|
<Text style={style.joined}>{i18n.t('Joined')}</Text>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,55 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { View, Text } from 'react-native';
|
||||||
|
|
||||||
|
import i18n from '../../../../i18n';
|
||||||
|
import { useTheme } from '../../../../theme';
|
||||||
|
import { CustomIcon, TIconsName } from '../../../CustomIcon';
|
||||||
|
import useStyle from './styles';
|
||||||
|
|
||||||
|
type VideoConfMessageIconProps = {
|
||||||
|
variant: 'ended' | 'incoming' | 'outgoing';
|
||||||
|
children: React.ReactElement | React.ReactElement[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const VideoConferenceBaseContainer = ({ variant, children }: VideoConfMessageIconProps): React.ReactElement => {
|
||||||
|
const { colors } = useTheme();
|
||||||
|
const style = useStyle();
|
||||||
|
|
||||||
|
const iconStyle: { [key: string]: { icon: TIconsName; color: string; backgroundColor: string; label: string } } = {
|
||||||
|
ended: {
|
||||||
|
icon: 'phone-end',
|
||||||
|
color: colors.conferenceCallEndedPhoneIcon,
|
||||||
|
backgroundColor: colors.conferenceCallEndedPhoneBackground,
|
||||||
|
label: i18n.t('Call_ended')
|
||||||
|
},
|
||||||
|
incoming: {
|
||||||
|
icon: 'phone-in',
|
||||||
|
color: colors.conferenceCallIncomingPhoneIcon,
|
||||||
|
backgroundColor: colors.conferenceCallIncomingPhoneBackground,
|
||||||
|
label: i18n.t('Calling')
|
||||||
|
},
|
||||||
|
outgoing: {
|
||||||
|
icon: 'phone',
|
||||||
|
color: colors.conferenceCallOngoingPhoneIcon,
|
||||||
|
backgroundColor: colors.conferenceCallOngoingPhoneBackground,
|
||||||
|
label: i18n.t('Call_ongoing')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={style.container}>
|
||||||
|
<View style={style.callInfoContainer}>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
...style.iconContainer,
|
||||||
|
backgroundColor: iconStyle[variant].backgroundColor
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CustomIcon name={iconStyle[variant].icon} size={variant === 'incoming' ? 16 : 24} color={iconStyle[variant].color} />
|
||||||
|
</View>
|
||||||
|
<Text style={style.infoContainerText}>{iconStyle[variant].label}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={style.callToActionContainer}>{children}</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Text } from 'react-native';
|
||||||
|
import Touchable from 'react-native-platform-touchable';
|
||||||
|
|
||||||
|
import i18n from '../../../../i18n';
|
||||||
|
import { useVideoConf } from '../../../../lib/hooks/useVideoConf';
|
||||||
|
import useStyle from './styles';
|
||||||
|
import { VideoConferenceBaseContainer } from './VideoConferenceBaseContainer';
|
||||||
|
|
||||||
|
const VideoConferenceDirect = React.memo(({ blockId }: { blockId: string }) => {
|
||||||
|
const style = useStyle();
|
||||||
|
const { joinCall } = useVideoConf();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VideoConferenceBaseContainer variant='incoming'>
|
||||||
|
<Touchable style={style.callToActionButton} onPress={() => joinCall(blockId)}>
|
||||||
|
<Text style={style.callToActionButtonText}>{i18n.t('Join')}</Text>
|
||||||
|
</Touchable>
|
||||||
|
<Text style={style.callBack}>{i18n.t('Waiting_for_answer')}</Text>
|
||||||
|
</VideoConferenceBaseContainer>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default VideoConferenceDirect;
|
|
@ -0,0 +1,64 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Text } from 'react-native';
|
||||||
|
import Touchable from 'react-native-platform-touchable';
|
||||||
|
|
||||||
|
import { IUser } from '../../../../definitions';
|
||||||
|
import { VideoConferenceType } from '../../../../definitions/IVideoConference';
|
||||||
|
import i18n from '../../../../i18n';
|
||||||
|
import { useAppSelector } from '../../../../lib/hooks';
|
||||||
|
import { useSnaps } from '../../../../lib/hooks/useSnaps';
|
||||||
|
import { useActionSheet } from '../../../ActionSheet';
|
||||||
|
import CallAgainActionSheet from './CallAgainActionSheet';
|
||||||
|
import { CallParticipants, TCallUsers } from './CallParticipants';
|
||||||
|
import useStyle from './styles';
|
||||||
|
import { VideoConferenceBaseContainer } from './VideoConferenceBaseContainer';
|
||||||
|
|
||||||
|
export default function VideoConferenceEnded({
|
||||||
|
users,
|
||||||
|
type,
|
||||||
|
createdBy,
|
||||||
|
rid
|
||||||
|
}: {
|
||||||
|
users: TCallUsers;
|
||||||
|
type: VideoConferenceType;
|
||||||
|
createdBy: Pick<IUser, '_id' | 'username' | 'name'>;
|
||||||
|
rid: string;
|
||||||
|
}): React.ReactElement {
|
||||||
|
const style = useStyle();
|
||||||
|
const username = useAppSelector(state => state.login.user.username);
|
||||||
|
const { showActionSheet } = useActionSheet();
|
||||||
|
const snaps = useSnaps([1250]);
|
||||||
|
|
||||||
|
const onlyAuthorOnCall = users.length === 1 && users.some(user => user.username === createdBy.username);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VideoConferenceBaseContainer variant='ended'>
|
||||||
|
{type === 'direct' ? (
|
||||||
|
<>
|
||||||
|
<Touchable
|
||||||
|
style={style.callToActionCallBack}
|
||||||
|
onPress={() =>
|
||||||
|
showActionSheet({
|
||||||
|
children: <CallAgainActionSheet rid={rid} />,
|
||||||
|
snaps
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Text style={style.callToActionCallBackText}>
|
||||||
|
{createdBy.username === username ? i18n.t('Call_back') : i18n.t('Call_again')}
|
||||||
|
</Text>
|
||||||
|
</Touchable>
|
||||||
|
<Text style={style.callBack}>{i18n.t('Call_was_not_answered')}</Text>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{users.length && !onlyAuthorOnCall ? (
|
||||||
|
<CallParticipants users={users} />
|
||||||
|
) : (
|
||||||
|
<Text style={style.notAnswered}>{i18n.t('Call_was_not_answered')}</Text>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</VideoConferenceBaseContainer>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Text } from 'react-native';
|
||||||
|
import Touchable from 'react-native-platform-touchable';
|
||||||
|
|
||||||
|
import i18n from '../../../../i18n';
|
||||||
|
import { useVideoConf } from '../../../../lib/hooks/useVideoConf';
|
||||||
|
import { CallParticipants, TCallUsers } from './CallParticipants';
|
||||||
|
import useStyle from './styles';
|
||||||
|
import { VideoConferenceBaseContainer } from './VideoConferenceBaseContainer';
|
||||||
|
|
||||||
|
export default function VideoConferenceOutgoing({ users, blockId }: { users: TCallUsers; blockId: string }): React.ReactElement {
|
||||||
|
const style = useStyle();
|
||||||
|
const { joinCall } = useVideoConf();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VideoConferenceBaseContainer variant='outgoing'>
|
||||||
|
<Touchable style={style.callToActionButton} onPress={() => joinCall(blockId)}>
|
||||||
|
<Text style={style.callToActionButtonText}>{i18n.t('Join')}</Text>
|
||||||
|
</Touchable>
|
||||||
|
<CallParticipants users={users} />
|
||||||
|
</VideoConferenceBaseContainer>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import React from 'react';
|
||||||
|
import SkeletonPlaceholder from 'react-native-skeleton-placeholder';
|
||||||
|
|
||||||
|
import { useTheme } from '../../../../theme';
|
||||||
|
|
||||||
|
export default function VideoConferenceSkeletonLoading(): React.ReactElement {
|
||||||
|
const { colors } = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SkeletonPlaceholder backgroundColor={colors.conferenceCallBackground}>
|
||||||
|
<SkeletonPlaceholder.Item borderWidth={1} borderColor={colors.conferenceCallBorder} borderRadius={4} marginTop={8}>
|
||||||
|
<SkeletonPlaceholder.Item alignItems={'center'} flexDirection='row' marginTop={16} marginLeft={16}>
|
||||||
|
<SkeletonPlaceholder.Item width={28} height={26} />
|
||||||
|
<SkeletonPlaceholder.Item width={75} height={16} marginLeft={8} borderRadius={0} />
|
||||||
|
</SkeletonPlaceholder.Item>
|
||||||
|
<SkeletonPlaceholder.Item
|
||||||
|
width={'100%'}
|
||||||
|
height={48}
|
||||||
|
marginTop={16}
|
||||||
|
borderBottomLeftRadius={4}
|
||||||
|
borderBottomRightRadius={4}
|
||||||
|
borderTopLeftRadius={0}
|
||||||
|
borderTopRightRadius={0}
|
||||||
|
/>
|
||||||
|
</SkeletonPlaceholder.Item>
|
||||||
|
</SkeletonPlaceholder>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
import { useTheme } from '../../../../theme';
|
||||||
|
import sharedStyles from '../../../../views/Styles';
|
||||||
|
|
||||||
|
export default function useStyle() {
|
||||||
|
const { colors } = useTheme();
|
||||||
|
return StyleSheet.create({
|
||||||
|
container: { height: 108, flex: 1, borderWidth: 1, borderRadius: 4, marginTop: 8, borderColor: colors.conferenceCallBorder },
|
||||||
|
callInfoContainer: { flex: 1, alignItems: 'center', paddingLeft: 16, flexDirection: 'row' },
|
||||||
|
infoContainerText: {
|
||||||
|
fontSize: 12,
|
||||||
|
marginLeft: 8,
|
||||||
|
...sharedStyles.textBold,
|
||||||
|
color: colors.auxiliaryTintColor
|
||||||
|
},
|
||||||
|
iconContainer: {
|
||||||
|
width: 28,
|
||||||
|
height: 28,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
borderRadius: 4
|
||||||
|
},
|
||||||
|
callToActionContainer: {
|
||||||
|
height: 48,
|
||||||
|
backgroundColor: colors.conferenceCallBackground,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingLeft: 16
|
||||||
|
},
|
||||||
|
callToActionButtonText: {
|
||||||
|
fontSize: 12,
|
||||||
|
...sharedStyles.textSemibold,
|
||||||
|
color: colors.buttonText
|
||||||
|
},
|
||||||
|
callToActionCallBackText: {
|
||||||
|
fontSize: 12,
|
||||||
|
...sharedStyles.textSemibold,
|
||||||
|
color: colors.conferenceCallCallBackText
|
||||||
|
},
|
||||||
|
callToActionButton: {
|
||||||
|
backgroundColor: colors.tintColor,
|
||||||
|
minWidth: 50,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
height: 32,
|
||||||
|
borderRadius: 4,
|
||||||
|
marginRight: 8,
|
||||||
|
paddingHorizontal: 8
|
||||||
|
},
|
||||||
|
joined: {
|
||||||
|
fontSize: 12,
|
||||||
|
...sharedStyles.textRegular,
|
||||||
|
color: colors.passcodeSecondary,
|
||||||
|
marginLeft: 8
|
||||||
|
},
|
||||||
|
plusUsers: {
|
||||||
|
width: 28,
|
||||||
|
height: 28,
|
||||||
|
backgroundColor: colors.conferenceCallPlusUsersButton,
|
||||||
|
borderRadius: 4,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center'
|
||||||
|
},
|
||||||
|
plusUsersText: {
|
||||||
|
fontSize: 14,
|
||||||
|
...sharedStyles.textSemibold,
|
||||||
|
color: colors.conferenceCallPlusUsersText,
|
||||||
|
alignSelf: 'center'
|
||||||
|
},
|
||||||
|
callBack: {
|
||||||
|
fontSize: 12,
|
||||||
|
...sharedStyles.textRegular,
|
||||||
|
color: colors.passcodeSecondary
|
||||||
|
},
|
||||||
|
callToActionCallBack: {
|
||||||
|
backgroundColor: colors.conferenceCallPlusUsersButton,
|
||||||
|
minWidth: 50,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
height: 32,
|
||||||
|
borderRadius: 4,
|
||||||
|
marginRight: 8,
|
||||||
|
paddingHorizontal: 8
|
||||||
|
},
|
||||||
|
notAnswered: {
|
||||||
|
fontSize: 12,
|
||||||
|
...sharedStyles.textRegular,
|
||||||
|
color: colors.passcodeSecondary
|
||||||
|
},
|
||||||
|
actionSheetContainer: {
|
||||||
|
paddingHorizontal: 24,
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
actionSheetHeaderTitle: {
|
||||||
|
fontSize: 14,
|
||||||
|
...sharedStyles.textBold,
|
||||||
|
color: colors.passcodePrimary
|
||||||
|
},
|
||||||
|
actionSheetUsername: {
|
||||||
|
fontSize: 16,
|
||||||
|
...sharedStyles.textBold,
|
||||||
|
color: colors.passcodePrimary
|
||||||
|
},
|
||||||
|
enabledBackground: {
|
||||||
|
backgroundColor: colors.conferenceCallEnabledIconBackground
|
||||||
|
},
|
||||||
|
iconCallContainer: {
|
||||||
|
padding: 6,
|
||||||
|
borderRadius: 4
|
||||||
|
},
|
||||||
|
actionSheetHeader: { flexDirection: 'row', alignItems: 'center' },
|
||||||
|
actionSheetHeaderButtons: { flex: 1, alignItems: 'center', flexDirection: 'row', justifyContent: 'flex-end' },
|
||||||
|
actionSheetUsernameContainer: { flexDirection: 'row', paddingTop: 8, alignItems: 'center' },
|
||||||
|
actionSheetPhotoContainer: {
|
||||||
|
height: 220,
|
||||||
|
width: 148,
|
||||||
|
backgroundColor: colors.conferenceCallPhotoBackground,
|
||||||
|
borderRadius: 8,
|
||||||
|
margin: 24,
|
||||||
|
alignSelf: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { useEndpointData } from '../../../lib/hooks/useEndpointData';
|
||||||
|
import VideoConferenceDirect from './components/VideoConferenceDirect';
|
||||||
|
import VideoConferenceEnded from './components/VideoConferenceEnded';
|
||||||
|
import VideoConferenceOutgoing from './components/VideoConferenceOutgoing';
|
||||||
|
import VideoConferenceSkeletonLoading from './components/VideoConferenceSkeletonLoading';
|
||||||
|
|
||||||
|
export default function VideoConferenceBlock({ callId, blockId }: { callId: string; blockId: string }): React.ReactElement {
|
||||||
|
const { result } = useEndpointData('video-conference.info', { callId });
|
||||||
|
|
||||||
|
if (result?.success) {
|
||||||
|
const { users, type, status, createdBy, rid } = result;
|
||||||
|
|
||||||
|
if ('endedAt' in result) return <VideoConferenceEnded createdBy={createdBy} rid={rid} type={type} users={users} />;
|
||||||
|
|
||||||
|
if (type === 'direct' && status === 0) return <VideoConferenceDirect blockId={blockId} />;
|
||||||
|
|
||||||
|
return <VideoConferenceOutgoing blockId={blockId} users={users} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <VideoConferenceSkeletonLoading />;
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ import { DatePicker } from './DatePicker';
|
||||||
import { Overflow } from './Overflow';
|
import { Overflow } from './Overflow';
|
||||||
import { ThemeContext } from '../../theme';
|
import { ThemeContext } from '../../theme';
|
||||||
import { IActions, IButton, IElement, IInputIndex, IParser, ISection } from './interfaces';
|
import { IActions, IButton, IElement, IInputIndex, IParser, ISection } from './interfaces';
|
||||||
|
import VideoConferenceBlock from './VideoConferenceBlock';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
input: {
|
input: {
|
||||||
|
@ -149,6 +150,10 @@ class MessageParser extends UiKitParserMessage<React.ReactElement> {
|
||||||
const [{ loading, value }, action] = useBlockContext(element, context);
|
const [{ loading, value }, action] = useBlockContext(element, context);
|
||||||
return <MultiSelect {...element} value={value} onChange={action} context={context} loading={loading} />;
|
return <MultiSelect {...element} value={value} onChange={action} context={context} loading={loading} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
video_conf(element: IElement & { callId: string }) {
|
||||||
|
return <VideoConferenceBlock callId={element.callId} blockId={element.blockId!} />;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// plain_text and mrkdwn functions are created in MessageParser and the ModalParser's constructor use the same functions
|
// plain_text and mrkdwn functions are created in MessageParser and the ModalParser's constructor use the same functions
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
/* eslint-disable no-shadow */
|
/* eslint-disable no-shadow */
|
||||||
import React, { useContext, useState } from 'react';
|
|
||||||
import { BlockContext } from '@rocket.chat/ui-kit';
|
import { BlockContext } from '@rocket.chat/ui-kit';
|
||||||
|
import React, { useContext, useState } from 'react';
|
||||||
|
|
||||||
|
import { useVideoConf } from '../../lib/hooks/useVideoConf';
|
||||||
import { IText } from './interfaces';
|
import { IText } from './interfaces';
|
||||||
import { videoConfJoin } from '../../lib/methods/videoConf';
|
|
||||||
import { TActionSheetOptionsItem, useActionSheet } from '../ActionSheet';
|
|
||||||
import i18n from '../../i18n';
|
|
||||||
|
|
||||||
export const textParser = ([{ text }]: IText[]) => text;
|
export const textParser = ([{ text }]: IText[]) => text;
|
||||||
|
|
||||||
|
@ -42,7 +40,7 @@ export const useBlockContext = ({ blockId, actionId, appId, initialValue }: IUse
|
||||||
const { action, appId: appIdFromContext, viewId, state, language, errors, values = {} } = useContext(KitContext);
|
const { action, appId: appIdFromContext, viewId, state, language, errors, values = {} } = useContext(KitContext);
|
||||||
const { value = initialValue } = values[actionId] || {};
|
const { value = initialValue } = values[actionId] || {};
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const { showActionSheet } = useActionSheet();
|
const { joinCall } = useVideoConf();
|
||||||
|
|
||||||
const error = errors && actionId && errors[actionId];
|
const error = errors && actionId && errors[actionId];
|
||||||
|
|
||||||
|
@ -60,20 +58,7 @@ export const useBlockContext = ({ blockId, actionId, appId, initialValue }: IUse
|
||||||
try {
|
try {
|
||||||
if (appId === 'videoconf-core' && blockId) {
|
if (appId === 'videoconf-core' && blockId) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
const options: TActionSheetOptionsItem[] = [
|
return joinCall(blockId);
|
||||||
{
|
|
||||||
title: i18n.t('Video_call'),
|
|
||||||
icon: 'camera',
|
|
||||||
onPress: () => videoConfJoin(blockId, true)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: i18n.t('Voice_call'),
|
|
||||||
icon: 'microphone',
|
|
||||||
onPress: () => videoConfJoin(blockId, false)
|
|
||||||
}
|
|
||||||
];
|
|
||||||
showActionSheet({ options });
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
await action({
|
await action({
|
||||||
blockId,
|
blockId,
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleProp, Text, TextStyle } from 'react-native';
|
import { StyleProp, Text, TextStyle } from 'react-native';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
|
||||||
import { StackNavigationProp } from '@react-navigation/stack';
|
|
||||||
|
|
||||||
import { themes } from '../../lib/constants';
|
import { themes } from '../../lib/constants';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { IUserChannel } from './interfaces';
|
import { IUserChannel } from './interfaces';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { getSubscriptionByRoomId } from '../../lib/database/services/Subscription';
|
import { getSubscriptionByRoomId } from '../../lib/database/services/Subscription';
|
||||||
import { ChatsStackParamList } from '../../stacks/types';
|
|
||||||
import { useAppSelector } from '../../lib/hooks';
|
import { useAppSelector } from '../../lib/hooks';
|
||||||
import { goRoom } from '../../lib/methods/helpers/goRoom';
|
import { goRoom } from '../../lib/methods/helpers/goRoom';
|
||||||
|
|
||||||
|
@ -22,7 +19,6 @@ interface IHashtag {
|
||||||
const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IHashtag) => {
|
const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IHashtag) => {
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
|
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
|
||||||
const navigation = useNavigation<StackNavigationProp<ChatsStackParamList, 'RoomView'>>();
|
|
||||||
|
|
||||||
const handlePress = async () => {
|
const handlePress = async () => {
|
||||||
const index = channels?.findIndex(channel => channel.name === hashtag);
|
const index = channels?.findIndex(channel => channel.name === hashtag);
|
||||||
|
@ -33,7 +29,7 @@ const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IH
|
||||||
};
|
};
|
||||||
const room = navParam.rid && (await getSubscriptionByRoomId(navParam.rid));
|
const room = navParam.rid && (await getSubscriptionByRoomId(navParam.rid));
|
||||||
if (room) {
|
if (room) {
|
||||||
goRoom({ item: room, isMasterDetail, navigationMethod: isMasterDetail ? navigation.replace : navigation.push });
|
goRoom({ item: room, isMasterDetail });
|
||||||
} else {
|
} else {
|
||||||
navToRoomInfo(navParam);
|
navToRoomInfo(navParam);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react';
|
import React, { useRef } from 'react';
|
||||||
|
|
||||||
import { messageBlockWithContext } from '../UIKit/MessageBlock';
|
import { messageBlockWithContext } from '../UIKit/MessageBlock';
|
||||||
import { IMessageBlocks } from './interfaces';
|
import { IMessageBlocks } from './interfaces';
|
||||||
|
@ -6,25 +6,29 @@ import { IMessageBlocks } from './interfaces';
|
||||||
const Blocks = React.memo(({ blocks, id: mid, rid, blockAction }: IMessageBlocks) => {
|
const Blocks = React.memo(({ blocks, id: mid, rid, blockAction }: IMessageBlocks) => {
|
||||||
if (blocks && blocks.length > 0) {
|
if (blocks && blocks.length > 0) {
|
||||||
const appId = blocks[0]?.appId || '';
|
const appId = blocks[0]?.appId || '';
|
||||||
return React.createElement(
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
messageBlockWithContext({
|
const comp = useRef(
|
||||||
action: async ({ actionId, value, blockId }: { actionId: string; value: string; blockId: string }) => {
|
React.createElement(
|
||||||
if (blockAction) {
|
messageBlockWithContext({
|
||||||
await blockAction({
|
action: async ({ actionId, value, blockId }: { actionId: string; value: string; blockId: string }) => {
|
||||||
actionId,
|
if (blockAction) {
|
||||||
appId,
|
await blockAction({
|
||||||
value,
|
actionId,
|
||||||
blockId,
|
appId,
|
||||||
rid,
|
value,
|
||||||
mid
|
blockId,
|
||||||
});
|
rid,
|
||||||
}
|
mid
|
||||||
},
|
});
|
||||||
appId,
|
}
|
||||||
rid
|
},
|
||||||
}),
|
appId,
|
||||||
{ blocks }
|
rid
|
||||||
|
}),
|
||||||
|
{ blocks }
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
return comp.current;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
|
@ -233,7 +233,9 @@ class MessageContainer extends React.Component<IMessageContainerProps, IMessageC
|
||||||
!(previousItem.groupable === false || item.groupable === false || broadcast === true) &&
|
!(previousItem.groupable === false || item.groupable === false || broadcast === true) &&
|
||||||
// @ts-ignore TODO: IMessage vs IMessageFromServer non-sense
|
// @ts-ignore TODO: IMessage vs IMessageFromServer non-sense
|
||||||
item.ts - previousItem.ts < Message_GroupingPeriod * 1000 &&
|
item.ts - previousItem.ts < Message_GroupingPeriod * 1000 &&
|
||||||
previousItem.tmid === item.tmid
|
previousItem.tmid === item.tmid &&
|
||||||
|
item.t !== 'rm' &&
|
||||||
|
previousItem.t !== 'rm'
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -270,7 +272,7 @@ class MessageContainer extends React.Component<IMessageContainerProps, IMessageC
|
||||||
|
|
||||||
get isInfo(): string | boolean {
|
get isInfo(): string | boolean {
|
||||||
const { item } = this.props;
|
const { item } = this.props;
|
||||||
if (['e2e', 'discussion-created', 'jitsi_call_started'].includes(item.t)) {
|
if (['e2e', 'discussion-created', 'jitsi_call_started', 'videoconf'].includes(item.t)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return item.t;
|
return item.t;
|
||||||
|
@ -405,8 +407,7 @@ class MessageContainer extends React.Component<IMessageContainerProps, IMessageC
|
||||||
threadBadgeColor,
|
threadBadgeColor,
|
||||||
toggleFollowThread,
|
toggleFollowThread,
|
||||||
replies
|
replies
|
||||||
}}
|
}}>
|
||||||
>
|
|
||||||
{/* @ts-ignore*/}
|
{/* @ts-ignore*/}
|
||||||
<Message
|
<Message
|
||||||
id={id}
|
id={id}
|
||||||
|
|
|
@ -65,6 +65,7 @@ const messagesWithAuthorName: MessageTypesValues[] = [
|
||||||
'room_changed_avatar',
|
'room_changed_avatar',
|
||||||
'room_e2e_disabled',
|
'room_e2e_disabled',
|
||||||
'room_e2e_enabled',
|
'room_e2e_enabled',
|
||||||
|
'room-allowed-reacting',
|
||||||
'room-disallowed-reacting',
|
'room-disallowed-reacting',
|
||||||
'room-set-read-only',
|
'room-set-read-only',
|
||||||
'room-removed-read-only',
|
'room-removed-read-only',
|
||||||
|
|
|
@ -222,6 +222,7 @@ export type MessageTypesValues =
|
||||||
| 'room-allowed-reacting'
|
| 'room-allowed-reacting'
|
||||||
| 'room-disallowed-reacting'
|
| 'room-disallowed-reacting'
|
||||||
| 'command'
|
| 'command'
|
||||||
|
| 'videoconf'
|
||||||
| LivechatMessageTypes
|
| LivechatMessageTypes
|
||||||
| TeamMessageTypes
|
| TeamMessageTypes
|
||||||
| VoipMessageTypesValues
|
| VoipMessageTypesValues
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
import type { AtLeast } from './utils';
|
||||||
|
import type { IRocketChatRecord } from './IRocketChatRecord';
|
||||||
|
import type { IRoom } from './IRoom';
|
||||||
|
import type { IUser } from './IUser';
|
||||||
|
import type { IMessage } from './IMessage';
|
||||||
|
|
||||||
|
export enum VideoConferenceStatus {
|
||||||
|
CALLING = 0,
|
||||||
|
STARTED = 1,
|
||||||
|
EXPIRED = 2,
|
||||||
|
ENDED = 3,
|
||||||
|
DECLINED = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DirectCallInstructions = {
|
||||||
|
type: 'direct';
|
||||||
|
callee: IUser['_id'];
|
||||||
|
callId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ConferenceInstructions = {
|
||||||
|
type: 'videoconference';
|
||||||
|
callId: string;
|
||||||
|
rid: IRoom['_id'];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LivechatInstructions = {
|
||||||
|
type: 'livechat';
|
||||||
|
callId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type VideoConferenceType = DirectCallInstructions['type'] | ConferenceInstructions['type'] | LivechatInstructions['type'];
|
||||||
|
|
||||||
|
export interface IVideoConferenceUser extends Pick<Required<IUser>, '_id' | 'username' | 'name' | 'avatarETag'> {
|
||||||
|
ts: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVideoConference extends IRocketChatRecord {
|
||||||
|
type: VideoConferenceType;
|
||||||
|
rid: string;
|
||||||
|
users: IVideoConferenceUser[];
|
||||||
|
status: VideoConferenceStatus;
|
||||||
|
messages: {
|
||||||
|
started?: IMessage['_id'];
|
||||||
|
ended?: IMessage['_id'];
|
||||||
|
};
|
||||||
|
url?: string;
|
||||||
|
|
||||||
|
createdBy: Pick<IUser, '_id' | 'username' | 'name'>;
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
endedBy?: Pick<IUser, '_id' | 'username' | 'name'>;
|
||||||
|
endedAt?: Date;
|
||||||
|
|
||||||
|
providerName: string;
|
||||||
|
providerData?: Record<string, any>;
|
||||||
|
|
||||||
|
ringing?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDirectVideoConference extends IVideoConference {
|
||||||
|
type: 'direct';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IGroupVideoConference extends IVideoConference {
|
||||||
|
type: 'videoconference';
|
||||||
|
anonymousUsers: number;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ILivechatVideoConference extends IVideoConference {
|
||||||
|
type: 'livechat';
|
||||||
|
}
|
||||||
|
|
||||||
|
export type VideoConference = IDirectVideoConference | IGroupVideoConference | ILivechatVideoConference;
|
||||||
|
|
||||||
|
export type VideoConferenceInstructions = DirectCallInstructions | ConferenceInstructions | LivechatInstructions;
|
||||||
|
|
||||||
|
export const isDirectVideoConference = (call: VideoConference | undefined | null): call is IDirectVideoConference =>
|
||||||
|
call?.type === 'direct';
|
||||||
|
|
||||||
|
export const isGroupVideoConference = (call: VideoConference | undefined | null): call is IGroupVideoConference =>
|
||||||
|
call?.type === 'videoconference';
|
||||||
|
|
||||||
|
export const isLivechatVideoConference = (call: VideoConference | undefined | null): call is ILivechatVideoConference =>
|
||||||
|
call?.type === 'livechat';
|
||||||
|
|
||||||
|
type GroupVideoConferenceCreateData = Omit<IGroupVideoConference, 'createdBy'> & { createdBy: IUser['_id'] };
|
||||||
|
type DirectVideoConferenceCreateData = Omit<IDirectVideoConference, 'createdBy'> & { createdBy: IUser['_id'] };
|
||||||
|
type LivechatVideoConferenceCreateData = Omit<ILivechatVideoConference, 'createdBy'> & { createdBy: IUser['_id'] };
|
||||||
|
|
||||||
|
export type VideoConferenceCreateData = AtLeast<
|
||||||
|
DirectVideoConferenceCreateData | GroupVideoConferenceCreateData | LivechatVideoConferenceCreateData,
|
||||||
|
'createdBy' | 'type' | 'rid' | 'providerName' | 'providerData'
|
||||||
|
>;
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { VideoConference } from '../../IVideoConference';
|
||||||
|
|
||||||
export type VideoConferenceEndpoints = {
|
export type VideoConferenceEndpoints = {
|
||||||
'video-conference/jitsi.update-timeout': {
|
'video-conference/jitsi.update-timeout': {
|
||||||
POST: (params: { roomId: string }) => void;
|
POST: (params: { roomId: string }) => void;
|
||||||
|
@ -8,4 +10,18 @@ export type VideoConferenceEndpoints = {
|
||||||
'video-conference.start': {
|
'video-conference.start': {
|
||||||
POST: (params: { roomId: string }) => { url: string };
|
POST: (params: { roomId: string }) => { url: string };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
'video-conference.cancel': {
|
||||||
|
POST: (params: { callId: string }) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
'video-conference.info': {
|
||||||
|
GET: (params: { callId: string }) => VideoConference & {
|
||||||
|
capabilities: {
|
||||||
|
mic?: boolean;
|
||||||
|
cam?: boolean;
|
||||||
|
title?: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
export type Optional<T, K extends keyof T> = Omit<T, K> & Partial<T>;
|
||||||
|
|
||||||
|
export type ExtractKeys<T, K extends keyof T, U> = T[K] extends U ? K : never;
|
||||||
|
|
||||||
|
export type ValueOf<T> = T[keyof T];
|
||||||
|
|
||||||
|
export type UnionToIntersection<T> = (T extends any ? (x: T) => void : never) extends (x: infer U) => void ? U : never;
|
||||||
|
|
||||||
|
export type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;
|
||||||
|
|
||||||
|
// `T extends any` is a trick to apply a operator to each member of a union
|
||||||
|
export type KeyOfEach<T> = T extends any ? keyof T : never;
|
||||||
|
|
||||||
|
// Taken from https://effectivetypescript.com/2020/04/09/jsonify/
|
||||||
|
export type Jsonify<T> = T extends Date
|
||||||
|
? string
|
||||||
|
: T extends object
|
||||||
|
? {
|
||||||
|
[k in keyof T]: Jsonify<T[k]>;
|
||||||
|
}
|
||||||
|
: T;
|
||||||
|
|
||||||
|
export type AtLeast<T, K extends keyof T> = Partial<T> & Pick<T, K>;
|
||||||
|
|
||||||
|
export type RequiredField<T, K extends keyof T> = T & Required<Pick<T, K>>;
|
|
@ -73,19 +73,14 @@ const QueueListView = React.memo(() => {
|
||||||
|
|
||||||
const onPressItem = (item = {} as IOmnichannelRoom) => {
|
const onPressItem = (item = {} as IOmnichannelRoom) => {
|
||||||
logEvent(events.QL_GO_ROOM);
|
logEvent(events.QL_GO_ROOM);
|
||||||
if (isMasterDetail) {
|
|
||||||
navigation.navigate('DrawerNavigator');
|
|
||||||
} else {
|
|
||||||
navigation.navigate('RoomsListView');
|
|
||||||
}
|
|
||||||
|
|
||||||
goRoom({
|
goRoom({
|
||||||
item: {
|
item: {
|
||||||
...item,
|
...item,
|
||||||
// we're calling v as visitor on our mergeSubscriptionsRooms
|
// we're calling v as visitor on our mergeSubscriptionsRooms
|
||||||
visitor: item.v
|
visitor: item.v
|
||||||
},
|
},
|
||||||
isMasterDetail
|
isMasterDetail,
|
||||||
|
popToRoot: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -862,6 +862,16 @@
|
||||||
"Select_Members": "Select Members",
|
"Select_Members": "Select Members",
|
||||||
"Also_send_thread_message_to_channel_behavior": "Also send thread message to channel",
|
"Also_send_thread_message_to_channel_behavior": "Also send thread message to channel",
|
||||||
"Accounts_Default_User_Preferences_alsoSendThreadToChannel_Description": "Allow users to select the Also send to channel behavior",
|
"Accounts_Default_User_Preferences_alsoSendThreadToChannel_Description": "Allow users to select the Also send to channel behavior",
|
||||||
|
"Waiting_for_answer": "Waiting for answer",
|
||||||
|
"Call_ended": "Call ended",
|
||||||
|
"Call_was_not_answered": "Call was not answered",
|
||||||
|
"Call_back": "Call Back",
|
||||||
|
"Call_again": "Call Again",
|
||||||
|
"Call_ongoing": "Call Ongoing",
|
||||||
|
"Joined": "Joined",
|
||||||
|
"Calling": "Calling...",
|
||||||
|
"Start_a_call": "Start a call",
|
||||||
|
"Call": "Call",
|
||||||
"Reply_in_direct_message": "Reply in Direct Message",
|
"Reply_in_direct_message": "Reply in Direct Message",
|
||||||
"room_archived": "archived room",
|
"room_archived": "archived room",
|
||||||
"room_unarchived": "unarchived room"
|
"room_unarchived": "unarchived room"
|
||||||
|
|
|
@ -784,6 +784,16 @@
|
||||||
"Select_Members": "Selecionar Membros",
|
"Select_Members": "Selecionar Membros",
|
||||||
"Also_send_thread_message_to_channel_behavior": "Também enviar mensagem do tópico para o canal",
|
"Also_send_thread_message_to_channel_behavior": "Também enviar mensagem do tópico para o canal",
|
||||||
"Accounts_Default_User_Preferences_alsoSendThreadToChannel_Description": "Permitir que os usuários selecionem o comportamento Também enviar para o canal",
|
"Accounts_Default_User_Preferences_alsoSendThreadToChannel_Description": "Permitir que os usuários selecionem o comportamento Também enviar para o canal",
|
||||||
|
"Waiting_for_answer": "Esperando por resposta",
|
||||||
|
"Call_ended": "Chamada finalizada",
|
||||||
|
"Call_was_not_answered": "A chamada não foi atendida",
|
||||||
|
"Call_back": "Ligue de volta",
|
||||||
|
"Call_again": "Ligue novamente",
|
||||||
|
"Call_ongoing": "Chamada em andamento",
|
||||||
|
"Joined": "Ingressou",
|
||||||
|
"Calling": "Chamando...",
|
||||||
|
"Start_a_call": "Inicie uma chamada",
|
||||||
|
"Call": "Ligar",
|
||||||
"Reply_in_direct_message": "Responder por mensagem direta",
|
"Reply_in_direct_message": "Responder por mensagem direta",
|
||||||
"room_archived": "{{username}} arquivou a sala",
|
"room_archived": "{{username}} arquivou a sala",
|
||||||
"room_unarchived": "{{username}} desarquivou a sala"
|
"room_unarchived": "{{username}} desarquivou a sala"
|
||||||
|
|
|
@ -70,6 +70,22 @@ export const colors = {
|
||||||
collapsibleQuoteBorder: '#CBCED1',
|
collapsibleQuoteBorder: '#CBCED1',
|
||||||
collapsibleChevron: '#6C727A',
|
collapsibleChevron: '#6C727A',
|
||||||
cancelButton: '#E4E7EA',
|
cancelButton: '#E4E7EA',
|
||||||
|
conferenceCallBorder: '#F2F3F5',
|
||||||
|
conferenceCallBackground: '#F7F8FA',
|
||||||
|
conferenceCallOngoingPhoneBackground: '#C0F6E4',
|
||||||
|
conferenceCallIncomingPhoneBackground: '#D1EBFE',
|
||||||
|
conferenceCallEndedPhoneBackground: '#E4E7EA',
|
||||||
|
conferenceCallOngoingPhoneIcon: '#158D65',
|
||||||
|
conferenceCallIncomingPhoneIcon: '#095AD2',
|
||||||
|
conferenceCallEndedPhoneIcon: '#6C727A',
|
||||||
|
conferenceCallPlusUsersButton: '#E4E7EA',
|
||||||
|
conferenceCallPlusUsersText: '#6C727A',
|
||||||
|
conferenceCallCallBackButton: '#EEEFF1',
|
||||||
|
conferenceCallCallBackText: '#1F2329',
|
||||||
|
conferenceCallDisabledIcon: '#6C727A',
|
||||||
|
conferenceCallEnabledIcon: '#FFFFFF',
|
||||||
|
conferenceCallEnabledIconBackground: '#156FF5',
|
||||||
|
conferenceCallPhotoBackground: '#E4E7EA',
|
||||||
textInputSecondaryBackground: '#E4E7EA',
|
textInputSecondaryBackground: '#E4E7EA',
|
||||||
...mentions
|
...mentions
|
||||||
},
|
},
|
||||||
|
@ -123,6 +139,22 @@ export const colors = {
|
||||||
collapsibleQuoteBorder: '#CBCED1',
|
collapsibleQuoteBorder: '#CBCED1',
|
||||||
collapsibleChevron: '#6C727A',
|
collapsibleChevron: '#6C727A',
|
||||||
cancelButton: '#E4E7EA',
|
cancelButton: '#E4E7EA',
|
||||||
|
conferenceCallBorder: '#1F2329',
|
||||||
|
conferenceCallBackground: '#1F2329',
|
||||||
|
conferenceCallOngoingPhoneBackground: '#106D4F',
|
||||||
|
conferenceCallIncomingPhoneBackground: '#D1EBFE',
|
||||||
|
conferenceCallEndedPhoneBackground: '#6C727A',
|
||||||
|
conferenceCallOngoingPhoneIcon: '#F7F8FA',
|
||||||
|
conferenceCallIncomingPhoneIcon: '#095AD2',
|
||||||
|
conferenceCallEndedPhoneIcon: '#F7F8FA',
|
||||||
|
conferenceCallPlusUsersButton: '#2F343D',
|
||||||
|
conferenceCallPlusUsersText: '#9EA2A8',
|
||||||
|
conferenceCallCallBackButton: '#E4E7EA',
|
||||||
|
conferenceCallCallBackText: '#FFFFFF',
|
||||||
|
conferenceCallDisabledIcon: '#6C727A',
|
||||||
|
conferenceCallEnabledIcon: '#FFFFFF',
|
||||||
|
conferenceCallEnabledIconBackground: '#156FF5',
|
||||||
|
conferenceCallPhotoBackground: '#E4E7EA',
|
||||||
textInputSecondaryBackground: '#030b1b', // backgroundColor
|
textInputSecondaryBackground: '#030b1b', // backgroundColor
|
||||||
...mentions
|
...mentions
|
||||||
},
|
},
|
||||||
|
@ -176,6 +208,22 @@ export const colors = {
|
||||||
collapsibleQuoteBorder: '#CBCED1',
|
collapsibleQuoteBorder: '#CBCED1',
|
||||||
collapsibleChevron: '#6C727A',
|
collapsibleChevron: '#6C727A',
|
||||||
cancelButton: '#E4E7EA',
|
cancelButton: '#E4E7EA',
|
||||||
|
conferenceCallBorder: '#1F2329',
|
||||||
|
conferenceCallBackground: '#1F2329',
|
||||||
|
conferenceCallOngoingPhoneBackground: '#106D4F',
|
||||||
|
conferenceCallIncomingPhoneBackground: '#D1EBFE',
|
||||||
|
conferenceCallEndedPhoneBackground: '#6C727A',
|
||||||
|
conferenceCallOngoingPhoneIcon: '#F7F8FA',
|
||||||
|
conferenceCallIncomingPhoneIcon: '#095AD2',
|
||||||
|
conferenceCallEndedPhoneIcon: '#F7F8FA',
|
||||||
|
conferenceCallPlusUsersButton: '#2F343D',
|
||||||
|
conferenceCallPlusUsersText: '#9EA2A8',
|
||||||
|
conferenceCallCallBackButton: '#E4E7EA',
|
||||||
|
conferenceCallCallBackText: '#FFFFFF',
|
||||||
|
conferenceCallDisabledIcon: '#6C727A',
|
||||||
|
conferenceCallEnabledIcon: '#FFFFFF',
|
||||||
|
conferenceCallEnabledIconBackground: '#156FF5',
|
||||||
|
conferenceCallPhotoBackground: '#E4E7EA',
|
||||||
textInputSecondaryBackground: '#000000', // backgroundColor
|
textInputSecondaryBackground: '#000000', // backgroundColor
|
||||||
...mentions
|
...mentions
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { useDimensions } from '@react-native-community/hooks';
|
||||||
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
|
// Not sure if it's worth adding this here in the context of the actionSheet
|
||||||
|
/**
|
||||||
|
* Return the snaps based on the size you pass (aka: Size of action sheet)
|
||||||
|
* @param {Number[]} snaps Sizes you want to pass, pass only one if you want the action sheet to start at a specific size
|
||||||
|
*/
|
||||||
|
export const useSnaps = (snaps: number[]): string[] => {
|
||||||
|
const insets = useSafeAreaInsets();
|
||||||
|
const { screen } = useDimensions();
|
||||||
|
const percentage = insets.bottom + insets.top > 75 ? 110 : 100;
|
||||||
|
return snaps.map(snap => `${((percentage * snap) / (screen.height * screen.scale)).toFixed(2)}%`);
|
||||||
|
};
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
import { TActionSheetOptionsItem, useActionSheet } from '../../containers/ActionSheet';
|
||||||
|
import i18n from '../../i18n';
|
||||||
|
import { videoConfJoin } from '../methods/videoConf';
|
||||||
|
|
||||||
|
export const useVideoConf = (): { joinCall: (blockId: string) => void } => {
|
||||||
|
const { showActionSheet } = useActionSheet();
|
||||||
|
|
||||||
|
const joinCall = useCallback(blockId => {
|
||||||
|
const options: TActionSheetOptionsItem[] = [
|
||||||
|
{
|
||||||
|
title: i18n.t('Video_call'),
|
||||||
|
icon: 'camera',
|
||||||
|
onPress: () => videoConfJoin(blockId, true)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18n.t('Voice_call'),
|
||||||
|
icon: 'microphone',
|
||||||
|
onPress: () => videoConfJoin(blockId, false)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
showActionSheet({ options });
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { joinCall };
|
||||||
|
};
|
|
@ -1,4 +1,5 @@
|
||||||
import { ChatsStackParamList } from '../../../stacks/types';
|
import { CommonActions } from '@react-navigation/native';
|
||||||
|
|
||||||
import Navigation from '../../navigation/appNavigation';
|
import Navigation from '../../navigation/appNavigation';
|
||||||
import { IOmnichannelRoom, SubscriptionType, IVisitor, TSubscriptionModel, ISubscription } from '../../../definitions';
|
import { IOmnichannelRoom, SubscriptionType, IVisitor, TSubscriptionModel, ISubscription } from '../../../definitions';
|
||||||
import { getRoomTitle, getUidDirectMessage } from './helpers';
|
import { getRoomTitle, getUidDirectMessage } from './helpers';
|
||||||
|
@ -19,19 +20,14 @@ export type TGoRoomItem = IGoRoomItem | TSubscriptionModel | ISubscription | IOm
|
||||||
const navigate = ({
|
const navigate = ({
|
||||||
item,
|
item,
|
||||||
isMasterDetail,
|
isMasterDetail,
|
||||||
|
popToRoot,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
item: TGoRoomItem;
|
item: TGoRoomItem;
|
||||||
isMasterDetail: boolean;
|
isMasterDetail: boolean;
|
||||||
navigationMethod?: () => ChatsStackParamList;
|
popToRoot: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
let navigationMethod = props.navigationMethod ?? Navigation.navigate;
|
const routeParams = {
|
||||||
|
|
||||||
if (isMasterDetail) {
|
|
||||||
navigationMethod = Navigation.replace;
|
|
||||||
}
|
|
||||||
|
|
||||||
navigationMethod('RoomView', {
|
|
||||||
rid: item.rid,
|
rid: item.rid,
|
||||||
name: getRoomTitle(item),
|
name: getRoomTitle(item),
|
||||||
t: item.t,
|
t: item.t,
|
||||||
|
@ -40,6 +36,44 @@ const navigate = ({
|
||||||
visitor: item.visitor,
|
visitor: item.visitor,
|
||||||
roomUserId: getUidDirectMessage(item),
|
roomUserId: getUidDirectMessage(item),
|
||||||
...props
|
...props
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isMasterDetail) {
|
||||||
|
if (popToRoot) {
|
||||||
|
Navigation.navigate('DrawerNavigator');
|
||||||
|
}
|
||||||
|
return Navigation.dispatch((state: any) => {
|
||||||
|
const routesRoomView = state.routes.filter((r: any) => r.name !== 'RoomView');
|
||||||
|
return CommonActions.reset({
|
||||||
|
...state,
|
||||||
|
routes: [
|
||||||
|
...routesRoomView,
|
||||||
|
{
|
||||||
|
name: 'RoomView',
|
||||||
|
params: routeParams
|
||||||
|
}
|
||||||
|
],
|
||||||
|
index: routesRoomView.length
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (popToRoot) {
|
||||||
|
Navigation.navigate('RoomsListView');
|
||||||
|
}
|
||||||
|
return Navigation.dispatch((state: any) => {
|
||||||
|
const routesRoomsListView = state.routes.filter((r: any) => r.name === 'RoomsListView');
|
||||||
|
return CommonActions.reset({
|
||||||
|
...state,
|
||||||
|
routes: [
|
||||||
|
...routesRoomsListView,
|
||||||
|
{
|
||||||
|
name: 'RoomView',
|
||||||
|
params: routeParams
|
||||||
|
}
|
||||||
|
],
|
||||||
|
index: routesRoomsListView.length
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -51,13 +85,14 @@ interface IOmnichannelRoomVisitor extends IOmnichannelRoom {
|
||||||
export const goRoom = async ({
|
export const goRoom = async ({
|
||||||
item,
|
item,
|
||||||
isMasterDetail = false,
|
isMasterDetail = false,
|
||||||
|
popToRoot = false,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
item: TGoRoomItem;
|
item: TGoRoomItem;
|
||||||
isMasterDetail: boolean;
|
isMasterDetail: boolean;
|
||||||
navigationMethod?: any;
|
|
||||||
jumpToMessageId?: string;
|
jumpToMessageId?: string;
|
||||||
usedCannedResponse?: string;
|
usedCannedResponse?: string;
|
||||||
|
popToRoot?: boolean;
|
||||||
}): Promise<void> => {
|
}): Promise<void> => {
|
||||||
if (!('id' in item) && item.t === SubscriptionType.DIRECT && item?.search) {
|
if (!('id' in item) && item.t === SubscriptionType.DIRECT && item?.search) {
|
||||||
// if user is using the search we need first to join/create room
|
// if user is using the search we need first to join/create room
|
||||||
|
@ -72,6 +107,7 @@ export const goRoom = async ({
|
||||||
t: SubscriptionType.DIRECT
|
t: SubscriptionType.DIRECT
|
||||||
},
|
},
|
||||||
isMasterDetail,
|
isMasterDetail,
|
||||||
|
popToRoot,
|
||||||
...props
|
...props
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -80,5 +116,5 @@ export const goRoom = async ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return navigate({ item, isMasterDetail, ...props });
|
return navigate({ item, isMasterDetail, popToRoot, ...props });
|
||||||
};
|
};
|
||||||
|
|
|
@ -125,8 +125,8 @@ export default class RoomSubscription {
|
||||||
if (ev === 'typing') {
|
if (ev === 'typing') {
|
||||||
const { user } = reduxStore.getState().login;
|
const { user } = reduxStore.getState().login;
|
||||||
const { UI_Use_Real_Name } = reduxStore.getState().settings;
|
const { UI_Use_Real_Name } = reduxStore.getState().settings;
|
||||||
const { rooms } = reduxStore.getState().room;
|
const { subscribedRoom } = reduxStore.getState().room;
|
||||||
if (rooms[0] !== _rid) {
|
if (subscribedRoom !== _rid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const [name, typing] = ddpMessage.fields.args;
|
const [name, typing] = ddpMessage.fields.args;
|
||||||
|
|
|
@ -197,8 +197,8 @@ const createOrUpdateSubscription = async (subscription: ISubscription, room: ISe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { rooms } = store.getState().room;
|
const { subscribedRoom } = store.getState().room;
|
||||||
if (tmp.lastMessage && !rooms.includes(tmp.rid)) {
|
if (tmp.lastMessage && subscribedRoom !== tmp.rid) {
|
||||||
const lastMessage = buildMessage(tmp.lastMessage);
|
const lastMessage = buildMessage(tmp.lastMessage);
|
||||||
const messagesCollection = db.get('messages');
|
const messagesCollection = db.get('messages');
|
||||||
let messageRecord = {} as TMessageModel | null;
|
let messageRecord = {} as TMessageModel | null;
|
||||||
|
|
|
@ -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 });
|
||||||
|
|
|
@ -21,11 +21,16 @@ function popToTop() {
|
||||||
navigationRef.current?.dispatch(StackActions.popToTop());
|
navigationRef.current?.dispatch(StackActions.popToTop());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dispatch(params: any) {
|
||||||
|
navigationRef.current?.dispatch(params);
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
navigationRef,
|
navigationRef,
|
||||||
routeNameRef,
|
routeNameRef,
|
||||||
navigate,
|
navigate,
|
||||||
back,
|
back,
|
||||||
replace,
|
replace,
|
||||||
popToTop
|
popToTop,
|
||||||
|
dispatch
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,13 +12,13 @@ describe('test room reducer', () => {
|
||||||
it('should return modified store after subscribeRoom', () => {
|
it('should return modified store after subscribeRoom', () => {
|
||||||
mockedStore.dispatch(subscribeRoom('GENERAL'));
|
mockedStore.dispatch(subscribeRoom('GENERAL'));
|
||||||
const state = mockedStore.getState().room;
|
const state = mockedStore.getState().room;
|
||||||
expect(state.rooms).toEqual(['GENERAL']);
|
expect(state.subscribedRoom).toEqual('GENERAL');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return empty store after remove unsubscribeRoom', () => {
|
it('should return empty store after remove unsubscribeRoom', () => {
|
||||||
mockedStore.dispatch(unsubscribeRoom('GENERAL'));
|
mockedStore.dispatch(unsubscribeRoom('GENERAL'));
|
||||||
const state = mockedStore.getState().room;
|
const state = mockedStore.getState().room;
|
||||||
expect(state.rooms).toEqual([]);
|
expect(state.subscribedRoom).toEqual('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return initial state after leaveRoom', () => {
|
it('should return initial state after leaveRoom', () => {
|
||||||
|
|
|
@ -6,13 +6,13 @@ export type IRoomRecord = string[];
|
||||||
export interface IRoom {
|
export interface IRoom {
|
||||||
rid: string;
|
rid: string;
|
||||||
isDeleting: boolean;
|
isDeleting: boolean;
|
||||||
rooms: IRoomRecord;
|
subscribedRoom: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialState: IRoom = {
|
export const initialState: IRoom = {
|
||||||
rid: '',
|
rid: '',
|
||||||
isDeleting: false,
|
isDeleting: false,
|
||||||
rooms: []
|
subscribedRoom: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function (state = initialState, action: TActionsRoom): IRoom {
|
export default function (state = initialState, action: TActionsRoom): IRoom {
|
||||||
|
@ -20,12 +20,12 @@ export default function (state = initialState, action: TActionsRoom): IRoom {
|
||||||
case ROOM.SUBSCRIBE:
|
case ROOM.SUBSCRIBE:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
rooms: [action.rid, ...state.rooms]
|
subscribedRoom: action.rid
|
||||||
};
|
};
|
||||||
case ROOM.UNSUBSCRIBE:
|
case ROOM.UNSUBSCRIBE:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
rooms: state.rooms.filter(rid => rid !== action.rid)
|
subscribedRoom: state.subscribedRoom === action.rid ? '' : state.subscribedRoom
|
||||||
};
|
};
|
||||||
case ROOM.LEAVE:
|
case ROOM.LEAVE:
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
|
||||||
import { CREATE_CHANNEL, LOGIN } from '../actions/actionsTypes';
|
import { CREATE_CHANNEL, LOGIN } from '../actions/actionsTypes';
|
||||||
import { createChannelFailure, createChannelSuccess } from '../actions/createChannel';
|
import { createChannelFailure, createChannelSuccess } from '../actions/createChannel';
|
||||||
import { showErrorAlert } from '../lib/methods/helpers/info';
|
import { showErrorAlert } from '../lib/methods/helpers/info';
|
||||||
import Navigation from '../lib/navigation/appNavigation';
|
|
||||||
import database from '../lib/database';
|
import database from '../lib/database';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import { events, logEvent } from '../lib/methods/helpers/log';
|
import { events, logEvent } from '../lib/methods/helpers/log';
|
||||||
|
@ -78,10 +77,7 @@ const handleRequest = function* handleRequest({ data }) {
|
||||||
|
|
||||||
const handleSuccess = function* handleSuccess({ data }) {
|
const handleSuccess = function* handleSuccess({ data }) {
|
||||||
const isMasterDetail = yield select(state => state.app.isMasterDetail);
|
const isMasterDetail = yield select(state => state.app.isMasterDetail);
|
||||||
if (isMasterDetail) {
|
goRoom({ item: data, isMasterDetail, popToRoot: true });
|
||||||
Navigation.navigate('DrawerNavigator');
|
|
||||||
}
|
|
||||||
goRoom({ item: data, isMasterDetail });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFailure = function handleFailure({ err, isTeam }) {
|
const handleFailure = function handleFailure({ err, isTeam }) {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { all, delay, put, select, take, takeLatest } from 'redux-saga/effects';
|
import { all, delay, put, select, take, takeLatest } from 'redux-saga/effects';
|
||||||
|
|
||||||
import UserPreferences from '../lib/methods/userPreferences';
|
import UserPreferences from '../lib/methods/userPreferences';
|
||||||
import Navigation from '../lib/navigation/appNavigation';
|
|
||||||
import * as types from '../actions/actionsTypes';
|
import * as types from '../actions/actionsTypes';
|
||||||
import { selectServerRequest, serverInitAdd } from '../actions/server';
|
import { selectServerRequest, serverInitAdd } from '../actions/server';
|
||||||
import { inviteLinksRequest, inviteLinksSetToken } from '../actions/inviteLinks';
|
import { inviteLinksRequest, inviteLinksSetToken } from '../actions/inviteLinks';
|
||||||
|
@ -36,14 +35,6 @@ const handleInviteLink = function* handleInviteLink({ params, requireLogin = fal
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const popToRoot = function popToRoot({ isMasterDetail }) {
|
|
||||||
if (isMasterDetail) {
|
|
||||||
Navigation.navigate('DrawerNavigator');
|
|
||||||
} else {
|
|
||||||
Navigation.navigate('RoomsListView');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const navigate = function* navigate({ params }) {
|
const navigate = function* navigate({ params }) {
|
||||||
yield put(appStart({ root: RootEnum.ROOT_INSIDE }));
|
yield put(appStart({ root: RootEnum.ROOT_INSIDE }));
|
||||||
if (params.path || params.rid) {
|
if (params.path || params.rid) {
|
||||||
|
@ -65,27 +56,9 @@ const navigate = function* navigate({ params }) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const isMasterDetail = yield select(state => state.app.isMasterDetail);
|
const isMasterDetail = yield select(state => state.app.isMasterDetail);
|
||||||
const focusedRooms = yield select(state => state.room.rooms);
|
|
||||||
const jumpToMessageId = params.messageId;
|
const jumpToMessageId = params.messageId;
|
||||||
|
|
||||||
if (focusedRooms.includes(room.rid)) {
|
yield goRoom({ item, isMasterDetail, jumpToMessageId, jumpToThreadId, popToRoot: true });
|
||||||
// if there's one room on the list or last room is the one
|
|
||||||
if (focusedRooms.length === 1 || focusedRooms[0] === room.rid) {
|
|
||||||
if (jumpToThreadId) {
|
|
||||||
// With this conditional when there is a jumpToThreadId we can avoid the thread open again
|
|
||||||
// above other thread and the room could call again the thread
|
|
||||||
popToRoot({ isMasterDetail });
|
|
||||||
}
|
|
||||||
yield goRoom({ item, isMasterDetail, jumpToMessageId, jumpToThreadId });
|
|
||||||
} else {
|
|
||||||
popToRoot({ isMasterDetail });
|
|
||||||
yield goRoom({ item, isMasterDetail, jumpToMessageId, jumpToThreadId });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
popToRoot({ isMasterDetail });
|
|
||||||
yield goRoom({ item, isMasterDetail, jumpToMessageId, jumpToThreadId });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.isCall) {
|
if (params.isCall) {
|
||||||
callJitsi(item);
|
callJitsi(item);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { select, takeLatest } from 'redux-saga/effects';
|
import { select, takeLatest } from 'redux-saga/effects';
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
|
|
||||||
import Navigation from '../lib/navigation/appNavigation';
|
|
||||||
import { MESSAGES } from '../actions/actionsTypes';
|
import { MESSAGES } from '../actions/actionsTypes';
|
||||||
import database from '../lib/database';
|
import database from '../lib/database';
|
||||||
import log from '../lib/methods/helpers/log';
|
import log from '../lib/methods/helpers/log';
|
||||||
|
@ -16,18 +15,13 @@ const handleReplyBroadcast = function* handleReplyBroadcast({ message }) {
|
||||||
const subscriptions = yield subsCollection.query(Q.where('name', username)).fetch();
|
const subscriptions = yield subsCollection.query(Q.where('name', username)).fetch();
|
||||||
|
|
||||||
const isMasterDetail = yield select(state => state.app.isMasterDetail);
|
const isMasterDetail = yield select(state => state.app.isMasterDetail);
|
||||||
if (isMasterDetail) {
|
|
||||||
Navigation.navigate('DrawerNavigator');
|
|
||||||
} else {
|
|
||||||
Navigation.navigate('RoomsListView');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subscriptions.length) {
|
if (subscriptions.length) {
|
||||||
goRoom({ item: subscriptions[0], isMasterDetail, message });
|
goRoom({ item: subscriptions[0], isMasterDetail, popToRoot: true, message });
|
||||||
} else {
|
} else {
|
||||||
const result = yield Services.createDirectMessage(username);
|
const result = yield Services.createDirectMessage(username);
|
||||||
if (result?.success) {
|
if (result?.success) {
|
||||||
goRoom({ item: result?.room, isMasterDetail, message });
|
goRoom({ item: result?.room, isMasterDetail, popToRoot: true, message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -58,13 +58,13 @@ const handleRoomsRequest = function* handleRoomsRequest({ params }) {
|
||||||
const subsToCreate = subscriptions.filter(i1 => !existingSubs.find(i2 => i1._id === i2._id));
|
const subsToCreate = subscriptions.filter(i1 => !existingSubs.find(i2 => i1._id === i2._id));
|
||||||
const subsToDelete = existingSubs.filter(i1 => !subscriptions.find(i2 => i1._id === i2._id));
|
const subsToDelete = existingSubs.filter(i1 => !subscriptions.find(i2 => i1._id === i2._id));
|
||||||
|
|
||||||
const openedRooms = yield select(state => state.room.rooms);
|
const subscribedRoom = yield select(state => state.room.subscribedRoom);
|
||||||
const lastMessages = subscriptions
|
const lastMessages = subscriptions
|
||||||
/** Checks for opened rooms and filter them out.
|
/** Checks for opened rooms and filter them out.
|
||||||
* It prevents this process to try persisting the same last message on the room messages fetch.
|
* It prevents this process to try persisting the same last message on the room messages fetch.
|
||||||
* This race condition is easy to reproduce on push notification tap.
|
* This race condition is easy to reproduce on push notification tap.
|
||||||
*/
|
*/
|
||||||
.filter(sub => !openedRooms.includes(sub.rid))
|
.filter(sub => subscribedRoom !== sub.rid)
|
||||||
.map(sub => sub.lastMessage && buildMessage(sub.lastMessage))
|
.map(sub => sub.lastMessage && buildMessage(sub.lastMessage))
|
||||||
.filter(lm => lm);
|
.filter(lm => lm);
|
||||||
const lastMessagesIds = lastMessages.map(lm => lm._id).filter(lm => lm);
|
const lastMessagesIds = lastMessages.map(lm => lm._id).filter(lm => lm);
|
||||||
|
|
|
@ -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>();
|
||||||
|
@ -222,7 +223,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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -17,7 +17,6 @@ import { TSupportedThemes, withTheme } from '../theme';
|
||||||
import SafeAreaView from '../containers/SafeAreaView';
|
import SafeAreaView from '../containers/SafeAreaView';
|
||||||
import { sendLoadingEvent } from '../containers/Loading';
|
import { sendLoadingEvent } from '../containers/Loading';
|
||||||
import { animateNextTransition } from '../lib/methods/helpers/layoutAnimation';
|
import { animateNextTransition } from '../lib/methods/helpers/layoutAnimation';
|
||||||
import { goRoom } from '../lib/methods/helpers/goRoom';
|
|
||||||
import { showErrorAlert } from '../lib/methods/helpers/info';
|
import { showErrorAlert } from '../lib/methods/helpers/info';
|
||||||
import { ChatsStackParamList } from '../stacks/types';
|
import { ChatsStackParamList } from '../stacks/types';
|
||||||
import { TSubscriptionModel, SubscriptionType, IApplicationState } from '../definitions';
|
import { TSubscriptionModel, SubscriptionType, IApplicationState } from '../definitions';
|
||||||
|
@ -126,7 +125,7 @@ class AddExistingChannelView extends React.Component<IAddExistingChannelViewProp
|
||||||
|
|
||||||
submit = async () => {
|
submit = async () => {
|
||||||
const { selected } = this.state;
|
const { selected } = this.state;
|
||||||
const { isMasterDetail } = this.props;
|
const { navigation } = this.props;
|
||||||
|
|
||||||
sendLoadingEvent({ visible: true });
|
sendLoadingEvent({ visible: true });
|
||||||
try {
|
try {
|
||||||
|
@ -134,9 +133,8 @@ class AddExistingChannelView extends React.Component<IAddExistingChannelViewProp
|
||||||
const result = await Services.addRoomsToTeam({ rooms: selected, teamId: this.teamId });
|
const result = await Services.addRoomsToTeam({ rooms: selected, teamId: this.teamId });
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
sendLoadingEvent({ visible: false });
|
sendLoadingEvent({ visible: false });
|
||||||
// @ts-ignore
|
// Expect that after you add an existing channel to a team, the user should move back to the team
|
||||||
// TODO: Verify goRoom interface for return of call
|
navigation.navigate('RoomView');
|
||||||
goRoom({ item: result, isMasterDetail });
|
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
logEvent(events.CT_ADD_ROOM_TO_TEAM_F);
|
logEvent(events.CT_ADD_ROOM_TO_TEAM_F);
|
||||||
|
|
|
@ -8,14 +8,12 @@ import SafeAreaView from '../containers/SafeAreaView';
|
||||||
import StatusBar from '../containers/StatusBar';
|
import StatusBar from '../containers/StatusBar';
|
||||||
import Button from '../containers/Button';
|
import Button from '../containers/Button';
|
||||||
import { TSupportedThemes, useTheme } from '../theme';
|
import { TSupportedThemes, useTheme } from '../theme';
|
||||||
import Navigation from '../lib/navigation/appNavigation';
|
|
||||||
import { goRoom } from '../lib/methods/helpers/goRoom';
|
import { goRoom } from '../lib/methods/helpers/goRoom';
|
||||||
import { themes } from '../lib/constants';
|
import { themes } from '../lib/constants';
|
||||||
import Markdown from '../containers/markdown';
|
import Markdown from '../containers/markdown';
|
||||||
import { ICannedResponse } from '../definitions/ICannedResponse';
|
import { ICannedResponse } from '../definitions/ICannedResponse';
|
||||||
import { ChatsStackParamList } from '../stacks/types';
|
import { ChatsStackParamList } from '../stacks/types';
|
||||||
import sharedStyles from './Styles';
|
import sharedStyles from './Styles';
|
||||||
import { getRoomTitle, getUidDirectMessage } from '../lib/methods/helpers';
|
|
||||||
import { useAppSelector } from '../lib/hooks';
|
import { useAppSelector } from '../lib/hooks';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
@ -97,7 +95,6 @@ const CannedResponseDetail = ({ navigation, route }: ICannedResponseDetailProps)
|
||||||
const { cannedResponse } = route?.params;
|
const { cannedResponse } = route?.params;
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const { isMasterDetail } = useAppSelector(state => state.app);
|
const { isMasterDetail } = useAppSelector(state => state.app);
|
||||||
const { rooms } = useAppSelector(state => state.room);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
|
@ -107,31 +104,9 @@ const CannedResponseDetail = ({ navigation, route }: ICannedResponseDetailProps)
|
||||||
|
|
||||||
const navigateToRoom = (item: ICannedResponse) => {
|
const navigateToRoom = (item: ICannedResponse) => {
|
||||||
const { room } = route.params;
|
const { room } = route.params;
|
||||||
const { name } = room;
|
|
||||||
const params = {
|
|
||||||
rid: room.rid,
|
|
||||||
name: getRoomTitle({
|
|
||||||
t: room.t,
|
|
||||||
fname: name
|
|
||||||
}),
|
|
||||||
t: room.t,
|
|
||||||
roomUserId: getUidDirectMessage(room),
|
|
||||||
usedCannedResponse: item.text
|
|
||||||
};
|
|
||||||
|
|
||||||
if (room.rid) {
|
if (room.rid) {
|
||||||
// if it's on master detail layout, we close the modal and replace RoomView
|
goRoom({ item: room, isMasterDetail, popToRoot: true, usedCannedResponse: item.text });
|
||||||
if (isMasterDetail) {
|
|
||||||
Navigation.navigate('DrawerNavigator');
|
|
||||||
goRoom({ item: params, isMasterDetail });
|
|
||||||
} else {
|
|
||||||
let navigate = navigation.push;
|
|
||||||
// if this is a room focused
|
|
||||||
if (rooms.includes(room.rid)) {
|
|
||||||
({ navigate } = navigation);
|
|
||||||
}
|
|
||||||
navigate('RoomView', params);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ import ActivityIndicator from '../../containers/ActivityIndicator';
|
||||||
import SearchHeader from '../../containers/SearchHeader';
|
import SearchHeader from '../../containers/SearchHeader';
|
||||||
import BackgroundContainer from '../../containers/BackgroundContainer';
|
import BackgroundContainer from '../../containers/BackgroundContainer';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import Navigation from '../../lib/navigation/appNavigation';
|
|
||||||
import { goRoom } from '../../lib/methods/helpers/goRoom';
|
import { goRoom } from '../../lib/methods/helpers/goRoom';
|
||||||
import * as HeaderButton from '../../containers/HeaderButton';
|
import * as HeaderButton from '../../containers/HeaderButton';
|
||||||
import * as List from '../../containers/List';
|
import * as List from '../../containers/List';
|
||||||
|
@ -24,7 +23,7 @@ import DropdownItemHeader from './Dropdown/DropdownItemHeader';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { ICannedResponse } from '../../definitions/ICannedResponse';
|
import { ICannedResponse } from '../../definitions/ICannedResponse';
|
||||||
import { ChatsStackParamList } from '../../stacks/types';
|
import { ChatsStackParamList } from '../../stacks/types';
|
||||||
import { getRoomTitle, getUidDirectMessage, useDebounce } from '../../lib/methods/helpers';
|
import { useDebounce } from '../../lib/methods/helpers';
|
||||||
import { Services } from '../../lib/services';
|
import { Services } from '../../lib/services';
|
||||||
import { ILivechatDepartment } from '../../definitions/ILivechatDepartment';
|
import { ILivechatDepartment } from '../../definitions/ILivechatDepartment';
|
||||||
import { useAppSelector } from '../../lib/hooks';
|
import { useAppSelector } from '../../lib/hooks';
|
||||||
|
@ -73,7 +72,6 @@ const CannedResponsesListView = ({ navigation, route }: ICannedResponsesListView
|
||||||
|
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
|
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
|
||||||
const rooms = useAppSelector(state => state.room.rooms);
|
|
||||||
|
|
||||||
const getRoomFromDb = async () => {
|
const getRoomFromDb = async () => {
|
||||||
const { rid } = route.params;
|
const { rid } = route.params;
|
||||||
|
@ -107,34 +105,8 @@ const CannedResponsesListView = ({ navigation, route }: ICannedResponsesListView
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigateToRoom = (item: ICannedResponse) => {
|
const navigateToRoom = (item: ICannedResponse) => {
|
||||||
if (!room) {
|
if (room?.rid) {
|
||||||
return;
|
goRoom({ item: room, isMasterDetail, popToRoot: true, usedCannedResponse: item.text });
|
||||||
}
|
|
||||||
const { name } = room;
|
|
||||||
const params = {
|
|
||||||
rid: room.rid,
|
|
||||||
name: getRoomTitle({
|
|
||||||
t: room.t,
|
|
||||||
fname: name
|
|
||||||
}),
|
|
||||||
t: room.t,
|
|
||||||
roomUserId: getUidDirectMessage(room),
|
|
||||||
usedCannedResponse: item.text
|
|
||||||
};
|
|
||||||
|
|
||||||
if (room.rid) {
|
|
||||||
// if it's on master detail layout, we close the modal and replace RoomView
|
|
||||||
if (isMasterDetail) {
|
|
||||||
Navigation.navigate('DrawerNavigator');
|
|
||||||
goRoom({ item: params, isMasterDetail });
|
|
||||||
} else {
|
|
||||||
let navigate = navigation.push;
|
|
||||||
// if this is a room focused
|
|
||||||
if (rooms.includes(room.rid)) {
|
|
||||||
({ navigate } = navigation);
|
|
||||||
}
|
|
||||||
navigate('RoomView', params);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ import StatusBar from '../../containers/StatusBar';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
import { FormTextInput } from '../../containers/TextInput';
|
import { FormTextInput } from '../../containers/TextInput';
|
||||||
import Navigation from '../../lib/navigation/appNavigation';
|
|
||||||
import { createDiscussionRequest, ICreateDiscussionRequestData } from '../../actions/createDiscussion';
|
import { createDiscussionRequest, ICreateDiscussionRequestData } from '../../actions/createDiscussion';
|
||||||
import SafeAreaView from '../../containers/SafeAreaView';
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
import { goRoom } from '../../lib/methods/helpers/goRoom';
|
import { goRoom } from '../../lib/methods/helpers/goRoom';
|
||||||
|
@ -60,18 +59,13 @@ class CreateChannelView extends React.Component<ICreateChannelViewProps, ICreate
|
||||||
showErrorAlert(msg);
|
showErrorAlert(msg);
|
||||||
} else {
|
} else {
|
||||||
const { rid, t, prid } = result;
|
const { rid, t, prid } = result;
|
||||||
if (isMasterDetail) {
|
|
||||||
Navigation.navigate('DrawerNavigator');
|
|
||||||
} else {
|
|
||||||
Navigation.navigate('RoomsListView');
|
|
||||||
}
|
|
||||||
const item = {
|
const item = {
|
||||||
rid,
|
rid,
|
||||||
name: getRoomTitle(result),
|
name: getRoomTitle(result),
|
||||||
t,
|
t,
|
||||||
prid
|
prid
|
||||||
};
|
};
|
||||||
goRoom({ item, isMasterDetail });
|
goRoom({ item, isMasterDetail, popToRoot: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,13 +152,8 @@ class DirectoryView extends React.Component<IDirectoryViewProps, IDirectoryViewS
|
||||||
};
|
};
|
||||||
|
|
||||||
goRoom = (item: TGoRoomItem) => {
|
goRoom = (item: TGoRoomItem) => {
|
||||||
const { navigation, isMasterDetail } = this.props;
|
const { isMasterDetail } = this.props;
|
||||||
if (isMasterDetail) {
|
goRoom({ item, isMasterDetail, popToRoot: true });
|
||||||
navigation.navigate('DrawerNavigator');
|
|
||||||
} else {
|
|
||||||
navigation.navigate('RoomsListView');
|
|
||||||
}
|
|
||||||
goRoom({ item, isMasterDetail });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onPressItem = async (item: IServerRoom) => {
|
onPressItem = async (item: IServerRoom) => {
|
||||||
|
|
|
@ -17,12 +17,9 @@ import { useTheme } from '../theme';
|
||||||
import sharedStyles from './Styles';
|
import sharedStyles from './Styles';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
|
||||||
padding: 28
|
|
||||||
},
|
|
||||||
info: {
|
info: {
|
||||||
fontSize: 14,
|
fontSize: 16,
|
||||||
marginVertical: 8,
|
marginVertical: 4,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -53,10 +50,7 @@ const E2EEnterYourPasswordView = (): React.ReactElement => {
|
||||||
>
|
>
|
||||||
<StatusBar />
|
<StatusBar />
|
||||||
<ScrollView {...scrollPersistTaps} style={sharedStyles.container} contentContainerStyle={sharedStyles.containerScrollView}>
|
<ScrollView {...scrollPersistTaps} style={sharedStyles.container} contentContainerStyle={sharedStyles.containerScrollView}>
|
||||||
<SafeAreaView
|
<SafeAreaView style={{ backgroundColor: colors.backgroundColor }} testID='e2e-enter-your-password-view'>
|
||||||
style={[styles.container, { backgroundColor: colors.backgroundColor }]}
|
|
||||||
testID='e2e-enter-your-password-view'
|
|
||||||
>
|
|
||||||
<FormTextInput
|
<FormTextInput
|
||||||
placeholder={I18n.t('Password')}
|
placeholder={I18n.t('Password')}
|
||||||
returnKeyType='send'
|
returnKeyType='send'
|
||||||
|
|
|
@ -15,8 +15,8 @@ const styles = StyleSheet.create({
|
||||||
padding: 16
|
padding: 16
|
||||||
},
|
},
|
||||||
info: {
|
info: {
|
||||||
fontSize: 14,
|
fontSize: 16,
|
||||||
marginVertical: 8
|
marginBottom: 16
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ import sharedStyles from './Styles';
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
padding: 44,
|
|
||||||
paddingTop: 32
|
paddingTop: 32
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
|
@ -33,10 +32,12 @@ const styles = StyleSheet.create({
|
||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
},
|
},
|
||||||
warning: {
|
warning: {
|
||||||
fontSize: 14,
|
fontSize: 16,
|
||||||
...sharedStyles.textMedium
|
...sharedStyles.textMedium,
|
||||||
|
textAlign: 'center'
|
||||||
},
|
},
|
||||||
passwordText: {
|
passwordText: {
|
||||||
|
fontSize: 14,
|
||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
...sharedStyles.textAlignCenter
|
...sharedStyles.textAlignCenter
|
||||||
},
|
},
|
||||||
|
@ -50,9 +51,10 @@ const styles = StyleSheet.create({
|
||||||
height: 32
|
height: 32
|
||||||
},
|
},
|
||||||
info: {
|
info: {
|
||||||
fontSize: 14,
|
fontSize: 16,
|
||||||
marginBottom: 64,
|
marginBottom: 64,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular,
|
||||||
|
textAlign: 'center'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -112,7 +114,7 @@ const E2ESaveYourPasswordView = () => {
|
||||||
style={[styles.copyButton, { backgroundColor: colors.auxiliaryBackground }]}
|
style={[styles.copyButton, { backgroundColor: colors.auxiliaryBackground }]}
|
||||||
title={I18n.t('Copy')}
|
title={I18n.t('Copy')}
|
||||||
type='secondary'
|
type='secondary'
|
||||||
fontSize={12}
|
fontSize={14}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<Text style={[styles.info, { color: colors.bodyText }]}>{I18n.t('Save_Your_Encryption_Password_info')}</Text>
|
<Text style={[styles.info, { color: colors.bodyText }]}>{I18n.t('Save_Your_Encryption_Password_info')}</Text>
|
||||||
|
|
|
@ -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}`;
|
||||||
|
@ -59,17 +58,15 @@ class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewS
|
||||||
const { route } = this.props;
|
const { route } = this.props;
|
||||||
const { userInfo } = this.state;
|
const { userInfo } = this.state;
|
||||||
|
|
||||||
if (isIOS) {
|
setTimeout(() => {
|
||||||
setTimeout(() => {
|
const onlyAudio = route.params?.onlyAudio ?? false;
|
||||||
const onlyAudio = route.params?.onlyAudio ?? false;
|
if (onlyAudio) {
|
||||||
if (onlyAudio) {
|
JitsiMeet.audioCall(this.url, userInfo);
|
||||||
JitsiMeet.audioCall(this.url, userInfo);
|
} else {
|
||||||
} else {
|
JitsiMeet.call(this.url, userInfo);
|
||||||
JitsiMeet.call(this.url, userInfo);
|
}
|
||||||
}
|
this.setState({ loading: false });
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
|
||||||
BackHandler.addEventListener('hardwareBackPress', this.endCall);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -79,16 +76,8 @@ class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewS
|
||||||
this.jitsiTimeout = null;
|
this.jitsiTimeout = null;
|
||||||
BackgroundTimer.stopBackgroundTimer();
|
BackgroundTimer.stopBackgroundTimer();
|
||||||
}
|
}
|
||||||
BackHandler.removeEventListener('hardwareBackPress', this.endCall);
|
|
||||||
if (isIOS) {
|
|
||||||
JitsiMeet.endCall();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
endCall = () => {
|
|
||||||
JitsiMeet.endCall();
|
JitsiMeet.endCall();
|
||||||
return null;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
onConferenceWillJoin = () => {
|
onConferenceWillJoin = () => {
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
|
@ -98,6 +87,7 @@ class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewS
|
||||||
// call is not ended and is available to web users.
|
// call is not ended and is available to web users.
|
||||||
onConferenceJoined = () => {
|
onConferenceJoined = () => {
|
||||||
logEvent(this.videoConf ? events.LIVECHAT_VIDEOCONF_JOIN : events.JM_CONFERENCE_JOIN);
|
logEvent(this.videoConf ? events.LIVECHAT_VIDEOCONF_JOIN : events.JM_CONFERENCE_JOIN);
|
||||||
|
this.setState({ loading: false });
|
||||||
if (this.rid && !this.videoConf) {
|
if (this.rid && !this.videoConf) {
|
||||||
Services.updateJitsiTimeout(this.rid).catch((e: unknown) => console.log(e));
|
Services.updateJitsiTimeout(this.rid).catch((e: unknown) => console.log(e));
|
||||||
if (this.jitsiTimeout) {
|
if (this.jitsiTimeout) {
|
||||||
|
@ -112,16 +102,18 @@ 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;
|
||||||
navigation.pop();
|
logEvent(this.videoConf ? events.LIVECHAT_VIDEOCONF_TERMINATE : events.JM_CONFERENCE_TERMINATE);
|
||||||
|
// fix to go back when the call ends
|
||||||
|
setTimeout(() => {
|
||||||
|
JitsiMeet.endCall();
|
||||||
|
navigation.pop();
|
||||||
|
}, 200);
|
||||||
};
|
};
|
||||||
|
|
||||||
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
|
||||||
|
@ -129,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}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -74,10 +74,7 @@ const NewMessageView = () => {
|
||||||
const goRoom = useCallback(
|
const goRoom = useCallback(
|
||||||
(item: TGoRoomItem) => {
|
(item: TGoRoomItem) => {
|
||||||
logEvent(events.NEW_MSG_CHAT_WITH_USER);
|
logEvent(events.NEW_MSG_CHAT_WITH_USER);
|
||||||
|
navigation.pop();
|
||||||
if (isMasterDetail) {
|
|
||||||
navigation.pop();
|
|
||||||
}
|
|
||||||
goRoomMethod({ item, isMasterDetail });
|
goRoomMethod({ item, isMasterDetail });
|
||||||
},
|
},
|
||||||
[isMasterDetail, navigation]
|
[isMasterDetail, navigation]
|
||||||
|
|
|
@ -87,7 +87,7 @@ interface IRoomInfoViewProps {
|
||||||
StackNavigationProp<MasterDetailInsideStackParamList>
|
StackNavigationProp<MasterDetailInsideStackParamList>
|
||||||
>;
|
>;
|
||||||
route: RouteProp<ChatsStackParamList, 'RoomInfoView'>;
|
route: RouteProp<ChatsStackParamList, 'RoomInfoView'>;
|
||||||
rooms: string[];
|
subscribedRoom: string;
|
||||||
theme: TSupportedThemes;
|
theme: TSupportedThemes;
|
||||||
isMasterDetail: boolean;
|
isMasterDetail: boolean;
|
||||||
jitsiEnabled: boolean;
|
jitsiEnabled: boolean;
|
||||||
|
@ -353,7 +353,7 @@ class RoomInfoView extends React.Component<IRoomInfoViewProps, IRoomInfoViewStat
|
||||||
goRoom = () => {
|
goRoom = () => {
|
||||||
logEvent(events.RI_GO_ROOM_USER);
|
logEvent(events.RI_GO_ROOM_USER);
|
||||||
const { room } = this.state;
|
const { room } = this.state;
|
||||||
const { rooms, navigation, isMasterDetail } = this.props;
|
const { navigation, isMasterDetail, subscribedRoom } = this.props;
|
||||||
const params = {
|
const params = {
|
||||||
rid: room.rid,
|
rid: room.rid,
|
||||||
name: getRoomTitle(room),
|
name: getRoomTitle(room),
|
||||||
|
@ -362,18 +362,14 @@ class RoomInfoView extends React.Component<IRoomInfoViewProps, IRoomInfoViewStat
|
||||||
};
|
};
|
||||||
|
|
||||||
if (room.rid) {
|
if (room.rid) {
|
||||||
// if it's on master detail layout, we close the modal and replace RoomView
|
if (room.rid === subscribedRoom) {
|
||||||
if (isMasterDetail) {
|
if (isMasterDetail) {
|
||||||
Navigation.navigate('DrawerNavigator');
|
return Navigation.navigate('DrawerNavigator');
|
||||||
goRoom({ item: params, isMasterDetail });
|
|
||||||
} else {
|
|
||||||
let navigate = navigation.push;
|
|
||||||
// if this is a room focused
|
|
||||||
if (rooms.includes(room.rid)) {
|
|
||||||
({ navigate } = navigation);
|
|
||||||
}
|
}
|
||||||
navigate('RoomView', params);
|
return navigation.goBack();
|
||||||
}
|
}
|
||||||
|
// if it's on master detail layout, we close the modal and replace RoomView
|
||||||
|
goRoom({ item: params, isMasterDetail, popToRoot: true });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -513,7 +509,7 @@ class RoomInfoView extends React.Component<IRoomInfoViewProps, IRoomInfoViewStat
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state: IApplicationState) => ({
|
const mapStateToProps = (state: IApplicationState) => ({
|
||||||
rooms: state.room.rooms,
|
subscribedRoom: state.room.subscribedRoom,
|
||||||
isMasterDetail: state.app.isMasterDetail,
|
isMasterDetail: state.app.isMasterDetail,
|
||||||
jitsiEnabled: (state.settings.Jitsi_Enabled as boolean) || false,
|
jitsiEnabled: (state.settings.Jitsi_Enabled as boolean) || false,
|
||||||
editRoomPermission: state.permissions['edit-room'],
|
editRoomPermission: state.permissions['edit-room'],
|
||||||
|
|
|
@ -15,12 +15,7 @@ import { RoomTypes } from '../../lib/methods';
|
||||||
export type TRoomType = SubscriptionType.CHANNEL | SubscriptionType.GROUP | SubscriptionType.OMNICHANNEL;
|
export type TRoomType = SubscriptionType.CHANNEL | SubscriptionType.GROUP | SubscriptionType.OMNICHANNEL;
|
||||||
|
|
||||||
const handleGoRoom = (item: TGoRoomItem, isMasterDetail: boolean): void => {
|
const handleGoRoom = (item: TGoRoomItem, isMasterDetail: boolean): void => {
|
||||||
if (isMasterDetail) {
|
goRoom({ item, isMasterDetail, popToRoot: true });
|
||||||
appNavigation.navigate('DrawerNavigator');
|
|
||||||
} else {
|
|
||||||
appNavigation.popToTop();
|
|
||||||
}
|
|
||||||
goRoom({ item, isMasterDetail });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchRole = (role: string, selectedUser: TUserModel, roomRoles: any): boolean => {
|
export const fetchRole = (role: string, selectedUser: TUserModel, roomRoles: any): boolean => {
|
||||||
|
|
|
@ -43,7 +43,6 @@ import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
import { withDimensions } from '../../dimensions';
|
import { withDimensions } from '../../dimensions';
|
||||||
import { takeInquiry, takeResume } from '../../ee/omnichannel/lib';
|
import { takeInquiry, takeResume } from '../../ee/omnichannel/lib';
|
||||||
import { sendLoadingEvent } from '../../containers/Loading';
|
import { sendLoadingEvent } from '../../containers/Loading';
|
||||||
import { goRoom, TGoRoomItem } from '../../lib/methods/helpers/goRoom';
|
|
||||||
import getThreadName from '../../lib/methods/getThreadName';
|
import getThreadName from '../../lib/methods/getThreadName';
|
||||||
import getRoomInfo from '../../lib/methods/getRoomInfo';
|
import getRoomInfo from '../../lib/methods/getRoomInfo';
|
||||||
import { ContainerTypes } from '../../containers/UIKit/interfaces';
|
import { ContainerTypes } from '../../containers/UIKit/interfaces';
|
||||||
|
@ -101,6 +100,7 @@ import {
|
||||||
} from '../../lib/methods/helpers';
|
} from '../../lib/methods/helpers';
|
||||||
import { Services } from '../../lib/services';
|
import { Services } from '../../lib/services';
|
||||||
import { withActionSheet, IActionSheetProvider } from '../../containers/ActionSheet';
|
import { withActionSheet, IActionSheetProvider } from '../../containers/ActionSheet';
|
||||||
|
import { goRoom, TGoRoomItem } from '../../lib/methods/helpers/goRoom';
|
||||||
|
|
||||||
type TStateAttrsUpdate = keyof IRoomViewState;
|
type TStateAttrsUpdate = keyof IRoomViewState;
|
||||||
|
|
||||||
|
@ -910,15 +910,15 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
|
|
||||||
onDiscussionPress = debounce(
|
onDiscussionPress = debounce(
|
||||||
async (item: TAnyMessageModel) => {
|
async (item: TAnyMessageModel) => {
|
||||||
const { navigation } = this.props;
|
const { isMasterDetail } = this.props;
|
||||||
if (!item.drid) return;
|
if (!item.drid) return;
|
||||||
const sub = await getRoomInfo(item.drid);
|
const sub = await getRoomInfo(item.drid);
|
||||||
navigation.push('RoomView', {
|
if (sub) {
|
||||||
rid: item.drid as string,
|
goRoom({
|
||||||
prid: item?.subscription?.id,
|
item: sub as TGoRoomItem,
|
||||||
name: item.msg,
|
isMasterDetail
|
||||||
t: (sub?.t as SubscriptionType) || (this.t as SubscriptionType)
|
});
|
||||||
});
|
}
|
||||||
},
|
},
|
||||||
1000,
|
1000,
|
||||||
true
|
true
|
||||||
|
@ -987,6 +987,11 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
} else {
|
} else {
|
||||||
this.navToThread(message);
|
this.navToThread(message);
|
||||||
}
|
}
|
||||||
|
} else if (!message.tmid && message.rid === this.rid && this.t === 'thread' && !message.replies) {
|
||||||
|
/**
|
||||||
|
* if the user is within a thread and the message that he is trying to jump to, is a message in the main room
|
||||||
|
*/
|
||||||
|
return this.navToRoom(message);
|
||||||
} else {
|
} else {
|
||||||
/**
|
/**
|
||||||
* if it's from server, we don't have it saved locally and so we fetch surroundings
|
* if it's from server, we don't have it saved locally and so we fetch surroundings
|
||||||
|
@ -1198,12 +1203,12 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
navToRoom = async (message: TAnyMessageModel) => {
|
navToRoom = async (message: TAnyMessageModel) => {
|
||||||
const { navigation, isMasterDetail } = this.props;
|
const { isMasterDetail } = this.props;
|
||||||
const roomInfo = await getRoomInfo(message.rid);
|
const roomInfo = await getRoomInfo(message.rid);
|
||||||
|
|
||||||
return goRoom({
|
return goRoom({
|
||||||
item: roomInfo as TGoRoomItem,
|
item: roomInfo as TGoRoomItem,
|
||||||
isMasterDetail,
|
isMasterDetail,
|
||||||
navigationMethod: navigation.push,
|
|
||||||
jumpToMessageId: message.id
|
jumpToMessageId: message.id
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,8 +6,10 @@ import Orientation from 'react-native-orientation-locker';
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
import { withSafeAreaInsets } from 'react-native-safe-area-context';
|
import { withSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { StackNavigationOptions } from '@react-navigation/stack';
|
import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
|
||||||
import { Header } from '@react-navigation/elements';
|
import { Header } from '@react-navigation/elements';
|
||||||
|
import { CompositeNavigationProp, RouteProp } from '@react-navigation/native';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
|
|
||||||
import database from '../../lib/database';
|
import database from '../../lib/database';
|
||||||
import RoomItem, { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from '../../containers/RoomItem';
|
import RoomItem, { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from '../../containers/RoomItem';
|
||||||
|
@ -20,7 +22,7 @@ import StatusBar from '../../containers/StatusBar';
|
||||||
import ActivityIndicator from '../../containers/ActivityIndicator';
|
import ActivityIndicator from '../../containers/ActivityIndicator';
|
||||||
import { serverInitAdd } from '../../actions/server';
|
import { serverInitAdd } from '../../actions/server';
|
||||||
import { animateNextTransition } from '../../lib/methods/helpers/layoutAnimation';
|
import { animateNextTransition } from '../../lib/methods/helpers/layoutAnimation';
|
||||||
import { withTheme } from '../../theme';
|
import { TSupportedThemes, withTheme } from '../../theme';
|
||||||
import EventEmitter from '../../lib/methods/helpers/events';
|
import EventEmitter from '../../lib/methods/helpers/events';
|
||||||
import { themedHeader } from '../../lib/methods/helpers/navigation';
|
import { themedHeader } from '../../lib/methods/helpers/navigation';
|
||||||
import {
|
import {
|
||||||
|
@ -39,20 +41,12 @@ import { goRoom } from '../../lib/methods/helpers/goRoom';
|
||||||
import SafeAreaView from '../../containers/SafeAreaView';
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
import { withDimensions } from '../../dimensions';
|
import { withDimensions } from '../../dimensions';
|
||||||
import { getInquiryQueueSelector } from '../../ee/omnichannel/selectors/inquiry';
|
import { getInquiryQueueSelector } from '../../ee/omnichannel/selectors/inquiry';
|
||||||
import {
|
import { IApplicationState, ISubscription, IUser, RootEnum, SubscriptionType, TSubscriptionModel } from '../../definitions';
|
||||||
IApplicationState,
|
|
||||||
IBaseScreen,
|
|
||||||
ISubscription,
|
|
||||||
IUser,
|
|
||||||
RootEnum,
|
|
||||||
SubscriptionType,
|
|
||||||
TSubscriptionModel
|
|
||||||
} from '../../definitions';
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import ServerDropdown from './ServerDropdown';
|
import ServerDropdown from './ServerDropdown';
|
||||||
import ListHeader, { TEncryptionBanner } from './ListHeader';
|
import ListHeader, { TEncryptionBanner } from './ListHeader';
|
||||||
import RoomsListHeaderView from './Header';
|
import RoomsListHeaderView from './Header';
|
||||||
import { ChatsStackParamList } from '../../stacks/types';
|
import { ChatsStackParamList, DrawerParamList } from '../../stacks/types';
|
||||||
import { RoomTypes, search } from '../../lib/methods';
|
import { RoomTypes, search } from '../../lib/methods';
|
||||||
import {
|
import {
|
||||||
getRoomAvatar,
|
getRoomAvatar,
|
||||||
|
@ -67,7 +61,16 @@ import {
|
||||||
import { E2E_BANNER_TYPE, DisplayMode, SortBy, MAX_SIDEBAR_WIDTH, themes } from '../../lib/constants';
|
import { E2E_BANNER_TYPE, DisplayMode, SortBy, MAX_SIDEBAR_WIDTH, themes } from '../../lib/constants';
|
||||||
import { Services } from '../../lib/services';
|
import { Services } from '../../lib/services';
|
||||||
|
|
||||||
interface IRoomsListViewProps extends IBaseScreen<ChatsStackParamList, 'RoomsListView'> {
|
type TNavigation = CompositeNavigationProp<
|
||||||
|
StackNavigationProp<ChatsStackParamList, 'RoomsListView'>,
|
||||||
|
CompositeNavigationProp<StackNavigationProp<ChatsStackParamList>, StackNavigationProp<DrawerParamList>>
|
||||||
|
>;
|
||||||
|
|
||||||
|
interface IRoomsListViewProps {
|
||||||
|
navigation: TNavigation;
|
||||||
|
route: RouteProp<ChatsStackParamList, 'RoomsListView'>;
|
||||||
|
theme: TSupportedThemes;
|
||||||
|
dispatch: Dispatch;
|
||||||
[key: string]: IUser | string | boolean | ISubscription[] | number | object | TEncryptionBanner;
|
[key: string]: IUser | string | boolean | ISubscription[] | number | object | TEncryptionBanner;
|
||||||
user: IUser;
|
user: IUser;
|
||||||
server: string;
|
server: string;
|
||||||
|
@ -83,7 +86,7 @@ interface IRoomsListViewProps extends IBaseScreen<ChatsStackParamList, 'RoomsLis
|
||||||
StoreLastMessage: boolean;
|
StoreLastMessage: boolean;
|
||||||
useRealName: boolean;
|
useRealName: boolean;
|
||||||
isMasterDetail: boolean;
|
isMasterDetail: boolean;
|
||||||
rooms: string[];
|
subscribedRoom: string;
|
||||||
width: number;
|
width: number;
|
||||||
insets: {
|
insets: {
|
||||||
left: number;
|
left: number;
|
||||||
|
@ -304,7 +307,7 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
|
||||||
}
|
}
|
||||||
|
|
||||||
const { loading, search } = this.state;
|
const { loading, search } = this.state;
|
||||||
const { rooms, width, insets } = this.props;
|
const { width, insets, subscribedRoom } = this.props;
|
||||||
if (nextState.loading !== loading) {
|
if (nextState.loading !== loading) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -314,7 +317,7 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
|
||||||
if (!dequal(nextState.search, search)) {
|
if (!dequal(nextState.search, search)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!dequal(nextProps.rooms, rooms)) {
|
if (nextProps.subscribedRoom !== subscribedRoom) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!dequal(nextProps.insets, insets)) {
|
if (!dequal(nextProps.insets, insets)) {
|
||||||
|
@ -334,7 +337,7 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
|
||||||
groupByType,
|
groupByType,
|
||||||
showFavorites,
|
showFavorites,
|
||||||
showUnread,
|
showUnread,
|
||||||
rooms,
|
subscribedRoom,
|
||||||
isMasterDetail,
|
isMasterDetail,
|
||||||
insets,
|
insets,
|
||||||
createTeamPermission,
|
createTeamPermission,
|
||||||
|
@ -359,9 +362,9 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
|
||||||
) {
|
) {
|
||||||
this.getSubscriptions();
|
this.getSubscriptions();
|
||||||
}
|
}
|
||||||
// Update current item in case of another action triggers an update on rooms reducer
|
// Update current item in case of another action triggers an update on room subscribed reducer
|
||||||
if (isMasterDetail && rooms[0] && item?.rid !== rooms[0] && !dequal(rooms, prevProps.rooms)) {
|
if (isMasterDetail && item?.rid !== subscribedRoom && subscribedRoom !== prevProps.subscribedRoom) {
|
||||||
this.setState({ item: { rid: rooms[0] } as ISubscription });
|
this.setState({ item: { rid: subscribedRoom } as ISubscription });
|
||||||
}
|
}
|
||||||
if (insets.left !== prevProps.insets.left || insets.right !== prevProps.insets.right) {
|
if (insets.left !== prevProps.insets.left || insets.right !== prevProps.insets.right) {
|
||||||
this.setHeader();
|
this.setHeader();
|
||||||
|
@ -765,9 +768,9 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
|
||||||
goRoom = ({ item, isMasterDetail }: { item: ISubscription; isMasterDetail: boolean }) => {
|
goRoom = ({ item, isMasterDetail }: { item: ISubscription; isMasterDetail: boolean }) => {
|
||||||
logEvent(events.RL_GO_ROOM);
|
logEvent(events.RL_GO_ROOM);
|
||||||
const { item: currentItem } = this.state;
|
const { item: currentItem } = this.state;
|
||||||
const { rooms } = this.props;
|
const { subscribedRoom } = this.props;
|
||||||
// @ts-ignore
|
|
||||||
if (currentItem?.rid === item.rid || rooms?.includes(item.rid)) {
|
if (currentItem?.rid === item.rid || subscribedRoom === item.rid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Only mark room as focused when in master detail layout
|
// Only mark room as focused when in master detail layout
|
||||||
|
@ -1040,7 +1043,7 @@ const mapStateToProps = (state: IApplicationState) => ({
|
||||||
showUnread: state.sortPreferences.showUnread,
|
showUnread: state.sortPreferences.showUnread,
|
||||||
useRealName: state.settings.UI_Use_Real_Name,
|
useRealName: state.settings.UI_Use_Real_Name,
|
||||||
StoreLastMessage: state.settings.Store_Last_Message,
|
StoreLastMessage: state.settings.Store_Last_Message,
|
||||||
rooms: state.room.rooms,
|
subscribedRoom: state.room.subscribedRoom,
|
||||||
queueSize: getInquiryQueueSelector(state).length,
|
queueSize: getInquiryQueueSelector(state).length,
|
||||||
inquiryEnabled: state.inquiry.enabled,
|
inquiryEnabled: state.inquiry.enabled,
|
||||||
encryptionBanner: state.encryption.banner,
|
encryptionBanner: state.encryption.banner,
|
||||||
|
|
|
@ -18,7 +18,7 @@ export default StyleSheet.create({
|
||||||
flexDirection: 'column'
|
flexDirection: 'column'
|
||||||
},
|
},
|
||||||
containerScrollView: {
|
containerScrollView: {
|
||||||
padding: 15,
|
padding: 16,
|
||||||
paddingBottom: 30
|
paddingBottom: 30
|
||||||
},
|
},
|
||||||
tabletScreenContent: {
|
tabletScreenContent: {
|
||||||
|
|
|
@ -322,7 +322,7 @@ class TeamChannelsView extends React.Component<ITeamChannelsViewProps, ITeamChan
|
||||||
onPressItem = debounce(
|
onPressItem = debounce(
|
||||||
async (item: IItem) => {
|
async (item: IItem) => {
|
||||||
logEvent(events.TC_GO_ROOM);
|
logEvent(events.TC_GO_ROOM);
|
||||||
const { navigation, isMasterDetail } = this.props;
|
const { isMasterDetail } = this.props;
|
||||||
try {
|
try {
|
||||||
let params = {};
|
let params = {};
|
||||||
const result = await Services.getRoomInfo(item._id);
|
const result = await Services.getRoomInfo(item._id);
|
||||||
|
@ -335,10 +335,7 @@ class TeamChannelsView extends React.Component<ITeamChannelsViewProps, ITeamChan
|
||||||
teamId: result.room.teamId
|
teamId: result.room.teamId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (isMasterDetail) {
|
goRoom({ item: params, isMasterDetail, popToRoot: !!isMasterDetail });
|
||||||
navigation.pop();
|
|
||||||
}
|
|
||||||
goRoom({ item: params, isMasterDetail, navigationMethod: navigation.push });
|
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (e.data.error === 'not-allowed') {
|
if (e.data.error === 'not-allowed') {
|
||||||
showErrorAlert(I18n.t('error-not-allowed'));
|
showErrorAlert(I18n.t('error-not-allowed'));
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
import { expect } from 'detox';
|
||||||
|
|
||||||
|
import data from '../../data';
|
||||||
|
import { navigateToLogin, login, sleep, tapBack } from '../../helpers/app';
|
||||||
|
import { sendMessage, post } from '../../helpers/data_setup';
|
||||||
|
|
||||||
|
describe('InApp Notification', () => {
|
||||||
|
let dmCreatedRid: string;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
|
||||||
|
await navigateToLogin();
|
||||||
|
await login(data.users.regular.username, data.users.regular.password);
|
||||||
|
const result = await post(`im.create`, { username: data.users.alternate.username });
|
||||||
|
dmCreatedRid = result.data.room.rid;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('receive in RoomsListView', () => {
|
||||||
|
const text = 'Message in DM';
|
||||||
|
it('should have rooms list screen', async () => {
|
||||||
|
await expect(element(by.id('rooms-list-view'))).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should send direct message from user alternate to user regular', async () => {
|
||||||
|
await sleep(1000);
|
||||||
|
await sendMessage(data.users.alternate, dmCreatedRid, text);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should tap on InApp Notification', async () => {
|
||||||
|
await waitFor(element(by.id(`in-app-notification-${text}`)))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(2000);
|
||||||
|
await sleep(500);
|
||||||
|
await element(by.id(`in-app-notification-${text}`)).tap();
|
||||||
|
await sleep(500);
|
||||||
|
await expect(element(by.id('room-header'))).toExist();
|
||||||
|
await expect(element(by.id(`room-view-title-${data.users.alternate.username}`))).toExist();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('receive in another room', () => {
|
||||||
|
const text = 'Another msg';
|
||||||
|
it('should back to RoomsListView and open the channel Detox Public', async () => {
|
||||||
|
await tapBack();
|
||||||
|
await sleep(500);
|
||||||
|
await element(by.id(`rooms-list-view-item-${data.userRegularChannels.detoxpublic.name}`)).tap();
|
||||||
|
await waitFor(element(by.id('room-view')))
|
||||||
|
.toBeVisible()
|
||||||
|
.withTimeout(5000);
|
||||||
|
await expect(element(by.id(`room-view-title-${data.userRegularChannels.detoxpublic.name}`))).toExist();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should receive and tap InAppNotification in another room', async () => {
|
||||||
|
await sendMessage(data.users.alternate, dmCreatedRid, text);
|
||||||
|
await waitFor(element(by.id(`in-app-notification-${text}`)))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(2000);
|
||||||
|
await sleep(500);
|
||||||
|
await element(by.id(`in-app-notification-${text}`)).tap();
|
||||||
|
await sleep(500);
|
||||||
|
await expect(element(by.id('room-header'))).toExist();
|
||||||
|
await expect(element(by.id(`room-view-title-${data.users.alternate.username}`))).toExist();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should back to RoomsListView', async () => {
|
||||||
|
await tapBack();
|
||||||
|
await sleep(500);
|
||||||
|
await expect(element(by.id('rooms-list-view'))).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -200,7 +200,7 @@ describe('Room', () => {
|
||||||
const expectThreadMessages = async (message: string) => {
|
const expectThreadMessages = async (message: string) => {
|
||||||
await waitFor(element(by.id('room-view-title-thread 1')))
|
await waitFor(element(by.id('room-view-title-thread 1')))
|
||||||
.toExist()
|
.toExist()
|
||||||
.withTimeout(5000);
|
.withTimeout(10000);
|
||||||
await waitFor(element(by[textMatcher](message)).atIndex(0))
|
await waitFor(element(by[textMatcher](message)).atIndex(0))
|
||||||
.toExist()
|
.toExist()
|
||||||
.withTimeout(10000);
|
.withTimeout(10000);
|
||||||
|
|
|
@ -199,6 +199,14 @@ describe('Team', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add existing channel to team', async () => {
|
it('should add existing channel to team', async () => {
|
||||||
|
await navigateToRoom(team);
|
||||||
|
await waitFor(element(by.id('room-view-header-team-channels')))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(5000);
|
||||||
|
await element(by.id('room-view-header-team-channels')).tap();
|
||||||
|
await waitFor(element(by.id('team-channels-view')))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(5000);
|
||||||
await element(by.id('team-channels-view-create')).tap();
|
await element(by.id('team-channels-view-create')).tap();
|
||||||
await waitFor(element(by.id('add-channel-team-view')))
|
await waitFor(element(by.id('add-channel-team-view')))
|
||||||
.toExist()
|
.toExist()
|
||||||
|
|
|
@ -2,6 +2,8 @@ PODS:
|
||||||
- boost (1.76.0)
|
- boost (1.76.0)
|
||||||
- BugsnagReactNative (7.10.5):
|
- BugsnagReactNative (7.10.5):
|
||||||
- React-Core
|
- React-Core
|
||||||
|
- BVLinearGradient (2.6.2):
|
||||||
|
- React-Core
|
||||||
- DoubleConversion (1.1.6)
|
- DoubleConversion (1.1.6)
|
||||||
- EXAppleAuthentication (4.2.1):
|
- EXAppleAuthentication (4.2.1):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
|
@ -123,9 +125,9 @@ PODS:
|
||||||
- libwebp/mux (1.2.1):
|
- libwebp/mux (1.2.1):
|
||||||
- libwebp/demux
|
- libwebp/demux
|
||||||
- libwebp/webp (1.2.1)
|
- libwebp/webp (1.2.1)
|
||||||
- MMKV (1.2.10):
|
- MMKV (1.2.13):
|
||||||
- MMKVCore (~> 1.2.10)
|
- MMKVCore (~> 1.2.13)
|
||||||
- MMKVCore (1.2.13)
|
- MMKVCore (1.2.14)
|
||||||
- nanopb (2.30908.0):
|
- nanopb (2.30908.0):
|
||||||
- nanopb/decode (= 2.30908.0)
|
- nanopb/decode (= 2.30908.0)
|
||||||
- nanopb/encode (= 2.30908.0)
|
- nanopb/encode (= 2.30908.0)
|
||||||
|
@ -370,8 +372,8 @@ PODS:
|
||||||
- react-native-jitsi-meet (3.6.0):
|
- react-native-jitsi-meet (3.6.0):
|
||||||
- JitsiMeetSDK (= 3.6.0)
|
- JitsiMeetSDK (= 3.6.0)
|
||||||
- React
|
- React
|
||||||
- react-native-mmkv-storage (0.7.6):
|
- react-native-mmkv-storage (0.8.0):
|
||||||
- MMKV (= 1.2.10)
|
- MMKV (= 1.2.13)
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-netinfo (6.0.0):
|
- react-native-netinfo (6.0.0):
|
||||||
- React-Core
|
- React-Core
|
||||||
|
@ -498,8 +500,8 @@ PODS:
|
||||||
- React-Core
|
- React-Core
|
||||||
- RNCClipboard (1.8.5):
|
- RNCClipboard (1.8.5):
|
||||||
- React-Core
|
- React-Core
|
||||||
- RNCMaskedView (0.1.11):
|
- RNCMaskedView (0.2.8):
|
||||||
- React
|
- React-Core
|
||||||
- RNConfigReader (1.0.0):
|
- RNConfigReader (1.0.0):
|
||||||
- React
|
- React
|
||||||
- RNCPicker (1.8.1):
|
- RNCPicker (1.8.1):
|
||||||
|
@ -592,6 +594,7 @@ PODS:
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
|
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
|
||||||
- "BugsnagReactNative (from `../node_modules/@bugsnag/react-native`)"
|
- "BugsnagReactNative (from `../node_modules/@bugsnag/react-native`)"
|
||||||
|
- BVLinearGradient (from `../node_modules/react-native-linear-gradient`)
|
||||||
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
||||||
- EXAppleAuthentication (from `../node_modules/expo-apple-authentication/ios`)
|
- EXAppleAuthentication (from `../node_modules/expo-apple-authentication/ios`)
|
||||||
- EXAV (from `../node_modules/expo-av/ios`)
|
- EXAV (from `../node_modules/expo-av/ios`)
|
||||||
|
@ -662,7 +665,7 @@ DEPENDENCIES:
|
||||||
- RNBootSplash (from `../node_modules/react-native-bootsplash`)
|
- RNBootSplash (from `../node_modules/react-native-bootsplash`)
|
||||||
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
|
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
|
||||||
- "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)"
|
- "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)"
|
||||||
- "RNCMaskedView (from `../node_modules/@react-native-community/masked-view`)"
|
- "RNCMaskedView (from `../node_modules/@react-native-masked-view/masked-view`)"
|
||||||
- RNConfigReader (from `../node_modules/react-native-config-reader`)
|
- RNConfigReader (from `../node_modules/react-native-config-reader`)
|
||||||
- "RNCPicker (from `../node_modules/@react-native-community/picker`)"
|
- "RNCPicker (from `../node_modules/@react-native-community/picker`)"
|
||||||
- "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)"
|
- "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)"
|
||||||
|
@ -715,6 +718,8 @@ EXTERNAL SOURCES:
|
||||||
:podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
|
:podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
|
||||||
BugsnagReactNative:
|
BugsnagReactNative:
|
||||||
:path: "../node_modules/@bugsnag/react-native"
|
:path: "../node_modules/@bugsnag/react-native"
|
||||||
|
BVLinearGradient:
|
||||||
|
:path: "../node_modules/react-native-linear-gradient"
|
||||||
DoubleConversion:
|
DoubleConversion:
|
||||||
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
|
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
|
||||||
EXAppleAuthentication:
|
EXAppleAuthentication:
|
||||||
|
@ -848,7 +853,7 @@ EXTERNAL SOURCES:
|
||||||
RNCClipboard:
|
RNCClipboard:
|
||||||
:path: "../node_modules/@react-native-clipboard/clipboard"
|
:path: "../node_modules/@react-native-clipboard/clipboard"
|
||||||
RNCMaskedView:
|
RNCMaskedView:
|
||||||
:path: "../node_modules/@react-native-community/masked-view"
|
:path: "../node_modules/@react-native-masked-view/masked-view"
|
||||||
RNConfigReader:
|
RNConfigReader:
|
||||||
:path: "../node_modules/react-native-config-reader"
|
:path: "../node_modules/react-native-config-reader"
|
||||||
RNCPicker:
|
RNCPicker:
|
||||||
|
@ -900,6 +905,7 @@ CHECKOUT OPTIONS:
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
boost: a7c83b31436843459a1961bfd74b96033dc77234
|
boost: a7c83b31436843459a1961bfd74b96033dc77234
|
||||||
BugsnagReactNative: a97b3132c1854fd7bf92350fabd505e3ebdd7829
|
BugsnagReactNative: a97b3132c1854fd7bf92350fabd505e3ebdd7829
|
||||||
|
BVLinearGradient: 34a999fda29036898a09c6a6b728b0b4189e1a44
|
||||||
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
|
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
|
||||||
EXAppleAuthentication: 709a807fe7f48ac6986a2ceed206ee6a8baf28df
|
EXAppleAuthentication: 709a807fe7f48ac6986a2ceed206ee6a8baf28df
|
||||||
EXAV: 88f61c5af8415715b7ee51f084c1020235b85c56
|
EXAV: 88f61c5af8415715b7ee51f084c1020235b85c56
|
||||||
|
@ -932,8 +938,8 @@ SPEC CHECKSUMS:
|
||||||
KeyCommands: f66c535f698ed14b3d3a4e58859d79a827ea907e
|
KeyCommands: f66c535f698ed14b3d3a4e58859d79a827ea907e
|
||||||
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
||||||
libwebp: 98a37e597e40bfdb4c911fc98f2c53d0b12d05fc
|
libwebp: 98a37e597e40bfdb4c911fc98f2c53d0b12d05fc
|
||||||
MMKV: 76033b9ace2006623308910a3afcc0e25eba3140
|
MMKV: aac95d817a100479445633f2b3ed8961b4ac5043
|
||||||
MMKVCore: 3388952ded307e41b3ed8a05892736a236ed1b8e
|
MMKVCore: 89f5c8a66bba2dcd551779dea4d412eeec8ff5bb
|
||||||
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
|
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
|
||||||
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
|
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
|
||||||
PromisesObjC: 99b6f43f9e1044bd87a95a60beff28c2c44ddb72
|
PromisesObjC: 99b6f43f9e1044bd87a95a60beff28c2c44ddb72
|
||||||
|
@ -957,7 +963,7 @@ SPEC CHECKSUMS:
|
||||||
react-native-cookies: f54fcded06bb0cda05c11d86788020b43528a26c
|
react-native-cookies: f54fcded06bb0cda05c11d86788020b43528a26c
|
||||||
react-native-document-picker: f5ec1a712ca2a975c233117f044817bb8393cad4
|
react-native-document-picker: f5ec1a712ca2a975c233117f044817bb8393cad4
|
||||||
react-native-jitsi-meet: 3e3ac5d0445091154119f94342efd55c8b1124ce
|
react-native-jitsi-meet: 3e3ac5d0445091154119f94342efd55c8b1124ce
|
||||||
react-native-mmkv-storage: ba38e5e813ea3d5d37af834a9d867b1ce10aa96d
|
react-native-mmkv-storage: 8ba3c0216a6df283ece11205b442a3e435aec4e5
|
||||||
react-native-netinfo: e849fc21ca2f4128a5726c801a82fc6f4a6db50d
|
react-native-netinfo: e849fc21ca2f4128a5726c801a82fc6f4a6db50d
|
||||||
react-native-notifications: 83b4fd4a127a6c918fc846cae90da60f84819e44
|
react-native-notifications: 83b4fd4a127a6c918fc846cae90da60f84819e44
|
||||||
react-native-orientation-locker: f0ca1a8e5031dab6b74bfb4ab33a17ed2c2fcb0d
|
react-native-orientation-locker: f0ca1a8e5031dab6b74bfb4ab33a17ed2c2fcb0d
|
||||||
|
@ -985,7 +991,7 @@ SPEC CHECKSUMS:
|
||||||
RNBootSplash: 7e91ea56c7010aae487489789dbe212e8c905a0c
|
RNBootSplash: 7e91ea56c7010aae487489789dbe212e8c905a0c
|
||||||
RNCAsyncStorage: 8616bd5a58af409453ea4e1b246521bb76578d60
|
RNCAsyncStorage: 8616bd5a58af409453ea4e1b246521bb76578d60
|
||||||
RNCClipboard: cc054ad1e8a33d2a74cd13e565588b4ca928d8fd
|
RNCClipboard: cc054ad1e8a33d2a74cd13e565588b4ca928d8fd
|
||||||
RNCMaskedView: 0e1bc4bfa8365eba5fbbb71e07fbdc0555249489
|
RNCMaskedView: bc0170f389056201c82a55e242e5d90070e18e5a
|
||||||
RNConfigReader: 396da6a6444182a76e8ae0930b9436c7575045cb
|
RNConfigReader: 396da6a6444182a76e8ae0930b9436c7575045cb
|
||||||
RNCPicker: 914b557e20b3b8317b084aca9ff4b4edb95f61e4
|
RNCPicker: 914b557e20b3b8317b084aca9ff4b4edb95f61e4
|
||||||
RNDateTimePicker: 7658208086d86d09e1627b5c34ba0cf237c60140
|
RNDateTimePicker: 7658208086d86d09e1627b5c34ba0cf237c60140
|
||||||
|
|
BIN
ios/custom.ttf
BIN
ios/custom.ttf
Binary file not shown.
|
@ -40,7 +40,6 @@
|
||||||
"@react-native-community/cameraroll": "4.1.2",
|
"@react-native-community/cameraroll": "4.1.2",
|
||||||
"@react-native-community/datetimepicker": "3.5.2",
|
"@react-native-community/datetimepicker": "3.5.2",
|
||||||
"@react-native-community/hooks": "2.6.0",
|
"@react-native-community/hooks": "2.6.0",
|
||||||
"@react-native-community/masked-view": "0.1.11",
|
|
||||||
"@react-native-community/netinfo": "6.0.0",
|
"@react-native-community/netinfo": "6.0.0",
|
||||||
"@react-native-community/picker": "^1.8.1",
|
"@react-native-community/picker": "^1.8.1",
|
||||||
"@react-native-community/slider": "4.2.2",
|
"@react-native-community/slider": "4.2.2",
|
||||||
|
@ -48,13 +47,14 @@
|
||||||
"@react-native-firebase/analytics": "^14.11.0",
|
"@react-native-firebase/analytics": "^14.11.0",
|
||||||
"@react-native-firebase/app": "^14.11.0",
|
"@react-native-firebase/app": "^14.11.0",
|
||||||
"@react-native-firebase/crashlytics": "^14.11.0",
|
"@react-native-firebase/crashlytics": "^14.11.0",
|
||||||
|
"@react-native-masked-view/masked-view": "^0.2.8",
|
||||||
"@react-navigation/drawer": "6.4.1",
|
"@react-navigation/drawer": "6.4.1",
|
||||||
"@react-navigation/elements": "^1.3.6",
|
"@react-navigation/elements": "^1.3.6",
|
||||||
"@react-navigation/native": "6.0.10",
|
"@react-navigation/native": "6.0.10",
|
||||||
"@react-navigation/stack": "6.2.1",
|
"@react-navigation/stack": "6.2.1",
|
||||||
"@rocket.chat/message-parser": "^0.31.14",
|
"@rocket.chat/message-parser": "^0.31.14",
|
||||||
"@rocket.chat/sdk": "RocketChat/Rocket.Chat.js.SDK#mobile",
|
"@rocket.chat/sdk": "RocketChat/Rocket.Chat.js.SDK#mobile",
|
||||||
"@rocket.chat/ui-kit": "^0.31.13",
|
"@rocket.chat/ui-kit": "^0.31.19",
|
||||||
"bytebuffer": "^5.0.1",
|
"bytebuffer": "^5.0.1",
|
||||||
"color2k": "1.2.4",
|
"color2k": "1.2.4",
|
||||||
"commonmark": "git+https://github.com/RocketChat/commonmark.js.git",
|
"commonmark": "git+https://github.com/RocketChat/commonmark.js.git",
|
||||||
|
@ -99,10 +99,11 @@
|
||||||
"react-native-image-progress": "^1.1.1",
|
"react-native-image-progress": "^1.1.1",
|
||||||
"react-native-jitsi-meet": "RocketChat/react-native-jitsi-meet",
|
"react-native-jitsi-meet": "RocketChat/react-native-jitsi-meet",
|
||||||
"react-native-keycommands": "2.0.3",
|
"react-native-keycommands": "2.0.3",
|
||||||
|
"react-native-linear-gradient": "^2.6.2",
|
||||||
"react-native-localize": "2.1.1",
|
"react-native-localize": "2.1.1",
|
||||||
"react-native-math-view": "^3.9.5",
|
"react-native-math-view": "^3.9.5",
|
||||||
"react-native-mime-types": "2.3.0",
|
"react-native-mime-types": "2.3.0",
|
||||||
"react-native-mmkv-storage": "^0.7.6",
|
"react-native-mmkv-storage": "^0.8.0",
|
||||||
"react-native-modal": "13.0.1",
|
"react-native-modal": "13.0.1",
|
||||||
"react-native-navigation-bar-color": "2.0.1",
|
"react-native-navigation-bar-color": "2.0.1",
|
||||||
"react-native-notifications": "^4.3.3",
|
"react-native-notifications": "^4.3.3",
|
||||||
|
@ -118,7 +119,9 @@
|
||||||
"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-slowlog": "^1.0.2",
|
"react-native-slowlog": "^1.0.2",
|
||||||
"react-native-svg": "^12.3.0",
|
"react-native-svg": "^12.3.0",
|
||||||
"react-native-ui-lib": "RocketChat/react-native-ui-lib",
|
"react-native-ui-lib": "RocketChat/react-native-ui-lib",
|
||||||
|
|
|
@ -1,55 +1,8 @@
|
||||||
diff --git a/node_modules/react-native-mmkv-storage/ios/MMKVNative.h b/node_modules/react-native-mmkv-storage/ios/MMKVNative.h
|
|
||||||
index 60a2a03..ca6fdfd 100644
|
|
||||||
--- a/node_modules/react-native-mmkv-storage/ios/MMKVNative.h
|
|
||||||
+++ b/node_modules/react-native-mmkv-storage/ios/MMKVNative.h
|
|
||||||
@@ -1,6 +1,6 @@
|
|
||||||
|
|
||||||
-#if __has_include("RCTBridgeModule.h")
|
|
||||||
-#import "RCTBridgeModule.h"
|
|
||||||
+#if __has_include(<React/RCTBridgeModule.h>)
|
|
||||||
+#import <React/RCTBridgeModule.h>
|
|
||||||
#else
|
|
||||||
#import <React/RCTBridgeModule.h>
|
|
||||||
#import <React/RCTEventEmitter.h>
|
|
||||||
diff --git a/node_modules/react-native-mmkv-storage/ios/MMKVStorage.h b/node_modules/react-native-mmkv-storage/ios/MMKVStorage.h
|
|
||||||
index b5f5823..62e2afa 100644
|
|
||||||
--- a/node_modules/react-native-mmkv-storage/ios/MMKVStorage.h
|
|
||||||
+++ b/node_modules/react-native-mmkv-storage/ios/MMKVStorage.h
|
|
||||||
@@ -1,6 +1,6 @@
|
|
||||||
|
|
||||||
-#if __has_include("RCTBridgeModule.h")
|
|
||||||
-#import "RCTBridgeModule.h"
|
|
||||||
+#if __has_include(<React/RCTBridgeModule.h>)
|
|
||||||
+#import <React/RCTBridgeModule.h>
|
|
||||||
#else
|
|
||||||
#import <React/RCTBridgeModule.h>
|
|
||||||
#import <React/RCTEventEmitter.h>
|
|
||||||
diff --git a/node_modules/react-native-mmkv-storage/ios/SecureStorage.h b/node_modules/react-native-mmkv-storage/ios/SecureStorage.h
|
|
||||||
index b804a64..291cc7e 100644
|
|
||||||
--- a/node_modules/react-native-mmkv-storage/ios/SecureStorage.h
|
|
||||||
+++ b/node_modules/react-native-mmkv-storage/ios/SecureStorage.h
|
|
||||||
@@ -1,6 +1,6 @@
|
|
||||||
|
|
||||||
-#if __has_include("RCTBridgeModule.h")
|
|
||||||
-#import "RCTBridgeModule.h"
|
|
||||||
+#if __has_include(<React/RCTBridgeModule.h>)
|
|
||||||
+#import <React/RCTBridgeModule.h>
|
|
||||||
#else
|
|
||||||
#import <React/RCTBridgeModule.h>
|
|
||||||
#endif
|
|
||||||
diff --git a/node_modules/react-native-mmkv-storage/ios/SecureStorage.m b/node_modules/react-native-mmkv-storage/ios/SecureStorage.m
|
diff --git a/node_modules/react-native-mmkv-storage/ios/SecureStorage.m b/node_modules/react-native-mmkv-storage/ios/SecureStorage.m
|
||||||
index 1c4e1c2..fd6ef68 100644
|
index dbea26b..7b2083c 100644
|
||||||
--- a/node_modules/react-native-mmkv-storage/ios/SecureStorage.m
|
--- a/node_modules/react-native-mmkv-storage/ios/SecureStorage.m
|
||||||
+++ b/node_modules/react-native-mmkv-storage/ios/SecureStorage.m
|
+++ b/node_modules/react-native-mmkv-storage/ios/SecureStorage.m
|
||||||
@@ -1,5 +1,5 @@
|
@@ -40,14 +40,14 @@ - (NSString *) getSecureKey:(NSString *)key
|
||||||
-#if __has_include("RCTBridgeModule.h")
|
|
||||||
-#import "RCTBridgeModule.h"
|
|
||||||
+#if __has_include(<React/RCTBridgeModule.h>)
|
|
||||||
+#import <React/RCTBridgeModule.h>
|
|
||||||
#else
|
|
||||||
#import <React/RCTBridgeModule.h>
|
|
||||||
#endif
|
|
||||||
@@ -45,14 +45,14 @@ - (NSString *) getSecureKey:(NSString *)key
|
|
||||||
@try {
|
@try {
|
||||||
[self handleAppUninstallation];
|
[self handleAppUninstallation];
|
||||||
NSString *value = [self searchKeychainCopyMatching:key];
|
NSString *value = [self searchKeychainCopyMatching:key];
|
||||||
|
@ -72,7 +25,7 @@ index 1c4e1c2..fd6ef68 100644
|
||||||
if (value == nil) {
|
if (value == nil) {
|
||||||
NSString* errorMessage = @"key does not present";
|
NSString* errorMessage = @"key does not present";
|
||||||
|
|
||||||
@@ -105,6 +105,9 @@ - (void) removeSecureKey:(NSString *)key
|
@@ -100,6 +100,9 @@ - (void) removeSecureKey:(NSString *)key
|
||||||
|
|
||||||
- (NSMutableDictionary *)newSearchDictionary:(NSString *)identifier {
|
- (NSMutableDictionary *)newSearchDictionary:(NSString *)identifier {
|
||||||
NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init];
|
NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init];
|
||||||
|
@ -82,7 +35,7 @@ index 1c4e1c2..fd6ef68 100644
|
||||||
if(serviceName == nil){
|
if(serviceName == nil){
|
||||||
serviceName = [[NSBundle mainBundle] bundleIdentifier];
|
serviceName = [[NSBundle mainBundle] bundleIdentifier];
|
||||||
}
|
}
|
||||||
@@ -116,6 +119,9 @@ - (NSMutableDictionary *)newSearchDictionary:(NSString *)identifier {
|
@@ -111,6 +114,9 @@ - (NSMutableDictionary *)newSearchDictionary:(NSString *)identifier {
|
||||||
[searchDictionary setObject:encodedIdentifier forKey:(id)kSecAttrAccount];
|
[searchDictionary setObject:encodedIdentifier forKey:(id)kSecAttrAccount];
|
||||||
[searchDictionary setObject:serviceName forKey:(id)kSecAttrService];
|
[searchDictionary setObject:serviceName forKey:(id)kSecAttrService];
|
||||||
|
|
||||||
|
@ -92,7 +45,7 @@ index 1c4e1c2..fd6ef68 100644
|
||||||
return searchDictionary;
|
return searchDictionary;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,11 +223,14 @@ - (void)clearSecureKeyStore
|
@@ -212,11 +218,14 @@ - (void)clearSecureKeyStore
|
||||||
|
|
||||||
- (void)handleAppUninstallation
|
- (void)handleAppUninstallation
|
||||||
{
|
{
|
|
@ -14,6 +14,11 @@ module.exports = {
|
||||||
platforms: {
|
platforms: {
|
||||||
android: null
|
android: null
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'react-native-jitsi-meet': {
|
||||||
|
platforms: {
|
||||||
|
android: null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
41
yarn.lock
41
yarn.lock
|
@ -5054,11 +5054,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@react-native-community/hooks/-/hooks-2.6.0.tgz#dd5f19601eb3684c6bcdd3df3d0c04cf44c24cff"
|
resolved "https://registry.yarnpkg.com/@react-native-community/hooks/-/hooks-2.6.0.tgz#dd5f19601eb3684c6bcdd3df3d0c04cf44c24cff"
|
||||||
integrity sha512-emBGKvhJ0h++lLJQ5ejsj+od9G67nEaihjvfSx7/JWvNrQGAhP9U0OZqgb9dkKzor9Ufaj9SGt8RNY97cGzttw==
|
integrity sha512-emBGKvhJ0h++lLJQ5ejsj+od9G67nEaihjvfSx7/JWvNrQGAhP9U0OZqgb9dkKzor9Ufaj9SGt8RNY97cGzttw==
|
||||||
|
|
||||||
"@react-native-community/masked-view@0.1.11":
|
|
||||||
version "0.1.11"
|
|
||||||
resolved "https://registry.yarnpkg.com/@react-native-community/masked-view/-/masked-view-0.1.11.tgz#2f4c6e10bee0786abff4604e39a37ded6f3980ce"
|
|
||||||
integrity sha512-rQfMIGSR/1r/SyN87+VD8xHHzDYeHaJq6elOSCAD+0iLagXkSI2pfA0LmSXP21uw5i3em7GkkRjfJ8wpqWXZNw==
|
|
||||||
|
|
||||||
"@react-native-community/netinfo@6.0.0":
|
"@react-native-community/netinfo@6.0.0":
|
||||||
version "6.0.0"
|
version "6.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@react-native-community/netinfo/-/netinfo-6.0.0.tgz#2a4d7190b508dd0c2293656c9c1aa068f6f60a71"
|
resolved "https://registry.yarnpkg.com/@react-native-community/netinfo/-/netinfo-6.0.0.tgz#2a4d7190b508dd0c2293656c9c1aa068f6f60a71"
|
||||||
|
@ -5108,6 +5103,11 @@
|
||||||
"@expo/config-plugins" "^4.1.5"
|
"@expo/config-plugins" "^4.1.5"
|
||||||
stacktrace-js "^2.0.0"
|
stacktrace-js "^2.0.0"
|
||||||
|
|
||||||
|
"@react-native-masked-view/masked-view@^0.2.8":
|
||||||
|
version "0.2.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@react-native-masked-view/masked-view/-/masked-view-0.2.8.tgz#34405a4361882dae7c81b1b771fe9f5fbd545a97"
|
||||||
|
integrity sha512-+1holBPDF1yi/y0uc1WB6lA5tSNHhM7PpTMapT3ypvSnKQ9+C6sy/zfjxNxRA/llBQ1Ci6f94EaK56UCKs5lTA==
|
||||||
|
|
||||||
"@react-native-picker/picker@^1.8.3":
|
"@react-native-picker/picker@^1.8.3":
|
||||||
version "1.16.1"
|
version "1.16.1"
|
||||||
resolved "https://registry.yarnpkg.com/@react-native-picker/picker/-/picker-1.16.1.tgz#cc5d05b0d651445afa519c67824d8af3e43fa10c"
|
resolved "https://registry.yarnpkg.com/@react-native-picker/picker/-/picker-1.16.1.tgz#cc5d05b0d651445afa519c67824d8af3e43fa10c"
|
||||||
|
@ -5250,10 +5250,10 @@
|
||||||
tiny-events "^1.0.1"
|
tiny-events "^1.0.1"
|
||||||
universal-websocket-client "^1.0.2"
|
universal-websocket-client "^1.0.2"
|
||||||
|
|
||||||
"@rocket.chat/ui-kit@^0.31.13":
|
"@rocket.chat/ui-kit@^0.31.19":
|
||||||
version "0.31.13"
|
version "0.31.19"
|
||||||
resolved "https://registry.yarnpkg.com/@rocket.chat/ui-kit/-/ui-kit-0.31.13.tgz#bd1c8a8726a74e13d072bb6f67cb2a6d3ba66e3b"
|
resolved "https://registry.yarnpkg.com/@rocket.chat/ui-kit/-/ui-kit-0.31.19.tgz#737103123bc7e635382217eef75965b7e0f44703"
|
||||||
integrity sha512-IWNIRca0fP8Ecka3DIvqZKF7PbjcUaS+LkWUbMa+9lkX6MeumZrpFqsf7MKUpT+ihJr0RD4lBybmEgFH2syDbw==
|
integrity sha512-8zRKQ5CoC4hIuYHVheO0d7etX9oizmM18fu99r5s/deciL/0MRWocdb4H/QsmbsNrkKCO6Z6wr7f9zzJCNTRHg==
|
||||||
|
|
||||||
"@segment/loosely-validate-event@^2.0.0":
|
"@segment/loosely-validate-event@^2.0.0":
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
|
@ -17285,6 +17285,11 @@ react-native-keycommands@2.0.3:
|
||||||
resolved "https://registry.yarnpkg.com/react-native-keycommands/-/react-native-keycommands-2.0.3.tgz#09b799c1f70832e5cd9fbdb712ddaa3ee683f70e"
|
resolved "https://registry.yarnpkg.com/react-native-keycommands/-/react-native-keycommands-2.0.3.tgz#09b799c1f70832e5cd9fbdb712ddaa3ee683f70e"
|
||||||
integrity sha512-s03K8JvCnfLhBg10Y2aRH3Bp9Uw9QOEr0uzuIj9OkgjjTB8/b+T4K5LSCxGuIAD30IxsEZvGZKjP1DzEMxaRhQ==
|
integrity sha512-s03K8JvCnfLhBg10Y2aRH3Bp9Uw9QOEr0uzuIj9OkgjjTB8/b+T4K5LSCxGuIAD30IxsEZvGZKjP1DzEMxaRhQ==
|
||||||
|
|
||||||
|
react-native-linear-gradient@^2.6.2:
|
||||||
|
version "2.6.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-native-linear-gradient/-/react-native-linear-gradient-2.6.2.tgz#56598a76832724b2afa7889747635b5c80948f38"
|
||||||
|
integrity sha512-Z8Xxvupsex+9BBFoSYS87bilNPWcRfRsGC0cpJk72Nxb5p2nEkGSBv73xZbEHnW2mUFvP+huYxrVvjZkr/gRjQ==
|
||||||
|
|
||||||
react-native-localize@2.1.1:
|
react-native-localize@2.1.1:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-native-localize/-/react-native-localize-2.1.1.tgz#da8f8776991d2b748708c408db05152602cefb38"
|
resolved "https://registry.yarnpkg.com/react-native-localize/-/react-native-localize-2.1.1.tgz#da8f8776991d2b748708c408db05152602cefb38"
|
||||||
|
@ -17307,10 +17312,10 @@ react-native-mime-types@2.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
mime-db "~1.37.0"
|
mime-db "~1.37.0"
|
||||||
|
|
||||||
react-native-mmkv-storage@^0.7.6:
|
react-native-mmkv-storage@^0.8.0:
|
||||||
version "0.7.6"
|
version "0.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-native-mmkv-storage/-/react-native-mmkv-storage-0.7.6.tgz#c4b2d6d342efda91f69edb6ba4b9c0ff6c34d70c"
|
resolved "https://registry.yarnpkg.com/react-native-mmkv-storage/-/react-native-mmkv-storage-0.8.0.tgz#2ece5f441a6a818224a85315c68e105e0d6446b5"
|
||||||
integrity sha512-Uxc8U3vRJMAeLIu9Dr8VpbaaCUlMd4vJfkEme2sXh2GGCYrbXqrE3XPLjtuA/fSd2VBKZNFBkJhyf4RzU9VX3A==
|
integrity sha512-L782Le5IuDYlDLGXF/qimbnzvkbYsSmV5PiDleo1DSS8Kr8Q31UK8YWtUICrDGQ9Fm7Xx4PxP9ffe2XzGeWaHQ==
|
||||||
|
|
||||||
react-native-modal@13.0.1:
|
react-native-modal@13.0.1:
|
||||||
version "13.0.1"
|
version "13.0.1"
|
||||||
|
@ -17429,6 +17434,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"
|
||||||
|
@ -17436,6 +17446,11 @@ react-native-simple-crypto@RocketChat/react-native-simple-crypto#0.5.1:
|
||||||
base64-js "^1.3.0"
|
base64-js "^1.3.0"
|
||||||
hex-lite "^1.5.0"
|
hex-lite "^1.5.0"
|
||||||
|
|
||||||
|
react-native-skeleton-placeholder@^5.2.3:
|
||||||
|
version "5.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-native-skeleton-placeholder/-/react-native-skeleton-placeholder-5.2.3.tgz#2dddf1f84d43110b90c22f715b2dbbe2c54732e1"
|
||||||
|
integrity sha512-nikmTfex3oydnZ4prV62KxibMvcu2l2NegsHGtXhsWwFIX5QaKneBohP7etinUq/c2PkSr3ZlfqooDG2yIHRdg==
|
||||||
|
|
||||||
react-native-slider@^0.11.0:
|
react-native-slider@^0.11.0:
|
||||||
version "0.11.0"
|
version "0.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-native-slider/-/react-native-slider-0.11.0.tgz#b68a0bc43c8422b24cd57947cc5ac2bcdb58fadc"
|
resolved "https://registry.yarnpkg.com/react-native-slider/-/react-native-slider-0.11.0.tgz#b68a0bc43c8422b24cd57947cc5ac2bcdb58fadc"
|
||||||
|
|
Loading…
Reference in New Issue