mirror of
https://github.com/tooot-app/app
synced 2025-02-02 19:47:06 +01:00
POC compose using the new emoji selector
This commit is contained in:
parent
7282434e69
commit
2df23a8a2e
@ -44,7 +44,7 @@ const EmojisList = () => {
|
||||
const contentFront = value.slice(0, selection.start)
|
||||
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]) ? '' : ' '
|
||||
|
||||
setValue(
|
||||
@ -52,7 +52,6 @@ const EmojisList = () => {
|
||||
)
|
||||
|
||||
const addedLength = spaceFront.length + shortcode.length + spaceRear.length
|
||||
|
||||
setSelection({ start: selection.start + addedLength })
|
||||
ref?.current?.setNativeProps({
|
||||
selection: { start: selection.start + addedLength }
|
||||
|
@ -7,6 +7,7 @@ type inputProps = {
|
||||
isFocused: MutableRefObject<boolean>
|
||||
ref?: RefObject<TextInput> // For controlling focus
|
||||
maxLength?: number
|
||||
addFunc?: (add: string) => void // For none default state update
|
||||
}
|
||||
|
||||
export type EmojisState = {
|
||||
|
@ -1,4 +1,6 @@
|
||||
import analytics from '@components/analytics'
|
||||
import { ComponentEmojis } from '@components/Emojis'
|
||||
import { EmojisState } from '@components/Emojis/helpers/EmojisContext'
|
||||
import { HeaderLeft, HeaderRight } from '@components/Header'
|
||||
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
||||
import haptics from '@root/components/haptics'
|
||||
@ -6,10 +8,7 @@ import { useAppDispatch } from '@root/store'
|
||||
import formatText from '@screens/Compose/formatText'
|
||||
import ComposeRoot from '@screens/Compose/Root'
|
||||
import { RootStackScreenProps } from '@utils/navigation/navigators'
|
||||
import {
|
||||
QueryKeyTimeline,
|
||||
useTimelineMutation
|
||||
} from '@utils/queryHooks/timeline'
|
||||
import { QueryKeyTimeline, useTimelineMutation } from '@utils/queryHooks/timeline'
|
||||
import { updateStoreReview } from '@utils/slices/contextsSlice'
|
||||
import {
|
||||
getInstanceAccount,
|
||||
@ -20,22 +19,9 @@ import {
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { filter } from 'lodash'
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useReducer,
|
||||
useState
|
||||
} from 'react'
|
||||
import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
Alert,
|
||||
Keyboard,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
StyleSheet
|
||||
} from 'react-native'
|
||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||
import { Alert, Keyboard, Platform } from 'react-native'
|
||||
import { useQueryClient } from 'react-query'
|
||||
import { useSelector } from 'react-redux'
|
||||
import * as Sentry from 'sentry-expo'
|
||||
@ -60,12 +46,8 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
||||
|
||||
const [hasKeyboard, setHasKeyboard] = useState(false)
|
||||
useEffect(() => {
|
||||
const keyboardShown = Keyboard.addListener('keyboardWillShow', () =>
|
||||
setHasKeyboard(true)
|
||||
)
|
||||
const keyboardHidden = Keyboard.addListener('keyboardWillHide', () =>
|
||||
setHasKeyboard(false)
|
||||
)
|
||||
const keyboardShown = Keyboard.addListener('keyboardWillShow', () => setHasKeyboard(true))
|
||||
const keyboardHidden = Keyboard.addListener('keyboardWillHide', () => setHasKeyboard(false))
|
||||
|
||||
return () => {
|
||||
keyboardShown.remove()
|
||||
@ -89,32 +71,23 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
||||
attachments: {
|
||||
...composeInitialState.attachments,
|
||||
sensitive:
|
||||
localAccount?.preferences &&
|
||||
localAccount?.preferences['posting:default:sensitive']
|
||||
localAccount?.preferences && localAccount?.preferences['posting:default:sensitive']
|
||||
? localAccount?.preferences['posting:default:sensitive']
|
||||
: false
|
||||
},
|
||||
visibility:
|
||||
localAccount?.preferences &&
|
||||
localAccount.preferences['posting:default:visibility']
|
||||
localAccount?.preferences && localAccount.preferences['posting:default:visibility']
|
||||
? localAccount.preferences['posting:default:visibility']
|
||||
: 'public'
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
const [composeState, composeDispatch] = useReducer(
|
||||
composeReducer,
|
||||
initialReducerState
|
||||
)
|
||||
const [composeState, composeDispatch] = useReducer(composeReducer, initialReducerState)
|
||||
|
||||
const maxTootChars = useSelector(
|
||||
getInstanceConfigurationStatusMaxChars,
|
||||
() => true
|
||||
)
|
||||
const maxTootChars = useSelector(getInstanceConfigurationStatusMaxChars, () => true)
|
||||
const totalTextCount =
|
||||
(composeState.spoiler.active ? composeState.spoiler.count : 0) +
|
||||
composeState.text.count
|
||||
(composeState.spoiler.active ? composeState.spoiler.count : 0) + composeState.text.count
|
||||
|
||||
// If compose state is dirty, then disallow add back drafts
|
||||
useEffect(() => {
|
||||
@ -173,8 +146,7 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
||||
})
|
||||
break
|
||||
case 'reply':
|
||||
const actualStatus =
|
||||
params.incomingStatus.reblog || params.incomingStatus
|
||||
const actualStatus = params.incomingStatus.reblog || params.incomingStatus
|
||||
if (actualStatus.spoiler_text) {
|
||||
formatText({
|
||||
textInput: 'spoiler',
|
||||
@ -278,16 +250,10 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
||||
if (totalTextCount > maxTootChars) {
|
||||
return true
|
||||
}
|
||||
if (
|
||||
composeState.attachments.uploads.filter(upload => upload.uploading)
|
||||
.length > 0
|
||||
) {
|
||||
if (composeState.attachments.uploads.filter(upload => upload.uploading).length > 0) {
|
||||
return true
|
||||
}
|
||||
if (
|
||||
composeState.attachments.uploads.length === 0 &&
|
||||
composeState.text.raw.length === 0
|
||||
) {
|
||||
if (composeState.attachments.uploads.length === 0 && composeState.text.raw.length === 0) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@ -309,18 +275,12 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
||||
composePost(params, composeState)
|
||||
.then(res => {
|
||||
haptics('Success')
|
||||
if (
|
||||
Platform.OS === 'ios' &&
|
||||
Platform.constants.osVersion === '13.3'
|
||||
) {
|
||||
if (Platform.OS === 'ios' && Platform.constants.osVersion === '13.3') {
|
||||
// https://github.com/tooot-app/app/issues/59
|
||||
} else {
|
||||
dispatch(updateStoreReview(1))
|
||||
}
|
||||
const queryKey: QueryKeyTimeline = [
|
||||
'Timeline',
|
||||
{ page: 'Following' }
|
||||
]
|
||||
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Following' }]
|
||||
queryClient.invalidateQueries(queryKey)
|
||||
|
||||
switch (params?.type) {
|
||||
@ -392,54 +352,61 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
||||
}`
|
||||
}, [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 (
|
||||
<KeyboardAvoidingView
|
||||
style={styles.base}
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||
<ComponentEmojis
|
||||
inputProps={inputProps}
|
||||
customButton
|
||||
customBehavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||
customEdges={hasKeyboard ? ['top'] : ['top', 'bottom']}
|
||||
>
|
||||
<SafeAreaView
|
||||
style={styles.base}
|
||||
edges={hasKeyboard ? ['top'] : ['top', 'bottom']}
|
||||
>
|
||||
<ComposeContext.Provider value={{ composeState, composeDispatch }}>
|
||||
<Stack.Navigator initialRouteName='Screen-Compose-Root'>
|
||||
<Stack.Screen
|
||||
name='Screen-Compose-Root'
|
||||
component={ComposeRoot}
|
||||
options={{
|
||||
title: headerContent,
|
||||
headerTitleStyle: {
|
||||
fontWeight:
|
||||
totalTextCount > maxTootChars
|
||||
? StyleConstants.Font.Weight.Bold
|
||||
: StyleConstants.Font.Weight.Normal,
|
||||
fontSize: StyleConstants.Font.Size.M
|
||||
},
|
||||
headerTintColor:
|
||||
totalTextCount > maxTootChars ? colors.red : colors.secondary,
|
||||
headerLeft,
|
||||
headerRight
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name='Screen-Compose-DraftsList'
|
||||
component={ComposeDraftsList}
|
||||
options={{ headerShown: false, presentation: 'modal' }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name='Screen-Compose-EditAttachment'
|
||||
component={ComposeEditAttachment}
|
||||
options={{ headerShown: false, presentation: 'modal' }}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
</ComposeContext.Provider>
|
||||
</SafeAreaView>
|
||||
</KeyboardAvoidingView>
|
||||
<ComposeContext.Provider value={{ composeState, composeDispatch }}>
|
||||
<Stack.Navigator initialRouteName='Screen-Compose-Root'>
|
||||
<Stack.Screen
|
||||
name='Screen-Compose-Root'
|
||||
component={ComposeRoot}
|
||||
options={{
|
||||
title: headerContent,
|
||||
headerTitleStyle: {
|
||||
fontWeight:
|
||||
totalTextCount > maxTootChars
|
||||
? StyleConstants.Font.Weight.Bold
|
||||
: StyleConstants.Font.Weight.Normal,
|
||||
fontSize: StyleConstants.Font.Size.M
|
||||
},
|
||||
headerTintColor: totalTextCount > maxTootChars ? colors.red : colors.secondary,
|
||||
headerLeft,
|
||||
headerRight
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name='Screen-Compose-DraftsList'
|
||||
component={ComposeDraftsList}
|
||||
options={{ headerShown: false, presentation: 'modal' }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name='Screen-Compose-EditAttachment'
|
||||
component={ComposeEditAttachment}
|
||||
options={{ headerShown: false, presentation: 'modal' }}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
</ComposeContext.Provider>
|
||||
</ComponentEmojis>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
base: { flex: 1 }
|
||||
})
|
||||
|
||||
export default ScreenCompose
|
||||
|
@ -5,7 +5,7 @@ import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { chunk, forEach, groupBy, sortBy } from 'lodash'
|
||||
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 ComposeActions from './Root/Actions'
|
||||
import ComposePosting from './Posting'
|
||||
@ -14,9 +14,7 @@ import ComposeRootHeader from './Root/Header'
|
||||
import ComposeRootSuggestion from './Root/Suggestion'
|
||||
import ComposeContext from './utils/createContext'
|
||||
import ComposeDrafts from './Root/Drafts'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||
import { ComposeState } from './utils/types'
|
||||
import { useSelector } from 'react-redux'
|
||||
import {
|
||||
getInstanceConfigurationStatusCharsURL,
|
||||
@ -24,30 +22,6 @@ import {
|
||||
} from '@utils/slices/instancesSlice'
|
||||
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
|
||||
|
||||
const ComposeRoot = React.memo(
|
||||
@ -62,7 +36,6 @@ const ComposeRoot = React.memo(
|
||||
|
||||
const accessibleRefDrafts = useRef(null)
|
||||
const accessibleRefAttachments = useRef(null)
|
||||
const accessibleRefEmojis = useRef(null)
|
||||
|
||||
useEffect(() => {
|
||||
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])
|
||||
|
||||
const listEmpty = useMemo(() => {
|
||||
if (isFetching) {
|
||||
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} />
|
||||
</View>
|
||||
)
|
||||
@ -129,17 +97,12 @@ const ComposeRoot = React.memo(
|
||||
}, [isFetching])
|
||||
|
||||
const Footer = useMemo(
|
||||
() => (
|
||||
<ComposeRootFooter
|
||||
accessibleRefAttachments={accessibleRefAttachments}
|
||||
accessibleRefEmojis={accessibleRefEmojis}
|
||||
/>
|
||||
),
|
||||
[accessibleRefAttachments.current, accessibleRefEmojis.current]
|
||||
() => <ComposeRootFooter accessibleRefAttachments={accessibleRefAttachments} />,
|
||||
[accessibleRefAttachments.current]
|
||||
)
|
||||
|
||||
return (
|
||||
<View style={styles.base}>
|
||||
<View style={{ flex: 1 }}>
|
||||
<FlatList
|
||||
renderItem={({ item }) => (
|
||||
<ComposeRootSuggestion
|
||||
@ -166,15 +129,4 @@ const ComposeRoot = React.memo(
|
||||
() => true
|
||||
)
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
base: {
|
||||
flex: 1
|
||||
},
|
||||
contentView: { flex: 1 },
|
||||
loading: {
|
||||
flex: 1,
|
||||
alignItems: 'center'
|
||||
}
|
||||
})
|
||||
|
||||
export default ComposeRoot
|
||||
|
@ -1,12 +1,13 @@
|
||||
import analytics from '@components/analytics'
|
||||
import EmojisContext from '@components/Emojis/helpers/EmojisContext'
|
||||
import Icon from '@components/Icon'
|
||||
import { useActionSheet } from '@expo/react-native-action-sheet'
|
||||
import { getInstanceConfigurationStatusMaxAttachments } from '@utils/slices/instancesSlice'
|
||||
import layoutAnimation from '@utils/styles/layoutAnimation'
|
||||
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 { Pressable, StyleSheet, View } from 'react-native'
|
||||
import { Keyboard, Pressable, StyleSheet, View } from 'react-native'
|
||||
import { useSelector } from 'react-redux'
|
||||
import ComposeContext from '../utils/createContext'
|
||||
import chooseAndUploadAttachment from './Footer/addAttachment'
|
||||
@ -30,22 +31,19 @@ const ComposeActions: React.FC = () => {
|
||||
return colors.secondary
|
||||
}
|
||||
}, [composeState.poll.active, composeState.attachments.uploads])
|
||||
const attachmentOnPress = useCallback(async () => {
|
||||
const attachmentOnPress = () => {
|
||||
if (composeState.poll.active) return
|
||||
|
||||
if (
|
||||
composeState.attachments.uploads.length <
|
||||
instanceConfigurationStatusMaxAttachments
|
||||
) {
|
||||
if (composeState.attachments.uploads.length < instanceConfigurationStatusMaxAttachments) {
|
||||
analytics('compose_actions_attachment_press', {
|
||||
count: composeState.attachments.uploads.length
|
||||
})
|
||||
return await chooseAndUploadAttachment({
|
||||
return chooseAndUploadAttachment({
|
||||
composeDispatch,
|
||||
showActionSheetWithOptions
|
||||
})
|
||||
}
|
||||
}, [composeState.poll.active, composeState.attachments.uploads])
|
||||
}
|
||||
|
||||
const pollColor = useMemo(() => {
|
||||
if (composeState.attachments.uploads.length) return colors.disabled
|
||||
@ -56,7 +54,7 @@ const ComposeActions: React.FC = () => {
|
||||
return colors.secondary
|
||||
}
|
||||
}, [composeState.poll.active, composeState.attachments.uploads])
|
||||
const pollOnPress = useCallback(() => {
|
||||
const pollOnPress = () => {
|
||||
if (!composeState.attachments.uploads.length) {
|
||||
analytics('compose_actions_poll_press', {
|
||||
current: composeState.poll.active
|
||||
@ -70,7 +68,7 @@ const ComposeActions: React.FC = () => {
|
||||
if (composeState.poll.active) {
|
||||
composeState.textInputFocus.refs.text.current?.focus()
|
||||
}
|
||||
}, [composeState.poll.active, composeState.attachments.uploads])
|
||||
}
|
||||
|
||||
const visibilityIcon = useMemo(() => {
|
||||
switch (composeState.visibility) {
|
||||
@ -84,7 +82,7 @@ const ComposeActions: React.FC = () => {
|
||||
return 'Mail'
|
||||
}
|
||||
}, [composeState.visibility])
|
||||
const visibilityOnPress = useCallback(() => {
|
||||
const visibilityOnPress = () => {
|
||||
if (!composeState.visibilityLock) {
|
||||
showActionSheetWithOptions(
|
||||
{
|
||||
@ -133,9 +131,9 @@ const ComposeActions: React.FC = () => {
|
||||
}
|
||||
)
|
||||
}
|
||||
}, [composeState.visibility])
|
||||
}
|
||||
|
||||
const spoilerOnPress = useCallback(() => {
|
||||
const spoilerOnPress = () => {
|
||||
analytics('compose_actions_spoiler_press', {
|
||||
current: composeState.spoiler.active
|
||||
})
|
||||
@ -147,29 +145,45 @@ const ComposeActions: React.FC = () => {
|
||||
type: 'spoiler',
|
||||
payload: { active: !composeState.spoiler.active }
|
||||
})
|
||||
}, [composeState.spoiler.active, composeState.textInputFocus])
|
||||
}
|
||||
|
||||
const { emojisState, emojisDispatch } = useContext(EmojisContext)
|
||||
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
|
||||
} else {
|
||||
return colors.secondary
|
||||
}
|
||||
}, [composeState.emoji.active, composeState.emoji.emojis])
|
||||
const emojiOnPress = useCallback(() => {
|
||||
analytics('compose_actions_emojis_press', {
|
||||
current: composeState.emoji.active
|
||||
})
|
||||
if (composeState.emoji.emojis) {
|
||||
layoutAnimation()
|
||||
composeDispatch({
|
||||
type: 'emoji',
|
||||
payload: { ...composeState.emoji, active: !composeState.emoji.active }
|
||||
})
|
||||
}, [emojisState.emojis.length, emojisState.targetIndex])
|
||||
// useEffect(() => {
|
||||
// const showSubscription = Keyboard.addListener('keyboardWillShow', () => {
|
||||
// composeDispatch({ type: 'emoji/shown', payload: false })
|
||||
// })
|
||||
|
||||
// return () => {
|
||||
// showSubscription.remove()
|
||||
// }
|
||||
// }, [])
|
||||
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 (
|
||||
<View
|
||||
@ -186,12 +200,8 @@ const ComposeActions: React.FC = () => {
|
||||
>
|
||||
<Pressable
|
||||
accessibilityRole='button'
|
||||
accessibilityLabel={t(
|
||||
'content.root.actions.attachment.accessibilityLabel'
|
||||
)}
|
||||
accessibilityHint={t(
|
||||
'content.root.actions.attachment.accessibilityHint'
|
||||
)}
|
||||
accessibilityLabel={t('content.root.actions.attachment.accessibilityLabel')}
|
||||
accessibilityHint={t('content.root.actions.attachment.accessibilityHint')}
|
||||
accessibilityState={{
|
||||
disabled: composeState.poll.active
|
||||
}}
|
||||
@ -213,10 +223,9 @@ const ComposeActions: React.FC = () => {
|
||||
/>
|
||||
<Pressable
|
||||
accessibilityRole='button'
|
||||
accessibilityLabel={t(
|
||||
'content.root.actions.visibility.accessibilityLabel',
|
||||
{ visibility: composeState.visibility }
|
||||
)}
|
||||
accessibilityLabel={t('content.root.actions.visibility.accessibilityLabel', {
|
||||
visibility: composeState.visibility
|
||||
})}
|
||||
accessibilityState={{ disabled: composeState.visibilityLock }}
|
||||
style={styles.button}
|
||||
onPress={visibilityOnPress}
|
||||
@ -224,17 +233,13 @@ const ComposeActions: React.FC = () => {
|
||||
<Icon
|
||||
name={visibilityIcon}
|
||||
size={24}
|
||||
color={
|
||||
composeState.visibilityLock ? colors.disabled : colors.secondary
|
||||
}
|
||||
color={composeState.visibilityLock ? colors.disabled : colors.secondary}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Pressable
|
||||
accessibilityRole='button'
|
||||
accessibilityLabel={t(
|
||||
'content.root.actions.spoiler.accessibilityLabel'
|
||||
)}
|
||||
accessibilityLabel={t('content.root.actions.spoiler.accessibilityLabel')}
|
||||
accessibilityState={{ expanded: composeState.spoiler.active }}
|
||||
style={styles.button}
|
||||
onPress={spoilerOnPress}
|
||||
@ -242,11 +247,7 @@ const ComposeActions: React.FC = () => {
|
||||
<Icon
|
||||
name='AlertTriangle'
|
||||
size={24}
|
||||
color={
|
||||
composeState.spoiler.active
|
||||
? colors.primaryDefault
|
||||
: colors.secondary
|
||||
}
|
||||
color={composeState.spoiler.active ? colors.primaryDefault : colors.secondary}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@ -255,8 +256,8 @@ const ComposeActions: React.FC = () => {
|
||||
accessibilityLabel={t('content.root.actions.emoji.accessibilityLabel')}
|
||||
accessibilityHint={t('content.root.actions.emoji.accessibilityHint')}
|
||||
accessibilityState={{
|
||||
disabled: composeState.emoji.emojis ? false : true,
|
||||
expanded: composeState.emoji.active
|
||||
disabled: emojisState.emojis.length ? false : true,
|
||||
expanded: emojisState.targetIndex !== -1
|
||||
}}
|
||||
style={styles.button}
|
||||
onPress={emojiOnPress}
|
||||
|
@ -1,31 +1,21 @@
|
||||
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 ComposeReply from '@screens/Compose/Root/Footer/Reply'
|
||||
import ComposeContext from '@screens/Compose/utils/createContext'
|
||||
import React, { RefObject, useContext } from 'react'
|
||||
import { SectionList, View } from 'react-native'
|
||||
import { View } from 'react-native'
|
||||
|
||||
export interface Props {
|
||||
accessibleRefAttachments: RefObject<View>
|
||||
accessibleRefEmojis: RefObject<SectionList>
|
||||
}
|
||||
|
||||
const ComposeRootFooter: React.FC<Props> = ({
|
||||
accessibleRefAttachments,
|
||||
accessibleRefEmojis
|
||||
}) => {
|
||||
const ComposeRootFooter: React.FC<Props> = ({ accessibleRefAttachments }) => {
|
||||
const { composeState } = useContext(ComposeContext)
|
||||
|
||||
return (
|
||||
<>
|
||||
{composeState.emoji.active ? (
|
||||
<ComposeEmojis accessibleRefEmojis={accessibleRefEmojis} />
|
||||
) : null}
|
||||
{composeState.attachments.uploads.length ? (
|
||||
<ComposeAttachments
|
||||
accessibleRefAttachments={accessibleRefAttachments}
|
||||
/>
|
||||
<ComposeAttachments accessibleRefAttachments={accessibleRefAttachments} />
|
||||
) : null}
|
||||
{composeState.poll.active ? <ComposePoll /> : null}
|
||||
{composeState.replyToStatus ? <ComposeReply /> : null}
|
||||
|
@ -26,9 +26,8 @@ const updateText = ({
|
||||
const whiteSpaceFront = /\s/g.test(contentFront.slice(-1))
|
||||
const whiteSpaceRear = /\s/g.test(contentRear.slice(-1))
|
||||
|
||||
const newTextWithSpace = `${
|
||||
whiteSpaceFront || type === 'suggestion' ? '' : ' '
|
||||
}${newText}${whiteSpaceRear ? '' : ' '}`
|
||||
const newTextWithSpace = `${whiteSpaceFront || type === 'suggestion' ? '' : ' '
|
||||
}${newText}${whiteSpaceRear ? '' : ' '}`
|
||||
|
||||
formatText({
|
||||
textInput,
|
||||
|
@ -9,16 +9,15 @@ const composeInitialState: Omit<ComposeState, 'timestamp'> = {
|
||||
count: 0,
|
||||
raw: '',
|
||||
formatted: undefined,
|
||||
selection: { start: 0, end: 0 }
|
||||
selection: { start: 0 }
|
||||
},
|
||||
text: {
|
||||
count: 0,
|
||||
raw: '',
|
||||
formatted: undefined,
|
||||
selection: { start: 0, end: 0 }
|
||||
selection: { start: 0 }
|
||||
},
|
||||
tag: undefined,
|
||||
emoji: { active: false, emojis: undefined },
|
||||
poll: {
|
||||
active: false,
|
||||
total: 2,
|
||||
|
@ -35,8 +35,6 @@ const composeReducer = (
|
||||
return { ...state, text: { ...state.text, ...action.payload } }
|
||||
case 'tag':
|
||||
return { ...state, tag: action.payload }
|
||||
case 'emoji':
|
||||
return { ...state, emoji: action.payload }
|
||||
case 'poll':
|
||||
return { ...state, poll: { ...state.poll, ...action.payload } }
|
||||
case 'attachments/sensitive':
|
||||
|
17
src/screens/Compose/utils/types.d.ts
vendored
17
src/screens/Compose/utils/types.d.ts
vendored
@ -26,13 +26,13 @@ export type ComposeState = {
|
||||
count: number
|
||||
raw: string
|
||||
formatted: ReactNode
|
||||
selection: { start: number; end: number }
|
||||
selection: { start: number; end?: number }
|
||||
}
|
||||
text: {
|
||||
count: number
|
||||
raw: string
|
||||
formatted: ReactNode
|
||||
selection: { start: number; end: number }
|
||||
selection: { start: number; end?: number }
|
||||
}
|
||||
tag?: {
|
||||
type: 'url' | 'accounts' | 'hashtags'
|
||||
@ -40,15 +40,6 @@ export type ComposeState = {
|
||||
offset: number
|
||||
length: number
|
||||
}
|
||||
emoji: {
|
||||
active: boolean
|
||||
emojis:
|
||||
| {
|
||||
title: string
|
||||
data: Pick<Mastodon.Emoji, 'shortcode' | 'url' | 'static_url'>[][]
|
||||
}[]
|
||||
| undefined
|
||||
}
|
||||
poll: {
|
||||
active: boolean
|
||||
total: number
|
||||
@ -96,10 +87,6 @@ export type ComposeAction =
|
||||
type: 'tag'
|
||||
payload: ComposeState['tag']
|
||||
}
|
||||
| {
|
||||
type: 'emoji'
|
||||
payload: ComposeState['emoji']
|
||||
}
|
||||
| {
|
||||
type: 'poll'
|
||||
payload: Partial<ComposeState['poll']>
|
||||
|
Loading…
x
Reference in New Issue
Block a user