diff options
author | Hailey <me@haileyok.com> | 2024-02-27 10:35:38 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-27 10:35:38 -0800 |
commit | 2a04546c7305b9bf03ea6cd26ce728ed773e2673 (patch) | |
tree | ac52dcc02d170bdc9970514387352ab8e08b2bc1 /src | |
parent | c8d02a791a84a243b290b3a1479aa6ac097a51fa (diff) | |
download | voidsky-2a04546c7305b9bf03ea6cd26ce728ed773e2673.tar.zst |
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>
Diffstat (limited to 'src')
-rw-r--r-- | src/App.native.tsx | 2 | ||||
-rw-r--r-- | src/App.web.tsx | 2 | ||||
-rw-r--r-- | src/Navigation.tsx | 8 | ||||
-rw-r--r-- | src/lib/hooks/useIntentHandler.ts | 64 |
4 files changed, 75 insertions, 1 deletions
diff --git a/src/App.native.tsx b/src/App.native.tsx index 1284154f3..f08a6235b 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -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(() => { diff --git a/src/App.web.tsx b/src/App.web.tsx index f10bb194d..6ac32a011 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -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(() => { diff --git a/src/Navigation.tsx b/src/Navigation.tsx index dfbe816f4..0aeeeb6ad 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -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') { diff --git a/src/lib/hooks/useIntentHandler.ts b/src/lib/hooks/useIntentHandler.ts new file mode 100644 index 000000000..249e6898e --- /dev/null +++ b/src/lib/hooks/useIntentHandler.ts @@ -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], + ) +} |