1
0
mirror of https://github.com/tooot-app/app synced 2025-02-13 18:30:42 +01:00

Basic mentions, hashtags and links highlighting working

This commit is contained in:
Zhiyuan Zheng 2020-11-08 01:10:38 +01:00
parent 4cbba779c5
commit 97cad12850
No known key found for this signature in database
GPG Key ID: 078A93AB607D85E0
9 changed files with 185 additions and 30 deletions

View File

@ -17,6 +17,7 @@
"@react-navigation/native": "^5.8.6",
"@react-navigation/stack": "^5.12.3",
"@reduxjs/toolkit": "^1.4.0",
"autolinker": "^3.14.2",
"expo": "~39.0.4",
"expo-app-auth": "~9.2.0",
"expo-av": "~8.6.0",
@ -58,4 +59,4 @@
"typescript": "~3.9.2"
},
"private": true
}
}

View File

@ -12,7 +12,7 @@ import { StatusBar } from 'expo-status-bar'
import Local from 'src/stacks/Local'
import Public from 'src/stacks/Public'
import Post from 'src/stacks/Post'
import PostRoot from 'src/stacks/PostRoot'
import Notifications from 'src/stacks/Notifications'
import Me from 'src/stacks/Me'
@ -36,7 +36,7 @@ export const Index: React.FC = () => {
case 'Public':
name = 'globe'
break
case 'Post':
case 'PostRoot':
name = 'plus'
break
case 'Notifications':
@ -60,7 +60,22 @@ export const Index: React.FC = () => {
>
<Tab.Screen name='Local' component={Local} />
<Tab.Screen name='Public' component={Public} />
<Tab.Screen name='Post' component={Post} />
<Tab.Screen
name='PostRoot'
component={PostRoot}
listeners={({ navigation, route }) => ({
tabPress: e => {
e.preventDefault()
const {
length,
[length - 1]: last
} = navigation.dangerouslyGetState().history
navigation.navigate(last.key.split(new RegExp(/(.*?)-/))[1], {
screen: 'PostToot'
})
}
})}
/>
<Tab.Screen name='Notifications' component={Notifications} />
<Tab.Screen name='Me' component={Me} />
</Tab.Navigator>

View File

@ -2,12 +2,14 @@ import store, { RootState } from 'src/stacks/common/store'
import ky from 'ky'
const client = async ({
version = 'v1',
method,
instance,
endpoint,
query,
body
}: {
version: 'v1' | 'v2'
method: 'get' | 'post' | 'delete'
instance: 'local' | 'remote'
endpoint: string
@ -16,13 +18,13 @@ const client = async ({
}
body?: object
}): Promise<any> => {
const state: RootState["instanceInfo"] = store.getState().instanceInfo
const state: RootState['instanceInfo'] = store.getState().instanceInfo
let response
try {
response = await ky(endpoint, {
method: method,
prefixUrl: `https://${state[instance]}/api/v1`,
prefixUrl: `https://${state[instance]}/api/${version}`,
searchParams: query,
headers: {
'Content-Type': 'application/json',

View File

@ -4,6 +4,8 @@ import { createNativeStackNavigator } from 'react-native-screens/native-stack'
import Base from './Me/Base'
import Authentication from 'src/stacks/Me/Authentication'
import sharedScreens from 'src/stacks/Shared/sharedScreens'
const Stack = createNativeStackNavigator()
const Me: React.FC = () => {
@ -17,6 +19,8 @@ const Me: React.FC = () => {
stackPresentation: 'modal'
}}
/>
{sharedScreens(Stack)}
</Stack.Navigator>
)
}

View File

@ -1,23 +0,0 @@
import React from 'react'
import { View } from 'react-native'
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
const Stack = createNativeStackNavigator()
const Post: React.FC = () => {
return (
// <Stack.Navigator>
// <Stack.Screen name='Me-Base' component={Base} />
// <Stack.Screen
// name='Me-Authentication'
// component={Authentication}
// options={{
// stackPresentation: 'modal'
// }}
// />
// </Stack.Navigator>
<View></View>
)
}
export default Post

7
src/stacks/PostRoot.tsx Normal file
View File

@ -0,0 +1,7 @@
import React from 'react'
const PostRoot: React.FC = () => {
return <></>
}
export default PostRoot

View File

@ -0,0 +1,128 @@
import { Feather } from '@expo/vector-icons'
import React, { useCallback, useEffect, useState } from 'react'
import {
Keyboard,
Pressable,
StyleSheet,
Text,
TextInput,
View
} from 'react-native'
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
import Autolinker, { HtmlTag } from 'autolinker'
const Stack = createNativeStackNavigator()
const PostTootMain = () => {
const [viewHeight, setViewHeight] = useState(0)
const [keyboardHeight, setKeyboardHeight] = useState(0)
useEffect(() => {
Keyboard.addListener('keyboardDidShow', _keyboardDidShow)
Keyboard.addListener('keyboardDidHide', _keyboardDidHide)
// cleanup function
return () => {
Keyboard.removeListener('keyboardDidShow', _keyboardDidShow)
Keyboard.removeListener('keyboardDidHide', _keyboardDidHide)
}
})
const _keyboardDidShow = (props: any) => {
setKeyboardHeight(props.endCoordinates.height)
}
const _keyboardDidHide = () => {
setKeyboardHeight(0)
}
const [charCount, setCharCount] = useState(0)
const [formattedText, setFormattedText] = useState<React.ReactNode>()
const onChangeText = useCallback(content => {
const tags: string[] = []
Autolinker.link(content, {
email: false,
phone: false,
mention: 'twitter',
hashtag: 'twitter',
replaceFn: props => {
const tag = props.getMatchedText()
tags.push(tag)
return tag
}
})
let _content = content
const children = []
tags.forEach(tag => {
const parts = _content.split(tag)
children.push(parts.shift())
children.push(<Text style={{ color: 'red' }}>{tag}</Text>)
_content = parts.join(tag)
})
children.push(_content)
setFormattedText(React.createElement(Text, null, children))
setCharCount(content.length)
}, [])
return (
<View
style={styles.main}
onLayout={({ nativeEvent }) => {
setViewHeight(nativeEvent.layout.height)
}}
>
<TextInput
style={[styles.textInput, { height: viewHeight - keyboardHeight - 44 }]}
autoCapitalize='none'
autoFocus
enablesReturnKeyAutomatically
multiline
placeholder='想说点什么'
// value={rawText}
onChangeText={onChangeText}
scrollEnabled
>
<Text>{formattedText}</Text>
</TextInput>
<Pressable style={styles.additions} onPress={() => Keyboard.dismiss()}>
<Feather name='paperclip' size={24} />
<Feather name='bar-chart-2' size={24} />
<Feather name='eye-off' size={24} />
<Text>{charCount}</Text>
</Pressable>
</View>
)
}
const PostToot: React.FC = () => {
return (
<Stack.Navigator>
<Stack.Screen
name='PostTootMain'
component={PostTootMain}
options={{
headerLeft: () => <Text></Text>,
headerCenter: () => <></>,
headerRight: () => <Text></Text>
}}
/>
</Stack.Navigator>
)
}
const styles = StyleSheet.create({
main: {
width: '100%',
height: '100%'
},
textInput: {
backgroundColor: 'gray'
},
additions: {
height: 44,
backgroundColor: 'red',
flexDirection: 'row'
}
})
export default PostToot

View File

@ -4,6 +4,7 @@ import Account from 'src/stacks/Shared/Account'
import Hashtag from 'src/stacks/Shared/Hashtag'
import Toot from 'src/stacks/Shared/Toot'
import Webview from 'src/stacks/Shared/Webview'
import PostToot from './PostToot'
const sharedScreens = (Stack: any) => {
return [
@ -21,7 +22,7 @@ const sharedScreens = (Stack: any) => {
key='Hashtag'
name='Hashtag'
component={Hashtag}
options={({ route }) => ({
options={({ route }: any) => ({
title: `#${decodeURIComponent(route.params.hashtag)}`
})}
/>,
@ -40,6 +41,14 @@ const sharedScreens = (Stack: any) => {
// options={({ route }) => ({
// title: `${route.params.domain}`
// })}
/>,
<Stack.Screen
key='PostToot'
name='PostToot'
component={PostToot}
options={{
stackPresentation: 'modal'
}}
/>
]
}

View File

@ -1670,6 +1670,13 @@ atob@^2.1.2:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
autolinker@^3.14.2:
version "3.14.2"
resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-3.14.2.tgz#71856274eb768fb7149039e24d3a2be2f5c55a63"
integrity sha512-VO66nXUCZFxTq7fVHAaiAkZNXRQ1l3IFi6D5P7DLoyIEAn2E8g7TWbyEgLlz1uW74LfWmu1A17IPWuPQyGuNVg==
dependencies:
tslib "^1.9.3"
available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5"
@ -5791,6 +5798,11 @@ toidentifier@1.0.0:
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
tslib@^1.9.3:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
type-fest@^0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48"