POC compose using the new emoji selector

This commit is contained in:
xmflsct 2022-09-18 23:54:50 +02:00
parent 7282434e69
commit 2df23a8a2e
10 changed files with 138 additions and 245 deletions

View File

@ -44,7 +44,7 @@ const EmojisList = () => {
const contentFront = value.slice(0, selection.start) const contentFront = value.slice(0, selection.start)
const contentRear = value.slice(selection.end) const contentRear = value.slice(selection.end)
const spaceFront = /\s/g.test(contentFront.slice(-1)) ? '' : ' ' const spaceFront = value.length === 0 || /\s/g.test(contentFront.slice(-1)) ? '' : ' '
const spaceRear = /\s/g.test(contentRear[0]) ? '' : ' ' const spaceRear = /\s/g.test(contentRear[0]) ? '' : ' '
setValue( setValue(
@ -52,7 +52,6 @@ const EmojisList = () => {
) )
const addedLength = spaceFront.length + shortcode.length + spaceRear.length const addedLength = spaceFront.length + shortcode.length + spaceRear.length
setSelection({ start: selection.start + addedLength }) setSelection({ start: selection.start + addedLength })
ref?.current?.setNativeProps({ ref?.current?.setNativeProps({
selection: { start: selection.start + addedLength } selection: { start: selection.start + addedLength }

View File

@ -7,6 +7,7 @@ type inputProps = {
isFocused: MutableRefObject<boolean> isFocused: MutableRefObject<boolean>
ref?: RefObject<TextInput> // For controlling focus ref?: RefObject<TextInput> // For controlling focus
maxLength?: number maxLength?: number
addFunc?: (add: string) => void // For none default state update
} }
export type EmojisState = { export type EmojisState = {

View File

@ -1,4 +1,6 @@
import analytics from '@components/analytics' import analytics from '@components/analytics'
import { ComponentEmojis } from '@components/Emojis'
import { EmojisState } from '@components/Emojis/helpers/EmojisContext'
import { HeaderLeft, HeaderRight } from '@components/Header' import { HeaderLeft, HeaderRight } from '@components/Header'
import { createNativeStackNavigator } from '@react-navigation/native-stack' import { createNativeStackNavigator } from '@react-navigation/native-stack'
import haptics from '@root/components/haptics' import haptics from '@root/components/haptics'
@ -6,10 +8,7 @@ import { useAppDispatch } from '@root/store'
import formatText from '@screens/Compose/formatText' import formatText from '@screens/Compose/formatText'
import ComposeRoot from '@screens/Compose/Root' import ComposeRoot from '@screens/Compose/Root'
import { RootStackScreenProps } from '@utils/navigation/navigators' import { RootStackScreenProps } from '@utils/navigation/navigators'
import { import { QueryKeyTimeline, useTimelineMutation } from '@utils/queryHooks/timeline'
QueryKeyTimeline,
useTimelineMutation
} from '@utils/queryHooks/timeline'
import { updateStoreReview } from '@utils/slices/contextsSlice' import { updateStoreReview } from '@utils/slices/contextsSlice'
import { import {
getInstanceAccount, getInstanceAccount,
@ -20,22 +19,9 @@ import {
import { StyleConstants } from '@utils/styles/constants' import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager' import { useTheme } from '@utils/styles/ThemeManager'
import { filter } from 'lodash' import { filter } from 'lodash'
import React, { import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react'
useCallback,
useEffect,
useMemo,
useReducer,
useState
} from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { import { Alert, Keyboard, Platform } from 'react-native'
Alert,
Keyboard,
KeyboardAvoidingView,
Platform,
StyleSheet
} from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'
import { useQueryClient } from 'react-query' import { useQueryClient } from 'react-query'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import * as Sentry from 'sentry-expo' import * as Sentry from 'sentry-expo'
@ -60,12 +46,8 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
const [hasKeyboard, setHasKeyboard] = useState(false) const [hasKeyboard, setHasKeyboard] = useState(false)
useEffect(() => { useEffect(() => {
const keyboardShown = Keyboard.addListener('keyboardWillShow', () => const keyboardShown = Keyboard.addListener('keyboardWillShow', () => setHasKeyboard(true))
setHasKeyboard(true) const keyboardHidden = Keyboard.addListener('keyboardWillHide', () => setHasKeyboard(false))
)
const keyboardHidden = Keyboard.addListener('keyboardWillHide', () =>
setHasKeyboard(false)
)
return () => { return () => {
keyboardShown.remove() keyboardShown.remove()
@ -89,32 +71,23 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
attachments: { attachments: {
...composeInitialState.attachments, ...composeInitialState.attachments,
sensitive: sensitive:
localAccount?.preferences && localAccount?.preferences && localAccount?.preferences['posting:default:sensitive']
localAccount?.preferences['posting:default:sensitive']
? localAccount?.preferences['posting:default:sensitive'] ? localAccount?.preferences['posting:default:sensitive']
: false : false
}, },
visibility: visibility:
localAccount?.preferences && localAccount?.preferences && localAccount.preferences['posting:default:visibility']
localAccount.preferences['posting:default:visibility']
? localAccount.preferences['posting:default:visibility'] ? localAccount.preferences['posting:default:visibility']
: 'public' : 'public'
} }
} }
}, []) }, [])
const [composeState, composeDispatch] = useReducer( const [composeState, composeDispatch] = useReducer(composeReducer, initialReducerState)
composeReducer,
initialReducerState
)
const maxTootChars = useSelector( const maxTootChars = useSelector(getInstanceConfigurationStatusMaxChars, () => true)
getInstanceConfigurationStatusMaxChars,
() => true
)
const totalTextCount = const totalTextCount =
(composeState.spoiler.active ? composeState.spoiler.count : 0) + (composeState.spoiler.active ? composeState.spoiler.count : 0) + composeState.text.count
composeState.text.count
// If compose state is dirty, then disallow add back drafts // If compose state is dirty, then disallow add back drafts
useEffect(() => { useEffect(() => {
@ -173,8 +146,7 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
}) })
break break
case 'reply': case 'reply':
const actualStatus = const actualStatus = params.incomingStatus.reblog || params.incomingStatus
params.incomingStatus.reblog || params.incomingStatus
if (actualStatus.spoiler_text) { if (actualStatus.spoiler_text) {
formatText({ formatText({
textInput: 'spoiler', textInput: 'spoiler',
@ -278,16 +250,10 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
if (totalTextCount > maxTootChars) { if (totalTextCount > maxTootChars) {
return true return true
} }
if ( if (composeState.attachments.uploads.filter(upload => upload.uploading).length > 0) {
composeState.attachments.uploads.filter(upload => upload.uploading)
.length > 0
) {
return true return true
} }
if ( if (composeState.attachments.uploads.length === 0 && composeState.text.raw.length === 0) {
composeState.attachments.uploads.length === 0 &&
composeState.text.raw.length === 0
) {
return true return true
} }
return false return false
@ -309,18 +275,12 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
composePost(params, composeState) composePost(params, composeState)
.then(res => { .then(res => {
haptics('Success') haptics('Success')
if ( if (Platform.OS === 'ios' && Platform.constants.osVersion === '13.3') {
Platform.OS === 'ios' &&
Platform.constants.osVersion === '13.3'
) {
// https://github.com/tooot-app/app/issues/59 // https://github.com/tooot-app/app/issues/59
} else { } else {
dispatch(updateStoreReview(1)) dispatch(updateStoreReview(1))
} }
const queryKey: QueryKeyTimeline = [ const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Following' }]
'Timeline',
{ page: 'Following' }
]
queryClient.invalidateQueries(queryKey) queryClient.invalidateQueries(queryKey)
switch (params?.type) { switch (params?.type) {
@ -392,54 +352,61 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
}` }`
}, [totalTextCount, maxTootChars, composeState.dirty]) }, [totalTextCount, maxTootChars, composeState.dirty])
const inputProps: EmojisState['inputProps'] = [
{
value: [
composeState.text.raw,
content => formatText({ textInput: 'text', composeDispatch, content })
],
selection: [
composeState.text.selection,
selection => composeDispatch({ type: 'text', payload: { selection } })
],
isFocused: useRef<boolean>(composeState.textInputFocus.current === 'text'),
maxLength: maxTootChars
}
]
return ( return (
<KeyboardAvoidingView <ComponentEmojis
style={styles.base} inputProps={inputProps}
behavior={Platform.OS === 'ios' ? 'padding' : undefined} customButton
customBehavior={Platform.OS === 'ios' ? 'padding' : undefined}
customEdges={hasKeyboard ? ['top'] : ['top', 'bottom']}
> >
<SafeAreaView <ComposeContext.Provider value={{ composeState, composeDispatch }}>
style={styles.base} <Stack.Navigator initialRouteName='Screen-Compose-Root'>
edges={hasKeyboard ? ['top'] : ['top', 'bottom']} <Stack.Screen
> name='Screen-Compose-Root'
<ComposeContext.Provider value={{ composeState, composeDispatch }}> component={ComposeRoot}
<Stack.Navigator initialRouteName='Screen-Compose-Root'> options={{
<Stack.Screen title: headerContent,
name='Screen-Compose-Root' headerTitleStyle: {
component={ComposeRoot} fontWeight:
options={{ totalTextCount > maxTootChars
title: headerContent, ? StyleConstants.Font.Weight.Bold
headerTitleStyle: { : StyleConstants.Font.Weight.Normal,
fontWeight: fontSize: StyleConstants.Font.Size.M
totalTextCount > maxTootChars },
? StyleConstants.Font.Weight.Bold headerTintColor: totalTextCount > maxTootChars ? colors.red : colors.secondary,
: StyleConstants.Font.Weight.Normal, headerLeft,
fontSize: StyleConstants.Font.Size.M headerRight
}, }}
headerTintColor: />
totalTextCount > maxTootChars ? colors.red : colors.secondary, <Stack.Screen
headerLeft, name='Screen-Compose-DraftsList'
headerRight component={ComposeDraftsList}
}} options={{ headerShown: false, presentation: 'modal' }}
/> />
<Stack.Screen <Stack.Screen
name='Screen-Compose-DraftsList' name='Screen-Compose-EditAttachment'
component={ComposeDraftsList} component={ComposeEditAttachment}
options={{ headerShown: false, presentation: 'modal' }} options={{ headerShown: false, presentation: 'modal' }}
/> />
<Stack.Screen </Stack.Navigator>
name='Screen-Compose-EditAttachment' </ComposeContext.Provider>
component={ComposeEditAttachment} </ComponentEmojis>
options={{ headerShown: false, presentation: 'modal' }}
/>
</Stack.Navigator>
</ComposeContext.Provider>
</SafeAreaView>
</KeyboardAvoidingView>
) )
} }
const styles = StyleSheet.create({
base: { flex: 1 }
})
export default ScreenCompose export default ScreenCompose

View File

@ -5,7 +5,7 @@ import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager' import { useTheme } from '@utils/styles/ThemeManager'
import { chunk, forEach, groupBy, sortBy } from 'lodash' import { chunk, forEach, groupBy, sortBy } from 'lodash'
import React, { useContext, useEffect, useMemo, useRef } from 'react' import React, { useContext, useEffect, useMemo, useRef } from 'react'
import { AccessibilityInfo, findNodeHandle, FlatList, StyleSheet, View } from 'react-native' import { AccessibilityInfo, findNodeHandle, FlatList, View } from 'react-native'
import { Circle } from 'react-native-animated-spinkit' import { Circle } from 'react-native-animated-spinkit'
import ComposeActions from './Root/Actions' import ComposeActions from './Root/Actions'
import ComposePosting from './Posting' import ComposePosting from './Posting'
@ -14,9 +14,7 @@ import ComposeRootHeader from './Root/Header'
import ComposeRootSuggestion from './Root/Suggestion' import ComposeRootSuggestion from './Root/Suggestion'
import ComposeContext from './utils/createContext' import ComposeContext from './utils/createContext'
import ComposeDrafts from './Root/Drafts' import ComposeDrafts from './Root/Drafts'
import FastImage from 'react-native-fast-image'
import { useAccessibility } from '@utils/accessibility/AccessibilityManager' import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
import { ComposeState } from './utils/types'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { import {
getInstanceConfigurationStatusCharsURL, getInstanceConfigurationStatusCharsURL,
@ -24,30 +22,6 @@ import {
} from '@utils/slices/instancesSlice' } from '@utils/slices/instancesSlice'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
const prefetchEmojis = (
sortedEmojis: NonNullable<ComposeState['emoji']['emojis']>,
reduceMotionEnabled: boolean
) => {
const prefetches: { uri: string }[] = []
let requestedIndex = 0
sortedEmojis.forEach(sorted => {
sorted.data.forEach(emojis =>
emojis.forEach(emoji => {
if (requestedIndex > 40) {
return
}
prefetches.push({
uri: reduceMotionEnabled ? emoji.static_url : emoji.url
})
requestedIndex++
})
)
})
try {
FastImage.preload(prefetches)
} catch {}
}
export let instanceConfigurationStatusCharsURL = 23 export let instanceConfigurationStatusCharsURL = 23
const ComposeRoot = React.memo( const ComposeRoot = React.memo(
@ -62,7 +36,6 @@ const ComposeRoot = React.memo(
const accessibleRefDrafts = useRef(null) const accessibleRefDrafts = useRef(null)
const accessibleRefAttachments = useRef(null) const accessibleRefAttachments = useRef(null)
const accessibleRefEmojis = useRef(null)
useEffect(() => { useEffect(() => {
const tagDrafts = findNodeHandle(accessibleRefDrafts.current) const tagDrafts = findNodeHandle(accessibleRefDrafts.current)
@ -110,18 +83,13 @@ const ComposeRoot = React.memo(
) )
}) })
} }
composeDispatch({
type: 'emoji',
payload: { ...composeState.emoji, emojis: sortedEmojis }
})
prefetchEmojis(sortedEmojis, reduceMotionEnabled)
} }
}, [emojisData, reduceMotionEnabled]) }, [emojisData, reduceMotionEnabled])
const listEmpty = useMemo(() => { const listEmpty = useMemo(() => {
if (isFetching) { if (isFetching) {
return ( return (
<View key='listEmpty' style={styles.loading}> <View key='listEmpty' style={{ flex: 1, alignItems: 'center' }}>
<Circle size={StyleConstants.Font.Size.M * 1.25} color={colors.secondary} /> <Circle size={StyleConstants.Font.Size.M * 1.25} color={colors.secondary} />
</View> </View>
) )
@ -129,17 +97,12 @@ const ComposeRoot = React.memo(
}, [isFetching]) }, [isFetching])
const Footer = useMemo( const Footer = useMemo(
() => ( () => <ComposeRootFooter accessibleRefAttachments={accessibleRefAttachments} />,
<ComposeRootFooter [accessibleRefAttachments.current]
accessibleRefAttachments={accessibleRefAttachments}
accessibleRefEmojis={accessibleRefEmojis}
/>
),
[accessibleRefAttachments.current, accessibleRefEmojis.current]
) )
return ( return (
<View style={styles.base}> <View style={{ flex: 1 }}>
<FlatList <FlatList
renderItem={({ item }) => ( renderItem={({ item }) => (
<ComposeRootSuggestion <ComposeRootSuggestion
@ -166,15 +129,4 @@ const ComposeRoot = React.memo(
() => true () => true
) )
const styles = StyleSheet.create({
base: {
flex: 1
},
contentView: { flex: 1 },
loading: {
flex: 1,
alignItems: 'center'
}
})
export default ComposeRoot export default ComposeRoot

View File

@ -1,12 +1,13 @@
import analytics from '@components/analytics' import analytics from '@components/analytics'
import EmojisContext from '@components/Emojis/helpers/EmojisContext'
import Icon from '@components/Icon' import Icon from '@components/Icon'
import { useActionSheet } from '@expo/react-native-action-sheet' import { useActionSheet } from '@expo/react-native-action-sheet'
import { getInstanceConfigurationStatusMaxAttachments } from '@utils/slices/instancesSlice' import { getInstanceConfigurationStatusMaxAttachments } from '@utils/slices/instancesSlice'
import layoutAnimation from '@utils/styles/layoutAnimation' import layoutAnimation from '@utils/styles/layoutAnimation'
import { useTheme } from '@utils/styles/ThemeManager' import { useTheme } from '@utils/styles/ThemeManager'
import React, { useCallback, useContext, useMemo } from 'react' import React, { useContext, useMemo } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Pressable, StyleSheet, View } from 'react-native' import { Keyboard, Pressable, StyleSheet, View } from 'react-native'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import ComposeContext from '../utils/createContext' import ComposeContext from '../utils/createContext'
import chooseAndUploadAttachment from './Footer/addAttachment' import chooseAndUploadAttachment from './Footer/addAttachment'
@ -30,22 +31,19 @@ const ComposeActions: React.FC = () => {
return colors.secondary return colors.secondary
} }
}, [composeState.poll.active, composeState.attachments.uploads]) }, [composeState.poll.active, composeState.attachments.uploads])
const attachmentOnPress = useCallback(async () => { const attachmentOnPress = () => {
if (composeState.poll.active) return if (composeState.poll.active) return
if ( if (composeState.attachments.uploads.length < instanceConfigurationStatusMaxAttachments) {
composeState.attachments.uploads.length <
instanceConfigurationStatusMaxAttachments
) {
analytics('compose_actions_attachment_press', { analytics('compose_actions_attachment_press', {
count: composeState.attachments.uploads.length count: composeState.attachments.uploads.length
}) })
return await chooseAndUploadAttachment({ return chooseAndUploadAttachment({
composeDispatch, composeDispatch,
showActionSheetWithOptions showActionSheetWithOptions
}) })
} }
}, [composeState.poll.active, composeState.attachments.uploads]) }
const pollColor = useMemo(() => { const pollColor = useMemo(() => {
if (composeState.attachments.uploads.length) return colors.disabled if (composeState.attachments.uploads.length) return colors.disabled
@ -56,7 +54,7 @@ const ComposeActions: React.FC = () => {
return colors.secondary return colors.secondary
} }
}, [composeState.poll.active, composeState.attachments.uploads]) }, [composeState.poll.active, composeState.attachments.uploads])
const pollOnPress = useCallback(() => { const pollOnPress = () => {
if (!composeState.attachments.uploads.length) { if (!composeState.attachments.uploads.length) {
analytics('compose_actions_poll_press', { analytics('compose_actions_poll_press', {
current: composeState.poll.active current: composeState.poll.active
@ -70,7 +68,7 @@ const ComposeActions: React.FC = () => {
if (composeState.poll.active) { if (composeState.poll.active) {
composeState.textInputFocus.refs.text.current?.focus() composeState.textInputFocus.refs.text.current?.focus()
} }
}, [composeState.poll.active, composeState.attachments.uploads]) }
const visibilityIcon = useMemo(() => { const visibilityIcon = useMemo(() => {
switch (composeState.visibility) { switch (composeState.visibility) {
@ -84,7 +82,7 @@ const ComposeActions: React.FC = () => {
return 'Mail' return 'Mail'
} }
}, [composeState.visibility]) }, [composeState.visibility])
const visibilityOnPress = useCallback(() => { const visibilityOnPress = () => {
if (!composeState.visibilityLock) { if (!composeState.visibilityLock) {
showActionSheetWithOptions( showActionSheetWithOptions(
{ {
@ -133,9 +131,9 @@ const ComposeActions: React.FC = () => {
} }
) )
} }
}, [composeState.visibility]) }
const spoilerOnPress = useCallback(() => { const spoilerOnPress = () => {
analytics('compose_actions_spoiler_press', { analytics('compose_actions_spoiler_press', {
current: composeState.spoiler.active current: composeState.spoiler.active
}) })
@ -147,29 +145,45 @@ const ComposeActions: React.FC = () => {
type: 'spoiler', type: 'spoiler',
payload: { active: !composeState.spoiler.active } payload: { active: !composeState.spoiler.active }
}) })
}, [composeState.spoiler.active, composeState.textInputFocus]) }
const { emojisState, emojisDispatch } = useContext(EmojisContext)
const emojiColor = useMemo(() => { const emojiColor = useMemo(() => {
if (!composeState.emoji.emojis) return colors.disabled if (!emojisState.emojis.length) return colors.disabled
if (composeState.emoji.active) { if (emojisState.targetIndex !== -1) {
return colors.primaryDefault return colors.primaryDefault
} else { } else {
return colors.secondary return colors.secondary
} }
}, [composeState.emoji.active, composeState.emoji.emojis]) }, [emojisState.emojis.length, emojisState.targetIndex])
const emojiOnPress = useCallback(() => { // useEffect(() => {
analytics('compose_actions_emojis_press', { // const showSubscription = Keyboard.addListener('keyboardWillShow', () => {
current: composeState.emoji.active // composeDispatch({ type: 'emoji/shown', payload: false })
}) // })
if (composeState.emoji.emojis) {
layoutAnimation() // return () => {
composeDispatch({ // showSubscription.remove()
type: 'emoji', // }
payload: { ...composeState.emoji, active: !composeState.emoji.active } // }, [])
}) const emojiOnPress = () => {
if (emojisState.targetIndex === -1) {
Keyboard.dismiss()
} }
}, [composeState.emoji.active, composeState.emoji.emojis]) const focusedPropsIndex = emojisState.inputProps?.findIndex(props => props.isFocused.current)
if (focusedPropsIndex === -1) return
emojisDispatch({ type: 'target', payload: focusedPropsIndex })
// Keyboard.dismiss()
// analytics('compose_actions_emojis_press', {
// current: composeState.emoji.active
// })
// if (composeState.emoji.emojis) {
// composeDispatch({
// type: 'emoji',
// payload: { ...composeState.emoji, active: !composeState.emoji.active }
// })
// }
}
return ( return (
<View <View
@ -186,12 +200,8 @@ const ComposeActions: React.FC = () => {
> >
<Pressable <Pressable
accessibilityRole='button' accessibilityRole='button'
accessibilityLabel={t( accessibilityLabel={t('content.root.actions.attachment.accessibilityLabel')}
'content.root.actions.attachment.accessibilityLabel' accessibilityHint={t('content.root.actions.attachment.accessibilityHint')}
)}
accessibilityHint={t(
'content.root.actions.attachment.accessibilityHint'
)}
accessibilityState={{ accessibilityState={{
disabled: composeState.poll.active disabled: composeState.poll.active
}} }}
@ -213,10 +223,9 @@ const ComposeActions: React.FC = () => {
/> />
<Pressable <Pressable
accessibilityRole='button' accessibilityRole='button'
accessibilityLabel={t( accessibilityLabel={t('content.root.actions.visibility.accessibilityLabel', {
'content.root.actions.visibility.accessibilityLabel', visibility: composeState.visibility
{ visibility: composeState.visibility } })}
)}
accessibilityState={{ disabled: composeState.visibilityLock }} accessibilityState={{ disabled: composeState.visibilityLock }}
style={styles.button} style={styles.button}
onPress={visibilityOnPress} onPress={visibilityOnPress}
@ -224,17 +233,13 @@ const ComposeActions: React.FC = () => {
<Icon <Icon
name={visibilityIcon} name={visibilityIcon}
size={24} size={24}
color={ color={composeState.visibilityLock ? colors.disabled : colors.secondary}
composeState.visibilityLock ? colors.disabled : colors.secondary
}
/> />
} }
/> />
<Pressable <Pressable
accessibilityRole='button' accessibilityRole='button'
accessibilityLabel={t( accessibilityLabel={t('content.root.actions.spoiler.accessibilityLabel')}
'content.root.actions.spoiler.accessibilityLabel'
)}
accessibilityState={{ expanded: composeState.spoiler.active }} accessibilityState={{ expanded: composeState.spoiler.active }}
style={styles.button} style={styles.button}
onPress={spoilerOnPress} onPress={spoilerOnPress}
@ -242,11 +247,7 @@ const ComposeActions: React.FC = () => {
<Icon <Icon
name='AlertTriangle' name='AlertTriangle'
size={24} size={24}
color={ color={composeState.spoiler.active ? colors.primaryDefault : colors.secondary}
composeState.spoiler.active
? colors.primaryDefault
: colors.secondary
}
/> />
} }
/> />
@ -255,8 +256,8 @@ const ComposeActions: React.FC = () => {
accessibilityLabel={t('content.root.actions.emoji.accessibilityLabel')} accessibilityLabel={t('content.root.actions.emoji.accessibilityLabel')}
accessibilityHint={t('content.root.actions.emoji.accessibilityHint')} accessibilityHint={t('content.root.actions.emoji.accessibilityHint')}
accessibilityState={{ accessibilityState={{
disabled: composeState.emoji.emojis ? false : true, disabled: emojisState.emojis.length ? false : true,
expanded: composeState.emoji.active expanded: emojisState.targetIndex !== -1
}} }}
style={styles.button} style={styles.button}
onPress={emojiOnPress} onPress={emojiOnPress}

View File

@ -1,31 +1,21 @@
import ComposeAttachments from '@screens/Compose/Root/Footer/Attachments' import ComposeAttachments from '@screens/Compose/Root/Footer/Attachments'
import ComposeEmojis from '@screens/Compose/Root/Footer/Emojis'
import ComposePoll from '@screens/Compose/Root/Footer/Poll' import ComposePoll from '@screens/Compose/Root/Footer/Poll'
import ComposeReply from '@screens/Compose/Root/Footer/Reply' import ComposeReply from '@screens/Compose/Root/Footer/Reply'
import ComposeContext from '@screens/Compose/utils/createContext' import ComposeContext from '@screens/Compose/utils/createContext'
import React, { RefObject, useContext } from 'react' import React, { RefObject, useContext } from 'react'
import { SectionList, View } from 'react-native' import { View } from 'react-native'
export interface Props { export interface Props {
accessibleRefAttachments: RefObject<View> accessibleRefAttachments: RefObject<View>
accessibleRefEmojis: RefObject<SectionList>
} }
const ComposeRootFooter: React.FC<Props> = ({ const ComposeRootFooter: React.FC<Props> = ({ accessibleRefAttachments }) => {
accessibleRefAttachments,
accessibleRefEmojis
}) => {
const { composeState } = useContext(ComposeContext) const { composeState } = useContext(ComposeContext)
return ( return (
<> <>
{composeState.emoji.active ? (
<ComposeEmojis accessibleRefEmojis={accessibleRefEmojis} />
) : null}
{composeState.attachments.uploads.length ? ( {composeState.attachments.uploads.length ? (
<ComposeAttachments <ComposeAttachments accessibleRefAttachments={accessibleRefAttachments} />
accessibleRefAttachments={accessibleRefAttachments}
/>
) : null} ) : null}
{composeState.poll.active ? <ComposePoll /> : null} {composeState.poll.active ? <ComposePoll /> : null}
{composeState.replyToStatus ? <ComposeReply /> : null} {composeState.replyToStatus ? <ComposeReply /> : null}

View File

@ -26,9 +26,8 @@ const updateText = ({
const whiteSpaceFront = /\s/g.test(contentFront.slice(-1)) const whiteSpaceFront = /\s/g.test(contentFront.slice(-1))
const whiteSpaceRear = /\s/g.test(contentRear.slice(-1)) const whiteSpaceRear = /\s/g.test(contentRear.slice(-1))
const newTextWithSpace = `${ const newTextWithSpace = `${whiteSpaceFront || type === 'suggestion' ? '' : ' '
whiteSpaceFront || type === 'suggestion' ? '' : ' ' }${newText}${whiteSpaceRear ? '' : ' '}`
}${newText}${whiteSpaceRear ? '' : ' '}`
formatText({ formatText({
textInput, textInput,

View File

@ -9,16 +9,15 @@ const composeInitialState: Omit<ComposeState, 'timestamp'> = {
count: 0, count: 0,
raw: '', raw: '',
formatted: undefined, formatted: undefined,
selection: { start: 0, end: 0 } selection: { start: 0 }
}, },
text: { text: {
count: 0, count: 0,
raw: '', raw: '',
formatted: undefined, formatted: undefined,
selection: { start: 0, end: 0 } selection: { start: 0 }
}, },
tag: undefined, tag: undefined,
emoji: { active: false, emojis: undefined },
poll: { poll: {
active: false, active: false,
total: 2, total: 2,

View File

@ -35,8 +35,6 @@ const composeReducer = (
return { ...state, text: { ...state.text, ...action.payload } } return { ...state, text: { ...state.text, ...action.payload } }
case 'tag': case 'tag':
return { ...state, tag: action.payload } return { ...state, tag: action.payload }
case 'emoji':
return { ...state, emoji: action.payload }
case 'poll': case 'poll':
return { ...state, poll: { ...state.poll, ...action.payload } } return { ...state, poll: { ...state.poll, ...action.payload } }
case 'attachments/sensitive': case 'attachments/sensitive':

View File

@ -26,13 +26,13 @@ export type ComposeState = {
count: number count: number
raw: string raw: string
formatted: ReactNode formatted: ReactNode
selection: { start: number; end: number } selection: { start: number; end?: number }
} }
text: { text: {
count: number count: number
raw: string raw: string
formatted: ReactNode formatted: ReactNode
selection: { start: number; end: number } selection: { start: number; end?: number }
} }
tag?: { tag?: {
type: 'url' | 'accounts' | 'hashtags' type: 'url' | 'accounts' | 'hashtags'
@ -40,15 +40,6 @@ export type ComposeState = {
offset: number offset: number
length: number length: number
} }
emoji: {
active: boolean
emojis:
| {
title: string
data: Pick<Mastodon.Emoji, 'shortcode' | 'url' | 'static_url'>[][]
}[]
| undefined
}
poll: { poll: {
active: boolean active: boolean
total: number total: number
@ -96,10 +87,6 @@ export type ComposeAction =
type: 'tag' type: 'tag'
payload: ComposeState['tag'] payload: ComposeState['tag']
} }
| {
type: 'emoji'
payload: ComposeState['emoji']
}
| { | {
type: 'poll' type: 'poll'
payload: Partial<ComposeState['poll']> payload: Partial<ComposeState['poll']>