1
0
mirror of https://github.com/tooot-app/app synced 2025-04-22 06:07:23 +02:00

Merge pull request #150 from tooot-app/main

Candidate release v2.1
This commit is contained in:
xmflsct 2021-06-22 15:01:43 +02:00 committed by GitHub
commit fdba05e702
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 199 additions and 101 deletions

View File

@ -5,7 +5,7 @@ export SENTRY_PROJECT=""
export SENTRY_AUTH_TOKEN="" export SENTRY_AUTH_TOKEN=""
export SENTRY_DSN="" export SENTRY_DSN=""
export TRANSLATE_KEY="" export TOOOT_API_KEY=""
# Fastlane start # Fastlane start
export LC_ALL="" export LC_ALL=""

View File

@ -40,7 +40,7 @@ jobs:
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }} SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
TRANSLATE_KEY: ${{ secrets.TRANSLATE_KEY }} TOOOT_API_KEY: ${{ secrets.TOOOT_API_KEY }}
FASTLANE_USER: ${{ secrets.FASTLANE_USER }} FASTLANE_USER: ${{ secrets.FASTLANE_USER }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }} MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}

View File

@ -14,7 +14,7 @@ export default (): ExpoConfig => ({
assetBundlePatterns: ['assets/*'], assetBundlePatterns: ['assets/*'],
extra: { extra: {
sentryDSN: process.env.SENTRY_DSN, sentryDSN: process.env.SENTRY_DSN,
translateKey: process.env.TRANSLATE_KEY toootApiKey: process.env.TOOOT_API_KEY
}, },
hooks: { hooks: {
postPublish: [ postPublish: [

View File

@ -3,8 +3,8 @@
"versions": { "versions": {
"native": "210511", "native": "210511",
"major": 2, "major": 2,
"minor": 0, "minor": 1,
"patch": 4, "patch": 0,
"expo": "41.0.0" "expo": "41.0.0"
}, },
"description": "tooot app for Mastodon", "description": "tooot app for Mastodon",

104
src/api/tooot.ts Normal file
View File

@ -0,0 +1,104 @@
import axios from 'axios'
import chalk from 'chalk'
import { Constants } from 'react-native-unimodules'
import * as Sentry from 'sentry-expo'
const ctx = new chalk.Instance({ level: 3 })
export type Params = {
service: 'push' | 'translate'
method: 'get' | 'post'
url: string
params?: {
[key: string]: string | number | boolean | string[] | number[] | boolean[]
}
headers?: { [key: string]: string }
body?: FormData | Object
sentry?: boolean
}
const DOMAIN = __DEV__ ? 'testapi.tooot.app' : 'api.tooot.app'
const apiTooot = async <T = unknown>({
service,
method,
url,
params,
headers,
body,
sentry = false
}: Params): Promise<{ body: T }> => {
const key = Constants.manifest.extra?.toootApiKey
console.log(
ctx.bgGreen.bold(' API tooot ') +
' ' +
method +
ctx.green(' -> ') +
`/${url}` +
(params ? ctx.green(' -> ') : ''),
params ? params : ''
)
return axios({
timeout: method === 'post' ? 1000 * 60 : 1000 * 15,
method,
baseURL: `https://${DOMAIN}/`,
url: `${service}/${url}`,
params,
headers: {
...(key && { 'x-tooot-key': key }),
'Content-Type': 'application/json',
'User-Agent': `tooot/${Constants.manifest.version}`,
Accept: '*/*',
...headers
},
...(body && { data: body })
})
.then(response => {
return Promise.resolve({
body: response.data
})
})
.catch(error => {
if (sentry) {
Sentry.Native.setExtras(error.response)
Sentry.Native.captureException(error)
}
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.error(
ctx.bold(' API tooot '),
ctx.bold('response'),
error.response.status,
error.response.data.error
)
return Promise.reject({
status: error.response.status,
message: error.response.data.error
})
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.error(
ctx.bold(' API tooot '),
ctx.bold('request'),
error.request
)
return Promise.reject()
} else {
console.error(
ctx.bold(' API tooot '),
ctx.bold('internal'),
error.message,
url
)
return Promise.reject()
}
})
}
export default apiTooot

View File

@ -128,7 +128,7 @@ const TimelineConversation: React.FC<Props> = ({
status={conversation.last_status} status={conversation.last_status}
highlighted={highlighted} highlighted={highlighted}
/> />
{conversation.last_status.poll && ( {conversation.last_status.poll ? (
<TimelinePoll <TimelinePoll
queryKey={queryKey} queryKey={queryKey}
statusId={conversation.last_status.id} statusId={conversation.last_status.id}
@ -138,7 +138,7 @@ const TimelineConversation: React.FC<Props> = ({
conversation.last_status.id === instanceAccount?.id conversation.last_status.id === instanceAccount?.id
} }
/> />
)} ) : null}
</View> </View>
<TimelineActions <TimelineActions
queryKey={queryKey} queryKey={queryKey}

View File

@ -116,13 +116,13 @@ const TimelineDefault: React.FC<Props> = ({
}} }}
> >
{typeof actualStatus.content === 'string' && {typeof actualStatus.content === 'string' &&
actualStatus.content.length > 0 && ( actualStatus.content.length > 0 ? (
<TimelineContent <TimelineContent
status={actualStatus} status={actualStatus}
highlighted={highlighted} highlighted={highlighted}
disableDetails={disableDetails} disableDetails={disableDetails}
/> />
)} ) : null}
{queryKey && actualStatus.poll ? ( {queryKey && actualStatus.poll ? (
<TimelinePoll <TimelinePoll
queryKey={queryKey} queryKey={queryKey}
@ -138,9 +138,9 @@ const TimelineDefault: React.FC<Props> = ({
actualStatus.media_attachments.length ? ( actualStatus.media_attachments.length ? (
<TimelineAttachment status={actualStatus} /> <TimelineAttachment status={actualStatus} />
) : null} ) : null}
{!disableDetails && actualStatus.card && ( {!disableDetails && actualStatus.card ? (
<TimelineCard card={actualStatus.card} /> <TimelineCard card={actualStatus.card} />
)} ) : null}
{!disableDetails ? ( {!disableDetails ? (
<TimelineFullConversation queryKey={queryKey} status={actualStatus} /> <TimelineFullConversation queryKey={queryKey} status={actualStatus} />
) : null} ) : null}
@ -148,7 +148,7 @@ const TimelineDefault: React.FC<Props> = ({
<TimelineActionsUsers status={actualStatus} highlighted={highlighted} /> <TimelineActionsUsers status={actualStatus} highlighted={highlighted} />
</View> </View>
{queryKey && !disableDetails && ( {queryKey && !disableDetails ? (
<TimelineActions <TimelineActions
queryKey={queryKey} queryKey={queryKey}
rootQueryKey={rootQueryKey} rootQueryKey={rootQueryKey}
@ -162,7 +162,7 @@ const TimelineDefault: React.FC<Props> = ({
).map(d => d?.acct)} ).map(d => d?.acct)}
reblog={item.reblog ? true : false} reblog={item.reblog ? true : false}
/> />
)} ) : null}
</Pressable> </Pressable>
) )
} }

View File

@ -113,13 +113,13 @@ const TimelineNotifications: React.FC<Props> = ({
: StyleConstants.Avatar.M + StyleConstants.Spacing.S : StyleConstants.Avatar.M + StyleConstants.Spacing.S
}} }}
> >
{notification.status.content.length > 0 && ( {notification.status.content.length > 0 ? (
<TimelineContent <TimelineContent
status={notification.status} status={notification.status}
highlighted={highlighted} highlighted={highlighted}
/> />
)} ) : null}
{notification.status.poll && ( {notification.status.poll ? (
<TimelinePoll <TimelinePoll
queryKey={queryKey} queryKey={queryKey}
statusId={notification.status.id} statusId={notification.status.id}
@ -127,13 +127,13 @@ const TimelineNotifications: React.FC<Props> = ({
reblog={false} reblog={false}
sameAccount={notification.account.id === instanceAccount?.id} sameAccount={notification.account.id === instanceAccount?.id}
/> />
)} ) : null}
{notification.status.media_attachments.length > 0 && ( {notification.status.media_attachments.length > 0 ? (
<TimelineAttachment status={notification.status} /> <TimelineAttachment status={notification.status} />
)} ) : null}
{notification.status.card && ( {notification.status.card ? (
<TimelineCard card={notification.status.card} /> <TimelineCard card={notification.status.card} />
)} ) : null}
<TimelineFullConversation <TimelineFullConversation
queryKey={queryKey} queryKey={queryKey}
status={notification.status} status={notification.status}

View File

@ -182,7 +182,7 @@ const TimelineActions: React.FC<Props> = ({
color={iconColor} color={iconColor}
size={StyleConstants.Font.Size.L} size={StyleConstants.Font.Size.L}
/> />
{status.replies_count > 0 && ( {status.replies_count > 0 ? (
<Text <Text
style={{ style={{
color: theme.secondary, color: theme.secondary,
@ -192,7 +192,7 @@ const TimelineActions: React.FC<Props> = ({
> >
{status.replies_count} {status.replies_count}
</Text> </Text>
)} ) : null}
</> </>
), ),
[status.replies_count] [status.replies_count]
@ -210,7 +210,7 @@ const TimelineActions: React.FC<Props> = ({
} }
size={StyleConstants.Font.Size.L} size={StyleConstants.Font.Size.L}
/> />
{status.reblogs_count > 0 && ( {status.reblogs_count > 0 ? (
<Text <Text
style={{ style={{
color: color(status.reblogged), color: color(status.reblogged),
@ -220,7 +220,7 @@ const TimelineActions: React.FC<Props> = ({
> >
{status.reblogs_count} {status.reblogs_count}
</Text> </Text>
)} ) : null}
</> </>
) )
}, [status.reblogged, status.reblogs_count]) }, [status.reblogged, status.reblogs_count])
@ -233,7 +233,7 @@ const TimelineActions: React.FC<Props> = ({
color={color(status.favourited)} color={color(status.favourited)}
size={StyleConstants.Font.Size.L} size={StyleConstants.Font.Size.L}
/> />
{status.favourites_count > 0 && ( {status.favourites_count > 0 ? (
<Text <Text
style={{ style={{
color: color(status.favourited), color: color(status.favourited),
@ -244,7 +244,7 @@ const TimelineActions: React.FC<Props> = ({
> >
{status.favourites_count} {status.favourites_count}
</Text> </Text>
)} ) : null}
</> </>
) )
}, [status.favourited, status.favourites_count]) }, [status.favourited, status.favourites_count])

View File

@ -74,7 +74,7 @@ const AttachmentAudio: React.FC<Props> = ({
) : null ) : null
) : ( ) : (
<> <>
{audio.preview_url && ( {audio.preview_url ? (
<GracefullyImage <GracefullyImage
uri={{ uri={{
original: audio.preview_url, original: audio.preview_url,
@ -82,7 +82,7 @@ const AttachmentAudio: React.FC<Props> = ({
}} }}
style={styles.background} style={styles.background}
/> />
)} ) : null}
<Button <Button
type='icon' type='icon'
content={audioPlaying ? 'PauseCircle' : 'PlayCircle'} content={audioPlaying ? 'PauseCircle' : 'PlayCircle'}

View File

@ -27,14 +27,14 @@ const TimelineCard = React.memo(
}} }}
testID='base' testID='base'
> >
{card.image && ( {card.image ? (
<GracefullyImage <GracefullyImage
uri={{ original: card.image }} uri={{ original: card.image }}
blurhash={card.blurhash} blurhash={card.blurhash}
style={styles.left} style={styles.left}
imageStyle={styles.image} imageStyle={styles.image}
/> />
)} ) : null}
<View style={styles.right}> <View style={styles.right}>
<Text <Text
numberOfLines={2} numberOfLines={2}

View File

@ -107,15 +107,15 @@ const ScreenActionsRoot = React.memo(
case 'status': case 'status':
return ( return (
<> <>
{!sameAccount && ( {!sameAccount ? (
<ActionsAccount <ActionsAccount
queryKey={params.queryKey} queryKey={params.queryKey}
rootQueryKey={params.rootQueryKey} rootQueryKey={params.rootQueryKey}
account={params.status.account} account={params.status.account}
dismiss={dismiss} dismiss={dismiss}
/> />
)} ) : null}
{sameAccount && params.status && ( {sameAccount && params.status ? (
<ActionsStatus <ActionsStatus
navigation={navigation} navigation={navigation}
queryKey={params.queryKey} queryKey={params.queryKey}
@ -123,22 +123,22 @@ const ScreenActionsRoot = React.memo(
status={params.status} status={params.status}
dismiss={dismiss} dismiss={dismiss}
/> />
)} ) : null}
{!sameDomain && statusDomain && ( {!sameDomain && statusDomain ? (
<ActionsDomain <ActionsDomain
queryKey={params.queryKey} queryKey={params.queryKey}
rootQueryKey={params.rootQueryKey} rootQueryKey={params.rootQueryKey}
domain={statusDomain} domain={statusDomain}
dismiss={dismiss} dismiss={dismiss}
/> />
)} ) : null}
{params.status.visibility !== 'direct' && ( {params.status.visibility !== 'direct' ? (
<ActionsShare <ActionsShare
url={params.status.url || params.status.uri} url={params.status.url || params.status.uri}
type={params.type} type={params.type}
dismiss={dismiss} dismiss={dismiss}
/> />
)} ) : null}
<Button <Button
type='text' type='text'
content={t('common:buttons.cancel')} content={t('common:buttons.cancel')}
@ -152,9 +152,9 @@ const ScreenActionsRoot = React.memo(
case 'account': case 'account':
return ( return (
<> <>
{!sameAccount && ( {!sameAccount ? (
<ActionsAccount account={params.account} dismiss={dismiss} /> <ActionsAccount account={params.account} dismiss={dismiss} />
)} ) : null}
<ActionsShare <ActionsShare
url={params.account.url} url={params.account.url}
type={params.type} type={params.type}

View File

@ -130,7 +130,7 @@ const ComposeAttachments: React.FC<Props> = ({ accessibleRefAttachments }) => {
uri: item.local?.local_thumbnail || item.remote?.preview_url uri: item.local?.local_thumbnail || item.remote?.preview_url
}} }}
/> />
{item.remote?.meta?.original?.duration && ( {item.remote?.meta?.original?.duration ? (
<Text <Text
style={[ style={[
styles.duration, styles.duration,
@ -142,7 +142,7 @@ const ComposeAttachments: React.FC<Props> = ({ accessibleRefAttachments }) => {
> >
{item.remote.meta.original.duration} {item.remote.meta.original.duration}
</Text> </Text>
)} ) : null}
{item.uploading ? ( {item.uploading ? (
<View <View
style={[ style={[

View File

@ -18,11 +18,11 @@ const ComposeRootHeader: React.FC = () => {
return ( return (
<> <>
{instanceActive !== -1 && localInstances.length > 1 && ( {instanceActive !== -1 && localInstances.length > 1 ? (
<View style={styles.postingAs}> <View style={styles.postingAs}>
<ComposePostingAs /> <ComposePostingAs />
</View> </View>
)} ) : null}
{composeState.spoiler.active ? <ComposeSpoilerInput /> : null} {composeState.spoiler.active ? <ComposeSpoilerInput /> : null}
<ComposeTextInput /> <ComposeTextInput />
</> </>

View File

@ -1,12 +1,9 @@
import apiGeneral from '@api/general' import apiGeneral from '@api/general'
import apiTooot from '@api/tooot'
import { displayMessage } from '@components/Message' import { displayMessage } from '@components/Message'
import { NavigationContainerRef } from '@react-navigation/native' import { NavigationContainerRef } from '@react-navigation/native'
import { Dispatch } from '@reduxjs/toolkit' import { Dispatch } from '@reduxjs/toolkit'
import { import { disableAllPushes, Instance } from '@utils/slices/instancesSlice'
disableAllPushes,
Instance,
PUSH_SERVER
} from '@utils/slices/instancesSlice'
import * as Notifications from 'expo-notifications' import * as Notifications from 'expo-notifications'
import { useEffect } from 'react' import { useEffect } from 'react'
import { TFunction } from 'react-i18next' import { TFunction } from 'react-i18next'
@ -34,10 +31,10 @@ const pushUseConnect = ({
}) })
).data ).data
apiGeneral({ apiTooot({
method: 'post', method: 'post',
domain: PUSH_SERVER, service: 'push',
url: 'v1/connect', url: 'connect',
body: { body: {
expoToken expoToken
}, },

View File

@ -1,4 +1,4 @@
import apiGeneral from '@api/general' import apiTooot from '@api/tooot'
import haptics from '@components/haptics' import haptics from '@components/haptics'
import { AxiosError } from 'axios' import { AxiosError } from 'axios'
import { Buffer } from 'buffer' import { Buffer } from 'buffer'
@ -21,16 +21,7 @@ export type QueryKeyTranslate = [
} }
] ]
export const TRANSLATE_SERVER = __DEV__
? 'testtranslate.tooot.app'
: 'translate.tooot.app'
const queryFunction = async ({ queryKey }: { queryKey: QueryKeyTranslate }) => { const queryFunction = async ({ queryKey }: { queryKey: QueryKeyTranslate }) => {
const key = Constants.manifest.extra?.translateKey
if (!key) {
return Promise.reject()
}
const { uri, source, target, text } = queryKey[1] const { uri, source, target, text } = queryKey[1]
const uriEncoded = Buffer.from(uri.replace(/https?:\/\//, '')) const uriEncoded = Buffer.from(uri.replace(/https?:\/\//, ''))
@ -42,11 +33,11 @@ const queryFunction = async ({ queryKey }: { queryKey: QueryKeyTranslate }) => {
'base64' 'base64'
) )
const res = await apiGeneral<Translations>({ const res = await apiTooot<Translations>({
domain: TRANSLATE_SERVER,
method: 'get', method: 'get',
url: `v1/translate/${uriEncoded}/${target}`, service: 'translate',
headers: { key, original } url: `source/${uriEncoded}/target/${target}`,
headers: { original }
}) })
haptics('Light') haptics('Light')
return res.body return res.body

View File

@ -1,12 +1,8 @@
import apiGeneral from '@api/general'
import apiInstance from '@api/instance' import apiInstance from '@api/instance'
import apiTooot from '@api/tooot'
import i18n from '@root/i18n/i18n' import i18n from '@root/i18n/i18n'
import { RootState } from '@root/store' import { RootState } from '@root/store'
import { import { getInstance, Instance } from '@utils/slices/instancesSlice'
getInstance,
Instance,
PUSH_SERVER
} from '@utils/slices/instancesSlice'
import * as Notifications from 'expo-notifications' import * as Notifications from 'expo-notifications'
import { Platform } from 'react-native' import { Platform } from 'react-native'
import androidDefaults from './androidDefaults' import androidDefaults from './androidDefaults'
@ -22,13 +18,13 @@ const register1 = async ({
accountId: Mastodon.Account['id'] accountId: Mastodon.Account['id']
accountFull: string accountFull: string
}) => { }) => {
return apiGeneral<{ return apiTooot<{
endpoint: string endpoint: string
keys: { public: string; private: string; auth: string } keys: { public: string; private: string; auth: string }
}>({ }>({
method: 'post', method: 'post',
domain: PUSH_SERVER, service: 'push',
url: 'v1/register1', url: 'register1',
body: { expoToken, instanceUrl, accountId, accountFull }, body: { expoToken, instanceUrl, accountId, accountFull },
sentry: true sentry: true
}) })
@ -47,10 +43,10 @@ const register2 = async ({
accountId: Mastodon.Account['id'] accountId: Mastodon.Account['id']
removeKeys: boolean removeKeys: boolean
}) => { }) => {
return apiGeneral({ return apiTooot({
method: 'post', method: 'post',
domain: PUSH_SERVER, service: 'push',
url: 'v1/register2', url: 'register2',
body: { expoToken, instanceUrl, accountId, serverKey, removeKeys }, body: { expoToken, instanceUrl, accountId, serverKey, removeKeys },
sentry: true sentry: true
}) })

View File

@ -1,7 +1,7 @@
import apiGeneral from '@api/general'
import apiInstance from '@api/instance' import apiInstance from '@api/instance'
import apiTooot from '@api/tooot'
import { RootState } from '@root/store' import { RootState } from '@root/store'
import { getInstance, PUSH_SERVER } from '@utils/slices/instancesSlice' import { getInstance } from '@utils/slices/instancesSlice'
import * as Notifications from 'expo-notifications' import * as Notifications from 'expo-notifications'
import { Platform } from 'react-native' import { Platform } from 'react-native'
@ -19,10 +19,10 @@ const pushUnregister = async (state: RootState, expoToken: string) => {
url: 'push/subscription' url: 'push/subscription'
}) })
await apiGeneral<{ endpoint: string; publicKey: string; auth: string }>({ await apiTooot<{ endpoint: string; publicKey: string; auth: string }>({
method: 'post', method: 'post',
domain: PUSH_SERVER, service: 'push',
url: 'v1/unregister', url: 'unregister',
body: { body: {
expoToken, expoToken,
instanceUrl: instance.url, instanceUrl: instance.url,

View File

@ -1,10 +1,10 @@
import apiGeneral from '@api/general' import apiTooot from '@api/tooot'
import { createAsyncThunk } from '@reduxjs/toolkit' import { createAsyncThunk } from '@reduxjs/toolkit'
import i18n from '@root/i18n/i18n' import i18n from '@root/i18n/i18n'
import { RootState } from '@root/store' import { RootState } from '@root/store'
import * as Notifications from 'expo-notifications' import * as Notifications from 'expo-notifications'
import { Platform } from 'react-native' import { Platform } from 'react-native'
import { getInstance, Instance, PUSH_SERVER } from '../instancesSlice' import { getInstance, Instance } from '../instancesSlice'
import androidDefaults from './push/androidDefaults' import androidDefaults from './push/androidDefaults'
export const updateInstancePushDecode = createAsyncThunk( export const updateInstancePushDecode = createAsyncThunk(
@ -25,10 +25,10 @@ export const updateInstancePushDecode = createAsyncThunk(
}) })
).data ).data
await apiGeneral({ await apiTooot({
method: 'post', method: 'post',
domain: PUSH_SERVER, service: 'push',
url: 'v1/update-decode', url: 'update-decode',
body: { body: {
expoToken, expoToken,
instanceUrl: instance.url, instanceUrl: instance.url,

View File

@ -11,8 +11,6 @@ import { updateInstancePush } from './instances/updatePush'
import { updateInstancePushAlert } from './instances/updatePushAlert' import { updateInstancePushAlert } from './instances/updatePushAlert'
import { updateInstancePushDecode } from './instances/updatePushDecode' import { updateInstancePushDecode } from './instances/updatePushDecode'
export const PUSH_SERVER = __DEV__ ? 'testpush.tooot.app' : 'push.tooot.app'
export type Instance = { export type Instance = {
active: boolean active: boolean
appData: { appData: {

View File

@ -2232,11 +2232,11 @@
xmldoc "^1.1.2" xmldoc "^1.1.2"
"@react-native-community/cli-platform-ios@^5.0.1-alpha.0": "@react-native-community/cli-platform-ios@^5.0.1-alpha.0":
version "5.0.1-alpha.2" version "5.0.1"
resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-5.0.1-alpha.2.tgz#58ab0641355cbe68a0d1737dde8c7d66eb0c0e39" resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-5.0.1.tgz#efa9c9b3bba0978d0a26d6442eefeffb5006a196"
integrity sha512-W15A75j+4bx6qbcapFia1A0M+W3JAt7Bc4VgEYvxDDRI62EsSHk1k6ZBNxs/j0cDPSYF9ZXHlRI+CWi3r9bTbQ== integrity sha512-Nr/edBEYJfElgBNvjDevs2BuDicsvQaM8nYkTGgp33pyuCZRBxsYxQqfsNmnLalTzcYaebjWj6AnjUSxzQBWqg==
dependencies: dependencies:
"@react-native-community/cli-tools" "^5.0.1-alpha.1" "@react-native-community/cli-tools" "^5.0.1"
chalk "^3.0.0" chalk "^3.0.0"
glob "^7.1.3" glob "^7.1.3"
js-yaml "^3.13.1" js-yaml "^3.13.1"
@ -2259,6 +2259,18 @@
serve-static "^1.13.1" serve-static "^1.13.1"
ws "^1.1.0" ws "^1.1.0"
"@react-native-community/cli-tools@^5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-5.0.1.tgz#9ee564dbe20448becd6bce9fbea1b59aa5797919"
integrity sha512-XOX5w98oSE8+KnkMZZPMRT7I5TaP8fLbDl0tCu40S7Epz+Zz924n80fmdu6nUDIfPT1nV6yH1hmHmWAWTDOR+Q==
dependencies:
chalk "^3.0.0"
lodash "^4.17.15"
mime "^2.4.1"
node-fetch "^2.6.0"
open "^6.2.0"
shell-quote "1.6.1"
"@react-native-community/cli-tools@^5.0.1-alpha.1": "@react-native-community/cli-tools@^5.0.1-alpha.1":
version "5.0.1-alpha.1" version "5.0.1-alpha.1"
resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-5.0.1-alpha.1.tgz#b8ceed3ee5f1c2c7d860518da3dd919dc5953870" resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-5.0.1-alpha.1.tgz#b8ceed3ee5f1c2c7d860518da3dd919dc5953870"