Add support for web links
parent
9d13e05dbf
commit
6e2b7a0b90
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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++
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue