diff --git a/src/Index.tsx b/src/Index.tsx index d8cbc5e2..5cffe4a8 100644 --- a/src/Index.tsx +++ b/src/Index.tsx @@ -7,7 +7,6 @@ import React from 'react' import { Feather } from '@expo/vector-icons' import store from 'src/stacks/common/store' import { Provider } from 'react-redux' -import { SafeAreaProvider } from 'react-native-safe-area-context' import Toast from 'react-native-toast-message' import { StatusBar } from 'expo-status-bar' @@ -25,66 +24,64 @@ export const Index: React.FC = () => { - - - ({ - tabBarIcon: ({ focused, color, size }) => { - let name: string - switch (route.name) { - case 'Local': - name = 'home' - break - case 'Public': - name = 'globe' - break - case 'PostRoot': - name = 'plus' - break - case 'Notifications': - name = 'bell' - break - case 'Me': - name = focused ? 'smile' : 'meh' - break - default: - name = 'alert-octagon' - break - } - return + + ({ + tabBarIcon: ({ focused, color, size }) => { + let name: string + switch (route.name) { + case 'Local': + name = 'home' + break + case 'Public': + name = 'globe' + break + case 'PostRoot': + name = 'plus' + break + case 'Notifications': + name = 'bell' + break + case 'Me': + name = focused ? 'smile' : 'meh' + break + default: + name = 'alert-octagon' + break + } + return + } + })} + tabBarOptions={{ + activeTintColor: 'black', + inactiveTintColor: 'gray', + showLabel: false + }} + > + + + ({ + tabPress: e => { + e.preventDefault() + const { + length, + [length - 1]: last + } = navigation.dangerouslyGetState().history + navigation.navigate(last.key.split(new RegExp(/(.*?)-/))[1], { + screen: 'PostToot' + }) } })} - tabBarOptions={{ - activeTintColor: 'black', - inactiveTintColor: 'gray', - showLabel: false - }} - > - - - ({ - tabPress: e => { - e.preventDefault() - const { - length, - [length - 1]: last - } = navigation.dangerouslyGetState().history - navigation.navigate(last.key.split(new RegExp(/(.*?)-/))[1], { - screen: 'PostToot' - }) - } - })} - /> - - - + /> + + + - Toast.setRef(ref)} /> - - + Toast.setRef(ref)} /> + ) } diff --git a/src/stacks/Shared/PostToot.tsx b/src/stacks/Shared/PostToot.tsx index e4e1882e..8ab8cf55 100644 --- a/src/stacks/Shared/PostToot.tsx +++ b/src/stacks/Shared/PostToot.tsx @@ -1,5 +1,12 @@ -import React, { ReactNode, useReducer } from 'react' -import { Alert, Pressable, Text } from 'react-native' +import React, { ReactNode, useEffect, useReducer, useState } from 'react' +import { + Alert, + Keyboard, + KeyboardAvoidingView, + Pressable, + Text +} from 'react-native' +import { SafeAreaView } from 'react-native-safe-area-context' import { createNativeStackNavigator } from 'react-native-screens/native-stack' import { useNavigation } from '@react-navigation/native' @@ -24,11 +31,6 @@ export type PostState = { } | undefined emojis: mastodon.Emoji[] | undefined - height: { - view: number - editor: number - keyboard: number - } } export type PostAction = @@ -52,10 +54,6 @@ export type PostAction = type: 'emojis' payload: PostState['emojis'] } - | { - type: 'height' - payload: Partial - } const postInitialState: PostState = { text: { @@ -66,12 +64,7 @@ const postInitialState: PostState = { selection: { start: 0, end: 0 }, overlay: null, tag: undefined, - emojis: undefined, - height: { - view: 0, - editor: 0, - keyboard: 0 - } + emojis: undefined } const postReducer = (state: PostState, action: PostAction): PostState => { switch (action.type) { @@ -85,8 +78,6 @@ const postReducer = (state: PostState, action: PostAction): PostState => { return { ...state, tag: action.payload } case 'emojis': return { ...state, emojis: action.payload } - case 'height': - return { ...state, height: { ...state.height, ...action.payload } } default: throw new Error('Unexpected action') } @@ -95,77 +86,102 @@ const postReducer = (state: PostState, action: PostAction): PostState => { const PostToot: React.FC = () => { const navigation = useNavigation() + const [hasKeyboard, setHasKeyboard] = useState(false) + useEffect(() => { + Keyboard.addListener('keyboardWillShow', _keyboardDidShow) + Keyboard.addListener('keyboardWillHide', _keyboardDidHide) + + // cleanup function + return () => { + Keyboard.removeListener('keyboardWillShow', _keyboardDidShow) + Keyboard.removeListener('keyboardWillHide', _keyboardDidHide) + } + }, []) + const _keyboardDidShow = () => { + setHasKeyboard(true) + } + const _keyboardDidHide = () => { + setHasKeyboard(false) + } + const [postState, postDispatch] = useReducer(postReducer, postInitialState) return ( - - ( - - Alert.alert('确认取消编辑?', '', [ - { text: '继续编辑', style: 'cancel' }, - { - text: '退出编辑', - style: 'destructive', - onPress: () => navigation.goBack() - } - ]) - } - > - 退出编辑 - - ), - headerCenter: () => <>, - headerRight: () => ( - { - if (postState.text.count < 0) { - Alert.alert('字数超限', '', [ - { - text: '返回继续编辑' - } - ]) - } else { - const res = await client({ - method: 'post', - instance: 'local', - endpoint: 'statuses', - headers: { - 'Idempotency-Key': - Date.now().toString() + Math.random().toString() - }, - query: { status: postState.text.raw } - }) - if (res.body.id) { - Alert.alert('发布成功', '', [ + + + + ( + + Alert.alert('确认取消编辑?', '', [ + { text: '继续编辑', style: 'cancel' }, { - text: '好的', + text: '退出编辑', + style: 'destructive', onPress: () => navigation.goBack() } ]) - } else { - Alert.alert('发布失败', '', [ - { - text: '返回重试' - } - ]) } - } - }} - > - 发嘟嘟 - - ) - }} - > - {props => ( - - )} - - + > + 退出编辑 + + ), + headerCenter: () => <>, + headerRight: () => ( + { + if (postState.text.count < 0) { + Alert.alert('字数超限', '', [ + { + text: '返回继续编辑' + } + ]) + } else { + const res = await client({ + method: 'post', + instance: 'local', + endpoint: 'statuses', + headers: { + 'Idempotency-Key': + Date.now().toString() + Math.random().toString() + }, + query: { status: postState.text.raw } + }) + if (res.body.id) { + Alert.alert('发布成功', '', [ + { + text: '好的', + onPress: () => navigation.goBack() + } + ]) + } else { + Alert.alert('发布失败', '', [ + { + text: '返回重试' + } + ]) + } + } + }} + > + 发嘟嘟 + + ) + }} + > + {props => ( + + )} + + + + ) } diff --git a/src/stacks/Shared/PostToot/PostMain.tsx b/src/stacks/Shared/PostToot/PostMain.tsx index 27d36d0e..ec5cdc41 100644 --- a/src/stacks/Shared/PostToot/PostMain.tsx +++ b/src/stacks/Shared/PostToot/PostMain.tsx @@ -1,4 +1,10 @@ -import React, { createElement, Dispatch, useCallback, useEffect } from 'react' +import React, { + createElement, + Dispatch, + useCallback, + useEffect, + useState +} from 'react' import { Keyboard, Pressable, @@ -7,7 +13,6 @@ import { TextInput, View } from 'react-native' -import { SafeAreaView } from 'react-native-safe-area-context' import { Feather } from '@expo/vector-icons' import { debounce, differenceWith, isEqual } from 'lodash' @@ -24,27 +29,7 @@ export interface Props { } const PostMain: React.FC = ({ postState, postDispatch }) => { - useEffect(() => { - Keyboard.addListener('keyboardDidShow', _keyboardDidShow) - Keyboard.addListener('keyboardDidHide', _keyboardDidHide) - - return () => { - Keyboard.removeListener('keyboardDidShow', _keyboardDidShow) - Keyboard.removeListener('keyboardDidHide', _keyboardDidHide) - } - }, []) - const _keyboardDidShow = (props: any) => { - postDispatch({ - type: 'height', - payload: { keyboard: props.endCoordinates.height } - }) - } - const _keyboardDidHide = () => { - postDispatch({ - type: 'height', - payload: { keyboard: 0 } - }) - } + const [editorMinHeight, setEditorMinHeight] = useState(0) const { data: emojisData } = useQuery(['Emojis'], emojisFetch) useEffect(() => { @@ -54,15 +39,10 @@ const PostMain: React.FC = ({ postState, postDispatch }) => { }, [emojisData]) const debouncedSuggestions = useCallback( - debounce( - tag => - (() => { - console.log(tag) - postDispatch({ type: 'overlay', payload: 'suggestions' }) - postDispatch({ type: 'tag', payload: tag }) - })(), - 300 - ), + debounce(tag => { + postDispatch({ type: 'overlay', payload: 'suggestions' }) + postDispatch({ type: 'tag', payload: tag }) + }, 500), [] ) let prevTags: PostState['tag'][] = [] @@ -87,7 +67,6 @@ const PostMain: React.FC = ({ postState, postDispatch }) => { newType = 'url' break } - // @ts-ignore tags.push({ type: newType, text: props.getMatchedText(), @@ -99,7 +78,18 @@ const PostMain: React.FC = ({ postState, postDispatch }) => { const changedTag = differenceWith(prevTags, tags, isEqual) // quick delete causes flicking of suggestion box - if (changedTag.length && tags.length && !disableDebounce) { + if ( + changedTag.length > 0 && + tags.length > 0 && + content.length > 0 && + !disableDebounce + ) { + console.log('changedTag length') + console.log(changedTag.length) + console.log('tags length') + console.log(tags.length) + console.log('changed Tag') + console.log(changedTag) if (changedTag[0]!.type !== 'url') { debouncedSuggestions(changedTag[0]) } @@ -147,92 +137,77 @@ const PostMain: React.FC = ({ postState, postDispatch }) => { } }) }, []) + return ( - - postDispatch({ - type: 'height', - payload: { view: nativeEvent.layout.height } - }) - } - > - + onChangeText({ content })} + onContentSizeChange={({ nativeEvent }) => { + setEditorMinHeight(nativeEvent.contentSize.height) + }} + onSelectionChange={({ + nativeEvent: { + selection: { start, end } + } + }) => { + postDispatch({ type: 'selection', payload: { start, end } }) + }} + scrollEnabled > - onChangeText({ content })} - onContentSizeChange={({ nativeEvent }) => { - postDispatch({ - type: 'height', - payload: { editor: nativeEvent.contentSize.height } - }) - }} - onSelectionChange={({ - nativeEvent: { - selection: { start, end } - } - }) => { - postDispatch({ type: 'selection', payload: { start, end } }) - }} - scrollEnabled - > - {postState.text.formatted} - - {postState.overlay === 'suggestions' ? ( - - - - ) : ( - <> - )} - {postState.overlay === 'emojis' ? ( - - - - ) : ( - <> - )} - Keyboard.dismiss()}> - - - - { - if (postState.emojis?.length && postState.overlay === null) { - postDispatch({ type: 'overlay', payload: 'emojis' }) - } - }} + {postState.text.formatted} + + {postState.overlay === 'suggestions' ? ( + + - {postState.text.count} - - - + + ) : ( + <> + )} + {postState.overlay === 'emojis' ? ( + + + + ) : ( + <> + )} + Keyboard.dismiss()}> + + + + { + if (postState.emojis?.length && postState.overlay === null) { + postDispatch({ type: 'overlay', payload: 'emojis' }) + } + }} + /> + {postState.text.count} + + ) }