[NEW] Video conf message block (#4619)
* create skeleton loading * add phone-in icon * fix avatar style props * fix ios icon * add new types * add Rocket.Chat definitions * fix block re-render * create VideoConferenceBlock * better composition * fix call originator * add pt-br translations * create useSnaps hook * update colors * init action sheet * fix to go back when the call ends * create CallAgainActionSheet * update pods * bump lib react-native-skeleton-placeholder * update hook location * remove loading prop * move files to components * update border radius * add verify on message options * update icons * apply patch
This commit is contained in:
parent
61dc320a68
commit
362df65bbe
Binary file not shown.
|
@ -1,10 +1,11 @@
|
|||
import React from 'react';
|
||||
import { ViewStyle } from 'react-native';
|
||||
|
||||
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||
|
||||
export interface IAvatar {
|
||||
server?: string;
|
||||
style?: any;
|
||||
style?: ViewStyle;
|
||||
text?: string;
|
||||
avatar?: string;
|
||||
emoji?: string;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
export const mappedIcons = {
|
||||
'lamp-bulb': 59812,
|
||||
'lamp-bulb': 59836,
|
||||
'phone-in': 59835,
|
||||
'basketball': 59776,
|
||||
'percentage': 59777,
|
||||
'glasses': 59812,
|
||||
'burger': 59813,
|
||||
'leaf': 59814,
|
||||
'airplane': 59815,
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -353,9 +353,10 @@ const MessageActions = React.memo(
|
|||
|
||||
const getOptions = (message: TAnyMessageModel) => {
|
||||
const options: TActionSheetOptionsItem[] = [];
|
||||
const videoConfBlock = !!(message?.blocks && message?.blocks[0]?.type === 'video_conf');
|
||||
|
||||
// Quote
|
||||
if (!isReadOnly) {
|
||||
if (!isReadOnly && !videoConfBlock) {
|
||||
options.push({
|
||||
title: I18n.t('Quote'),
|
||||
icon: 'quote',
|
||||
|
@ -373,7 +374,7 @@ const MessageActions = React.memo(
|
|||
}
|
||||
|
||||
// Reply in DM
|
||||
if (room.t !== 'd' && room.t !== 'l' && createDirectMessagePermission) {
|
||||
if (room.t !== 'd' && room.t !== 'l' && createDirectMessagePermission && !videoConfBlock) {
|
||||
options.push({
|
||||
title: I18n.t('Reply_in_direct_message'),
|
||||
icon: 'arrow-back',
|
||||
|
@ -396,11 +397,13 @@ const MessageActions = React.memo(
|
|||
});
|
||||
|
||||
// Copy
|
||||
options.push({
|
||||
title: I18n.t('Copy'),
|
||||
icon: 'copy',
|
||||
onPress: () => handleCopy(message)
|
||||
});
|
||||
if (!videoConfBlock) {
|
||||
options.push({
|
||||
title: I18n.t('Copy'),
|
||||
icon: 'copy',
|
||||
onPress: () => handleCopy(message)
|
||||
});
|
||||
}
|
||||
|
||||
// Share
|
||||
options.push({
|
||||
|
@ -410,7 +413,7 @@ const MessageActions = React.memo(
|
|||
});
|
||||
|
||||
// Edit
|
||||
if (allowEdit(message)) {
|
||||
if (allowEdit(message) && !videoConfBlock) {
|
||||
options.push({
|
||||
title: I18n.t('Edit'),
|
||||
icon: 'edit',
|
||||
|
@ -419,7 +422,7 @@ const MessageActions = React.memo(
|
|||
}
|
||||
|
||||
// Pin
|
||||
if (Message_AllowPinning && permissions?.hasPinPermission) {
|
||||
if (Message_AllowPinning && permissions?.hasPinPermission && !videoConfBlock) {
|
||||
options.push({
|
||||
title: I18n.t(message.pinned ? 'Unpin' : 'Pin'),
|
||||
icon: 'pin',
|
||||
|
@ -428,7 +431,7 @@ const MessageActions = React.memo(
|
|||
}
|
||||
|
||||
// Star
|
||||
if (Message_AllowStarring) {
|
||||
if (Message_AllowStarring && !videoConfBlock) {
|
||||
options.push({
|
||||
title: I18n.t(message.starred ? 'Unstar' : 'Star'),
|
||||
icon: message.starred ? 'star-filled' : 'star',
|
||||
|
|
|
@ -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} borderRadius={4} marginTop={8}>
|
||||
<SkeletonPlaceholder.Item alignItems={'center'} flexDirection='row' marginTop={16} marginLeft={16}>
|
||||
<SkeletonPlaceholder.Item width={28} height={28} />
|
||||
<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 { ThemeContext } from '../../theme';
|
||||
import { IActions, IButton, IElement, IInputIndex, IParser, ISection } from './interfaces';
|
||||
import VideoConferenceBlock from './VideoConferenceBlock';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
input: {
|
||||
|
@ -149,6 +150,10 @@ class MessageParser extends UiKitParserMessage<React.ReactElement> {
|
|||
const [{ loading, value }, action] = useBlockContext(element, context);
|
||||
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
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
/* eslint-disable no-shadow */
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { BlockContext } from '@rocket.chat/ui-kit';
|
||||
import React, { useContext, useState } from 'react';
|
||||
|
||||
import { useVideoConf } from '../../lib/hooks/useVideoConf';
|
||||
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;
|
||||
|
||||
|
@ -42,7 +40,7 @@ export const useBlockContext = ({ blockId, actionId, appId, initialValue }: IUse
|
|||
const { action, appId: appIdFromContext, viewId, state, language, errors, values = {} } = useContext(KitContext);
|
||||
const { value = initialValue } = values[actionId] || {};
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { showActionSheet } = useActionSheet();
|
||||
const { joinCall } = useVideoConf();
|
||||
|
||||
const error = errors && actionId && errors[actionId];
|
||||
|
||||
|
@ -60,20 +58,7 @@ export const useBlockContext = ({ blockId, actionId, appId, initialValue }: IUse
|
|||
try {
|
||||
if (appId === 'videoconf-core' && blockId) {
|
||||
setLoading(false);
|
||||
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;
|
||||
return joinCall(blockId);
|
||||
}
|
||||
await action({
|
||||
blockId,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
|
||||
import { messageBlockWithContext } from '../UIKit/MessageBlock';
|
||||
import { IMessageBlocks } from './interfaces';
|
||||
|
@ -6,25 +6,29 @@ import { IMessageBlocks } from './interfaces';
|
|||
const Blocks = React.memo(({ blocks, id: mid, rid, blockAction }: IMessageBlocks) => {
|
||||
if (blocks && blocks.length > 0) {
|
||||
const appId = blocks[0]?.appId || '';
|
||||
return React.createElement(
|
||||
messageBlockWithContext({
|
||||
action: async ({ actionId, value, blockId }: { actionId: string; value: string; blockId: string }) => {
|
||||
if (blockAction) {
|
||||
await blockAction({
|
||||
actionId,
|
||||
appId,
|
||||
value,
|
||||
blockId,
|
||||
rid,
|
||||
mid
|
||||
});
|
||||
}
|
||||
},
|
||||
appId,
|
||||
rid
|
||||
}),
|
||||
{ blocks }
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const comp = useRef(
|
||||
React.createElement(
|
||||
messageBlockWithContext({
|
||||
action: async ({ actionId, value, blockId }: { actionId: string; value: string; blockId: string }) => {
|
||||
if (blockAction) {
|
||||
await blockAction({
|
||||
actionId,
|
||||
appId,
|
||||
value,
|
||||
blockId,
|
||||
rid,
|
||||
mid
|
||||
});
|
||||
}
|
||||
},
|
||||
appId,
|
||||
rid
|
||||
}),
|
||||
{ blocks }
|
||||
)
|
||||
);
|
||||
return comp.current;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
|
|
@ -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 = {
|
||||
'video-conference/jitsi.update-timeout': {
|
||||
POST: (params: { roomId: string }) => void;
|
||||
|
@ -8,4 +10,18 @@ export type VideoConferenceEndpoints = {
|
|||
'video-conference.start': {
|
||||
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>>;
|
|
@ -862,6 +862,16 @@
|
|||
"Select_Members": "Select Members",
|
||||
"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",
|
||||
"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",
|
||||
"room_archived": "archived room",
|
||||
"room_unarchived": "unarchived room"
|
||||
|
|
|
@ -784,6 +784,16 @@
|
|||
"Select_Members": "Selecionar Membros",
|
||||
"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",
|
||||
"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",
|
||||
"room_archived": "{{username}} arquivou a sala",
|
||||
"room_unarchived": "{{username}} desarquivou a sala"
|
||||
|
|
|
@ -70,6 +70,22 @@ export const colors = {
|
|||
collapsibleQuoteBorder: '#CBCED1',
|
||||
collapsibleChevron: '#6C727A',
|
||||
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',
|
||||
...mentions
|
||||
},
|
||||
|
@ -123,6 +139,22 @@ export const colors = {
|
|||
collapsibleQuoteBorder: '#CBCED1',
|
||||
collapsibleChevron: '#6C727A',
|
||||
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
|
||||
...mentions
|
||||
},
|
||||
|
@ -176,6 +208,22 @@ export const colors = {
|
|||
collapsibleQuoteBorder: '#CBCED1',
|
||||
collapsibleChevron: '#6C727A',
|
||||
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
|
||||
...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 };
|
||||
};
|
|
@ -59,6 +59,10 @@ class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewS
|
|||
const { route } = this.props;
|
||||
const { userInfo } = this.state;
|
||||
|
||||
setTimeout(() => {
|
||||
this.setState({ loading: false });
|
||||
}, 1000);
|
||||
|
||||
if (isIOS) {
|
||||
setTimeout(() => {
|
||||
const onlyAudio = route.params?.onlyAudio ?? false;
|
||||
|
@ -69,7 +73,7 @@ class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewS
|
|||
}
|
||||
}, 1000);
|
||||
}
|
||||
BackHandler.addEventListener('hardwareBackPress', this.endCall);
|
||||
BackHandler.addEventListener('hardwareBackPress', () => null);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
@ -79,7 +83,7 @@ class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewS
|
|||
this.jitsiTimeout = null;
|
||||
BackgroundTimer.stopBackgroundTimer();
|
||||
}
|
||||
BackHandler.removeEventListener('hardwareBackPress', this.endCall);
|
||||
BackHandler.removeEventListener('hardwareBackPress', () => null);
|
||||
if (isIOS) {
|
||||
JitsiMeet.endCall();
|
||||
}
|
||||
|
@ -98,6 +102,7 @@ class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewS
|
|||
// call is not ended and is available to web users.
|
||||
onConferenceJoined = () => {
|
||||
logEvent(this.videoConf ? events.LIVECHAT_VIDEOCONF_JOIN : events.JM_CONFERENCE_JOIN);
|
||||
this.setState({ loading: false });
|
||||
if (this.rid && !this.videoConf) {
|
||||
Services.updateJitsiTimeout(this.rid).catch((e: unknown) => console.log(e));
|
||||
if (this.jitsiTimeout) {
|
||||
|
@ -114,7 +119,11 @@ class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewS
|
|||
onConferenceTerminated = () => {
|
||||
logEvent(this.videoConf ? events.LIVECHAT_VIDEOCONF_TERMINATE : events.JM_CONFERENCE_TERMINATE);
|
||||
const { navigation } = this.props;
|
||||
navigation.pop();
|
||||
// fix to go back when the call ends
|
||||
setTimeout(() => {
|
||||
JitsiMeet.endCall();
|
||||
navigation.pop();
|
||||
}, 200);
|
||||
};
|
||||
|
||||
render() {
|
||||
|
|
|
@ -2,6 +2,8 @@ PODS:
|
|||
- boost (1.76.0)
|
||||
- BugsnagReactNative (7.10.5):
|
||||
- React-Core
|
||||
- BVLinearGradient (2.6.2):
|
||||
- React-Core
|
||||
- DoubleConversion (1.1.6)
|
||||
- EXAppleAuthentication (4.2.1):
|
||||
- ExpoModulesCore
|
||||
|
@ -493,8 +495,8 @@ PODS:
|
|||
- React-Core
|
||||
- RNCClipboard (1.8.5):
|
||||
- React-Core
|
||||
- RNCMaskedView (0.1.11):
|
||||
- React
|
||||
- RNCMaskedView (0.2.8):
|
||||
- React-Core
|
||||
- RNConfigReader (1.0.0):
|
||||
- React
|
||||
- RNCPicker (1.8.1):
|
||||
|
@ -587,6 +589,7 @@ PODS:
|
|||
DEPENDENCIES:
|
||||
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
|
||||
- "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`)
|
||||
- EXAppleAuthentication (from `../node_modules/expo-apple-authentication/ios`)
|
||||
- EXAV (from `../node_modules/expo-av/ios`)
|
||||
|
@ -655,7 +658,7 @@ DEPENDENCIES:
|
|||
- RNBootSplash (from `../node_modules/react-native-bootsplash`)
|
||||
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
|
||||
- "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`)
|
||||
- "RNCPicker (from `../node_modules/@react-native-community/picker`)"
|
||||
- "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)"
|
||||
|
@ -708,6 +711,8 @@ EXTERNAL SOURCES:
|
|||
:podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
|
||||
BugsnagReactNative:
|
||||
:path: "../node_modules/@bugsnag/react-native"
|
||||
BVLinearGradient:
|
||||
:path: "../node_modules/react-native-linear-gradient"
|
||||
DoubleConversion:
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
|
||||
EXAppleAuthentication:
|
||||
|
@ -837,7 +842,7 @@ EXTERNAL SOURCES:
|
|||
RNCClipboard:
|
||||
:path: "../node_modules/@react-native-clipboard/clipboard"
|
||||
RNCMaskedView:
|
||||
:path: "../node_modules/@react-native-community/masked-view"
|
||||
:path: "../node_modules/@react-native-masked-view/masked-view"
|
||||
RNConfigReader:
|
||||
:path: "../node_modules/react-native-config-reader"
|
||||
RNCPicker:
|
||||
|
@ -889,6 +894,7 @@ CHECKOUT OPTIONS:
|
|||
SPEC CHECKSUMS:
|
||||
boost: a7c83b31436843459a1961bfd74b96033dc77234
|
||||
BugsnagReactNative: a97b3132c1854fd7bf92350fabd505e3ebdd7829
|
||||
BVLinearGradient: 34a999fda29036898a09c6a6b728b0b4189e1a44
|
||||
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
|
||||
EXAppleAuthentication: 709a807fe7f48ac6986a2ceed206ee6a8baf28df
|
||||
EXAV: 88f61c5af8415715b7ee51f084c1020235b85c56
|
||||
|
@ -972,7 +978,7 @@ SPEC CHECKSUMS:
|
|||
RNBootSplash: 7e91ea56c7010aae487489789dbe212e8c905a0c
|
||||
RNCAsyncStorage: 8616bd5a58af409453ea4e1b246521bb76578d60
|
||||
RNCClipboard: cc054ad1e8a33d2a74cd13e565588b4ca928d8fd
|
||||
RNCMaskedView: 0e1bc4bfa8365eba5fbbb71e07fbdc0555249489
|
||||
RNCMaskedView: bc0170f389056201c82a55e242e5d90070e18e5a
|
||||
RNConfigReader: 396da6a6444182a76e8ae0930b9436c7575045cb
|
||||
RNCPicker: 914b557e20b3b8317b084aca9ff4b4edb95f61e4
|
||||
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/datetimepicker": "3.5.2",
|
||||
"@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/picker": "^1.8.1",
|
||||
"@react-native-community/slider": "4.2.2",
|
||||
|
@ -48,13 +47,14 @@
|
|||
"@react-native-firebase/analytics": "^14.11.0",
|
||||
"@react-native-firebase/app": "^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/elements": "^1.3.6",
|
||||
"@react-navigation/native": "6.0.10",
|
||||
"@react-navigation/stack": "6.2.1",
|
||||
"@rocket.chat/message-parser": "^0.31.14",
|
||||
"@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",
|
||||
"color2k": "1.2.4",
|
||||
"commonmark": "git+https://github.com/RocketChat/commonmark.js.git",
|
||||
|
@ -98,6 +98,7 @@
|
|||
"react-native-image-progress": "^1.1.1",
|
||||
"react-native-jitsi-meet": "RocketChat/react-native-jitsi-meet",
|
||||
"react-native-keycommands": "2.0.3",
|
||||
"react-native-linear-gradient": "^2.6.2",
|
||||
"react-native-localize": "2.1.1",
|
||||
"react-native-math-view": "^3.9.5",
|
||||
"react-native-mime-types": "2.3.0",
|
||||
|
@ -118,6 +119,7 @@
|
|||
"react-native-screens": "3.13.1",
|
||||
"react-native-scrollable-tab-view": "ptomasroos/react-native-scrollable-tab-view",
|
||||
"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-svg": "^12.3.0",
|
||||
"react-native-ui-lib": "RocketChat/react-native-ui-lib",
|
||||
|
|
28
yarn.lock
28
yarn.lock
|
@ -4969,11 +4969,6 @@
|
|||
resolved "https://registry.yarnpkg.com/@react-native-community/hooks/-/hooks-2.6.0.tgz#dd5f19601eb3684c6bcdd3df3d0c04cf44c24cff"
|
||||
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":
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-native-community/netinfo/-/netinfo-6.0.0.tgz#2a4d7190b508dd0c2293656c9c1aa068f6f60a71"
|
||||
|
@ -5023,6 +5018,11 @@
|
|||
"@expo/config-plugins" "^4.1.5"
|
||||
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":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-native-picker/picker/-/picker-1.16.1.tgz#cc5d05b0d651445afa519c67824d8af3e43fa10c"
|
||||
|
@ -5165,10 +5165,10 @@
|
|||
tiny-events "^1.0.1"
|
||||
universal-websocket-client "^1.0.2"
|
||||
|
||||
"@rocket.chat/ui-kit@^0.31.13":
|
||||
version "0.31.13"
|
||||
resolved "https://registry.yarnpkg.com/@rocket.chat/ui-kit/-/ui-kit-0.31.13.tgz#bd1c8a8726a74e13d072bb6f67cb2a6d3ba66e3b"
|
||||
integrity sha512-IWNIRca0fP8Ecka3DIvqZKF7PbjcUaS+LkWUbMa+9lkX6MeumZrpFqsf7MKUpT+ihJr0RD4lBybmEgFH2syDbw==
|
||||
"@rocket.chat/ui-kit@^0.31.19":
|
||||
version "0.31.19"
|
||||
resolved "https://registry.yarnpkg.com/@rocket.chat/ui-kit/-/ui-kit-0.31.19.tgz#737103123bc7e635382217eef75965b7e0f44703"
|
||||
integrity sha512-8zRKQ5CoC4hIuYHVheO0d7etX9oizmM18fu99r5s/deciL/0MRWocdb4H/QsmbsNrkKCO6Z6wr7f9zzJCNTRHg==
|
||||
|
||||
"@segment/loosely-validate-event@^2.0.0":
|
||||
version "2.0.0"
|
||||
|
@ -17181,6 +17181,11 @@ react-native-keycommands@2.0.3:
|
|||
resolved "https://registry.yarnpkg.com/react-native-keycommands/-/react-native-keycommands-2.0.3.tgz#09b799c1f70832e5cd9fbdb712ddaa3ee683f70e"
|
||||
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:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/react-native-localize/-/react-native-localize-2.1.1.tgz#da8f8776991d2b748708c408db05152602cefb38"
|
||||
|
@ -17332,6 +17337,11 @@ react-native-simple-crypto@RocketChat/react-native-simple-crypto#0.5.1:
|
|||
base64-js "^1.3.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:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-slider/-/react-native-slider-0.11.0.tgz#b68a0bc43c8422b24cd57947cc5ac2bcdb58fadc"
|
||||
|
|
Loading…
Reference in New Issue