Add support for web links

zio/stable
Paul Frazee 2022-11-18 13:59:17 -06:00
parent 9d13e05dbf
commit 6e2b7a0b90
4 changed files with 67 additions and 30 deletions

View File

@ -108,7 +108,7 @@ export const ComposePost = observer(function ComposePost({
: undefined : undefined
const textDecorated = useMemo(() => { const textDecorated = useMemo(() => {
const re = /(@[a-z0-9\.]*)/gi const re = /(@[a-z0-9\.]*)|(https?:\/\/[\S]+)/gi
const segments = [] const segments = []
let match let match
let start = 0 let start = 0

View File

@ -1,6 +1,7 @@
import React from 'react' import React from 'react'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import { import {
Linking,
StyleProp, StyleProp,
Text, Text,
TouchableOpacity, TouchableOpacity,
@ -8,6 +9,7 @@ import {
ViewStyle, ViewStyle,
} from 'react-native' } from 'react-native'
import {useStores} from '../../../state' import {useStores} from '../../../state'
import {RootStoreModel} from '../../../state'
import {LinkActionsModel} from '../../../state/models/shell-ui' import {LinkActionsModel} from '../../../state/models/shell-ui'
export const Link = observer(function Link({ export const Link = observer(function Link({
@ -23,13 +25,10 @@ export const Link = observer(function Link({
}) { }) {
const store = useStores() const store = useStores()
const onPress = () => { const onPress = () => {
store.shell.closeModal() // close any active modals handleLink(store, href, false)
store.nav.navigate(href)
} }
const onLongPress = () => { const onLongPress = () => {
store.shell.closeModal() // close any active modals handleLink(store, href, true)
store.nav.newTab(href, title)
// store.shell.openModal(new LinkActionsModel(href, title || href))
} }
return ( return (
<TouchableOpacity <TouchableOpacity
@ -55,12 +54,10 @@ export const TextLink = observer(function Link({
}) { }) {
const store = useStores() const store = useStores()
const onPress = () => { const onPress = () => {
store.shell.closeModal() // close any active modals handleLink(store, href, false)
store.nav.navigate(href)
} }
const onLongPress = () => { const onLongPress = () => {
store.shell.closeModal() // close any active modals handleLink(store, href, true)
store.nav.newTab(href, title)
} }
return ( return (
<Text style={style} onPress={onPress} onLongPress={onLongPress}> <Text style={style} onPress={onPress} onLongPress={onLongPress}>
@ -68,3 +65,15 @@ export const TextLink = observer(function Link({
</Text> </Text>
) )
}) })
function handleLink(store: RootStoreModel, href: string, longPress: boolean) {
if (href.startsWith('http')) {
Linking.openURL(href)
} else if (longPress) {
store.shell.closeModal() // close any active modals
store.nav.newTab(href)
} else {
store.shell.closeModal() // close any active modals
store.nav.navigate(href)
}
}

View File

@ -32,14 +32,25 @@ export function RichText({
if (typeof segment === 'string') { if (typeof segment === 'string') {
els.push(segment) els.push(segment)
} else { } else {
els.push( if (segment.entity.type === 'mention') {
<TextLink els.push(
key={key} <TextLink
text={segment.text} key={key}
href={`/profile/${segment.entity.value}`} text={segment.text}
style={[style, s.blue3]} href={`/profile/${segment.entity.value}`}
/>, style={[style, s.blue3]}
) />,
)
} else if (segment.entity.type === 'link') {
els.push(
<TextLink
key={key}
text={segment.text}
href={segment.entity.value}
style={[style, s.blue3]}
/>,
)
}
} }
key++ key++
} }

View File

@ -63,19 +63,36 @@ export function extractEntities(
): Entity[] | undefined { ): Entity[] | undefined {
let match let match
let ents: Entity[] = [] let ents: Entity[] = []
const re = /(^|\s)(@)([a-zA-Z0-9\.-]+)(\b)/dg {
while ((match = re.exec(text))) { // mentions
if (knownHandles && !knownHandles.has(match[3])) { const re = /(^|\s)(@)([a-zA-Z0-9\.-]+)(\b)/dg
continue // not a known handle while ((match = re.exec(text))) {
if (knownHandles && !knownHandles.has(match[3])) {
continue // not a known handle
}
ents.push({
type: 'mention',
value: match[3],
index: {
start: match.indices[2][0], // skip the (^|\s) but include the '@'
end: match.indices[3][1],
},
})
}
}
{
// links
const re = /(^|\s)(https?:\/\/[\S]+)(\b)/dg
while ((match = re.exec(text))) {
ents.push({
type: 'link',
value: match[2],
index: {
start: match.indices[1][0], // skip the (^|\s) but include the '@'
end: match.indices[2][1],
},
})
} }
ents.push({
type: 'mention',
value: match[3],
index: {
start: match.indices[2][0], // skip the (^|\s) but include the '@'
end: match.indices[3][1],
},
})
} }
return ents.length > 0 ? ents : undefined return ents.length > 0 ? ents : undefined
} }