Compare commits
13 Commits
develop
...
start-a-ne
Author | SHA1 | Date |
---|---|---|
GleidsonDaniel | 14481b368d | |
GleidsonDaniel | 1d144cf19c | |
GleidsonDaniel | 60defec633 | |
GleidsonDaniel | 4c4a0ba9b9 | |
GleidsonDaniel | 76c41474a6 | |
GleidsonDaniel | c6658d570b | |
GleidsonDaniel | 0cb9897bdf | |
GleidsonDaniel | d47de6193b | |
GleidsonDaniel | 1b3796ef17 | |
GleidsonDaniel | 9722550c9f | |
GleidsonDaniel | d36d5fd4e3 | |
GleidsonDaniel | 7a1aa58925 | |
GleidsonDaniel | 1fb060a982 |
File diff suppressed because one or more lines are too long
|
@ -62,6 +62,11 @@ allprojects {
|
||||||
url "$rootDir/../node_modules/detox/Detox-android"
|
url "$rootDir/../node_modules/detox/Detox-android"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maven {
|
||||||
|
// expo-camera bundles a custom com.google.android:cameraview
|
||||||
|
url "$rootDir/../node_modules/expo-camera/android/maven"
|
||||||
|
}
|
||||||
|
|
||||||
mavenCentral {
|
mavenCentral {
|
||||||
content {
|
content {
|
||||||
excludeGroup "com.facebook.react"
|
excludeGroup "com.facebook.react"
|
||||||
|
|
|
@ -84,3 +84,10 @@ export const ENCRYPTION = createRequestTypes('ENCRYPTION', ['INIT', 'STOP', 'DEC
|
||||||
|
|
||||||
export const PERMISSIONS = createRequestTypes('PERMISSIONS', ['SET', 'UPDATE']);
|
export const PERMISSIONS = createRequestTypes('PERMISSIONS', ['SET', 'UPDATE']);
|
||||||
export const ROLES = createRequestTypes('ROLES', ['SET', 'UPDATE', 'REMOVE']);
|
export const ROLES = createRequestTypes('ROLES', ['SET', 'UPDATE', 'REMOVE']);
|
||||||
|
export const VIDEO_CONF = createRequestTypes('VIDEO_CONF', [
|
||||||
|
'HANDLE_INCOMING_WEBSOCKET_MESSAGES',
|
||||||
|
'SET',
|
||||||
|
'REMOVE',
|
||||||
|
'CLEAR',
|
||||||
|
'INIT_CALL'
|
||||||
|
]);
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
import { ICallInfo } from '../reducers/videoConf';
|
||||||
|
import { VIDEO_CONF } from './actionsTypes';
|
||||||
|
|
||||||
|
interface IHandleVideoConfIncomingWebsocketMessages extends Action {
|
||||||
|
data: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TCallProps = { mic: boolean; cam: boolean; direct: boolean; roomId: string };
|
||||||
|
|
||||||
|
export interface IVideoConfGenericAction extends Action {
|
||||||
|
payload: ICallInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TActionVideoConf = IHandleVideoConfIncomingWebsocketMessages & IVideoConfGenericAction & Action;
|
||||||
|
|
||||||
|
export function handleVideoConfIncomingWebsocketMessages(data: any): IHandleVideoConfIncomingWebsocketMessages {
|
||||||
|
return {
|
||||||
|
type: VIDEO_CONF.HANDLE_INCOMING_WEBSOCKET_MESSAGES,
|
||||||
|
data
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setVideoConfCall(payload: ICallInfo): IVideoConfGenericAction {
|
||||||
|
return {
|
||||||
|
type: VIDEO_CONF.SET,
|
||||||
|
payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeVideoConfCall(payload: ICallInfo): IVideoConfGenericAction {
|
||||||
|
return {
|
||||||
|
type: VIDEO_CONF.REMOVE,
|
||||||
|
payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearVideoConfCalls(): Action {
|
||||||
|
return {
|
||||||
|
type: VIDEO_CONF.CLEAR
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initVideoCall(payload: TCallProps): Action & { payload: TCallProps } {
|
||||||
|
return {
|
||||||
|
type: VIDEO_CONF.INIT_CALL,
|
||||||
|
payload
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,31 +1,34 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useWindowDimensions } from 'react-native';
|
||||||
import { FlatList } from 'react-native-gesture-handler';
|
import { FlatList } from 'react-native-gesture-handler';
|
||||||
|
|
||||||
import { IEmoji } from '../../definitions/IEmoji';
|
|
||||||
import scrollPersistTaps from '../../lib/methods/helpers/scrollPersistTaps';
|
|
||||||
import { PressableEmoji } from './PressableEmoji';
|
|
||||||
import { EMOJI_BUTTON_SIZE } from './styles';
|
import { EMOJI_BUTTON_SIZE } from './styles';
|
||||||
|
import scrollPersistTaps from '../../lib/methods/helpers/scrollPersistTaps';
|
||||||
|
import { IEmoji } from '../../definitions/IEmoji';
|
||||||
|
import { PressableEmoji } from './PressableEmoji';
|
||||||
|
|
||||||
interface IEmojiCategoryProps {
|
interface IEmojiCategoryProps {
|
||||||
emojis: IEmoji[];
|
emojis: IEmoji[];
|
||||||
onEmojiSelected: (emoji: IEmoji) => void;
|
onEmojiSelected: (emoji: IEmoji) => void;
|
||||||
tabLabel?: string; // needed for react-native-scrollable-tab-view only
|
tabLabel?: string; // needed for react-native-scrollable-tab-view only
|
||||||
parentWidth: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const EmojiCategory = ({ onEmojiSelected, emojis, parentWidth }: IEmojiCategoryProps): React.ReactElement | null => {
|
const EmojiCategory = ({ onEmojiSelected, emojis }: IEmojiCategoryProps): React.ReactElement | null => {
|
||||||
if (!parentWidth) {
|
const { width } = useWindowDimensions();
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const numColumns = Math.trunc(parentWidth / EMOJI_BUTTON_SIZE);
|
const numColumns = Math.trunc(width / EMOJI_BUTTON_SIZE);
|
||||||
const marginHorizontal = (parentWidth % EMOJI_BUTTON_SIZE) / 2;
|
const marginHorizontal = (width % EMOJI_BUTTON_SIZE) / 2;
|
||||||
|
|
||||||
const renderItem = ({ item }: { item: IEmoji }) => <PressableEmoji emoji={item} onPress={onEmojiSelected} />;
|
const renderItem = ({ item }: { item: IEmoji }) => <PressableEmoji emoji={item} onPress={onEmojiSelected} />;
|
||||||
|
|
||||||
|
if (!width) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FlatList
|
<FlatList
|
||||||
key={`emoji-category-${parentWidth}`}
|
// needed to update the numColumns when the width changes
|
||||||
|
key={`emoji-category-${width}`}
|
||||||
keyExtractor={item => (typeof item === 'string' ? item : item.name)}
|
keyExtractor={item => (typeof item === 'string' ? item : item.name)}
|
||||||
data={emojis}
|
data={emojis}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import ScrollableTabView from 'react-native-scrollable-tab-view';
|
import ScrollableTabView from 'react-native-scrollable-tab-view';
|
||||||
|
|
||||||
|
@ -20,8 +20,6 @@ const EmojiPicker = ({
|
||||||
searchedEmojis = []
|
searchedEmojis = []
|
||||||
}: IEmojiPickerProps): React.ReactElement | null => {
|
}: IEmojiPickerProps): React.ReactElement | null => {
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
const [parentWidth, setParentWidth] = useState(0);
|
|
||||||
|
|
||||||
const { frequentlyUsed, loaded } = useFrequentlyUsedEmoji();
|
const { frequentlyUsed, loaded } = useFrequentlyUsedEmoji();
|
||||||
|
|
||||||
const allCustomEmojis: ICustomEmojis = useAppSelector(
|
const allCustomEmojis: ICustomEmojis = useAppSelector(
|
||||||
|
@ -52,14 +50,7 @@ const EmojiPicker = ({
|
||||||
if (!emojis.length) {
|
if (!emojis.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return <EmojiCategory emojis={emojis} onEmojiSelected={(emoji: IEmoji) => handleEmojiSelect(emoji)} tabLabel={label} />;
|
||||||
<EmojiCategory
|
|
||||||
parentWidth={parentWidth}
|
|
||||||
emojis={emojis}
|
|
||||||
onEmojiSelected={(emoji: IEmoji) => handleEmojiSelect(emoji)}
|
|
||||||
tabLabel={label}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!loaded) {
|
if (!loaded) {
|
||||||
|
@ -67,13 +58,9 @@ const EmojiPicker = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.emojiPickerContainer} onLayout={e => setParentWidth(e.nativeEvent.layout.width)}>
|
<View style={styles.emojiPickerContainer}>
|
||||||
{searching ? (
|
{searching ? (
|
||||||
<EmojiCategory
|
<EmojiCategory emojis={searchedEmojis} onEmojiSelected={(emoji: IEmoji) => handleEmojiSelect(emoji)} />
|
||||||
emojis={searchedEmojis}
|
|
||||||
onEmojiSelected={(emoji: IEmoji) => handleEmojiSelect(emoji)}
|
|
||||||
parentWidth={parentWidth}
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<ScrollableTabView
|
<ScrollableTabView
|
||||||
renderTabBar={() => <TabBar />}
|
renderTabBar={() => <TabBar />}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Camera, CameraType } from 'expo-camera';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Text, View } from 'react-native';
|
import { Text, View } from 'react-native';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
import Touchable from 'react-native-platform-touchable';
|
||||||
|
@ -7,7 +8,6 @@ import { getSubscriptionByRoomId } from '../../../../lib/database/services/Subsc
|
||||||
import { useAppSelector } from '../../../../lib/hooks';
|
import { useAppSelector } from '../../../../lib/hooks';
|
||||||
import { getRoomAvatar, getUidDirectMessage } from '../../../../lib/methods/helpers';
|
import { getRoomAvatar, getUidDirectMessage } from '../../../../lib/methods/helpers';
|
||||||
import { useTheme } from '../../../../theme';
|
import { useTheme } from '../../../../theme';
|
||||||
import { useActionSheet } from '../../../ActionSheet';
|
|
||||||
import AvatarContainer from '../../../Avatar';
|
import AvatarContainer from '../../../Avatar';
|
||||||
import Button from '../../../Button';
|
import Button from '../../../Button';
|
||||||
import { CustomIcon } from '../../../CustomIcon';
|
import { CustomIcon } from '../../../CustomIcon';
|
||||||
|
@ -15,16 +15,20 @@ import { BUTTON_HIT_SLOP } from '../../../message/utils';
|
||||||
import StatusContainer from '../../../Status';
|
import StatusContainer from '../../../Status';
|
||||||
import useStyle from './styles';
|
import useStyle from './styles';
|
||||||
|
|
||||||
|
const CAM_SIZE = { height: 220, width: 148 };
|
||||||
|
// fixed colors, do not change with theme change.
|
||||||
|
const gray300 = '#5f656e';
|
||||||
|
const gray100 = '#CBCED1';
|
||||||
|
|
||||||
export default function StartACallActionSheet({ rid, initCall }: { rid: string; initCall: Function }): React.ReactElement {
|
export default function StartACallActionSheet({ rid, initCall }: { rid: string; initCall: Function }): React.ReactElement {
|
||||||
const style = useStyle();
|
const style = useStyle();
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
const [user, setUser] = useState({ username: '', avatar: '', uid: '' });
|
const [user, setUser] = useState({ username: '', avatar: '', uid: '' });
|
||||||
const [mic, setMic] = useState(true);
|
const [mic, setMic] = useState(true);
|
||||||
const [cam, setCam] = useState(false);
|
const [cam, setCam] = useState(false);
|
||||||
|
const [calling, setCalling] = useState(true);
|
||||||
const username = useAppSelector(state => state.login.user.username);
|
const username = useAppSelector(state => state.login.user.username);
|
||||||
|
|
||||||
const { hideActionSheet } = useActionSheet();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const room = await getSubscriptionByRoomId(rid);
|
const room = await getSubscriptionByRoomId(rid);
|
||||||
|
@ -34,26 +38,37 @@ export default function StartACallActionSheet({ rid, initCall }: { rid: string;
|
||||||
})();
|
})();
|
||||||
}, [rid]);
|
}, [rid]);
|
||||||
|
|
||||||
const handleColor = (enabled: boolean) => (enabled ? colors.conferenceCallEnabledIcon : colors.conferenceCallDisabledIcon);
|
const handleColors = (enabled: boolean) => {
|
||||||
|
if (calling) {
|
||||||
|
if (enabled) {
|
||||||
|
return { button: colors.conferenceCallCallBackButton, icon: gray300 };
|
||||||
|
}
|
||||||
|
return { button: 'transparent', icon: gray100 };
|
||||||
|
}
|
||||||
|
if (enabled) {
|
||||||
|
return { button: colors.conferenceCallEnabledIconBackground, icon: colors.conferenceCallEnabledIcon };
|
||||||
|
}
|
||||||
|
return { button: 'transparent', icon: colors.conferenceCallDisabledIcon };
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={style.actionSheetContainer}>
|
<View style={style.actionSheetContainer}>
|
||||||
<View style={style.actionSheetHeader}>
|
<View style={style.actionSheetHeader}>
|
||||||
<Text style={style.actionSheetHeaderTitle}>{i18n.t('Start_a_call')}</Text>
|
<Text style={style.actionSheetHeaderTitle}>{calling ? i18n.t('Calling') : i18n.t('Start_a_call')}</Text>
|
||||||
<View style={style.actionSheetHeaderButtons}>
|
<View style={style.actionSheetHeaderButtons}>
|
||||||
<Touchable
|
<Touchable
|
||||||
onPress={() => setCam(!cam)}
|
onPress={() => setCam(!cam)}
|
||||||
style={[style.iconCallContainer, cam && style.enabledBackground, { marginRight: 6 }]}
|
style={[style.iconCallContainer, { backgroundColor: handleColors(cam).button }, { marginRight: 6 }]}
|
||||||
hitSlop={BUTTON_HIT_SLOP}
|
hitSlop={BUTTON_HIT_SLOP}
|
||||||
>
|
>
|
||||||
<CustomIcon name={cam ? 'camera' : 'camera-disabled'} size={20} color={handleColor(cam)} />
|
<CustomIcon name={cam ? 'camera' : 'camera-disabled'} size={20} color={handleColors(cam).icon} />
|
||||||
</Touchable>
|
</Touchable>
|
||||||
<Touchable
|
<Touchable
|
||||||
onPress={() => setMic(!mic)}
|
onPress={() => setMic(!mic)}
|
||||||
style={[style.iconCallContainer, mic && style.enabledBackground]}
|
style={[style.iconCallContainer, { backgroundColor: handleColors(mic).button }]}
|
||||||
hitSlop={BUTTON_HIT_SLOP}
|
hitSlop={BUTTON_HIT_SLOP}
|
||||||
>
|
>
|
||||||
<CustomIcon name={mic ? 'microphone' : 'microphone-disabled'} size={20} color={handleColor(mic)} />
|
<CustomIcon name={mic ? 'microphone' : 'microphone-disabled'} size={20} color={handleColors(mic).icon} />
|
||||||
</Touchable>
|
</Touchable>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
@ -64,17 +79,27 @@ export default function StartACallActionSheet({ rid, initCall }: { rid: string;
|
||||||
{user.username}
|
{user.username}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={style.actionSheetPhotoContainer}>
|
<View
|
||||||
<AvatarContainer size={62} text={username} />
|
style={[
|
||||||
|
style.actionSheetPhotoContainer,
|
||||||
|
CAM_SIZE,
|
||||||
|
{ backgroundColor: cam ? undefined : colors.conferenceCallPhotoBackground }
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{cam ? <Camera style={CAM_SIZE} type={CameraType.front} /> : <AvatarContainer size={62} text={username} />}
|
||||||
</View>
|
</View>
|
||||||
<Button
|
<Button
|
||||||
|
backgroundColor={calling ? colors.conferenceCallCallBackButton : colors.actionTintColor}
|
||||||
|
color={calling ? gray300 : colors.conferenceCallEnabledIcon}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
hideActionSheet();
|
if (!calling) {
|
||||||
setTimeout(() => {
|
setCalling(true);
|
||||||
initCall({ cam, mic });
|
initCall({ cam, mic });
|
||||||
}, 100);
|
} else {
|
||||||
|
setCalling(false);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
title={i18n.t('Call')}
|
title={calling ? i18n.t('Cancel') : i18n.t('Call')}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -116,12 +116,12 @@ export default function useStyle() {
|
||||||
actionSheetPhotoContainer: {
|
actionSheetPhotoContainer: {
|
||||||
height: 220,
|
height: 220,
|
||||||
width: 148,
|
width: 148,
|
||||||
backgroundColor: colors.conferenceCallPhotoBackground,
|
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
margin: 24,
|
margin: 24,
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center'
|
alignItems: 'center',
|
||||||
|
overflow: 'hidden'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ const Content = React.memo(
|
||||||
content = (
|
content = (
|
||||||
<Markdown
|
<Markdown
|
||||||
msg={props.msg}
|
msg={props.msg}
|
||||||
md={props.type !== 'e2e' ? props.md : undefined}
|
md={props.md}
|
||||||
getCustomEmoji={props.getCustomEmoji}
|
getCustomEmoji={props.getCustomEmoji}
|
||||||
enableMessageParser={user.enableMessageParserEarlyAdoption}
|
enableMessageParser={user.enableMessageParserEarlyAdoption}
|
||||||
username={user.username}
|
username={user.username}
|
||||||
|
|
|
@ -18,7 +18,7 @@ const MessageAvatar = React.memo(({ isHeader, avatar, author, small, navToRoomIn
|
||||||
style={small ? styles.avatarSmall : styles.avatar}
|
style={small ? styles.avatarSmall : styles.avatar}
|
||||||
text={avatar ? '' : author.username}
|
text={avatar ? '' : author.username}
|
||||||
size={small ? 20 : 36}
|
size={small ? 20 : 36}
|
||||||
borderRadius={4}
|
borderRadius={small ? 2 : 4}
|
||||||
onPress={author._id === user.id ? undefined : () => navToRoomInfo(navParam)}
|
onPress={author._id === user.id ? undefined : () => navToRoomInfo(navParam)}
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
avatar={avatar}
|
avatar={avatar}
|
||||||
|
|
|
@ -247,8 +247,6 @@ const Reply = React.memo(
|
||||||
>
|
>
|
||||||
<View style={styles.attachmentContainer}>
|
<View style={styles.attachmentContainer}>
|
||||||
<Title attachment={attachment} timeFormat={timeFormat} theme={theme} />
|
<Title attachment={attachment} timeFormat={timeFormat} theme={theme} />
|
||||||
<Description attachment={attachment} getCustomEmoji={getCustomEmoji} theme={theme} />
|
|
||||||
<UrlImage image={attachment.thumb_url} />
|
|
||||||
<Attachments
|
<Attachments
|
||||||
attachments={attachment.attachments}
|
attachments={attachment.attachments}
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
|
@ -257,6 +255,8 @@ const Reply = React.memo(
|
||||||
isReply
|
isReply
|
||||||
id={messageId}
|
id={messageId}
|
||||||
/>
|
/>
|
||||||
|
<UrlImage image={attachment.thumb_url} />
|
||||||
|
<Description attachment={attachment} getCustomEmoji={getCustomEmoji} theme={theme} />
|
||||||
<Fields attachment={attachment} getCustomEmoji={getCustomEmoji} theme={theme} />
|
<Fields attachment={attachment} getCustomEmoji={getCustomEmoji} theme={theme} />
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<View style={[styles.backdrop]}>
|
<View style={[styles.backdrop]}>
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import database from '..';
|
import database from '..';
|
||||||
|
import { TSubscriptionModel } from '../../../definitions';
|
||||||
import { TAppDatabase } from '../interfaces';
|
import { TAppDatabase } from '../interfaces';
|
||||||
import { SUBSCRIPTIONS_TABLE } from '../model/Subscription';
|
import { SUBSCRIPTIONS_TABLE } from '../model/Subscription';
|
||||||
|
|
||||||
const getCollection = (db: TAppDatabase) => db.get(SUBSCRIPTIONS_TABLE);
|
const getCollection = (db: TAppDatabase) => db.get(SUBSCRIPTIONS_TABLE);
|
||||||
|
|
||||||
export const getSubscriptionByRoomId = async (rid: string) => {
|
export const getSubscriptionByRoomId = async (rid: string): Promise<TSubscriptionModel | null> => {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const subCollection = getCollection(db);
|
const subCollection = getCollection(db);
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
import { TypedUseSelectorHook, useSelector } from 'react-redux';
|
import { TypedUseSelectorHook, useSelector } from 'react-redux';
|
||||||
|
import { select } from 'redux-saga/effects';
|
||||||
|
|
||||||
import { IApplicationState } from '../../definitions';
|
import { IApplicationState } from '../../definitions';
|
||||||
|
|
||||||
export const useAppSelector: TypedUseSelectorHook<IApplicationState> = useSelector;
|
export const useAppSelector: TypedUseSelectorHook<IApplicationState> = useSelector;
|
||||||
|
|
||||||
|
export function* appSelector<TSelected>(selector: (state: IApplicationState) => TSelected): Generator<any, TSelected, TSelected> {
|
||||||
|
return yield select(selector);
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useDimensions } from '@react-native-community/hooks';
|
import { useWindowDimensions } from 'react-native';
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
// Not sure if it's worth adding this here in the context of the actionSheet
|
// Not sure if it's worth adding this here in the context of the actionSheet
|
||||||
|
@ -8,7 +8,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
*/
|
*/
|
||||||
export const useSnaps = (snaps: number[]): string[] => {
|
export const useSnaps = (snaps: number[]): string[] => {
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const { screen } = useDimensions();
|
const { height, scale } = useWindowDimensions();
|
||||||
const percentage = insets.bottom + insets.top > 75 ? 110 : 100;
|
const percentage = insets.bottom + insets.top > 75 ? 110 : 100;
|
||||||
return snaps.map(snap => `${((percentage * snap) / (screen.height * screen.scale)).toFixed(2)}%`);
|
return snaps.map(snap => `${((percentage * snap) / (height * scale)).toFixed(2)}%`);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
|
import { Camera } from 'expo-camera';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { useActionSheet } from '../../containers/ActionSheet';
|
import { useActionSheet } from '../../containers/ActionSheet';
|
||||||
import StartACallActionSheet from '../../containers/UIKit/VideoConferenceBlock/components/StartACallActionSheet';
|
import StartACallActionSheet from '../../containers/UIKit/VideoConferenceBlock/components/StartACallActionSheet';
|
||||||
|
@ -10,7 +11,7 @@ import database from '../database';
|
||||||
import { getSubscriptionByRoomId } from '../database/services/Subscription';
|
import { getSubscriptionByRoomId } from '../database/services/Subscription';
|
||||||
import { callJitsi } from '../methods';
|
import { callJitsi } from '../methods';
|
||||||
import { compareServerVersion, showErrorAlert } from '../methods/helpers';
|
import { compareServerVersion, showErrorAlert } from '../methods/helpers';
|
||||||
import { videoConfStartAndJoin } from '../methods/videoConf';
|
import { handleAndroidBltPermission, videoConfStartAndJoin } from '../methods/videoConf';
|
||||||
import { Services } from '../services';
|
import { Services } from '../services';
|
||||||
import { useAppSelector } from './useAppSelector';
|
import { useAppSelector } from './useAppSelector';
|
||||||
import { useSnaps } from './useSnaps';
|
import { useSnaps } from './useSnaps';
|
||||||
|
@ -35,6 +36,8 @@ export const useVideoConf = (rid: string): { showInitCallActionSheet: () => Prom
|
||||||
const jitsiEnableChannels = useAppSelector(state => state.settings.Jitsi_Enable_Channels);
|
const jitsiEnableChannels = useAppSelector(state => state.settings.Jitsi_Enable_Channels);
|
||||||
const user = useAppSelector(state => getUserSelector(state));
|
const user = useAppSelector(state => getUserSelector(state));
|
||||||
|
|
||||||
|
const [permission, requestPermission] = Camera.useCameraPermissions();
|
||||||
|
|
||||||
const isServer5OrNewer = compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '5.0.0');
|
const isServer5OrNewer = compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '5.0.0');
|
||||||
|
|
||||||
const { showActionSheet } = useActionSheet();
|
const { showActionSheet } = useActionSheet();
|
||||||
|
@ -87,6 +90,10 @@ export const useVideoConf = (rid: string): { showInitCallActionSheet: () => Prom
|
||||||
children: <StartACallActionSheet rid={rid} initCall={initCall} />,
|
children: <StartACallActionSheet rid={rid} initCall={initCall} />,
|
||||||
snaps
|
snaps
|
||||||
});
|
});
|
||||||
|
if (!permission?.granted) {
|
||||||
|
requestPermission();
|
||||||
|
handleAndroidBltPermission();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ import { E2E_MESSAGE_TYPE } from '../../constants';
|
||||||
import { getRoom } from '../getRoom';
|
import { getRoom } from '../getRoom';
|
||||||
import { merge } from '../helpers/mergeSubscriptionsRooms';
|
import { merge } from '../helpers/mergeSubscriptionsRooms';
|
||||||
import { getRoomAvatar, getRoomTitle, getSenderName, random } from '../helpers';
|
import { getRoomAvatar, getRoomTitle, getSenderName, random } from '../helpers';
|
||||||
|
import { handleVideoConfIncomingWebsocketMessages } from '../../../actions/videoConf';
|
||||||
|
|
||||||
const removeListener = (listener: { stop: () => void }) => listener.stop();
|
const removeListener = (listener: { stop: () => void }) => listener.stop();
|
||||||
|
|
||||||
|
@ -402,6 +403,11 @@ export default function subscribeRooms() {
|
||||||
log(e);
|
log(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (/video-conference/.test(ev)) {
|
||||||
|
const [action, params] = ddpMessage.fields.args;
|
||||||
|
store.dispatch(handleVideoConfIncomingWebsocketMessages({ action, params }));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const stop = () => {
|
const stop = () => {
|
||||||
|
|
|
@ -19,18 +19,17 @@ const handleBltPermission = async (): Promise<Permission[]> => {
|
||||||
return [PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION];
|
return [PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const handleAndroidBltPermission = async (): Promise<void> => {
|
||||||
|
if (isAndroid) {
|
||||||
|
const bltPermission = await handleBltPermission();
|
||||||
|
await PermissionsAndroid.requestMultiple(bltPermission);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const videoConfJoin = async (callId: string, cam?: boolean, mic?: boolean): Promise<void> => {
|
export const videoConfJoin = async (callId: string, cam?: boolean, mic?: boolean): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const result = await Services.videoConferenceJoin(callId, cam, mic);
|
const result = await Services.videoConferenceJoin(callId, cam, mic);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
if (isAndroid) {
|
|
||||||
const bltPermission = await handleBltPermission();
|
|
||||||
await PermissionsAndroid.requestMultiple([
|
|
||||||
PermissionsAndroid.PERMISSIONS.CAMERA,
|
|
||||||
PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
|
|
||||||
...bltPermission
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
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,6 +21,7 @@ import enterpriseModules from './enterpriseModules';
|
||||||
import encryption from './encryption';
|
import encryption from './encryption';
|
||||||
import permissions from './permissions';
|
import permissions from './permissions';
|
||||||
import roles from './roles';
|
import roles from './roles';
|
||||||
|
import videoConf from './videoConf';
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
settings,
|
settings,
|
||||||
|
@ -43,5 +44,6 @@ export default combineReducers({
|
||||||
enterpriseModules,
|
enterpriseModules,
|
||||||
encryption,
|
encryption,
|
||||||
permissions,
|
permissions,
|
||||||
roles
|
roles,
|
||||||
|
videoConf
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { clearVideoConfCalls, removeVideoConfCall, setVideoConfCall } from '../actions/videoConf';
|
||||||
|
import { mockedStore } from './mockedStore';
|
||||||
|
import { initialState, ICallInfo } from './videoConf';
|
||||||
|
|
||||||
|
describe('test videoConf reducer', () => {
|
||||||
|
it('should return initial state', () => {
|
||||||
|
const state = mockedStore.getState().settings;
|
||||||
|
expect(state).toEqual(initialState);
|
||||||
|
});
|
||||||
|
|
||||||
|
const call1: ICallInfo = {
|
||||||
|
callId: '123',
|
||||||
|
rid: '123',
|
||||||
|
type: 'accepted',
|
||||||
|
uid: '123'
|
||||||
|
};
|
||||||
|
|
||||||
|
const call2: ICallInfo = {
|
||||||
|
callId: '321',
|
||||||
|
rid: '321',
|
||||||
|
type: 'accepted',
|
||||||
|
uid: '321'
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should return call1 after call addSettings action with call1 as parameter', () => {
|
||||||
|
mockedStore.dispatch(setVideoConfCall(call1));
|
||||||
|
const state = mockedStore.getState().videoConf;
|
||||||
|
expect(state[call1.callId]).toEqual(call1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return call2 after call addSettings action with call2 as parameter', () => {
|
||||||
|
mockedStore.dispatch(setVideoConfCall(call2));
|
||||||
|
const state = mockedStore.getState().videoConf;
|
||||||
|
expect(state[call2.callId]).toEqual(call2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove call1 after call removeVideoConfCall action with call1 as parameter', () => {
|
||||||
|
mockedStore.dispatch(removeVideoConfCall(call1));
|
||||||
|
const state = mockedStore.getState().videoConf;
|
||||||
|
expect(state[call1.callId]).toEqual(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return initial state after clearSettings', () => {
|
||||||
|
mockedStore.dispatch(clearVideoConfCalls());
|
||||||
|
const state = mockedStore.getState().videoConf;
|
||||||
|
expect(state).toEqual({});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { VIDEO_CONF } from '../actions/actionsTypes';
|
||||||
|
import { TActionVideoConf } from '../actions/videoConf';
|
||||||
|
|
||||||
|
export type TSupportedCallStatus = 'call' | 'canceled' | 'accepted' | 'rejected' | 'confirmed' | 'join' | 'end';
|
||||||
|
|
||||||
|
export interface ICallInfo {
|
||||||
|
callId: string;
|
||||||
|
rid: string;
|
||||||
|
uid: string;
|
||||||
|
type: TSupportedCallStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ICallInfoRecord {
|
||||||
|
[key: string]: ICallInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initialState: ICallInfoRecord = {};
|
||||||
|
|
||||||
|
export default (state = initialState, action: TActionVideoConf): ICallInfoRecord => {
|
||||||
|
switch (action.type) {
|
||||||
|
case VIDEO_CONF.SET:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
[action.payload.callId]: action.payload
|
||||||
|
};
|
||||||
|
case VIDEO_CONF.REMOVE:
|
||||||
|
return Object.fromEntries(Object.entries(state).filter(([key]) => key !== action.payload.callId));
|
||||||
|
case VIDEO_CONF.CLEAR:
|
||||||
|
return initialState;
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
|
@ -13,6 +13,7 @@ import deepLinking from './deepLinking';
|
||||||
import inviteLinks from './inviteLinks';
|
import inviteLinks from './inviteLinks';
|
||||||
import createDiscussion from './createDiscussion';
|
import createDiscussion from './createDiscussion';
|
||||||
import encryption from './encryption';
|
import encryption from './encryption';
|
||||||
|
import videoConf from './videoConf';
|
||||||
|
|
||||||
const root = function* root() {
|
const root = function* root() {
|
||||||
yield all([
|
yield all([
|
||||||
|
@ -28,7 +29,8 @@ const root = function* root() {
|
||||||
inviteLinks(),
|
inviteLinks(),
|
||||||
createDiscussion(),
|
createDiscussion(),
|
||||||
inquiry(),
|
inquiry(),
|
||||||
encryption()
|
encryption(),
|
||||||
|
videoConf()
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
import { call, takeLatest } from 'typed-redux-saga';
|
||||||
|
|
||||||
|
import { VIDEO_CONF } from '../actions/actionsTypes';
|
||||||
|
import { IVideoConfGenericAction, TCallProps } from '../actions/videoConf';
|
||||||
|
import i18n from '../i18n';
|
||||||
|
import { getSubscriptionByRoomId } from '../lib/database/services/Subscription';
|
||||||
|
import { appSelector } from '../lib/hooks';
|
||||||
|
import { callJitsi } from '../lib/methods';
|
||||||
|
import { compareServerVersion, showErrorAlert } from '../lib/methods/helpers';
|
||||||
|
import log from '../lib/methods/helpers/log';
|
||||||
|
import { videoConfJoin } from '../lib/methods/videoConf';
|
||||||
|
import { Services } from '../lib/services';
|
||||||
|
import { ICallInfo } from '../reducers/videoConf';
|
||||||
|
|
||||||
|
type TGenerator = Generator<IVideoConfGenericAction>;
|
||||||
|
|
||||||
|
function* onDirectCall(payload: ICallInfo): TGenerator {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function* onDirectCallCanceled(payload: ICallInfo): TGenerator {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function* onDirectCallAccepted(payload: ICallInfo): TGenerator {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function* onDirectCallRejected(payload: ICallInfo): TGenerator {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function* onDirectCallConfirmed(payload: ICallInfo): TGenerator {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function* onDirectCallJoined(payload: ICallInfo): TGenerator {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function* onDirectCallEnded(payload: ICallInfo): TGenerator {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function* handleVideoConfIncomingWebsocketMessages({ data }: { data: any }) {
|
||||||
|
const { action, params } = data.action;
|
||||||
|
|
||||||
|
if (!action || typeof action !== 'string') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!params || typeof params !== 'object' || !params.callId || !params.uid || !params.rid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const prop = { ...params, action };
|
||||||
|
switch (action) {
|
||||||
|
case 'call':
|
||||||
|
yield call(onDirectCall, prop);
|
||||||
|
break;
|
||||||
|
case 'canceled':
|
||||||
|
yield call(onDirectCallCanceled, prop);
|
||||||
|
break;
|
||||||
|
case 'accepted':
|
||||||
|
yield call(onDirectCallAccepted, prop);
|
||||||
|
break;
|
||||||
|
case 'rejected':
|
||||||
|
yield call(onDirectCallRejected, prop);
|
||||||
|
break;
|
||||||
|
case 'confirmed':
|
||||||
|
yield call(onDirectCallConfirmed, prop);
|
||||||
|
break;
|
||||||
|
case 'join':
|
||||||
|
yield call(onDirectCallJoined, prop);
|
||||||
|
break;
|
||||||
|
case 'end':
|
||||||
|
yield call(onDirectCallEnded, prop);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function* initCall({ payload: { mic, cam, direct, roomId } }: { payload: TCallProps }) {
|
||||||
|
const serverVersion = yield* appSelector(state => state.server.version);
|
||||||
|
const isServer5OrNewer = compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '5.0.0');
|
||||||
|
if (isServer5OrNewer) {
|
||||||
|
try {
|
||||||
|
const videoConfResponse = yield* call(Services.videoConferenceStart, roomId);
|
||||||
|
if (videoConfResponse.success) {
|
||||||
|
if (direct) {
|
||||||
|
// callUser({ uid: data.calleeId, rid: roomId, callId: data.callId });
|
||||||
|
} else {
|
||||||
|
videoConfJoin(videoConfResponse.data.callId, cam, mic);
|
||||||
|
}
|
||||||
|
// setCalling(false);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// setCalling(false);
|
||||||
|
showErrorAlert(i18n.t('error-init-video-conf'));
|
||||||
|
log(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const sub = yield* call(getSubscriptionByRoomId, roomId);
|
||||||
|
if (sub) {
|
||||||
|
callJitsi({ room: sub, cam });
|
||||||
|
// setCalling(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IGenericAction extends Action {
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function* root(): Generator {
|
||||||
|
yield takeLatest<
|
||||||
|
IGenericAction & {
|
||||||
|
data: any;
|
||||||
|
}
|
||||||
|
>(VIDEO_CONF.HANDLE_INCOMING_WEBSOCKET_MESSAGES, handleVideoConfIncomingWebsocketMessages);
|
||||||
|
yield takeLatest<IGenericAction & { payload: TCallProps }>(VIDEO_CONF.INIT_CALL, initCall);
|
||||||
|
}
|
|
@ -47,7 +47,7 @@
|
||||||
"@react-native-community/blur": "^4.1.0",
|
"@react-native-community/blur": "^4.1.0",
|
||||||
"@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": "3.0.0",
|
||||||
"@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",
|
||||||
|
@ -73,6 +73,7 @@
|
||||||
"expo": "^46.0.9",
|
"expo": "^46.0.9",
|
||||||
"expo-apple-authentication": "4.2.1",
|
"expo-apple-authentication": "4.2.1",
|
||||||
"expo-av": "11.2.3",
|
"expo-av": "11.2.3",
|
||||||
|
"expo-camera": "12.5.0",
|
||||||
"expo-file-system": "14.0.0",
|
"expo-file-system": "14.0.0",
|
||||||
"expo-haptics": "11.2.0",
|
"expo-haptics": "11.2.0",
|
||||||
"expo-keep-awake": "10.1.1",
|
"expo-keep-awake": "10.1.1",
|
||||||
|
@ -145,6 +146,7 @@
|
||||||
"rn-fetch-blob": "^0.12.0",
|
"rn-fetch-blob": "^0.12.0",
|
||||||
"rn-root-view": "RocketChat/rn-root-view",
|
"rn-root-view": "RocketChat/rn-root-view",
|
||||||
"semver": "^7.3.8",
|
"semver": "^7.3.8",
|
||||||
|
"typed-redux-saga": "^1.5.0",
|
||||||
"ua-parser-js": "^1.0.32",
|
"ua-parser-js": "^1.0.32",
|
||||||
"uri-js": "^4.4.1",
|
"uri-js": "^4.4.1",
|
||||||
"url-parse": "1.5.10",
|
"url-parse": "1.5.10",
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
/* Basic Options */
|
/* Basic Options */
|
||||||
// "incremental": true, /* Enable incremental compilation */
|
// "incremental": true, /* Enable incremental compilation */
|
||||||
"target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */,
|
"target": "ESNEXT" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */,
|
||||||
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
|
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
|
||||||
// "lib": [], /* Specify library files to be included in the compilation. */
|
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||||
"allowJs": true /* Allow javascript files to be compiled. */,
|
"allowJs": true /* Allow javascript files to be compiled. */,
|
||||||
|
|
53
yarn.lock
53
yarn.lock
|
@ -801,6 +801,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.14.5"
|
"@babel/types" "^7.14.5"
|
||||||
|
|
||||||
|
"@babel/helper-module-imports@^7.14.5", "@babel/helper-module-imports@^7.18.6":
|
||||||
|
version "7.18.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e"
|
||||||
|
integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/types" "^7.18.6"
|
||||||
|
|
||||||
"@babel/helper-module-imports@^7.16.7":
|
"@babel/helper-module-imports@^7.16.7":
|
||||||
version "7.16.7"
|
version "7.16.7"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437"
|
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437"
|
||||||
|
@ -808,13 +815,6 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.16.7"
|
"@babel/types" "^7.16.7"
|
||||||
|
|
||||||
"@babel/helper-module-imports@^7.18.6":
|
|
||||||
version "7.18.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e"
|
|
||||||
integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==
|
|
||||||
dependencies:
|
|
||||||
"@babel/types" "^7.18.6"
|
|
||||||
|
|
||||||
"@babel/helper-module-transforms@^7.10.4":
|
"@babel/helper-module-transforms@^7.10.4":
|
||||||
version "7.10.4"
|
version "7.10.4"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.10.4.tgz#ca1f01fdb84e48c24d7506bb818c961f1da8805d"
|
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.10.4.tgz#ca1f01fdb84e48c24d7506bb818c961f1da8805d"
|
||||||
|
@ -4831,6 +4831,13 @@
|
||||||
"@jridgewell/resolve-uri" "^3.0.3"
|
"@jridgewell/resolve-uri" "^3.0.3"
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
|
|
||||||
|
"@koale/useworker@^4.0.2":
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@koale/useworker/-/useworker-4.0.2.tgz#cb540a2581cd6025307c3ca6685bc60748773e58"
|
||||||
|
integrity sha512-xPIPADtom8/3/4FLNj7MvNcBM/Z2FleH85Fdx2O869eoKW8+PoEgtSVvoxWjCWMA46Sm9A5/R1TyzNGc+yM0wg==
|
||||||
|
dependencies:
|
||||||
|
dequal "^1.0.0"
|
||||||
|
|
||||||
"@mdx-js/mdx@^1.6.22":
|
"@mdx-js/mdx@^1.6.22":
|
||||||
version "1.6.22"
|
version "1.6.22"
|
||||||
resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba"
|
resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba"
|
||||||
|
@ -5176,10 +5183,10 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
invariant "^2.2.4"
|
invariant "^2.2.4"
|
||||||
|
|
||||||
"@react-native-community/hooks@2.6.0":
|
"@react-native-community/hooks@3.0.0":
|
||||||
version "2.6.0"
|
version "3.0.0"
|
||||||
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-3.0.0.tgz#af5f2ca32eea59b792ce9e3d9a4cf0354f9b195f"
|
||||||
integrity sha512-emBGKvhJ0h++lLJQ5ejsj+od9G67nEaihjvfSx7/JWvNrQGAhP9U0OZqgb9dkKzor9Ufaj9SGt8RNY97cGzttw==
|
integrity sha512-g2OyxXHfwIytXUJitBR6Z/ISoOfp0WKx5FOv+NqJ/CrWjRDcTw6zXE5I1C9axfuh30kJqzWchVfCDrkzZYTxqg==
|
||||||
|
|
||||||
"@react-native-community/netinfo@6.0.0":
|
"@react-native-community/netinfo@6.0.0":
|
||||||
version "6.0.0"
|
version "6.0.0"
|
||||||
|
@ -7698,7 +7705,7 @@ babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.8.0:
|
||||||
cosmiconfig "^6.0.0"
|
cosmiconfig "^6.0.0"
|
||||||
resolve "^1.12.0"
|
resolve "^1.12.0"
|
||||||
|
|
||||||
babel-plugin-macros@^3.0.1:
|
babel-plugin-macros@^3.0.1, babel-plugin-macros@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1"
|
resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1"
|
||||||
integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==
|
integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==
|
||||||
|
@ -9762,6 +9769,11 @@ deprecated-react-native-prop-types@^2.3.0:
|
||||||
invariant "*"
|
invariant "*"
|
||||||
prop-types "*"
|
prop-types "*"
|
||||||
|
|
||||||
|
dequal@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/dequal/-/dequal-1.0.1.tgz#dbbf9795ec626e9da8bd68782f4add1d23700d8b"
|
||||||
|
integrity sha512-Fx8jxibzkJX2aJgyfSdLhr9tlRoTnHKrRJuu2XHlAgKioN2j19/Bcbe0d4mFXYZ3+wpE2KVobUVTfDutcD17xQ==
|
||||||
|
|
||||||
dequal@^2.0.2:
|
dequal@^2.0.2:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.2.tgz#85ca22025e3a87e65ef75a7a437b35284a7e319d"
|
resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.2.tgz#85ca22025e3a87e65ef75a7a437b35284a7e319d"
|
||||||
|
@ -10878,6 +10890,15 @@ expo-av@11.2.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
"@expo/config-plugins" "^4.0.14"
|
"@expo/config-plugins" "^4.0.14"
|
||||||
|
|
||||||
|
expo-camera@12.5.0:
|
||||||
|
version "12.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/expo-camera/-/expo-camera-12.5.0.tgz#aac8441ea661ed21becf064e715dd50d5448aaae"
|
||||||
|
integrity sha512-oiQccJ9d2FnGdlLKaRphZ2DpwI9C1yD4HUZDacsAR+8904xv3Mhf6u78mh9yBPs6Z7dbtgSjOFtFu5s29PeTxA==
|
||||||
|
dependencies:
|
||||||
|
"@expo/config-plugins" "~5.0.0"
|
||||||
|
"@koale/useworker" "^4.0.2"
|
||||||
|
invariant "^2.2.4"
|
||||||
|
|
||||||
expo-constants@~13.2.2, expo-constants@~13.2.4:
|
expo-constants@~13.2.2, expo-constants@~13.2.4:
|
||||||
version "13.2.4"
|
version "13.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-13.2.4.tgz#eab4a553f074b2c60ad7a158d3b82e3484a94606"
|
resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-13.2.4.tgz#eab4a553f074b2c60ad7a158d3b82e3484a94606"
|
||||||
|
@ -20114,6 +20135,14 @@ type-is@~1.6.17, type-is@~1.6.18:
|
||||||
media-typer "0.3.0"
|
media-typer "0.3.0"
|
||||||
mime-types "~2.1.24"
|
mime-types "~2.1.24"
|
||||||
|
|
||||||
|
typed-redux-saga@^1.5.0:
|
||||||
|
version "1.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/typed-redux-saga/-/typed-redux-saga-1.5.0.tgz#f70b47c92c6e29e0184d0c30d563c18d6ad0ae54"
|
||||||
|
integrity sha512-XHKliNtRNUegYAAztbVDb5Q+FMqYNQPaed6Xq2N8kz8AOmiOCVxW3uIj7TEptR1/ms6M9u3HEDfJr4qqz/PYrw==
|
||||||
|
optionalDependencies:
|
||||||
|
"@babel/helper-module-imports" "^7.14.5"
|
||||||
|
babel-plugin-macros "^3.1.0"
|
||||||
|
|
||||||
typedarray-to-buffer@^3.1.5:
|
typedarray-to-buffer@^3.1.5:
|
||||||
version "3.1.5"
|
version "3.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
|
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
|
||||||
|
|
Loading…
Reference in New Issue