mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Use context to provide compose state and dispatch
This commit is contained in:
16
App.tsx
16
App.tsx
@ -16,14 +16,14 @@ setConsole({
|
|||||||
error: console.warn
|
error: console.warn
|
||||||
})
|
})
|
||||||
|
|
||||||
if (__DEV__) {
|
// if (__DEV__) {
|
||||||
const whyDidYouRender = require('@welldone-software/why-did-you-render')
|
// const whyDidYouRender = require('@welldone-software/why-did-you-render')
|
||||||
whyDidYouRender(React, {
|
// whyDidYouRender(React, {
|
||||||
trackAllPureComponents: true,
|
// trackAllPureComponents: true,
|
||||||
trackHooks: true,
|
// trackHooks: true,
|
||||||
hotReloadBufferMs: 1000
|
// hotReloadBufferMs: 1000
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
const App: React.FC = () => {
|
const App: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
|
@ -1,11 +1,22 @@
|
|||||||
import React, { ReactNode, useEffect, useReducer, useState } from 'react'
|
import React, {
|
||||||
|
createContext,
|
||||||
|
createRef,
|
||||||
|
Dispatch,
|
||||||
|
ReactNode,
|
||||||
|
RefObject,
|
||||||
|
useEffect,
|
||||||
|
useReducer,
|
||||||
|
useRef,
|
||||||
|
useState
|
||||||
|
} from 'react'
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
Alert,
|
Alert,
|
||||||
Keyboard,
|
Keyboard,
|
||||||
KeyboardAvoidingView,
|
KeyboardAvoidingView,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
Text
|
Text,
|
||||||
|
TextInput
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||||
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
||||||
@ -67,10 +78,17 @@ export type ComposeState = {
|
|||||||
| '604800'
|
| '604800'
|
||||||
| string
|
| string
|
||||||
}
|
}
|
||||||
attachments: { sensitive: boolean; uploads: Mastodon.Attachment[] }
|
attachments: {
|
||||||
|
sensitive: boolean
|
||||||
|
uploads: (Mastodon.Attachment & { local_url?: string })[]
|
||||||
|
}
|
||||||
attachmentUploadProgress?: { progress: number; aspect?: number }
|
attachmentUploadProgress?: { progress: number; aspect?: number }
|
||||||
visibility: 'public' | 'unlisted' | 'private' | 'direct'
|
visibility: 'public' | 'unlisted' | 'private' | 'direct'
|
||||||
replyToStatus?: Mastodon.Status
|
replyToStatus?: Mastodon.Status
|
||||||
|
textInputFocus: {
|
||||||
|
current: 'text' | 'spoiler'
|
||||||
|
refs: { text: RefObject<TextInput>; spoiler: RefObject<TextInput> }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PostAction =
|
export type PostAction =
|
||||||
@ -110,6 +128,10 @@ export type PostAction =
|
|||||||
type: 'visibility'
|
type: 'visibility'
|
||||||
payload: ComposeState['visibility']
|
payload: ComposeState['visibility']
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
type: 'textInputFocus'
|
||||||
|
payload: Partial<ComposeState['textInputFocus']>
|
||||||
|
}
|
||||||
|
|
||||||
const composeInitialState: ComposeState = {
|
const composeInitialState: ComposeState = {
|
||||||
spoiler: {
|
spoiler: {
|
||||||
@ -145,7 +167,11 @@ const composeInitialState: ComposeState = {
|
|||||||
getLocalAccountPreferences(store.getState())[
|
getLocalAccountPreferences(store.getState())[
|
||||||
'posting:default:visibility'
|
'posting:default:visibility'
|
||||||
] || 'public',
|
] || 'public',
|
||||||
replyToStatus: undefined
|
replyToStatus: undefined,
|
||||||
|
textInputFocus: {
|
||||||
|
current: 'text',
|
||||||
|
refs: { text: createRef(), spoiler: createRef() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const composeExistingState = ({
|
const composeExistingState = ({
|
||||||
type,
|
type,
|
||||||
@ -244,11 +270,22 @@ const postReducer = (state: ComposeState, action: PostAction): ComposeState => {
|
|||||||
}
|
}
|
||||||
case 'visibility':
|
case 'visibility':
|
||||||
return { ...state, visibility: action.payload }
|
return { ...state, visibility: action.payload }
|
||||||
|
case 'textInputFocus':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
textInputFocus: { ...state.textInputFocus, ...action.payload }
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error('Unexpected action')
|
throw new Error('Unexpected action')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ContextType = {
|
||||||
|
composeState: ComposeState
|
||||||
|
composeDispatch: Dispatch<PostAction>
|
||||||
|
}
|
||||||
|
export const ComposeContext = createContext<ContextType>({} as ContextType)
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
route: {
|
route: {
|
||||||
params:
|
params:
|
||||||
@ -298,14 +335,14 @@ const Compose: React.FC<Props> = ({ route: { params } }) => {
|
|||||||
case 'edit':
|
case 'edit':
|
||||||
if (params.incomingStatus.spoiler_text) {
|
if (params.incomingStatus.spoiler_text) {
|
||||||
formatText({
|
formatText({
|
||||||
origin: 'spoiler',
|
textInput: 'spoiler',
|
||||||
composeDispatch,
|
composeDispatch,
|
||||||
content: params.incomingStatus.spoiler_text,
|
content: params.incomingStatus.spoiler_text,
|
||||||
disableDebounce: true
|
disableDebounce: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
formatText({
|
formatText({
|
||||||
origin: 'text',
|
textInput: 'text',
|
||||||
composeDispatch,
|
composeDispatch,
|
||||||
content: params.incomingStatus.text!,
|
content: params.incomingStatus.text!,
|
||||||
disableDebounce: true
|
disableDebounce: true
|
||||||
@ -313,7 +350,7 @@ const Compose: React.FC<Props> = ({ route: { params } }) => {
|
|||||||
break
|
break
|
||||||
case 'reply':
|
case 'reply':
|
||||||
formatText({
|
formatText({
|
||||||
origin: 'text',
|
textInput: 'text',
|
||||||
composeDispatch,
|
composeDispatch,
|
||||||
content: `@${
|
content: `@${
|
||||||
params.incomingStatus.reblog
|
params.incomingStatus.reblog
|
||||||
@ -429,6 +466,8 @@ const Compose: React.FC<Props> = ({ route: { params } }) => {
|
|||||||
const totalTextCount =
|
const totalTextCount =
|
||||||
(composeState.spoiler.active ? composeState.spoiler.count : 0) +
|
(composeState.spoiler.active ? composeState.spoiler.count : 0) +
|
||||||
composeState.text.count
|
composeState.text.count
|
||||||
|
// doesn't work
|
||||||
|
const rawCount = composeState.text.raw.length
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyboardAvoidingView behavior='padding' style={{ flex: 1 }}>
|
<KeyboardAvoidingView behavior='padding' style={{ flex: 1 }}>
|
||||||
@ -475,18 +514,17 @@ const Compose: React.FC<Props> = ({ route: { params } }) => {
|
|||||||
<HeaderRight
|
<HeaderRight
|
||||||
onPress={async () => tootPost()}
|
onPress={async () => tootPost()}
|
||||||
text='发嘟嘟'
|
text='发嘟嘟'
|
||||||
disabled={
|
disabled={rawCount < 1 || totalTextCount > 500}
|
||||||
composeState.text.raw.length < 1 || totalTextCount > 500
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{() => (
|
{() => (
|
||||||
<ComposeRoot
|
<ComposeContext.Provider
|
||||||
composeState={composeState}
|
value={{ composeState, composeDispatch }}
|
||||||
composeDispatch={composeDispatch}
|
>
|
||||||
/>
|
<ComposeRoot />
|
||||||
|
</ComposeContext.Provider>
|
||||||
)}
|
)}
|
||||||
</Stack.Screen>
|
</Stack.Screen>
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
@ -502,4 +540,4 @@ const styles = StyleSheet.create({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export default React.memo(Compose, () => true)
|
export default Compose
|
||||||
|
@ -1,22 +1,13 @@
|
|||||||
import { Feather } from '@expo/vector-icons'
|
import { Feather } from '@expo/vector-icons'
|
||||||
import React, { Dispatch, useCallback, useMemo } from 'react'
|
import React, { useCallback, useContext, useMemo } from 'react'
|
||||||
import { ActionSheetIOS, StyleSheet, TextInput, View } from 'react-native'
|
import { ActionSheetIOS, StyleSheet, View } from 'react-native'
|
||||||
import { StyleConstants } from 'src/utils/styles/constants'
|
import { StyleConstants } from 'src/utils/styles/constants'
|
||||||
import { useTheme } from 'src/utils/styles/ThemeManager'
|
import { useTheme } from 'src/utils/styles/ThemeManager'
|
||||||
import { PostAction, ComposeState } from '../Compose'
|
import { ComposeContext } from '../Compose'
|
||||||
import addAttachments from './addAttachments'
|
import addAttachments from './addAttachments'
|
||||||
|
|
||||||
export interface Props {
|
const ComposeActions: React.FC = () => {
|
||||||
textInputRef: React.RefObject<TextInput>
|
const { composeState, composeDispatch } = useContext(ComposeContext)
|
||||||
composeState: ComposeState
|
|
||||||
composeDispatch: Dispatch<PostAction>
|
|
||||||
}
|
|
||||||
|
|
||||||
const ComposeActions: React.FC<Props> = ({
|
|
||||||
textInputRef,
|
|
||||||
composeState,
|
|
||||||
composeDispatch
|
|
||||||
}) => {
|
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
|
||||||
const attachmentColor = useMemo(() => {
|
const attachmentColor = useMemo(() => {
|
||||||
@ -71,7 +62,7 @@ const ComposeActions: React.FC<Props> = ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (composeState.poll.active) {
|
if (composeState.poll.active) {
|
||||||
textInputRef.current?.focus()
|
composeState.textInputFocus.refs.text.current?.focus()
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
composeState.poll.active,
|
composeState.poll.active,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Dispatch, useCallback } from 'react'
|
import React, { useCallback, useContext } from 'react'
|
||||||
import {
|
import {
|
||||||
FlatList,
|
FlatList,
|
||||||
Image,
|
Image,
|
||||||
@ -8,7 +8,7 @@ import {
|
|||||||
View
|
View
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
|
|
||||||
import { PostAction, ComposeState } from '../Compose'
|
import { ComposeContext } from '../Compose'
|
||||||
import { StyleConstants } from 'src/utils/styles/constants'
|
import { StyleConstants } from 'src/utils/styles/constants'
|
||||||
import { useTheme } from 'src/utils/styles/ThemeManager'
|
import { useTheme } from 'src/utils/styles/ThemeManager'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
@ -19,12 +19,8 @@ import { Feather } from '@expo/vector-icons'
|
|||||||
|
|
||||||
const DEFAULT_HEIGHT = 200
|
const DEFAULT_HEIGHT = 200
|
||||||
|
|
||||||
export interface Props {
|
const ComposeAttachments: React.FC = () => {
|
||||||
composeState: ComposeState
|
const { composeState, composeDispatch } = useContext(ComposeContext)
|
||||||
composeDispatch: Dispatch<PostAction>
|
|
||||||
}
|
|
||||||
|
|
||||||
const ComposeAttachments: React.FC<Props> = ({ composeState, composeDispatch }) => {
|
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
|
|
||||||
@ -106,7 +102,8 @@ const ComposeAttachments: React.FC<Props> = ({ composeState, composeDispatch })
|
|||||||
style={styles.progressContainer}
|
style={styles.progressContainer}
|
||||||
visible={composeState.attachmentUploadProgress === undefined}
|
visible={composeState.attachmentUploadProgress === undefined}
|
||||||
width={
|
width={
|
||||||
(composeState.attachmentUploadProgress?.aspect || 3 / 2) * DEFAULT_HEIGHT
|
(composeState.attachmentUploadProgress?.aspect || 3 / 2) *
|
||||||
|
DEFAULT_HEIGHT
|
||||||
}
|
}
|
||||||
height={200}
|
height={200}
|
||||||
>
|
>
|
||||||
|
@ -349,4 +349,4 @@ const styles = StyleSheet.create({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export default React.memo(ComposeEditAttachment, () => true)
|
export default ComposeEditAttachment
|
||||||
|
@ -1,34 +1,22 @@
|
|||||||
import React, { Dispatch, useCallback, useMemo } from 'react'
|
import React, { useCallback, useContext, useMemo } from 'react'
|
||||||
import {
|
import {
|
||||||
Image,
|
Image,
|
||||||
Pressable,
|
Pressable,
|
||||||
SectionList,
|
SectionList,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
|
||||||
View
|
View
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import { StyleConstants } from 'src/utils/styles/constants'
|
import { StyleConstants } from 'src/utils/styles/constants'
|
||||||
import { useTheme } from 'src/utils/styles/ThemeManager'
|
import { useTheme } from 'src/utils/styles/ThemeManager'
|
||||||
|
|
||||||
import { PostAction, ComposeState } from '../Compose'
|
import { ComposeContext } from '../Compose'
|
||||||
import updateText from './updateText'
|
import updateText from './updateText'
|
||||||
|
|
||||||
export interface Props {
|
const SingleEmoji = ({ emoji }: { emoji: Mastodon.Emoji }) => {
|
||||||
textInputRef: React.RefObject<TextInput>
|
const { composeState, composeDispatch } = useContext(ComposeContext)
|
||||||
composeState: ComposeState
|
|
||||||
composeDispatch: Dispatch<PostAction>
|
|
||||||
}
|
|
||||||
|
|
||||||
const SingleEmoji = ({
|
|
||||||
emoji,
|
|
||||||
textInputRef,
|
|
||||||
composeState,
|
|
||||||
composeDispatch
|
|
||||||
}: { emoji: Mastodon.Emoji } & Props) => {
|
|
||||||
const onPress = useCallback(() => {
|
const onPress = useCallback(() => {
|
||||||
updateText({
|
updateText({
|
||||||
origin: textInputRef.current?.isFocused() ? 'text' : 'spoiler',
|
|
||||||
composeState,
|
composeState,
|
||||||
composeDispatch,
|
composeDispatch,
|
||||||
newText: `:${emoji.shortcode}:`,
|
newText: `:${emoji.shortcode}:`,
|
||||||
@ -48,7 +36,8 @@ const SingleEmoji = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const ComposeEmojis: React.FC<Props> = ({ ...props }) => {
|
const ComposeEmojis: React.FC = () => {
|
||||||
|
const { composeState } = useContext(ComposeContext)
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
|
||||||
const listHeader = useCallback(
|
const listHeader = useCallback(
|
||||||
@ -61,7 +50,7 @@ const ComposeEmojis: React.FC<Props> = ({ ...props }) => {
|
|||||||
const emojiList = useCallback(
|
const emojiList = useCallback(
|
||||||
section =>
|
section =>
|
||||||
section.data.map((emoji: Mastodon.Emoji) => (
|
section.data.map((emoji: Mastodon.Emoji) => (
|
||||||
<SingleEmoji key={emoji.shortcode} emoji={emoji} {...props} />
|
<SingleEmoji key={emoji.shortcode} emoji={emoji} />
|
||||||
)),
|
)),
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
@ -80,7 +69,7 @@ const ComposeEmojis: React.FC<Props> = ({ ...props }) => {
|
|||||||
<SectionList
|
<SectionList
|
||||||
horizontal
|
horizontal
|
||||||
keyboardShouldPersistTaps='handled'
|
keyboardShouldPersistTaps='handled'
|
||||||
sections={props.composeState.emoji.emojis!}
|
sections={composeState.emoji.emojis!}
|
||||||
keyExtractor={item => item.shortcode}
|
keyExtractor={item => item.shortcode}
|
||||||
renderSectionHeader={listHeader}
|
renderSectionHeader={listHeader}
|
||||||
renderItem={listItem}
|
renderItem={listItem}
|
||||||
@ -116,4 +105,4 @@ const styles = StyleSheet.create({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export default ComposeEmojis
|
export default React.memo(ComposeEmojis, () => true)
|
||||||
|
@ -1,22 +1,20 @@
|
|||||||
import React, { Dispatch, useCallback, useEffect, useState } from 'react'
|
import React, { useContext, useEffect, useState } from 'react'
|
||||||
import { ActionSheetIOS, StyleSheet, TextInput, View } from 'react-native'
|
import { ActionSheetIOS, StyleSheet, TextInput, View } from 'react-native'
|
||||||
import { Feather } from '@expo/vector-icons'
|
import { Feather } from '@expo/vector-icons'
|
||||||
|
|
||||||
import { PostAction, ComposeState } from '../Compose'
|
import { ComposeContext } from '../Compose'
|
||||||
import { useTheme } from 'src/utils/styles/ThemeManager'
|
import { useTheme } from 'src/utils/styles/ThemeManager'
|
||||||
import { StyleConstants } from 'src/utils/styles/constants'
|
import { StyleConstants } from 'src/utils/styles/constants'
|
||||||
import { ButtonRow } from 'src/components/Button'
|
import { ButtonRow } from 'src/components/Button'
|
||||||
import { MenuContainer, MenuRow } from 'src/components/Menu'
|
import { MenuContainer, MenuRow } from 'src/components/Menu'
|
||||||
|
|
||||||
export interface Props {
|
const ComposePoll: React.FC = () => {
|
||||||
poll: ComposeState['poll']
|
const {
|
||||||
composeDispatch: Dispatch<PostAction>
|
composeState: {
|
||||||
}
|
poll: { total, options, multiple, expire }
|
||||||
|
},
|
||||||
const ComposePoll: React.FC<Props> = ({
|
|
||||||
poll: { total, options, multiple, expire },
|
|
||||||
composeDispatch
|
composeDispatch
|
||||||
}) => {
|
} = useContext(ComposeContext)
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
|
||||||
const expireMapping: { [key: string]: string } = {
|
const expireMapping: { [key: string]: string } = {
|
||||||
@ -34,24 +32,6 @@ const ComposePoll: React.FC<Props> = ({
|
|||||||
setFirstRender(false)
|
setFirstRender(false)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const minusOnPress = useCallback(
|
|
||||||
() =>
|
|
||||||
total > 2 &&
|
|
||||||
composeDispatch({
|
|
||||||
type: 'poll',
|
|
||||||
payload: { total: total - 1 }
|
|
||||||
}),
|
|
||||||
[total]
|
|
||||||
)
|
|
||||||
console.log('total: ', total)
|
|
||||||
const plusOnPress = useCallback(() => {
|
|
||||||
total < 4 &&
|
|
||||||
composeDispatch({
|
|
||||||
type: 'poll',
|
|
||||||
payload: { total: total + 1 }
|
|
||||||
})
|
|
||||||
}, [total])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.base, { borderColor: theme.border }]}>
|
<View style={[styles.base, { borderColor: theme.border }]}>
|
||||||
<View style={styles.options}>
|
<View style={styles.options}>
|
||||||
@ -101,14 +81,28 @@ const ComposePoll: React.FC<Props> = ({
|
|||||||
<View style={styles.controlAmount}>
|
<View style={styles.controlAmount}>
|
||||||
<View style={styles.firstButton}>
|
<View style={styles.firstButton}>
|
||||||
<ButtonRow
|
<ButtonRow
|
||||||
onPress={minusOnPress}
|
key={total + 'minus'}
|
||||||
|
onPress={() => {
|
||||||
|
total > 2 &&
|
||||||
|
composeDispatch({
|
||||||
|
type: 'poll',
|
||||||
|
payload: { total: total - 1 }
|
||||||
|
})
|
||||||
|
}}
|
||||||
icon='minus'
|
icon='minus'
|
||||||
disabled={!(total > 2)}
|
disabled={!(total > 2)}
|
||||||
buttonSize='S'
|
buttonSize='S'
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<ButtonRow
|
<ButtonRow
|
||||||
onPress={plusOnPress}
|
key={total + 'plus'}
|
||||||
|
onPress={() => {
|
||||||
|
total < 4 &&
|
||||||
|
composeDispatch({
|
||||||
|
type: 'poll',
|
||||||
|
payload: { total: total + 1 }
|
||||||
|
})
|
||||||
|
}}
|
||||||
icon='plus'
|
icon='plus'
|
||||||
disabled={!(total < 4)}
|
disabled={!(total < 4)}
|
||||||
buttonSize='S'
|
buttonSize='S'
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import React, { Dispatch, RefObject } from 'react'
|
import React, { useContext } from 'react'
|
||||||
import { StyleSheet, Text, TextInput, View } from 'react-native'
|
import { StyleSheet, View } from 'react-native'
|
||||||
import { StyleConstants } from 'src/utils/styles/constants'
|
|
||||||
import { useTheme } from 'src/utils/styles/ThemeManager'
|
import { useTheme } from 'src/utils/styles/ThemeManager'
|
||||||
|
|
||||||
import TimelineAttachment from 'src/components/Timelines/Timeline/Shared/Attachment'
|
import TimelineAttachment from 'src/components/Timelines/Timeline/Shared/Attachment'
|
||||||
@ -8,26 +7,26 @@ import TimelineAvatar from 'src/components/Timelines/Timeline/Shared/Avatar'
|
|||||||
import TimelineCard from 'src/components/Timelines/Timeline/Shared/Card'
|
import TimelineCard from 'src/components/Timelines/Timeline/Shared/Card'
|
||||||
import TimelineContent from 'src/components/Timelines/Timeline/Shared/Content'
|
import TimelineContent from 'src/components/Timelines/Timeline/Shared/Content'
|
||||||
import TimelineHeaderDefault from 'src/components/Timelines/Timeline/Shared/HeaderDefault'
|
import TimelineHeaderDefault from 'src/components/Timelines/Timeline/Shared/HeaderDefault'
|
||||||
|
import { ComposeContext } from '../Compose'
|
||||||
|
|
||||||
export interface Props {
|
const ComposeReply: React.FC = () => {
|
||||||
replyToStatus: Mastodon.Status
|
const {
|
||||||
}
|
composeState: { replyToStatus }
|
||||||
|
} = useContext(ComposeContext)
|
||||||
const ComposeReply: React.FC<Props> = ({ replyToStatus }) => {
|
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.status}>
|
<View style={styles.status}>
|
||||||
<TimelineAvatar account={replyToStatus.account} />
|
<TimelineAvatar account={replyToStatus!.account} />
|
||||||
<View style={styles.details}>
|
<View style={styles.details}>
|
||||||
<TimelineHeaderDefault status={replyToStatus} />
|
<TimelineHeaderDefault status={replyToStatus!} />
|
||||||
{replyToStatus.content.length > 0 && (
|
{replyToStatus!.content.length > 0 && (
|
||||||
<TimelineContent status={replyToStatus} />
|
<TimelineContent status={replyToStatus!} />
|
||||||
)}
|
)}
|
||||||
{replyToStatus.media_attachments.length > 0 && (
|
{replyToStatus!.media_attachments.length > 0 && (
|
||||||
<TimelineAttachment status={replyToStatus} width={200} />
|
<TimelineAttachment status={replyToStatus!} width={200} />
|
||||||
)}
|
)}
|
||||||
{replyToStatus.card && <TimelineCard card={replyToStatus.card} />}
|
{replyToStatus!.card && <TimelineCard card={replyToStatus!.card} />}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
@ -3,6 +3,7 @@ import React, {
|
|||||||
Dispatch,
|
Dispatch,
|
||||||
RefObject,
|
RefObject,
|
||||||
useCallback,
|
useCallback,
|
||||||
|
useContext,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef
|
useRef
|
||||||
@ -24,21 +25,12 @@ import { emojisFetch } from 'src/utils/fetches/emojisFetch'
|
|||||||
import { searchFetch } from 'src/utils/fetches/searchFetch'
|
import { searchFetch } from 'src/utils/fetches/searchFetch'
|
||||||
import { StyleConstants } from 'src/utils/styles/constants'
|
import { StyleConstants } from 'src/utils/styles/constants'
|
||||||
import { useTheme } from 'src/utils/styles/ThemeManager'
|
import { useTheme } from 'src/utils/styles/ThemeManager'
|
||||||
import { PostAction, ComposeState } from '../Compose'
|
import { PostAction, ComposeState, ComposeContext } from '../Compose'
|
||||||
import ComposeActions from './Actions'
|
import ComposeActions from './Actions'
|
||||||
import ComposeAttachments from './Attachments'
|
|
||||||
import ComposeEmojis from './Emojis'
|
|
||||||
import ComposePoll from './Poll'
|
|
||||||
import ComposeReply from './Reply'
|
|
||||||
import ComposeSpoilerInput from './SpoilerInput'
|
|
||||||
import ComposeTextInput from './TextInput'
|
|
||||||
import updateText from './updateText'
|
import updateText from './updateText'
|
||||||
import * as Permissions from 'expo-permissions'
|
import * as Permissions from 'expo-permissions'
|
||||||
|
import ComposeRootFooter from './Root/Footer'
|
||||||
export interface Props {
|
import ComposeRootHeader from './Root/Header'
|
||||||
composeState: ComposeState
|
|
||||||
composeDispatch: Dispatch<PostAction>
|
|
||||||
}
|
|
||||||
|
|
||||||
const ListItem = React.memo(
|
const ListItem = React.memo(
|
||||||
({
|
({
|
||||||
@ -58,7 +50,6 @@ const ListItem = React.memo(
|
|||||||
? 'text'
|
? 'text'
|
||||||
: 'spoiler'
|
: 'spoiler'
|
||||||
updateText({
|
updateText({
|
||||||
origin: focusedInput,
|
|
||||||
composeState: {
|
composeState: {
|
||||||
...composeState,
|
...composeState,
|
||||||
[focusedInput]: {
|
[focusedInput]: {
|
||||||
@ -117,7 +108,9 @@ const ListItem = React.memo(
|
|||||||
() => true
|
() => true
|
||||||
)
|
)
|
||||||
|
|
||||||
const ComposeRoot: React.FC<Props> = ({ composeState, composeDispatch }) => {
|
const ComposeRoot: React.FC = () => {
|
||||||
|
const { composeState, composeDispatch } = useContext(ComposeContext)
|
||||||
|
|
||||||
const { isFetching, isSuccess, data, refetch } = useQuery(
|
const { isFetching, isSuccess, data, refetch } = useQuery(
|
||||||
[
|
[
|
||||||
'Search',
|
'Search',
|
||||||
@ -172,112 +165,6 @@ const ComposeRoot: React.FC<Props> = ({ composeState, composeDispatch }) => {
|
|||||||
}
|
}
|
||||||
}, [isFetching])
|
}, [isFetching])
|
||||||
|
|
||||||
const listHeader = useMemo(() => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{composeState.spoiler.active ? (
|
|
||||||
<ComposeSpoilerInput
|
|
||||||
composeState={composeState}
|
|
||||||
composeDispatch={composeDispatch}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
<ComposeTextInput
|
|
||||||
composeState={composeState}
|
|
||||||
composeDispatch={composeDispatch}
|
|
||||||
textInputRef={textInputRef}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}, [composeState.spoiler.active, composeState.text.formatted])
|
|
||||||
|
|
||||||
const listFooterEmojis = useMemo(
|
|
||||||
() =>
|
|
||||||
composeState.emoji.active && (
|
|
||||||
<View style={styles.emojis}>
|
|
||||||
<ComposeEmojis
|
|
||||||
textInputRef={textInputRef}
|
|
||||||
composeState={composeState}
|
|
||||||
composeDispatch={composeDispatch}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
),
|
|
||||||
[composeState.emoji.active]
|
|
||||||
)
|
|
||||||
const listFooterAttachments = useMemo(
|
|
||||||
() =>
|
|
||||||
(composeState.attachments.uploads.length > 0 ||
|
|
||||||
composeState.attachmentUploadProgress) && (
|
|
||||||
<View style={styles.attachments}>
|
|
||||||
<ComposeAttachments
|
|
||||||
composeState={composeState}
|
|
||||||
composeDispatch={composeDispatch}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
),
|
|
||||||
[composeState.attachments.uploads, composeState.attachmentUploadProgress]
|
|
||||||
)
|
|
||||||
// const listFooterPoll = useMemo(
|
|
||||||
// () =>
|
|
||||||
// composeState.poll.active && (
|
|
||||||
// <View style={styles.poll}>
|
|
||||||
// <ComposePoll
|
|
||||||
// poll={composeState.poll}
|
|
||||||
// composeDispatch={composeDispatch}
|
|
||||||
// />
|
|
||||||
// </View>
|
|
||||||
// ),
|
|
||||||
// [
|
|
||||||
// composeState.poll.active,
|
|
||||||
// composeState.poll.total,
|
|
||||||
// composeState.poll.options['0'],
|
|
||||||
// composeState.poll.options['1'],
|
|
||||||
// composeState.poll.options['2'],
|
|
||||||
// composeState.poll.options['3'],
|
|
||||||
// composeState.poll.multiple,
|
|
||||||
// composeState.poll.expire
|
|
||||||
// ]
|
|
||||||
// )
|
|
||||||
const listFooterPoll = () =>
|
|
||||||
composeState.poll.active && (
|
|
||||||
<View style={styles.poll}>
|
|
||||||
<ComposePoll
|
|
||||||
poll={composeState.poll}
|
|
||||||
composeDispatch={composeDispatch}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
const listFooterReply = useMemo(
|
|
||||||
() =>
|
|
||||||
composeState.replyToStatus && (
|
|
||||||
<View style={styles.replyTo}>
|
|
||||||
<ComposeReply replyToStatus={composeState.replyToStatus} />
|
|
||||||
</View>
|
|
||||||
),
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
const listFooter = useMemo(() => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{listFooterEmojis}
|
|
||||||
{listFooterAttachments}
|
|
||||||
{listFooterPoll()}
|
|
||||||
{listFooterReply}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}, [
|
|
||||||
composeState.emoji.active,
|
|
||||||
composeState.attachments.uploads,
|
|
||||||
composeState.attachmentUploadProgress,
|
|
||||||
composeState.poll.active,
|
|
||||||
composeState.poll.total,
|
|
||||||
composeState.poll.options['0'],
|
|
||||||
composeState.poll.options['1'],
|
|
||||||
composeState.poll.options['2'],
|
|
||||||
composeState.poll.options['3'],
|
|
||||||
composeState.poll.multiple,
|
|
||||||
composeState.poll.expire
|
|
||||||
])
|
|
||||||
|
|
||||||
const listKey = useCallback(
|
const listKey = useCallback(
|
||||||
(item: Mastodon.Account | Mastodon.Tag) => item.url,
|
(item: Mastodon.Account | Mastodon.Tag) => item.url,
|
||||||
[isSuccess]
|
[isSuccess]
|
||||||
@ -302,18 +189,14 @@ const ComposeRoot: React.FC<Props> = ({ composeState, composeDispatch }) => {
|
|||||||
/>
|
/>
|
||||||
<FlatList
|
<FlatList
|
||||||
keyboardShouldPersistTaps='handled'
|
keyboardShouldPersistTaps='handled'
|
||||||
ListHeaderComponent={listHeader}
|
ListHeaderComponent={<ComposeRootHeader textInputRef={textInputRef} />}
|
||||||
ListFooterComponent={listFooter}
|
ListFooterComponent={<ComposeRootFooter textInputRef={textInputRef} />}
|
||||||
ListEmptyComponent={listEmpty}
|
ListEmptyComponent={listEmpty}
|
||||||
data={data}
|
data={data}
|
||||||
keyExtractor={listKey}
|
keyExtractor={listKey}
|
||||||
renderItem={listItem}
|
renderItem={listItem}
|
||||||
/>
|
/>
|
||||||
<ComposeActions
|
<ComposeActions />
|
||||||
textInputRef={textInputRef}
|
|
||||||
composeState={composeState}
|
|
||||||
composeDispatch={composeDispatch}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -323,18 +206,6 @@ const styles = StyleSheet.create({
|
|||||||
flex: 1
|
flex: 1
|
||||||
},
|
},
|
||||||
contentView: { flex: 1 },
|
contentView: { flex: 1 },
|
||||||
|
|
||||||
attachments: {
|
|
||||||
flex: 1
|
|
||||||
},
|
|
||||||
poll: {
|
|
||||||
flex: 1,
|
|
||||||
padding: StyleConstants.Spacing.Global.PagePadding
|
|
||||||
},
|
|
||||||
replyTo: {
|
|
||||||
flex: 1,
|
|
||||||
padding: StyleConstants.Spacing.Global.PagePadding
|
|
||||||
},
|
|
||||||
suggestion: {
|
suggestion: {
|
||||||
flex: 1
|
flex: 1
|
||||||
},
|
},
|
||||||
@ -374,9 +245,6 @@ const styles = StyleSheet.create({
|
|||||||
fontSize: StyleConstants.Font.Size.S,
|
fontSize: StyleConstants.Font.Size.S,
|
||||||
fontWeight: StyleConstants.Font.Weight.Bold,
|
fontWeight: StyleConstants.Font.Weight.Bold,
|
||||||
marginBottom: StyleConstants.Spacing.XS
|
marginBottom: StyleConstants.Spacing.XS
|
||||||
},
|
|
||||||
emojis: {
|
|
||||||
flex: 1
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
64
src/screens/Shared/Compose/Root/Footer.tsx
Normal file
64
src/screens/Shared/Compose/Root/Footer.tsx
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import React, { useContext } from 'react'
|
||||||
|
import { StyleSheet, TextInput, View } from 'react-native'
|
||||||
|
import { StyleConstants } from 'src/utils/styles/constants'
|
||||||
|
import { ComposeContext } from '../../Compose'
|
||||||
|
import ComposeAttachments from '../Attachments'
|
||||||
|
import ComposeEmojis from '../Emojis'
|
||||||
|
import ComposePoll from '../Poll'
|
||||||
|
import ComposeReply from '../Reply'
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
textInputRef: React.RefObject<TextInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
const ComposeRootFooter: React.FC<Props> = ({ textInputRef }) => {
|
||||||
|
const { composeState, composeDispatch } = useContext(ComposeContext)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{composeState.emoji.active && (
|
||||||
|
<View style={styles.emojis}>
|
||||||
|
<ComposeEmojis textInputRef={textInputRef} />
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{(composeState.attachments.uploads.length > 0 ||
|
||||||
|
composeState.attachmentUploadProgress) && (
|
||||||
|
<View style={styles.attachments}>
|
||||||
|
<ComposeAttachments />
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{composeState.poll.active && (
|
||||||
|
<View style={styles.poll} key='poll'>
|
||||||
|
<ComposePoll />
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{composeState.replyToStatus && (
|
||||||
|
<View style={styles.reply}>
|
||||||
|
<ComposeReply />
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
emojis: {
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
attachments: {
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
poll: {
|
||||||
|
flex: 1,
|
||||||
|
padding: StyleConstants.Spacing.Global.PagePadding
|
||||||
|
},
|
||||||
|
reply: {
|
||||||
|
flex: 1,
|
||||||
|
padding: StyleConstants.Spacing.Global.PagePadding
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default ComposeRootFooter
|
17
src/screens/Shared/Compose/Root/Header.tsx
Normal file
17
src/screens/Shared/Compose/Root/Header.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import React, { useContext } from 'react'
|
||||||
|
import { ComposeContext } from '../../Compose'
|
||||||
|
import ComposeSpoilerInput from '../SpoilerInput'
|
||||||
|
import ComposeTextInput from '../TextInput'
|
||||||
|
|
||||||
|
const ComposeRootHeader: React.FC = () => {
|
||||||
|
const { composeState } = useContext(ComposeContext)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{composeState.spoiler.active ? <ComposeSpoilerInput /> : null}
|
||||||
|
<ComposeTextInput />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ComposeRootHeader
|
@ -1,19 +1,12 @@
|
|||||||
import React, { Dispatch } from 'react'
|
import React, { useContext } from 'react'
|
||||||
import { StyleSheet, Text, TextInput } from 'react-native'
|
import { StyleSheet, Text, TextInput } from 'react-native'
|
||||||
import { StyleConstants } from 'src/utils/styles/constants'
|
import { StyleConstants } from 'src/utils/styles/constants'
|
||||||
import { useTheme } from 'src/utils/styles/ThemeManager'
|
import { useTheme } from 'src/utils/styles/ThemeManager'
|
||||||
import { PostAction, ComposeState } from '../Compose'
|
import { ComposeContext } from '../Compose'
|
||||||
import formatText from './formatText'
|
import formatText from './formatText'
|
||||||
|
|
||||||
export interface Props {
|
const ComposeSpoilerInput: React.FC = () => {
|
||||||
composeState: ComposeState
|
const { composeState, composeDispatch } = useContext(ComposeContext)
|
||||||
composeDispatch: Dispatch<PostAction>
|
|
||||||
}
|
|
||||||
|
|
||||||
const ComposeSpoilerInput: React.FC<Props> = ({
|
|
||||||
composeState,
|
|
||||||
composeDispatch,
|
|
||||||
}) => {
|
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -34,7 +27,7 @@ const ComposeSpoilerInput: React.FC<Props> = ({
|
|||||||
placeholderTextColor={theme.secondary}
|
placeholderTextColor={theme.secondary}
|
||||||
onChangeText={content =>
|
onChangeText={content =>
|
||||||
formatText({
|
formatText({
|
||||||
origin: 'spoiler',
|
textInput: 'spoiler',
|
||||||
composeDispatch,
|
composeDispatch,
|
||||||
content
|
content
|
||||||
})
|
})
|
||||||
@ -49,7 +42,7 @@ const ComposeSpoilerInput: React.FC<Props> = ({
|
|||||||
payload: { selection: { start, end } }
|
payload: { selection: { start, end } }
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
// ref={textInputRef}
|
ref={composeState.textInputFocus.refs.spoiler}
|
||||||
scrollEnabled
|
scrollEnabled
|
||||||
>
|
>
|
||||||
<Text>{composeState.spoiler.formatted}</Text>
|
<Text>{composeState.spoiler.formatted}</Text>
|
||||||
@ -68,8 +61,4 @@ const styles = StyleSheet.create({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export default React.memo(
|
export default ComposeSpoilerInput
|
||||||
ComposeSpoilerInput,
|
|
||||||
(prev, next) =>
|
|
||||||
prev.composeState.spoiler.formatted === next.composeState.spoiler.formatted
|
|
||||||
)
|
|
||||||
|
@ -1,21 +1,12 @@
|
|||||||
import React, { Dispatch, RefObject } from 'react'
|
import React, { useContext } from 'react'
|
||||||
import { StyleSheet, Text, TextInput } from 'react-native'
|
import { StyleSheet, Text, TextInput } from 'react-native'
|
||||||
import { StyleConstants } from 'src/utils/styles/constants'
|
import { StyleConstants } from 'src/utils/styles/constants'
|
||||||
import { useTheme } from 'src/utils/styles/ThemeManager'
|
import { useTheme } from 'src/utils/styles/ThemeManager'
|
||||||
import { PostAction, ComposeState } from '../Compose'
|
import { ComposeContext } from '../Compose'
|
||||||
import formatText from './formatText'
|
import formatText from './formatText'
|
||||||
|
|
||||||
export interface Props {
|
const ComposeTextInput: React.FC = () => {
|
||||||
composeState: ComposeState
|
const { composeState, composeDispatch } = useContext(ComposeContext)
|
||||||
composeDispatch: Dispatch<PostAction>
|
|
||||||
textInputRef: RefObject<TextInput>
|
|
||||||
}
|
|
||||||
|
|
||||||
const ComposeTextInput: React.FC<Props> = ({
|
|
||||||
composeState,
|
|
||||||
composeDispatch,
|
|
||||||
textInputRef
|
|
||||||
}) => {
|
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -36,11 +27,17 @@ const ComposeTextInput: React.FC<Props> = ({
|
|||||||
placeholderTextColor={theme.secondary}
|
placeholderTextColor={theme.secondary}
|
||||||
onChangeText={content =>
|
onChangeText={content =>
|
||||||
formatText({
|
formatText({
|
||||||
origin: 'text',
|
textInput: 'text',
|
||||||
composeDispatch,
|
composeDispatch,
|
||||||
content
|
content
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
onFocus={() =>
|
||||||
|
composeDispatch({
|
||||||
|
type: 'textInputFocus',
|
||||||
|
payload: { current: 'text' }
|
||||||
|
})
|
||||||
|
}
|
||||||
onSelectionChange={({
|
onSelectionChange={({
|
||||||
nativeEvent: {
|
nativeEvent: {
|
||||||
selection: { start, end }
|
selection: { start, end }
|
||||||
@ -51,7 +48,7 @@ const ComposeTextInput: React.FC<Props> = ({
|
|||||||
payload: { selection: { start, end } }
|
payload: { selection: { start, end } }
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
ref={textInputRef}
|
ref={composeState.textInputFocus.refs.text}
|
||||||
scrollEnabled
|
scrollEnabled
|
||||||
>
|
>
|
||||||
<Text>{composeState.text.formatted}</Text>
|
<Text>{composeState.text.formatted}</Text>
|
||||||
@ -70,9 +67,4 @@ const styles = StyleSheet.create({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export default React.memo(
|
export default ComposeTextInput
|
||||||
ComposeTextInput,
|
|
||||||
(prev, next) =>
|
|
||||||
prev.composeState.text.raw === next.composeState.text.raw &&
|
|
||||||
prev.composeState.text.formatted === next.composeState.text.formatted
|
|
||||||
)
|
|
||||||
|
@ -7,7 +7,7 @@ import { useTheme } from 'src/utils/styles/ThemeManager'
|
|||||||
import { PostAction, ComposeState } from '../Compose'
|
import { PostAction, ComposeState } from '../Compose'
|
||||||
|
|
||||||
export interface Params {
|
export interface Params {
|
||||||
origin: 'text' | 'spoiler'
|
textInput: ComposeState['textInputFocus']['current']
|
||||||
composeDispatch: Dispatch<PostAction>
|
composeDispatch: Dispatch<PostAction>
|
||||||
content: string
|
content: string
|
||||||
refetch?: (options?: RefetchOptions | undefined) => Promise<any>
|
refetch?: (options?: RefetchOptions | undefined) => Promise<any>
|
||||||
@ -33,7 +33,7 @@ const debouncedSuggestions = debounce(
|
|||||||
let prevTags: ComposeState['tag'][] = []
|
let prevTags: ComposeState['tag'][] = []
|
||||||
|
|
||||||
const formatText = ({
|
const formatText = ({
|
||||||
origin,
|
textInput,
|
||||||
composeDispatch,
|
composeDispatch,
|
||||||
content,
|
content,
|
||||||
disableDebounce = false
|
disableDebounce = false
|
||||||
@ -108,7 +108,7 @@ const formatText = ({
|
|||||||
contentLength = contentLength + _content.length
|
contentLength = contentLength + _content.length
|
||||||
|
|
||||||
composeDispatch({
|
composeDispatch({
|
||||||
type: origin,
|
type: textInput,
|
||||||
payload: {
|
payload: {
|
||||||
count: contentLength,
|
count: contentLength,
|
||||||
raw: content,
|
raw: content,
|
||||||
|
@ -3,25 +3,24 @@ import { PostAction, ComposeState } from '../Compose'
|
|||||||
import formatText from './formatText'
|
import formatText from './formatText'
|
||||||
|
|
||||||
const updateText = ({
|
const updateText = ({
|
||||||
origin,
|
|
||||||
composeState,
|
composeState,
|
||||||
composeDispatch,
|
composeDispatch,
|
||||||
newText,
|
newText,
|
||||||
type
|
type
|
||||||
}: {
|
}: {
|
||||||
origin: 'text' | 'spoiler'
|
|
||||||
composeState: ComposeState
|
composeState: ComposeState
|
||||||
composeDispatch: Dispatch<PostAction>
|
composeDispatch: Dispatch<PostAction>
|
||||||
newText: string
|
newText: string
|
||||||
type: 'emoji' | 'suggestion'
|
type: 'emoji' | 'suggestion'
|
||||||
}) => {
|
}) => {
|
||||||
if (composeState[origin].raw.length) {
|
const textInput = composeState.textInputFocus.current
|
||||||
const contentFront = composeState[origin].raw.slice(
|
if (composeState[textInput].raw.length) {
|
||||||
|
const contentFront = composeState[textInput].raw.slice(
|
||||||
0,
|
0,
|
||||||
composeState[origin].selection.start
|
composeState[textInput].selection.start
|
||||||
)
|
)
|
||||||
const contentRear = composeState[origin].raw.slice(
|
const contentRear = composeState[textInput].raw.slice(
|
||||||
composeState[origin].selection.end
|
composeState[textInput].selection.end
|
||||||
)
|
)
|
||||||
|
|
||||||
const whiteSpaceFront = /\s/g.test(contentFront.slice(-1))
|
const whiteSpaceFront = /\s/g.test(contentFront.slice(-1))
|
||||||
@ -32,14 +31,14 @@ const updateText = ({
|
|||||||
}${newText}${whiteSpaceRear ? '' : ' '}`
|
}${newText}${whiteSpaceRear ? '' : ' '}`
|
||||||
|
|
||||||
formatText({
|
formatText({
|
||||||
origin,
|
textInput,
|
||||||
composeDispatch,
|
composeDispatch,
|
||||||
content: [contentFront, newTextWithSpace, contentRear].join(''),
|
content: [contentFront, newTextWithSpace, contentRear].join(''),
|
||||||
disableDebounce: true
|
disableDebounce: true
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
formatText({
|
formatText({
|
||||||
origin,
|
textInput,
|
||||||
composeDispatch,
|
composeDispatch,
|
||||||
content: `${newText} `,
|
content: `${newText} `,
|
||||||
disableDebounce: true
|
disableDebounce: true
|
||||||
|
Reference in New Issue
Block a user