* add native ios code outside of ios project * helper script * going to be a lot of these commits to squash...backing up * save * start of an expo plugin * create info.plist * copy the view controller * maybe working * working * wait working now * working plugin * use current scheme * update intent path * use better params * support text in uri * build * use better encoding * handle images * cleanup ios plugin * android * move bash script to /scripts * handle cases where loaded data is uiimage rather than uri * remove unnecessary logic, allow more than 4 images and just take first 4 * android build plugin * limit images to four on android * use js for plugins, no need to build * revert changes to app config * use correct scheme on android * android readme * move ios extension to /modules * remove unnecessary event * revert typo * plugin readme * scripts readme * add configurable scheme to .env, default to `bluesky` * remove debug * revert .gitignore change * add comment about updating .env to app.config.js for those modifying scheme * modify .env * update android module to use the proper url * update ios extension * remove comment * parse and validate incoming image uris * fix types * rm oops * fix a few typos
87 lines
2.7 KiB
TypeScript
87 lines
2.7 KiB
TypeScript
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'
|
|
|
|
const VALID_IMAGE_REGEX = /^[\w.:\-_/]+\|\d+(\.\d+)?\|\d+(\.\d+)?$/
|
|
|
|
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'),
|
|
imageUrisStr: params.get('imageUris'),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
if (incomingUrl) handleIncomingURL(incomingUrl)
|
|
}, [incomingUrl, composeIntent])
|
|
}
|
|
|
|
function useComposeIntent() {
|
|
const {openComposer} = useComposerControls()
|
|
const {hasSession} = useSession()
|
|
|
|
return React.useCallback(
|
|
({
|
|
text,
|
|
imageUrisStr,
|
|
}: {
|
|
text: string | null
|
|
imageUrisStr: string | null // unused for right now, will be used later with intents
|
|
}) => {
|
|
if (!hasSession) return
|
|
|
|
const imageUris = imageUrisStr
|
|
?.split(',')
|
|
.filter(part => {
|
|
// For some security, we're going to filter out any image uri that is external. We don't want someone to
|
|
// be able to provide some link like "bluesky://intent/compose?imageUris=https://IHaveYourIpNow.com/image.jpeg
|
|
// and we load that image
|
|
if (part.includes('https://') || part.includes('http://')) {
|
|
return false
|
|
}
|
|
// We also should just filter out cases that don't have all the info we need
|
|
if (!VALID_IMAGE_REGEX.test(part)) {
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
.map(part => {
|
|
const [uri, width, height] = part.split('|')
|
|
return {uri, width: Number(width), height: Number(height)}
|
|
})
|
|
|
|
setTimeout(() => {
|
|
openComposer({
|
|
text: text ?? undefined,
|
|
imageUris: isNative ? imageUris : undefined,
|
|
})
|
|
}, 500)
|
|
},
|
|
[openComposer, hasSession],
|
|
)
|
|
}
|