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

Spoiler is done

This commit is contained in:
Zhiyuan Zheng
2020-12-06 23:51:13 +01:00
parent 37ad208f8b
commit 662fd08c3a
12 changed files with 409 additions and 162 deletions

View File

@ -1,24 +1,41 @@
import React, { ReactNode, useEffect, useReducer, useState } from 'react'
import { Alert, Keyboard, KeyboardAvoidingView } from 'react-native'
import {
ActivityIndicator,
Alert,
Keyboard,
KeyboardAvoidingView,
StyleSheet,
Text
} from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
import { useNavigation } from '@react-navigation/native'
import sha256 from 'crypto-js/sha256'
import { store } from 'src/store'
import ComposeRoot from './Compose/Root'
import client from 'src/api/client'
import { getLocalAccountPreferences } from 'src/utils/slices/instancesSlice'
import { HeaderLeft, HeaderRight } from 'src/components/Header'
import { StyleConstants } from 'src/utils/styles/constants'
import { useTheme } from 'src/utils/styles/ThemeManager'
const Stack = createNativeStackNavigator()
export type PostState = {
spoiler: {
active: boolean
count: number
raw: string
formatted: ReactNode
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'
@ -57,12 +74,12 @@ export type PostState = {
export type PostAction =
| {
type: 'text'
payload: Partial<PostState['text']>
type: 'spoiler'
payload: Partial<PostState['spoiler']>
}
| {
type: 'selection'
payload: PostState['selection']
type: 'text'
payload: Partial<PostState['text']>
}
| {
type: 'tag'
@ -94,22 +111,29 @@ export type PostAction =
}
const postInitialState: PostState = {
text: {
count: 500,
spoiler: {
active: false,
count: 0,
raw: '',
formatted: undefined
formatted: undefined,
selection: { start: 0, end: 0 }
},
text: {
count: 0,
raw: '',
formatted: undefined,
selection: { start: 0, end: 0 }
},
selection: { start: 0, end: 0 },
tag: undefined,
emoji: { active: false, emojis: undefined },
poll: {
active: false,
total: 2,
options: {
'0': undefined,
'1': undefined,
'2': undefined,
'3': undefined,
'4': undefined
'3': undefined
},
multiple: false,
expire: '86400'
@ -123,10 +147,10 @@ const postInitialState: PostState = {
}
const postReducer = (state: PostState, action: PostAction): PostState => {
switch (action.type) {
case 'spoiler':
return { ...state, spoiler: { ...state.spoiler, ...action.payload } }
case 'text':
return { ...state, text: { ...state.text, ...action.payload } }
case 'selection':
return { ...state, selection: action.payload }
case 'tag':
return { ...state, tag: action.payload }
case 'emoji':
@ -152,8 +176,11 @@ const postReducer = (state: PostState, action: PostAction): PostState => {
}
const Compose: React.FC = () => {
const { theme } = useTheme()
const navigation = useNavigation()
const [isSubmitting, setIsSubmitting] = useState(false)
const [hasKeyboard, setHasKeyboard] = useState(false)
useEffect(() => {
Keyboard.addListener('keyboardWillShow', _keyboardDidShow)
@ -175,6 +202,7 @@ const Compose: React.FC = () => {
const [postState, postDispatch] = useReducer(postReducer, postInitialState)
const tootPost = async () => {
setIsSubmitting(true)
if (postState.text.count < 0) {
Alert.alert('字数超限', '', [
{
@ -184,6 +212,10 @@ const Compose: React.FC = () => {
} else {
const formData = new FormData()
if (postState.spoiler.active) {
formData.append('spoiler_text', postState.spoiler.raw)
}
formData.append('status', postState.text.raw)
if (postState.poll.active) {
@ -207,13 +239,25 @@ const Compose: React.FC = () => {
instance: 'local',
url: 'statuses',
headers: {
'Idempotency-Key': Date.now().toString() + Math.random().toString()
'Idempotency-Key': sha256(
postState.spoiler.raw +
postState.text.raw +
postState.poll.options['0'] +
postState.poll.options['1'] +
postState.poll.options['2'] +
postState.poll.options['3'] +
postState.poll.multiple +
postState.poll.expire +
postState.attachments.map(attachment => attachment.id) +
postState.visibility
).toString()
},
body: formData
})
.then(
res => {
if (res.body.id) {
setIsSubmitting(false)
Alert.alert('发布成功', '', [
{
text: '好的',
@ -224,6 +268,7 @@ const Compose: React.FC = () => {
}
])
} else {
setIsSubmitting(false)
Alert.alert('发布失败', '', [
{
text: '返回重试'
@ -232,6 +277,7 @@ const Compose: React.FC = () => {
}
},
error => {
setIsSubmitting(false)
Alert.alert('发布失败', error.body, [
{
text: '返回重试'
@ -240,6 +286,7 @@ const Compose: React.FC = () => {
}
)
.catch(() => {
setIsSubmitting(false)
Alert.alert('发布失败', '', [
{
text: '返回重试'
@ -249,6 +296,10 @@ const Compose: React.FC = () => {
}
}
const totalTextCount =
(postState.spoiler.active ? postState.spoiler.count : 0) +
postState.text.count
return (
<KeyboardAvoidingView behavior='padding' style={{ flex: 1 }}>
<SafeAreaView
@ -274,16 +325,31 @@ const Compose: React.FC = () => {
text='退出编辑'
/>
),
headerCenter: () => <></>,
headerRight: () => (
<HeaderRight
onPress={async () => tootPost()}
text='发嘟嘟'
disabled={
postState.text.raw.length < 1 || postState.text.count < 0
}
/>
)
headerCenter: () => (
<Text
style={[
styles.count,
{
color:
totalTextCount > 500 ? theme.error : theme.secondary
}
]}
>
{totalTextCount} / 500
</Text>
),
headerRight: () =>
isSubmitting ? (
<ActivityIndicator />
) : (
<HeaderRight
onPress={async () => tootPost()}
text='发嘟嘟'
disabled={
postState.text.raw.length < 1 || totalTextCount > 500
}
/>
)
}}
>
{() => (
@ -296,4 +362,11 @@ const Compose: React.FC = () => {
)
}
const styles = StyleSheet.create({
count: {
textAlign: 'center',
fontSize: StyleConstants.Font.Size.M
}
})
export default Compose