Fix translations on Android using PROCESS_TEXT intent (#8486)
* use intents to translate text on android * clean up config plugins * restore day night plugin just to be safe * leave a comment for why we can't open translate directly * add todo * fix lockfile lint
This commit is contained in:
+2
-1
@@ -234,8 +234,9 @@ module.exports = function (_config) {
|
|||||||
],
|
],
|
||||||
'./plugins/starterPackAppClipExtension/withStarterPackAppClip.js',
|
'./plugins/starterPackAppClipExtension/withStarterPackAppClip.js',
|
||||||
'./plugins/withGradleJVMHeapSizeIncrease.js',
|
'./plugins/withGradleJVMHeapSizeIncrease.js',
|
||||||
'./plugins/withAndroidManifestPlugin.js',
|
'./plugins/withAndroidManifestLargeHeapPlugin.js',
|
||||||
'./plugins/withAndroidManifestFCMIconPlugin.js',
|
'./plugins/withAndroidManifestFCMIconPlugin.js',
|
||||||
|
'./plugins/withAndroidManifestIntentQueriesPlugin.js',
|
||||||
'./plugins/withAndroidStylesAccentColorPlugin.js',
|
'./plugins/withAndroidStylesAccentColorPlugin.js',
|
||||||
'./plugins/withAndroidDayNightThemePlugin.js',
|
'./plugins/withAndroidDayNightThemePlugin.js',
|
||||||
'./plugins/withAndroidNoJitpackPlugin.js',
|
'./plugins/withAndroidNoJitpackPlugin.js',
|
||||||
|
|||||||
@@ -146,6 +146,7 @@
|
|||||||
"expo-image-crop-tool": "^0.1.8",
|
"expo-image-crop-tool": "^0.1.8",
|
||||||
"expo-image-manipulator": "~13.1.7",
|
"expo-image-manipulator": "~13.1.7",
|
||||||
"expo-image-picker": "~16.1.4",
|
"expo-image-picker": "~16.1.4",
|
||||||
|
"expo-intent-launcher": "^12.1.5",
|
||||||
"expo-linear-gradient": "~14.1.5",
|
"expo-linear-gradient": "~14.1.5",
|
||||||
"expo-linking": "~7.1.5",
|
"expo-linking": "~7.1.5",
|
||||||
"expo-localization": "~16.1.5",
|
"expo-localization": "~16.1.5",
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
const {withAndroidManifest} = require('@expo/config-plugins')
|
||||||
|
|
||||||
|
const withProcessTextQuery = config =>
|
||||||
|
// eslint-disable-next-line no-shadow
|
||||||
|
withAndroidManifest(config, config => {
|
||||||
|
const manifest = config.modResults.manifest
|
||||||
|
|
||||||
|
// Ensure <queries> stub exists
|
||||||
|
if (!manifest.queries) manifest.queries = [{}]
|
||||||
|
const queries = manifest.queries[0]
|
||||||
|
|
||||||
|
queries.intent = queries.intent || []
|
||||||
|
|
||||||
|
const exists = queries.intent.some(
|
||||||
|
i =>
|
||||||
|
i.action?.[0]?.$?.['android:name'] ===
|
||||||
|
'android.intent.action.PROCESS_TEXT',
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!exists) {
|
||||||
|
queries.intent.push({
|
||||||
|
action: [{$: {'android:name': 'android.intent.action.PROCESS_TEXT'}}],
|
||||||
|
data: [{$: {'android:mimeType': 'text/plain'}}],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports = withProcessTextQuery
|
||||||
@@ -19,6 +19,7 @@ import {useNavigation} from '@react-navigation/native'
|
|||||||
|
|
||||||
import {DISCOVER_DEBUG_DIDS} from '#/lib/constants'
|
import {DISCOVER_DEBUG_DIDS} from '#/lib/constants'
|
||||||
import {useOpenLink} from '#/lib/hooks/useOpenLink'
|
import {useOpenLink} from '#/lib/hooks/useOpenLink'
|
||||||
|
import {useTranslate} from '#/lib/hooks/useTranslate'
|
||||||
import {getCurrentRoute} from '#/lib/routes/helpers'
|
import {getCurrentRoute} from '#/lib/routes/helpers'
|
||||||
import {makeProfileLink} from '#/lib/routes/links'
|
import {makeProfileLink} from '#/lib/routes/links'
|
||||||
import {
|
import {
|
||||||
@@ -28,7 +29,6 @@ import {
|
|||||||
import {logEvent, useGate} from '#/lib/statsig/statsig'
|
import {logEvent, useGate} from '#/lib/statsig/statsig'
|
||||||
import {richTextToString} from '#/lib/strings/rich-text-helpers'
|
import {richTextToString} from '#/lib/strings/rich-text-helpers'
|
||||||
import {toShareUrl} from '#/lib/strings/url-helpers'
|
import {toShareUrl} from '#/lib/strings/url-helpers'
|
||||||
import {getTranslatorLink} from '#/locale/helpers'
|
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {type Shadow} from '#/state/cache/post-shadow'
|
import {type Shadow} from '#/state/cache/post-shadow'
|
||||||
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
||||||
@@ -118,6 +118,7 @@ let PostMenuItems = ({
|
|||||||
const {hidePost} = useHiddenPostsApi()
|
const {hidePost} = useHiddenPostsApi()
|
||||||
const feedFeedback = useFeedFeedbackContext()
|
const feedFeedback = useFeedFeedbackContext()
|
||||||
const openLink = useOpenLink()
|
const openLink = useOpenLink()
|
||||||
|
const translate = useTranslate()
|
||||||
const navigation = useNavigation<NavigationProp>()
|
const navigation = useNavigation<NavigationProp>()
|
||||||
const {mutedWordsDialogControl} = useGlobalDialogsControlContext()
|
const {mutedWordsDialogControl} = useGlobalDialogsControlContext()
|
||||||
const blockPromptControl = useDialogControl()
|
const blockPromptControl = useDialogControl()
|
||||||
@@ -172,11 +173,6 @@ let PostMenuItems = ({
|
|||||||
return makeProfileLink(postAuthor, 'post', urip.rkey)
|
return makeProfileLink(postAuthor, 'post', urip.rkey)
|
||||||
}, [postUri, postAuthor])
|
}, [postUri, postAuthor])
|
||||||
|
|
||||||
const translatorUrl = getTranslatorLink(
|
|
||||||
record.text,
|
|
||||||
langPrefs.primaryLanguage,
|
|
||||||
)
|
|
||||||
|
|
||||||
const onDeletePost = () => {
|
const onDeletePost = () => {
|
||||||
deletePostMutate({uri: postUri}).then(
|
deletePostMutate({uri: postUri}).then(
|
||||||
() => {
|
() => {
|
||||||
@@ -234,8 +230,8 @@ let PostMenuItems = ({
|
|||||||
Toast.show(_(msg`Copied to clipboard`), 'clipboard-check')
|
Toast.show(_(msg`Copied to clipboard`), 'clipboard-check')
|
||||||
}
|
}
|
||||||
|
|
||||||
const onPressTranslate = async () => {
|
const onPressTranslate = () => {
|
||||||
await openLink(translatorUrl, true)
|
translate(record.text, langPrefs.primaryLanguage)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
bsky.dangerousIsType<AppBskyFeedPost.Record>(
|
bsky.dangerousIsType<AppBskyFeedPost.Record>(
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ import {type ChatBskyConvoDefs, RichText} from '@atproto/api'
|
|||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
import {useOpenLink} from '#/lib/hooks/useOpenLink'
|
import {useTranslate} from '#/lib/hooks/useTranslate'
|
||||||
import {richTextToString} from '#/lib/strings/rich-text-helpers'
|
import {richTextToString} from '#/lib/strings/rich-text-helpers'
|
||||||
import {getTranslatorLink} from '#/locale/helpers'
|
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {isNative} from '#/platform/detection'
|
import {isNative} from '#/platform/detection'
|
||||||
import {useConvoActive} from '#/state/messages/convo'
|
import {useConvoActive} from '#/state/messages/convo'
|
||||||
@@ -39,7 +38,7 @@ export let MessageContextMenu = ({
|
|||||||
const deleteControl = usePromptControl()
|
const deleteControl = usePromptControl()
|
||||||
const reportControl = usePromptControl()
|
const reportControl = usePromptControl()
|
||||||
const langPrefs = useLanguagePrefs()
|
const langPrefs = useLanguagePrefs()
|
||||||
const openLink = useOpenLink()
|
const translate = useTranslate()
|
||||||
|
|
||||||
const isFromSelf = message.sender?.did === currentAccount?.did
|
const isFromSelf = message.sender?.did === currentAccount?.did
|
||||||
|
|
||||||
@@ -57,11 +56,7 @@ export let MessageContextMenu = ({
|
|||||||
}, [_, message.text, message.facets])
|
}, [_, message.text, message.facets])
|
||||||
|
|
||||||
const onPressTranslateMessage = useCallback(() => {
|
const onPressTranslateMessage = useCallback(() => {
|
||||||
const translatorUrl = getTranslatorLink(
|
translate(message.text, langPrefs.primaryLanguage)
|
||||||
message.text,
|
|
||||||
langPrefs.primaryLanguage,
|
|
||||||
)
|
|
||||||
openLink(translatorUrl, true)
|
|
||||||
|
|
||||||
logger.metric(
|
logger.metric(
|
||||||
'translate',
|
'translate',
|
||||||
@@ -72,7 +67,7 @@ export let MessageContextMenu = ({
|
|||||||
},
|
},
|
||||||
{statsig: false},
|
{statsig: false},
|
||||||
)
|
)
|
||||||
}, [langPrefs.primaryLanguage, message.text, openLink])
|
}, [langPrefs.primaryLanguage, message.text, translate])
|
||||||
|
|
||||||
const onDelete = useCallback(() => {
|
const onDelete = useCallback(() => {
|
||||||
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
|
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import {useCallback} from 'react'
|
||||||
|
import * as IntentLauncher from 'expo-intent-launcher'
|
||||||
|
|
||||||
|
import {getTranslatorLink} from '#/locale/helpers'
|
||||||
|
import {isAndroid} from '#/platform/detection'
|
||||||
|
import {useOpenLink} from './useOpenLink'
|
||||||
|
|
||||||
|
export function useTranslate() {
|
||||||
|
const openLink = useOpenLink()
|
||||||
|
|
||||||
|
return useCallback(
|
||||||
|
async (text: string, language: string) => {
|
||||||
|
const translateUrl = getTranslatorLink(text, language)
|
||||||
|
if (isAndroid) {
|
||||||
|
try {
|
||||||
|
// use getApplicationIconAsync to determine if the translate app is installed
|
||||||
|
if (
|
||||||
|
!(await IntentLauncher.getApplicationIconAsync(
|
||||||
|
'com.google.android.apps.translate',
|
||||||
|
))
|
||||||
|
) {
|
||||||
|
throw new Error('Translate app not installed')
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this should only be called one at a time, use something like
|
||||||
|
// RQ's `scope` - otherwise can trigger the browser to open unexpectedly when the call throws -sfn
|
||||||
|
await IntentLauncher.startActivityAsync(
|
||||||
|
'android.intent.action.PROCESS_TEXT',
|
||||||
|
{
|
||||||
|
type: 'text/plain',
|
||||||
|
extra: {
|
||||||
|
'android.intent.extra.PROCESS_TEXT': text,
|
||||||
|
'android.intent.extra.PROCESS_TEXT_READONLY': true,
|
||||||
|
},
|
||||||
|
// note: to skip the intermediate app select, we need to specify a
|
||||||
|
// `className`. however, this isn't safe to hardcode, we'd need to query the
|
||||||
|
// package manager for the correct activity. this requires native code, so
|
||||||
|
// skip for now -sfn
|
||||||
|
// packageName: 'com.google.android.apps.translate',
|
||||||
|
// className: 'com.google.android.apps.translate.TranslateActivity',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} catch (err) {
|
||||||
|
if (__DEV__) console.error(err)
|
||||||
|
// most likely means they don't have the translate app
|
||||||
|
await openLink(translateUrl)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await openLink(translateUrl)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[openLink],
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ import {useLingui} from '@lingui/react'
|
|||||||
|
|
||||||
import {useActorStatus} from '#/lib/actor-status'
|
import {useActorStatus} from '#/lib/actor-status'
|
||||||
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||||
import {useOpenLink} from '#/lib/hooks/useOpenLink'
|
import {useTranslate} from '#/lib/hooks/useTranslate'
|
||||||
import {makeProfileLink} from '#/lib/routes/links'
|
import {makeProfileLink} from '#/lib/routes/links'
|
||||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||||
@@ -509,14 +509,10 @@ function ExpandedPostDetails({
|
|||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_, i18n} = useLingui()
|
const {_, i18n} = useLingui()
|
||||||
const openLink = useOpenLink()
|
const translate = useTranslate()
|
||||||
const isRootPost = !('reply' in post.record)
|
const isRootPost = !('reply' in post.record)
|
||||||
const langPrefs = useLanguagePrefs()
|
const langPrefs = useLanguagePrefs()
|
||||||
|
|
||||||
const translatorUrl = getTranslatorLink(
|
|
||||||
post.record?.text || '',
|
|
||||||
langPrefs.primaryLanguage,
|
|
||||||
)
|
|
||||||
const needsTranslation = useMemo(
|
const needsTranslation = useMemo(
|
||||||
() =>
|
() =>
|
||||||
Boolean(
|
Boolean(
|
||||||
@@ -529,7 +525,7 @@ function ExpandedPostDetails({
|
|||||||
const onTranslatePress = useCallback(
|
const onTranslatePress = useCallback(
|
||||||
(e: GestureResponderEvent) => {
|
(e: GestureResponderEvent) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
openLink(translatorUrl, true)
|
translate(post.record.text || '', langPrefs.primaryLanguage)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
bsky.dangerousIsType<AppBskyFeedPost.Record>(
|
bsky.dangerousIsType<AppBskyFeedPost.Record>(
|
||||||
@@ -546,7 +542,7 @@ function ExpandedPostDetails({
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
[openLink, translatorUrl, langPrefs, post],
|
[translate, langPrefs, post],
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -566,7 +562,12 @@ function ExpandedPostDetails({
|
|||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<InlineLinkText
|
<InlineLinkText
|
||||||
to={translatorUrl}
|
// overridden to open an intent on android, but keep
|
||||||
|
// as anchor tag for accessibility
|
||||||
|
to={getTranslatorLink(
|
||||||
|
post.record.text,
|
||||||
|
langPrefs.primaryLanguage,
|
||||||
|
)}
|
||||||
label={_(msg`Translate`)}
|
label={_(msg`Translate`)}
|
||||||
style={[a.text_sm]}
|
style={[a.text_sm]}
|
||||||
onPress={onTranslatePress}>
|
onPress={onTranslatePress}>
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import {useLingui} from '@lingui/react'
|
|||||||
import {useActorStatus} from '#/lib/actor-status'
|
import {useActorStatus} from '#/lib/actor-status'
|
||||||
import {MAX_POST_LINES} from '#/lib/constants'
|
import {MAX_POST_LINES} from '#/lib/constants'
|
||||||
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||||
import {useOpenLink} from '#/lib/hooks/useOpenLink'
|
|
||||||
import {usePalette} from '#/lib/hooks/usePalette'
|
import {usePalette} from '#/lib/hooks/usePalette'
|
||||||
|
import {useTranslate} from '#/lib/hooks/useTranslate'
|
||||||
import {makeProfileLink} from '#/lib/routes/links'
|
import {makeProfileLink} from '#/lib/routes/links'
|
||||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||||
@@ -273,10 +273,6 @@ let PostThreadItemLoaded = ({
|
|||||||
const showFollowButton =
|
const showFollowButton =
|
||||||
currentAccount?.did !== post.author.did && !onlyFollowersCanReply
|
currentAccount?.did !== post.author.did && !onlyFollowersCanReply
|
||||||
|
|
||||||
const translatorUrl = getTranslatorLink(
|
|
||||||
record?.text || '',
|
|
||||||
langPrefs.primaryLanguage,
|
|
||||||
)
|
|
||||||
const needsTranslation = useMemo(
|
const needsTranslation = useMemo(
|
||||||
() =>
|
() =>
|
||||||
Boolean(
|
Boolean(
|
||||||
@@ -477,8 +473,8 @@ let PostThreadItemLoaded = ({
|
|||||||
</ContentHider>
|
</ContentHider>
|
||||||
<ExpandedPostDetails
|
<ExpandedPostDetails
|
||||||
post={post}
|
post={post}
|
||||||
|
record={record}
|
||||||
isThreadAuthor={isThreadAuthor}
|
isThreadAuthor={isThreadAuthor}
|
||||||
translatorUrl={translatorUrl}
|
|
||||||
needsTranslation={needsTranslation}
|
needsTranslation={needsTranslation}
|
||||||
/>
|
/>
|
||||||
{post.repostCount !== 0 ||
|
{post.repostCount !== 0 ||
|
||||||
@@ -824,26 +820,26 @@ function PostOuterWrapper({
|
|||||||
|
|
||||||
function ExpandedPostDetails({
|
function ExpandedPostDetails({
|
||||||
post,
|
post,
|
||||||
|
record,
|
||||||
isThreadAuthor,
|
isThreadAuthor,
|
||||||
needsTranslation,
|
needsTranslation,
|
||||||
translatorUrl,
|
|
||||||
}: {
|
}: {
|
||||||
post: AppBskyFeedDefs.PostView
|
post: AppBskyFeedDefs.PostView
|
||||||
|
record: AppBskyFeedPost.Record
|
||||||
isThreadAuthor: boolean
|
isThreadAuthor: boolean
|
||||||
needsTranslation: boolean
|
needsTranslation: boolean
|
||||||
translatorUrl: string
|
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {_, i18n} = useLingui()
|
const {_, i18n} = useLingui()
|
||||||
const openLink = useOpenLink()
|
const translate = useTranslate()
|
||||||
const isRootPost = !('reply' in post.record)
|
const isRootPost = !('reply' in post.record)
|
||||||
const langPrefs = useLanguagePrefs()
|
const langPrefs = useLanguagePrefs()
|
||||||
|
|
||||||
const onTranslatePress = useCallback(
|
const onTranslatePress = useCallback(
|
||||||
(e: GestureResponderEvent) => {
|
(e: GestureResponderEvent) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
openLink(translatorUrl, true)
|
translate(record.text || '', langPrefs.primaryLanguage)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
bsky.dangerousIsType<AppBskyFeedPost.Record>(
|
bsky.dangerousIsType<AppBskyFeedPost.Record>(
|
||||||
@@ -864,7 +860,7 @@ function ExpandedPostDetails({
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
[openLink, translatorUrl, langPrefs, post],
|
[translate, record.text, langPrefs, post],
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -884,7 +880,9 @@ function ExpandedPostDetails({
|
|||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<InlineLinkText
|
<InlineLinkText
|
||||||
to={translatorUrl}
|
// overridden to open an intent on android, but keep
|
||||||
|
// as anchor tag for accessibility
|
||||||
|
to={getTranslatorLink(record.text, langPrefs.primaryLanguage)}
|
||||||
label={_(msg`Translate`)}
|
label={_(msg`Translate`)}
|
||||||
style={[a.text_sm, pal.link]}
|
style={[a.text_sm, pal.link]}
|
||||||
onPress={onTranslatePress}>
|
onPress={onTranslatePress}>
|
||||||
|
|||||||
@@ -77,20 +77,6 @@
|
|||||||
tlds "^1.234.0"
|
tlds "^1.234.0"
|
||||||
zod "^3.23.8"
|
zod "^3.23.8"
|
||||||
|
|
||||||
"@atproto/api@^0.16.2":
|
|
||||||
version "0.16.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.16.2.tgz#1b2870e9a03d88f00a27602281755fa82ec824dd"
|
|
||||||
integrity sha512-sSTg31J8ws8DNaoiizp+/uJideRxRaJsq+Nyl8rnSxGw0w3oCvoeRU19iRWh2t0jZEmiRJAGkveGu23NKmPYEQ==
|
|
||||||
dependencies:
|
|
||||||
"@atproto/common-web" "^0.4.2"
|
|
||||||
"@atproto/lexicon" "^0.4.12"
|
|
||||||
"@atproto/syntax" "^0.4.0"
|
|
||||||
"@atproto/xrpc" "^0.7.1"
|
|
||||||
await-lock "^2.2.2"
|
|
||||||
multiformats "^9.9.0"
|
|
||||||
tlds "^1.234.0"
|
|
||||||
zod "^3.23.8"
|
|
||||||
|
|
||||||
"@atproto/aws@^0.2.25":
|
"@atproto/aws@^0.2.25":
|
||||||
version "0.2.25"
|
version "0.2.25"
|
||||||
resolved "https://registry.yarnpkg.com/@atproto/aws/-/aws-0.2.25.tgz#d07265a656db990ffd54b254cae54388468d1dca"
|
resolved "https://registry.yarnpkg.com/@atproto/aws/-/aws-0.2.25.tgz#d07265a656db990ffd54b254cae54388468d1dca"
|
||||||
@@ -11321,6 +11307,11 @@ expo-image@^2.4.0:
|
|||||||
resolved "https://registry.yarnpkg.com/expo-image/-/expo-image-2.4.0.tgz#02f7fd743387206914cd431a6367f5be53509e3e"
|
resolved "https://registry.yarnpkg.com/expo-image/-/expo-image-2.4.0.tgz#02f7fd743387206914cd431a6367f5be53509e3e"
|
||||||
integrity sha512-TQ/LvrtJ9JBr+Tf198CAqflxcvdhuj7P24n0LQ1jHaWIVA7Z+zYKbYHnSMPSDMul/y0U46Z5bFLbiZiSidgcNw==
|
integrity sha512-TQ/LvrtJ9JBr+Tf198CAqflxcvdhuj7P24n0LQ1jHaWIVA7Z+zYKbYHnSMPSDMul/y0U46Z5bFLbiZiSidgcNw==
|
||||||
|
|
||||||
|
expo-intent-launcher@^12.1.5:
|
||||||
|
version "12.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/expo-intent-launcher/-/expo-intent-launcher-12.1.5.tgz#ed3051292b33e131535d9b35ca20b48cf56d1364"
|
||||||
|
integrity sha512-KmCc/dJHTnVf2ZdrZhYSkvQ588K7qQW+nBGfJj5woCwhEXwYz1xOLQcShnPQgQWRf8conAvQDkI3pbjYNPcECw==
|
||||||
|
|
||||||
expo-json-utils@~0.15.0:
|
expo-json-utils@~0.15.0:
|
||||||
version "0.15.0"
|
version "0.15.0"
|
||||||
resolved "https://registry.yarnpkg.com/expo-json-utils/-/expo-json-utils-0.15.0.tgz#6723574814b9e6b0a90e4e23662be76123ab6ae9"
|
resolved "https://registry.yarnpkg.com/expo-json-utils/-/expo-json-utils-0.15.0.tgz#6723574814b9e6b0a90e4e23662be76123ab6ae9"
|
||||||
|
|||||||
Reference in New Issue
Block a user