2020-12-04 01:17:10 +01:00
|
|
|
import { debounce, differenceWith, isEqual } from 'lodash'
|
|
|
|
import React, { createElement, Dispatch } from 'react'
|
|
|
|
import { Text } from 'react-native'
|
2020-12-18 23:58:53 +01:00
|
|
|
import { FetchOptions } from 'react-query/types/core/query'
|
2020-12-13 14:04:25 +01:00
|
|
|
import Autolinker from '@root/modules/autolinker'
|
|
|
|
import { useTheme } from '@utils/styles/ThemeManager'
|
2020-12-30 00:56:25 +01:00
|
|
|
import { ComposeAction, ComposeState } from './utils/types'
|
2020-12-04 01:17:10 +01:00
|
|
|
|
|
|
|
export interface Params {
|
2020-12-11 00:29:22 +01:00
|
|
|
textInput: ComposeState['textInputFocus']['current']
|
2020-12-18 23:58:53 +01:00
|
|
|
composeDispatch: Dispatch<ComposeAction>
|
2020-12-04 01:17:10 +01:00
|
|
|
content: string
|
2020-12-18 23:58:53 +01:00
|
|
|
refetch?: (options?: FetchOptions | undefined) => Promise<any>
|
2020-12-04 01:17:10 +01:00
|
|
|
disableDebounce?: boolean
|
|
|
|
}
|
|
|
|
|
2020-12-06 22:32:36 +01:00
|
|
|
const TagText = ({ text }: { text: string }) => {
|
|
|
|
const { theme } = useTheme()
|
|
|
|
|
2020-12-26 00:40:27 +01:00
|
|
|
return <Text style={{ color: theme.blue }}>{text}</Text>
|
2020-12-06 22:32:36 +01:00
|
|
|
}
|
|
|
|
|
2020-12-04 19:04:23 +01:00
|
|
|
const debouncedSuggestions = debounce(
|
2020-12-07 12:31:40 +01:00
|
|
|
(composeDispatch, tag) => {
|
|
|
|
composeDispatch({ type: 'tag', payload: tag })
|
2020-12-04 19:04:23 +01:00
|
|
|
},
|
|
|
|
500,
|
|
|
|
{
|
|
|
|
trailing: true
|
|
|
|
}
|
|
|
|
)
|
2020-12-04 01:17:10 +01:00
|
|
|
|
2020-12-07 12:31:40 +01:00
|
|
|
let prevTags: ComposeState['tag'][] = []
|
2020-12-04 01:17:10 +01:00
|
|
|
|
|
|
|
const formatText = ({
|
2020-12-11 00:29:22 +01:00
|
|
|
textInput,
|
2020-12-07 12:31:40 +01:00
|
|
|
composeDispatch,
|
2020-12-04 01:17:10 +01:00
|
|
|
content,
|
|
|
|
disableDebounce = false
|
|
|
|
}: Params) => {
|
2020-12-07 12:31:40 +01:00
|
|
|
const tags: ComposeState['tag'][] = []
|
2020-12-04 01:17:10 +01:00
|
|
|
Autolinker.link(content, {
|
|
|
|
email: false,
|
|
|
|
phone: false,
|
|
|
|
mention: 'mastodon',
|
|
|
|
hashtag: 'twitter',
|
|
|
|
replaceFn: props => {
|
|
|
|
const type = props.getType()
|
|
|
|
let newType: 'url' | 'accounts' | 'hashtags'
|
|
|
|
switch (type) {
|
|
|
|
case 'mention':
|
|
|
|
newType = 'accounts'
|
|
|
|
break
|
|
|
|
case 'hashtag':
|
|
|
|
newType = 'hashtags'
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
newType = 'url'
|
|
|
|
break
|
|
|
|
}
|
|
|
|
tags.push({
|
|
|
|
type: newType,
|
|
|
|
text: props.getMatchedText(),
|
2020-12-07 23:34:47 +01:00
|
|
|
offset: props.getOffset(),
|
|
|
|
length: props.getMatchedText().length
|
2020-12-04 01:17:10 +01:00
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2020-12-04 19:04:23 +01:00
|
|
|
const changedTag = differenceWith(tags, prevTags, isEqual)
|
2021-01-22 01:34:20 +01:00
|
|
|
if (changedTag.length > 0 && !disableDebounce) {
|
|
|
|
if (changedTag[0]?.type !== 'url') {
|
2020-12-07 12:31:40 +01:00
|
|
|
debouncedSuggestions(composeDispatch, changedTag[0])
|
2020-12-04 01:17:10 +01:00
|
|
|
}
|
|
|
|
} else {
|
2020-12-04 19:04:23 +01:00
|
|
|
debouncedSuggestions.cancel()
|
2020-12-07 12:31:40 +01:00
|
|
|
composeDispatch({ type: 'tag', payload: undefined })
|
2020-12-04 01:17:10 +01:00
|
|
|
}
|
|
|
|
prevTags = tags
|
|
|
|
let _content = content
|
2020-12-07 23:34:47 +01:00
|
|
|
let pointer = 0
|
2020-12-04 01:17:10 +01:00
|
|
|
let contentLength: number = 0
|
|
|
|
const children = []
|
2020-12-07 23:34:47 +01:00
|
|
|
tags.forEach((tag, index) => {
|
2021-01-22 01:34:20 +01:00
|
|
|
if (tag) {
|
|
|
|
const prev = _content.substr(0, tag.offset - pointer)
|
|
|
|
const main = _content.substr(tag.offset - pointer, tag.length)
|
|
|
|
const next = _content.substr(tag.offset - pointer + tag.length)
|
|
|
|
children.push(prev)
|
|
|
|
contentLength = contentLength + prev.length
|
|
|
|
children.push(<TagText key={index} text={main} />)
|
|
|
|
switch (tag.type) {
|
|
|
|
case 'url':
|
|
|
|
contentLength = contentLength + 23
|
|
|
|
break
|
|
|
|
case 'accounts':
|
|
|
|
const theMatch = main.match(/@/g)
|
|
|
|
if (theMatch && theMatch.length > 1) {
|
|
|
|
contentLength =
|
|
|
|
contentLength + main.split(new RegExp('(@.*?)@'))[1].length
|
|
|
|
} else {
|
|
|
|
contentLength = contentLength + main.length
|
|
|
|
}
|
|
|
|
break
|
|
|
|
case 'hashtags':
|
2021-01-01 16:48:16 +01:00
|
|
|
contentLength = contentLength + main.length
|
2021-01-22 01:34:20 +01:00
|
|
|
break
|
|
|
|
}
|
|
|
|
_content = next
|
|
|
|
pointer = pointer + prev.length + tag.length
|
2020-12-04 01:17:10 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
children.push(_content)
|
|
|
|
contentLength = contentLength + _content.length
|
|
|
|
|
2020-12-07 12:31:40 +01:00
|
|
|
composeDispatch({
|
2020-12-11 00:29:22 +01:00
|
|
|
type: textInput,
|
2020-12-04 01:17:10 +01:00
|
|
|
payload: {
|
2020-12-06 23:51:13 +01:00
|
|
|
count: contentLength,
|
2020-12-04 01:17:10 +01:00
|
|
|
raw: content,
|
|
|
|
formatted: createElement(Text, null, children)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
export default formatText
|