1
0
mirror of https://github.com/tooot-app/app synced 2025-06-05 22:19:13 +02:00

Draft basic done

#6
This commit is contained in:
Zhiyuan Zheng
2021-02-07 00:39:11 +01:00
parent 700b9ad433
commit 03dc94c7c9
32 changed files with 684 additions and 170 deletions

View File

@ -2,32 +2,40 @@ import analytics from '@components/analytics'
import { HeaderLeft, HeaderRight } from '@components/Header'
import { StackScreenProps } from '@react-navigation/stack'
import haptics from '@root/components/haptics'
import { store } from '@root/store'
import formatText from '@screens/Compose/formatText'
import ComposeRoot from '@screens/Compose/Root'
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
import { updateStoreReview } from '@utils/slices/contextsSlice'
import {
getLocalAccount,
getLocalMaxTootChar
getLocalMaxTootChar,
removeLocalDraft,
updateLocalDraft
} from '@utils/slices/instancesSlice'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useCallback, useEffect, useReducer, useState } from 'react'
import { filter } from 'lodash'
import React, {
useCallback,
useEffect,
useMemo,
useReducer,
useState
} from 'react'
import { useTranslation } from 'react-i18next'
import {
Alert,
Keyboard,
KeyboardAvoidingView,
Platform,
StyleSheet,
Text
StyleSheet
} from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
import { useQueryClient } from 'react-query'
import { useDispatch, useSelector } from 'react-redux'
import * as Sentry from 'sentry-expo'
import ComposeDraftsList from './Compose/DraftsList'
import ComposeEditAttachment from './Compose/EditAttachment'
import ComposeContext from './Compose/utils/createContext'
import composeInitialState from './Compose/utils/initialState'
@ -55,7 +63,6 @@ const ScreenCompose: React.FC<ScreenComposeProp> = ({
Keyboard.addListener('keyboardWillShow', _keyboardDidShow)
Keyboard.addListener('keyboardWillHide', _keyboardDidHide)
// cleanup function
return () => {
Keyboard.removeListener('keyboardWillShow', _keyboardDidShow)
Keyboard.removeListener('keyboardWillHide', _keyboardDidHide)
@ -68,21 +75,53 @@ const ScreenCompose: React.FC<ScreenComposeProp> = ({
setHasKeyboard(false)
}
const localAccount = getLocalAccount(store.getState())
// const draft = useSelector(getLocalDraft, () => true)
const initialReducerState = useMemo(() => {
if (params) {
return composeParseState(params)
} else {
return {
...composeInitialState,
timestamp: Date.now(),
visibility:
localAccount?.preferences &&
localAccount.preferences['posting:default:visibility']
? localAccount.preferences['posting:default:visibility']
: 'public'
}
}
}, [])
const localAccount = useSelector(getLocalAccount)
const [composeState, composeDispatch] = useReducer(
composeReducer,
params
? composeParseState(params)
: {
...composeInitialState,
visibility:
localAccount?.preferences &&
localAccount.preferences['posting:default:visibility']
? localAccount.preferences['posting:default:visibility']
: 'public'
}
initialReducerState
)
const maxTootChars = useSelector(getLocalMaxTootChar)
const totalTextCount =
(composeState.spoiler.active ? composeState.spoiler.count : 0) +
composeState.text.count
// If compose state is dirty, then disallow add back drafts
useEffect(() => {
composeDispatch({
type: 'dirty',
payload:
totalTextCount !== 0 ||
composeState.attachments.uploads.length !== 0 ||
(composeState.poll.active === true &&
filter(composeState.poll.options, o => {
return o !== undefined && o.length > 0
}).length > 0)
})
}, [
totalTextCount,
composeState.attachments.uploads.length,
composeState.poll.active,
composeState.poll.options
])
useEffect(() => {
switch (params?.type) {
case 'edit':
@ -113,10 +152,31 @@ const ScreenCompose: React.FC<ScreenComposeProp> = ({
}
}, [params?.type])
const maxTootChars = useSelector(getLocalMaxTootChar)
const totalTextCount =
(composeState.spoiler.active ? composeState.spoiler.count : 0) +
composeState.text.count
const saveDraft = () => {
dispatch(
updateLocalDraft({
timestamp: composeState.timestamp,
spoiler: composeState.spoiler.raw,
text: composeState.text.raw,
poll: composeState.poll,
attachments: composeState.attachments,
visibility: composeState.visibility,
visibilityLock: composeState.visibilityLock,
replyToStatus: composeState.replyToStatus
})
)
}
const removeDraft = useCallback(() => {
dispatch(removeLocalDraft(composeState.timestamp))
}, [composeState.timestamp])
useEffect(() => {
const autoSave = composeState.dirty
? setInterval(() => {
saveDraft()
}, 2000)
: removeDraft()
return () => autoSave && clearInterval(autoSave)
}, [composeState])
const headerLeft = useCallback(
() => (
@ -125,11 +185,7 @@ const ScreenCompose: React.FC<ScreenComposeProp> = ({
content={t('heading.left.button')}
onPress={() => {
analytics('compose_header_back_press')
if (
totalTextCount === 0 &&
composeState.attachments.uploads.length === 0 &&
composeState.poll.active === false
) {
if (!composeState.dirty) {
analytics('compose_header_back_empty')
navigation.goBack()
return
@ -137,15 +193,24 @@ const ScreenCompose: React.FC<ScreenComposeProp> = ({
analytics('compose_header_back_state_occupied')
Alert.alert(t('heading.left.alert.title'), undefined, [
{
text: t('heading.left.alert.buttons.exit'),
text: t('heading.left.alert.buttons.delete'),
style: 'destructive',
onPress: () => {
analytics('compose_header_back_occupied_confirm')
analytics('compose_header_back_occupied_save')
removeDraft()
navigation.goBack()
}
},
{
text: t('heading.left.alert.buttons.continue'),
text: t('heading.left.alert.buttons.save'),
onPress: () => {
analytics('compose_header_back_occupied_delete')
saveDraft()
navigation.goBack()
}
},
{
text: t('heading.left.alert.buttons.cancel'),
style: 'cancel',
onPress: () => {
analytics('compose_header_back_occupied_cancel')
@ -156,22 +221,7 @@ const ScreenCompose: React.FC<ScreenComposeProp> = ({
}}
/>
),
[totalTextCount, composeState]
)
const headerCenter = useCallback(
() => (
<Text
style={[
styles.count,
{
color: totalTextCount > maxTootChars ? theme.red : theme.secondary
}
]}
>
{totalTextCount} / {maxTootChars}
</Text>
),
[totalTextCount, maxTootChars]
[composeState]
)
const dispatch = useDispatch()
const headerRight = useCallback(
@ -228,7 +278,7 @@ const ScreenCompose: React.FC<ScreenComposeProp> = ({
}
/>
),
[totalTextCount, maxTootChars, composeState]
[totalTextCount, composeState]
)
return (
@ -249,7 +299,26 @@ const ScreenCompose: React.FC<ScreenComposeProp> = ({
<Stack.Screen
name='Screen-Compose-Root'
component={ComposeRoot}
options={{ headerLeft, headerCenter, headerRight }}
options={{
headerLeft,
headerTitle: `${totalTextCount} / ${maxTootChars}${__DEV__ &&
` Dirty: ${composeState.dirty.toString()}`}`,
headerTitleStyle: {
fontWeight:
totalTextCount > maxTootChars
? StyleConstants.Font.Weight.Bold
: StyleConstants.Font.Weight.Normal,
fontSize: StyleConstants.Font.Size.M
},
headerTintColor:
totalTextCount > maxTootChars ? theme.red : theme.secondary,
headerRight
}}
/>
<Stack.Screen
name='Screen-Compose-DraftsList'
component={ComposeDraftsList}
options={{ stackPresentation: 'modal' }}
/>
<Stack.Screen
name='Screen-Compose-EditAttachment'
@ -264,11 +333,7 @@ const ScreenCompose: React.FC<ScreenComposeProp> = ({
}
const styles = StyleSheet.create({
base: { flex: 1 },
count: {
textAlign: 'center',
...StyleConstants.FontStyle.M
}
base: { flex: 1 }
})
export default ScreenCompose