Intent handler (#2992)
* Handle URL params * Add resources * Add other params * refactor for scope * modify the pr to support intents rather than utm remove linebreak remove linebreak handle web adjust path check to work on web add a short delay for opening the composer setup compose intent, move to `intents` directory fix intent logic ignore incoming intents in the navigation router * refactor --------- Co-authored-by: Eric Bailey <git@esb.lol>
This commit is contained in:
parent
c8d02a791a
commit
2a04546c73
7 changed files with 88 additions and 1 deletions
|
@ -45,6 +45,7 @@ import {Splash} from '#/Splash'
|
|||
import {Provider as PortalProvider} from '#/components/Portal'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useIntentHandler} from 'lib/hooks/useIntentHandler'
|
||||
|
||||
SplashScreen.preventAutoHideAsync()
|
||||
|
||||
|
@ -53,6 +54,7 @@ function InnerApp() {
|
|||
const {resumeSession} = useSessionApi()
|
||||
const theme = useColorModeTheme()
|
||||
const {_} = useLingui()
|
||||
useIntentHandler()
|
||||
|
||||
// init
|
||||
useEffect(() => {
|
||||
|
|
|
@ -32,11 +32,13 @@ import {
|
|||
import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unread'
|
||||
import * as persisted from '#/state/persisted'
|
||||
import {Provider as PortalProvider} from '#/components/Portal'
|
||||
import {useIntentHandler} from 'lib/hooks/useIntentHandler'
|
||||
|
||||
function InnerApp() {
|
||||
const {isInitialLoad, currentAccount} = useSession()
|
||||
const {resumeSession} = useSessionApi()
|
||||
const theme = useColorModeTheme()
|
||||
useIntentHandler()
|
||||
|
||||
// init
|
||||
useEffect(() => {
|
||||
|
|
|
@ -460,7 +460,8 @@ const FlatNavigator = () => {
|
|||
*/
|
||||
|
||||
const LINKING = {
|
||||
prefixes: ['bsky://', 'https://bsky.app'],
|
||||
// TODO figure out what we are going to use
|
||||
prefixes: ['bsky://', 'bluesky://', 'https://bsky.app'],
|
||||
|
||||
getPathFromState(state: State) {
|
||||
// find the current node in the navigation tree
|
||||
|
@ -478,6 +479,11 @@ const LINKING = {
|
|||
},
|
||||
|
||||
getStateFromPath(path: string) {
|
||||
// Any time we receive a url that starts with `intent/` we want to ignore it here. It will be handled in the
|
||||
// intent handler hook. We should check for the trailing slash, because if there isn't one then it isn't a valid
|
||||
// intent
|
||||
if (path.includes('intent/')) return
|
||||
|
||||
const [name, params] = router.matchPath(path)
|
||||
if (isNative) {
|
||||
if (name === 'Search') {
|
||||
|
|
64
src/lib/hooks/useIntentHandler.ts
Normal file
64
src/lib/hooks/useIntentHandler.ts
Normal file
|
@ -0,0 +1,64 @@
|
|||
import React from 'react'
|
||||
import * as Linking from 'expo-linking'
|
||||
import {isNative} from 'platform/detection'
|
||||
import {useComposerControls} from 'state/shell'
|
||||
import {useSession} from 'state/session'
|
||||
|
||||
type IntentType = 'compose'
|
||||
|
||||
export function useIntentHandler() {
|
||||
const incomingUrl = Linking.useURL()
|
||||
const composeIntent = useComposeIntent()
|
||||
|
||||
React.useEffect(() => {
|
||||
const handleIncomingURL = (url: string) => {
|
||||
const urlp = new URL(url)
|
||||
const [_, intentTypeNative, intentTypeWeb] = urlp.pathname.split('/')
|
||||
|
||||
// On native, our links look like bluesky://intent/SomeIntent, so we have to check the hostname for the
|
||||
// intent check. On web, we have to check the first part of the path since we have an actual hostname
|
||||
const intentType = isNative ? intentTypeNative : intentTypeWeb
|
||||
const isIntent = isNative
|
||||
? urlp.hostname === 'intent'
|
||||
: intentTypeNative === 'intent'
|
||||
const params = urlp.searchParams
|
||||
|
||||
if (!isIntent) return
|
||||
|
||||
switch (intentType as IntentType) {
|
||||
case 'compose': {
|
||||
composeIntent({
|
||||
text: params.get('text'),
|
||||
imageUris: params.get('imageUris'),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (incomingUrl) handleIncomingURL(incomingUrl)
|
||||
}, [incomingUrl, composeIntent])
|
||||
}
|
||||
|
||||
function useComposeIntent() {
|
||||
const {openComposer} = useComposerControls()
|
||||
const {hasSession} = useSession()
|
||||
|
||||
return React.useCallback(
|
||||
({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
text,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
imageUris,
|
||||
}: {
|
||||
text: string | null
|
||||
imageUris: string | null // unused for right now, will be used later with intents
|
||||
}) => {
|
||||
if (!hasSession) return
|
||||
|
||||
setTimeout(() => {
|
||||
openComposer({}) // will pass in values to the composer here in the share extension
|
||||
}, 500)
|
||||
},
|
||||
[openComposer, hasSession],
|
||||
)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue