[FIX] Storybook not able to import Avatar (#2607)

* [FIX] Storybook not able to import Avatar

* Fix lint

* Mock Date.now

* Fix RU translation

* isLegacy -> serverVersion

* Remove change avatar from room info edit for servers below 3.6

* Mock for storyshots only

* lint

Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
Djorkaeff Alexandre 2020-11-04 13:53:44 -03:00 committed by GitHub
parent 25c4637d39
commit 080b8cc3fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 103 additions and 157 deletions

View File

@ -1,3 +1,6 @@
import initStoryshots from '@storybook/addon-storyshots';
jest.mock('../app/lib/database', () => jest.fn(() => null));
global.Date.now = jest.fn(() => new Date('2019-10-10').getTime());
initStoryshots();

View File

@ -25,7 +25,8 @@ const Avatar = React.memo(({
avatarETag,
isStatic,
rid,
blockUnauthenticatedAccess
blockUnauthenticatedAccess,
serverVersion
}) => {
if ((!text && !avatar && !emoji && !rid) || !server) {
return null;
@ -60,6 +61,7 @@ const Avatar = React.memo(({
avatar,
server,
avatarETag,
serverVersion,
rid,
blockUnauthenticatedAccess
});
@ -114,7 +116,8 @@ Avatar.propTypes = {
avatarETag: PropTypes.string,
isStatic: PropTypes.bool,
rid: PropTypes.string,
blockUnauthenticatedAccess: PropTypes.bool
blockUnauthenticatedAccess: PropTypes.bool,
serverVersion: PropTypes.string
};
Avatar.defaultProps = {

View File

@ -13,7 +13,8 @@ class AvatarContainer extends React.Component {
rid: PropTypes.string,
text: PropTypes.string,
type: PropTypes.string,
blockUnauthenticatedAccess: PropTypes.bool
blockUnauthenticatedAccess: PropTypes.bool,
serverVersion: PropTypes.string
};
static defaultProps = {
@ -83,9 +84,11 @@ class AvatarContainer extends React.Component {
render() {
const { avatarETag } = this.state;
const { serverVersion } = this.props;
return (
<Avatar
avatarETag={avatarETag}
serverVersion={serverVersion}
{...this.props}
/>
);
@ -94,7 +97,8 @@ class AvatarContainer extends React.Component {
const mapStateToProps = state => ({
user: getUserSelector(state),
server: state.share.server || state.server.server,
server: state.share.server.server || state.server.server,
serverVersion: state.share.server.version || state.server.version,
blockUnauthenticatedAccess:
state.share.settings?.Accounts_AvatarBlockUnauthenticatedAccess
?? state.settings.Accounts_AvatarBlockUnauthenticatedAccess

View File

@ -17,7 +17,7 @@ export default class EmojiKeyboard extends React.PureComponent {
constructor(props) {
super(props);
const state = store.getState();
this.baseUrl = state.share.server || state.server.server;
this.baseUrl = state.share.server.server || state.server.server;
}
onEmojiSelected = (emoji) => {

View File

@ -1,14 +1,14 @@
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import Avatar from '../Avatar/Avatar';
import Avatar from '../Avatar';
import styles from './styles';
import MessageContext from './Context';
const MessageAvatar = React.memo(({
isHeader, avatar, author, small, navToRoomInfo, emoji, getCustomEmoji, theme
}) => {
const { baseUrl, user } = useContext(MessageContext);
const { user } = useContext(MessageContext);
if (isHeader && author) {
const navParam = {
t: 'd',
@ -22,9 +22,6 @@ const MessageAvatar = React.memo(({
borderRadius={small ? 2 : 4}
onPress={author._id === user.id ? undefined : () => navToRoomInfo(navParam)}
getCustomEmoji={getCustomEmoji}
user={user}
server={baseUrl}
avatarETag={author.avatarETag}
avatar={avatar}
emoji={emoji}
theme={theme}

View File

@ -9,7 +9,6 @@ import { SYSTEM_MESSAGES, getMessageTranslation } from './utils';
import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../../lib/encryption/constants';
import messagesStatus from '../../constants/messagesStatus';
import { withTheme } from '../../theme';
import database from '../../lib/database';
class MessageContainer extends React.Component {
static propTypes = {
@ -74,11 +73,7 @@ class MessageContainer extends React.Component {
theme: 'light'
}
state = {
author: null
}
async componentDidMount() {
componentDidMount() {
const { item } = this.props;
if (item && item.observe) {
const observable = item.observe();
@ -86,19 +81,6 @@ class MessageContainer extends React.Component {
this.forceUpdate();
});
}
const db = database.active;
const usersCollection = db.collections.get('users');
try {
const user = await usersCollection.find(item.u?._id);
const observable = user.observe();
this.userSubscription = observable.subscribe((author) => {
this.setState({ author });
this.forceUpdate();
});
} catch {
// Do nothing
}
}
shouldComponentUpdate(nextProps) {
@ -113,9 +95,6 @@ class MessageContainer extends React.Component {
if (this.subscription && this.subscription.unsubscribe) {
this.subscription.unsubscribe();
}
if (this.userSubscription && this.userSubscription.unsubscribe) {
this.userSubscription.unsubscribe();
}
}
onPress = debounce(() => {
@ -264,7 +243,6 @@ class MessageContainer extends React.Component {
}
render() {
const { author } = this.state;
const {
item, user, style, archived, baseUrl, useRealName, broadcast, fetchThreadName, showAttachment, timeFormat, isReadReceiptEnabled, autoTranslateRoom, autoTranslateLanguage, navToRoomInfo, getCustomEmoji, isThreadRoom, callJitsi, blockAction, rid, theme, getBadgeColor, toggleFollowThread
} = this.props;
@ -302,7 +280,7 @@ class MessageContainer extends React.Component {
id={id}
msg={message}
rid={rid}
author={author || u}
author={u}
ts={ts}
type={t}
attachments={attachments}

View File

@ -662,7 +662,7 @@ export default {
Logout_failed: 'Выход не успешен!',
Log_analytics_events: 'Журнал событий аналитики',
E2E_encryption_change_password_title: 'Изменить пароль шифрования',
E2E_encryption_change_password_description: 'Теперь вы можете создавать зашифрованные прватные чаты и личные беседы. Вы так же можете сделать существующие приватные чаты и личные беседы шифрованными. \nЭто сквозное шифрование, поэтому ключь для шифрования\дешифрования ваших сообщений не будет сохранен на сервере. Именно поэтому вам необходимо сохранить ваш пароль в надежном и безопасном месте. Вам необходимо вводить этот пароль на всех устройствах, где вы хотите использовать E2E шифрование.',
E2E_encryption_change_password_description: 'Теперь вы можете создавать зашифрованные прватные чаты и личные беседы. Вы так же можете сделать существующие приватные чаты и личные беседы шифрованными. \nЭто сквозное шифрование, поэтому ключь для шифрования\nдешифрования ваших сообщений не будет сохранен на сервере. Именно поэтому вам необходимо сохранить ваш пароль в надежном и безопасном месте. Вам необходимо вводить этот пароль на всех устройствах, где вы хотите использовать E2E шифрование.',
E2E_encryption_change_password_error: 'Ошибка при смене пароля E2E ключа!',
E2E_encryption_change_password_success: 'Пароль ключа E2E изменен успешно!',
E2E_encryption_change_password_message: 'Убедитесь, что вы сохранили пароль в надежном мпсте.',

View File

@ -320,8 +320,16 @@ const RocketChat = {
this.shareSDK = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) });
// set Server
const currentServer = { server };
const serversDB = database.servers;
reduxStore.dispatch(shareSelectServer(server));
const serversCollection = serversDB.collections.get('servers');
try {
const serverRecord = await serversCollection.find(server);
currentServer.version = serverRecord.version;
} catch {
// Record not found
}
reduxStore.dispatch(shareSelectServer(currentServer));
RocketChat.setCustomEmojis();
@ -368,6 +376,7 @@ const RocketChat = {
}
database.share = null;
reduxStore.dispatch(shareSelectServer({}));
reduxStore.dispatch(shareSetUser({}));
reduxStore.dispatch(shareSetSettings({}));
},

View File

@ -19,10 +19,7 @@ const RoomItem = ({
avatar,
width,
avatarSize,
baseUrl,
userId,
username,
token,
showLastMessage,
status,
useRealName,
@ -48,8 +45,7 @@ const RoomItem = ({
onPress,
toggleFav,
toggleRead,
hideChannel,
avatarETag
hideChannel
}) => (
<Touchable
onPress={onPress}
@ -70,11 +66,7 @@ const RoomItem = ({
accessibilityLabel={accessibilityLabel}
avatar={avatar}
avatarSize={avatarSize}
avatarETag={avatarETag}
type={type}
baseUrl={baseUrl}
userId={userId}
token={token}
theme={theme}
rid={rid}
>
@ -160,11 +152,8 @@ RoomItem.propTypes = {
prid: PropTypes.string,
name: PropTypes.string.isRequired,
avatar: PropTypes.string.isRequired,
baseUrl: PropTypes.string.isRequired,
showLastMessage: PropTypes.bool,
userId: PropTypes.string,
username: PropTypes.string,
token: PropTypes.string,
avatarSize: PropTypes.number,
testID: PropTypes.string,
width: PropTypes.number,
@ -191,8 +180,7 @@ RoomItem.propTypes = {
toggleFav: PropTypes.func,
toggleRead: PropTypes.func,
onPress: PropTypes.func,
hideChannel: PropTypes.func,
avatarETag: PropTypes.string
hideChannel: PropTypes.func
};
RoomItem.defaultProps = {

View File

@ -4,17 +4,13 @@ import PropTypes from 'prop-types';
import styles from './styles';
import { themes } from '../../constants/colors';
import Avatar from '../../containers/Avatar/Avatar';
import Avatar from '../../containers/Avatar';
const Wrapper = ({
accessibilityLabel,
avatar,
avatarSize,
avatarETag,
type,
baseUrl,
userId,
token,
theme,
rid,
children
@ -28,9 +24,6 @@ const Wrapper = ({
size={avatarSize}
type={type}
style={styles.avatar}
server={baseUrl}
user={{ id: userId, token }}
avatarETag={avatarETag}
rid={rid}
/>
<View
@ -50,11 +43,7 @@ Wrapper.propTypes = {
accessibilityLabel: PropTypes.string,
avatar: PropTypes.string,
avatarSize: PropTypes.number,
avatarETag: PropTypes.string,
type: PropTypes.string,
baseUrl: PropTypes.string,
userId: PropTypes.string,
token: PropTypes.string,
theme: PropTypes.string,
rid: PropTypes.string,
children: PropTypes.element

View File

@ -5,7 +5,6 @@ import { connect } from 'react-redux';
import I18n from '../../i18n';
import { ROW_HEIGHT } from './styles';
import { formatDate } from '../../utils/room';
import database from '../../lib/database';
import RoomItem from './RoomItem';
export { ROW_HEIGHT };
@ -23,13 +22,10 @@ const attrs = [
class RoomItemContainer extends React.Component {
static propTypes = {
item: PropTypes.object.isRequired,
baseUrl: PropTypes.string.isRequired,
showLastMessage: PropTypes.bool,
id: PropTypes.string,
onPress: PropTypes.func,
userId: PropTypes.string,
username: PropTypes.string,
token: PropTypes.string,
avatarSize: PropTypes.number,
testID: PropTypes.string,
width: PropTypes.number,
@ -63,7 +59,6 @@ class RoomItemContainer extends React.Component {
constructor(props) {
super(props);
this.mounted = false;
this.state = { avatarETag: '' };
this.init();
}
@ -71,11 +66,7 @@ class RoomItemContainer extends React.Component {
this.mounted = true;
}
shouldComponentUpdate(nextProps, nextState) {
const { avatarETag } = this.state;
if (nextState.avatarETag !== avatarETag) {
return true;
}
shouldComponentUpdate(nextProps) {
const { props } = this;
return !attrs.every(key => props[key] === nextProps[key]);
}
@ -88,9 +79,6 @@ class RoomItemContainer extends React.Component {
}
componentWillUnmount() {
if (this.avatarSubscription?.unsubscribe) {
this.avatarSubscription.unsubscribe();
}
if (this.roomSubscription?.unsubscribe) {
this.roomSubscription.unsubscribe();
}
@ -106,7 +94,7 @@ class RoomItemContainer extends React.Component {
return t === 'd' && id && !this.isGroupChat;
}
init = async() => {
init = () => {
const { item } = this.props;
if (item?.observe) {
const observable = item.observe();
@ -114,35 +102,6 @@ class RoomItemContainer extends React.Component {
this.forceUpdate();
});
}
const db = database.active;
const usersCollection = db.collections.get('users');
const subsCollection = db.collections.get('subscriptions');
try {
const { id } = this.props;
const { rid } = item;
let record;
if (this.isDirect) {
record = await usersCollection.find(id);
} else {
record = await subsCollection.find(rid);
}
if (record) {
const observable = record.observe();
this.avatarSubscription = observable.subscribe((u) => {
const { avatarETag } = u;
if (this.mounted) {
this.setState({ avatarETag });
} else {
this.state.avatarETag = avatarETag;
}
});
}
} catch {
// Record not found
}
}
onPress = () => {
@ -151,7 +110,6 @@ class RoomItemContainer extends React.Component {
}
render() {
const { avatarETag } = this.state;
const {
item,
getRoomTitle,
@ -165,9 +123,6 @@ class RoomItemContainer extends React.Component {
theme,
isFocused,
avatarSize,
baseUrl,
userId,
token,
status,
showLastMessage,
username,
@ -215,9 +170,6 @@ class RoomItemContainer extends React.Component {
theme={theme}
isFocused={isFocused}
size={avatarSize}
baseUrl={baseUrl}
userId={userId}
token={token}
prid={item.prid}
status={status}
hideUnreadStatus={item.hideUnreadStatus}
@ -233,7 +185,6 @@ class RoomItemContainer extends React.Component {
tunread={item.tunread}
tunreadUser={item.tunreadUser}
tunreadGroup={item.tunreadGroup}
avatarETag={avatarETag || item.avatarETag}
swipeEnabled={swipeEnabled}
/>
);

View File

@ -2,7 +2,7 @@ import { SHARE } from '../actions/actionsTypes';
const initialState = {
user: {},
server: '',
server: {},
settings: {}
};

View File

@ -19,7 +19,7 @@ import { showErrorAlert } from '../utils/info';
import I18n from '../i18n';
import log from '../utils/log';
const getServer = state => state.share.server || state.server.server;
const getServer = state => state.share.server.server || state.server.server;
const getE2eEnable = state => state.settings.E2E_Enable;
const handleEncryptionInit = function* handleEncryptionInit() {

View File

@ -1,19 +1,14 @@
import semver from 'semver';
import reduxStore from '../lib/createStore';
const formatUrl = (url, size, query) => `${ url }?format=png&size=${ size }${ query }`;
export const avatarURL = ({
type, text, size, user = {}, avatar, server, avatarETag, rid, blockUnauthenticatedAccess
type, text, size, user = {}, avatar, server, avatarETag, rid, blockUnauthenticatedAccess, serverVersion
}) => {
const { version: serverVersion } = reduxStore.getState().server;
const isLegacy = serverVersion && semver.lt(semver.coerce(serverVersion), '3.6.0');
let room;
if (type === 'd') {
room = text;
} else if (rid && !isLegacy) {
} else if (rid && !(serverVersion && semver.lt(semver.coerce(serverVersion), '3.6.0'))) {
room = `room/${ rid }`;
} else {
room = `@${ text }`;

View File

@ -12,7 +12,7 @@ import { themes } from '../../constants/colors';
import styles from './styles';
const SelectChannel = ({
server, token, userId, onChannelSelect, initial, blockUnauthenticatedAccess, theme
server, token, userId, onChannelSelect, initial, blockUnauthenticatedAccess, serverVersion, theme
}) => {
const [channels, setChannels] = useState([]);
@ -32,7 +32,8 @@ const SelectChannel = ({
server,
avatarETag: item.avatarETag,
rid: item.rid,
blockUnauthenticatedAccess
blockUnauthenticatedAccess,
serverVersion
});
return (
@ -63,6 +64,7 @@ SelectChannel.propTypes = {
initial: PropTypes.object,
onChannelSelect: PropTypes.func,
blockUnauthenticatedAccess: PropTypes.bool,
serverVersion: PropTypes.string,
theme: PropTypes.string
};

View File

@ -15,7 +15,7 @@ import styles from './styles';
import { themes } from '../../constants/colors';
const SelectUsers = ({
server, token, userId, selected, onUserSelect, blockUnauthenticatedAccess, theme
server, token, userId, selected, onUserSelect, blockUnauthenticatedAccess, serverVersion, theme
}) => {
const [users, setUsers] = useState([]);
@ -53,7 +53,8 @@ const SelectUsers = ({
user: { id: userId, token },
server,
avatarETag: item.avatarETag,
blockUnauthenticatedAccess
blockUnauthenticatedAccess,
serverVersion
});
return (
@ -84,6 +85,7 @@ SelectUsers.propTypes = {
selected: PropTypes.array,
onUserSelect: PropTypes.func,
blockUnauthenticatedAccess: PropTypes.bool,
serverVersion: PropTypes.string,
theme: PropTypes.string
};

View File

@ -40,7 +40,8 @@ class CreateChannelView extends React.Component {
error: PropTypes.object,
theme: PropTypes.string,
isMasterDetail: PropTypes.bool,
blockUnauthenticatedAccess: PropTypes.bool
blockUnauthenticatedAccess: PropTypes.bool,
serverVersion: PropTypes.string
}
constructor(props) {
@ -144,7 +145,7 @@ class CreateChannelView extends React.Component {
render() {
const { name, users } = this.state;
const {
server, user, loading, blockUnauthenticatedAccess, theme
server, user, loading, blockUnauthenticatedAccess, theme, serverVersion
} = this.props;
return (
<KeyboardView
@ -163,6 +164,7 @@ class CreateChannelView extends React.Component {
initial={this.channel && { text: RocketChat.getRoomTitle(this.channel) }}
onChannelSelect={this.selectChannel}
blockUnauthenticatedAccess={blockUnauthenticatedAccess}
serverVersion={serverVersion}
theme={theme}
/>
<TextInput
@ -180,6 +182,7 @@ class CreateChannelView extends React.Component {
selected={users}
onUserSelect={this.selectUsers}
blockUnauthenticatedAccess={blockUnauthenticatedAccess}
serverVersion={serverVersion}
theme={theme}
/>
<TextInput
@ -207,6 +210,7 @@ const mapStateToProps = state => ({
loading: state.createDiscussion.isFetching,
result: state.createDiscussion.result,
blockUnauthenticatedAccess: state.settings.Accounts_AvatarBlockUnauthenticatedAccess ?? true,
serverVersion: state.share.server.version || state.server.version,
isMasterDetail: state.app.isMasterDetail
});

View File

@ -405,6 +405,11 @@ class RoomInfoEditView extends React.Component {
this.setState({ encrypted: value });
}
isServerVersionLowerThan = (version) => {
const { serverVersion } = this.props;
return serverVersion && semver.lt(semver.coerce(serverVersion), version);
}
render() {
const {
name, nameError, description, topic, announcement, t, ro, reactWhenReadOnly, room, joinCode, saving, permissions, archived, enableSysMes, encrypted, avatar
@ -428,7 +433,11 @@ class RoomInfoEditView extends React.Component {
testID='room-info-edit-view-list'
{...scrollPersistTaps}
>
<TouchableOpacity style={styles.avatarContainer} onPress={this.changeAvatar}>
<TouchableOpacity
style={styles.avatarContainer}
onPress={this.changeAvatar}
disabled={this.isServerVersionLowerThan('3.6.0')}
>
<Avatar
type={room.t}
text={room.name}
@ -437,9 +446,14 @@ class RoomInfoEditView extends React.Component {
rid={isEmpty(avatar) && room.rid}
size={100}
>
<TouchableOpacity style={[styles.resetButton, { backgroundColor: themes[theme].dangerColor }]} onPress={this.resetAvatar}>
<CustomIcon name='delete' color={themes[theme].backgroundColor} size={24} />
</TouchableOpacity>
{this.isServerVersionLowerThan('3.6.0')
? null
: (
<TouchableOpacity style={[styles.resetButton, { backgroundColor: themes[theme].dangerColor }]} onPress={this.resetAvatar}>
<CustomIcon name='delete' color={themes[theme].backgroundColor} size={24} />
</TouchableOpacity>
)
}
</Avatar>
</TouchableOpacity>
<RCTextInput
@ -650,7 +664,7 @@ class RoomInfoEditView extends React.Component {
}
const mapStateToProps = state => ({
serverVersion: state.server.version,
serverVersion: state.share.server.version || state.server.version,
e2eEnabled: state.settings.E2E_Enable || false
});

View File

@ -896,12 +896,7 @@ class RoomsListView extends React.Component {
const { item: currentItem } = this.state;
const {
user: {
id: userId,
username,
token
},
server,
user: { username },
StoreLastMessage,
useRealName,
theme,
@ -916,10 +911,7 @@ class RoomsListView extends React.Component {
theme={theme}
id={id}
type={item.t}
userId={userId}
username={username}
token={token}
baseUrl={server}
showLastMessage={StoreLastMessage}
onPress={this.onPressItem}
testID={`rooms-list-view-item-${ item.name }`}

View File

@ -106,7 +106,7 @@ class SelectServerView extends React.Component {
}
const mapStateToProps = (({ share }) => ({
server: share.server
server: share.server.server
}));
export default connect(mapStateToProps)(withTheme(SelectServerView));

View File

@ -473,7 +473,7 @@ class ShareListView extends React.Component {
const mapStateToProps = (({ share }) => ({
userId: share.user && share.user.id,
token: share.user && share.user.token,
server: share.server
server: share.server.server
}));
export default connect(mapStateToProps)(withTheme(ShareListView));

View File

@ -345,7 +345,7 @@ ShareView.propTypes = {
const mapStateToProps = state => ({
user: getUserSelector(state),
server: state.share.server || state.server.server,
server: state.share.server.server || state.server.server,
FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList,
FileUpload_MaxFileSize: state.settings.FileUpload_MaxFileSize
});

View File

@ -56,8 +56,13 @@ const reducers = combineReducers({
name: 'Rocket Cat'
}
}),
server: () => ({
server: 'https://open.rocket.chat',
version: '3.7.0'
}),
share: () => ({
server: 'https://open.rocket.chat'
server: 'https://open.rocket.chat',
version: '3.7.0'
}),
settings: () => ({
blockUnauthenticatedAccess: false

View File

@ -4,11 +4,6 @@ import { getStorybookUI, configure } from '@storybook/react-native'; // eslint-d
import RNBootSplash from 'react-native-bootsplash';
import 'react-native-gesture-handler';
// eslint-disable-next-line no-undef
jest.mock('../app/lib/database', () => jest.fn(() => null)); // comment this line to make storybook work
// eslint-disable-next-line no-undef
jest.mock('../app/lib/createStore', () => ({ getState: () => ({ server: {} }) })); // comment this line to make storybook work
RNBootSplash.hide();
// import stories

View File

@ -37,7 +37,15 @@ const reducers = combineReducers({
username: 'diego.mello'
}
}),
share: () => ({ settings: {} }),
server: () => ({
server: 'https://open.rocket.chat',
version: '3.7.0'
}),
share: () => ({
server: 'https://open.rocket.chat',
version: '3.7.0',
settings: {}
}),
meteor: () => ({ connected: true }),
activeUsers: () => ({ abc: { status: 'online', statusText: 'dog' } })
});

View File

@ -6162,16 +6162,16 @@ entities@^2.0.0:
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.2.tgz#ac74db0bba8d33808bbf36809c3a5c3683531436"
integrity sha512-dmD3AvJQBUjKpcNkoqr+x+IF0SdRtPz9Vk0uTy4yWqga9ibB6s4v++QFWNohjiUGoMlF552ZvNyXDxz5iW0qmw==
envinfo@^7.1.0:
version "7.5.1"
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.5.1.tgz#93c26897225a00457c75e734d354ea9106a72236"
integrity sha512-hQBkDf2iO4Nv0CNHpCuSBeaSrveU6nThVxFGTrq/eDlV716UQk09zChaJae4mZRsos1x4YLY2TaH3LHUae3ZmQ==
envinfo@^7.5.0:
version "7.7.0"
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.7.0.tgz#fbfa46d739dec0554ef40220cd91fb20f64c9698"
integrity sha512-XX0+kACx7HcIFhar/JjsDtDIVcC8hnzQO1Asehq+abs+v9MtzpUuujFb6eBTT4lF9j2Bh6d2XFngbFRryjUAeQ==
envinfo@^7.7.2:
version "7.7.3"
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.7.3.tgz#4b2d8622e3e7366afb8091b23ed95569ea0208cc"
integrity sha512-46+j5QxbPWza0PB1i15nZx0xQ4I/EfQxg9J8Had3b408SV63nEtor2e+oiY63amTo9KTuh2a3XLObNwduxYwwA==
errno@^0.1.3, errno@~0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
@ -7829,6 +7829,13 @@ hermes-engine@~0.5.0:
resolved "https://registry.yarnpkg.com/hermes-engine/-/hermes-engine-0.5.0.tgz#d914acce72e9657b3c98875ad3f9094d8643f327"
integrity sha512-jSuHiOhdh2+IF3bH2gLpQ37eMkdUrEb9GK6PoG3rLRaUDK3Zn2Y9fXM+wyDfoUTA3gz9EET0/IIWk5k21qp4kw==
hermes-profile-transformer@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/hermes-profile-transformer/-/hermes-profile-transformer-0.0.6.tgz#bd0f5ecceda80dd0ddaae443469ab26fb38fc27b"
integrity sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ==
dependencies:
source-map "^0.7.3"
hex-lite@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/hex-lite/-/hex-lite-1.5.0.tgz#482db64f673dcacdb8be93c629a799ce5a76b24d"