diff --git a/src/api/client.ts b/src/api/client.ts index 0bfa71c2..d684a4c4 100644 --- a/src/api/client.ts +++ b/src/api/client.ts @@ -6,6 +6,7 @@ const client = async ({ method, instance, endpoint, + headers, query, body }: { @@ -13,6 +14,7 @@ const client = async ({ method: 'get' | 'post' | 'delete' instance: 'local' | 'remote' endpoint: string + headers?: { [key: string]: string } query?: { [key: string]: string | number | boolean } @@ -28,6 +30,7 @@ const client = async ({ searchParams: query, headers: { 'Content-Type': 'application/json', + ...headers, ...(instance === 'local' && { Authorization: `Bearer ${state.localToken}` }) diff --git a/src/stacks/Shared/PostToot.tsx b/src/stacks/Shared/PostToot.tsx index 61694233..e4e1882e 100644 --- a/src/stacks/Shared/PostToot.tsx +++ b/src/stacks/Shared/PostToot.tsx @@ -1,20 +1,106 @@ -import React from 'react' +import React, { ReactNode, useReducer } from 'react' import { Alert, Pressable, Text } from 'react-native' import { createNativeStackNavigator } from 'react-native-screens/native-stack' import { useNavigation } from '@react-navigation/native' import PostMain from './PostToot/PostMain' +import client from 'src/api/client' const Stack = createNativeStackNavigator() +export type PostState = { + text: { + count: number + raw: string + formatted: ReactNode + } + selection: { start: number; end: number } + overlay: null | 'suggestions' | 'emojis' + tag: + | { + type: 'url' | 'accounts' | 'hashtags' + text: string + offset: number + } + | undefined + emojis: mastodon.Emoji[] | undefined + height: { + view: number + editor: number + keyboard: number + } +} + +export type PostAction = + | { + type: 'text' + payload: Partial + } + | { + type: 'selection' + payload: PostState['selection'] + } + | { + type: 'overlay' + payload: PostState['overlay'] + } + | { + type: 'tag' + payload: PostState['tag'] + } + | { + type: 'emojis' + payload: PostState['emojis'] + } + | { + type: 'height' + payload: Partial + } + +const postInitialState: PostState = { + text: { + count: 0, + raw: '', + formatted: undefined + }, + selection: { start: 0, end: 0 }, + overlay: null, + tag: undefined, + emojis: undefined, + height: { + view: 0, + editor: 0, + keyboard: 0 + } +} +const postReducer = (state: PostState, action: PostAction): PostState => { + switch (action.type) { + case 'text': + return { ...state, text: { ...state.text, ...action.payload } } + case 'selection': + return { ...state, selection: action.payload } + case 'overlay': + return { ...state, overlay: action.payload } + case 'tag': + return { ...state, tag: action.payload } + case 'emojis': + return { ...state, emojis: action.payload } + case 'height': + return { ...state, height: { ...state.height, ...action.payload } } + default: + throw new Error('Unexpected action') + } +} + const PostToot: React.FC = () => { const navigation = useNavigation() + const [postState, postDispatch] = useReducer(postReducer, postInitialState) + return ( ( { ), headerCenter: () => <>, headerRight: () => ( - + { + if (postState.text.count < 0) { + Alert.alert('字数超限', '', [ + { + text: '返回继续编辑' + } + ]) + } else { + const res = await client({ + method: 'post', + instance: 'local', + endpoint: 'statuses', + headers: { + 'Idempotency-Key': + Date.now().toString() + Math.random().toString() + }, + query: { status: postState.text.raw } + }) + if (res.body.id) { + Alert.alert('发布成功', '', [ + { + text: '好的', + onPress: () => navigation.goBack() + } + ]) + } else { + Alert.alert('发布失败', '', [ + { + text: '返回重试' + } + ]) + } + } + }} + > 发嘟嘟 ) }} - /> + > + {props => ( + + )} + ) } diff --git a/src/stacks/Shared/PostToot/PostEmojis.tsx b/src/stacks/Shared/PostToot/PostEmojis.tsx index b65ad8f3..feca10e8 100644 --- a/src/stacks/Shared/PostToot/PostEmojis.tsx +++ b/src/stacks/Shared/PostToot/PostEmojis.tsx @@ -1,7 +1,7 @@ import React, { Dispatch } from 'react' import { Image, Pressable } from 'react-native' -import { PostAction, PostState } from './PostMain' +import { PostAction, PostState } from '../PostToot' import updateText from './updateText' export interface Props { diff --git a/src/stacks/Shared/PostToot/PostMain.tsx b/src/stacks/Shared/PostToot/PostMain.tsx index f1123cb1..27d36d0e 100644 --- a/src/stacks/Shared/PostToot/PostMain.tsx +++ b/src/stacks/Shared/PostToot/PostMain.tsx @@ -1,10 +1,4 @@ -import React, { - createElement, - ReactNode, - useCallback, - useEffect, - useReducer -} from 'react' +import React, { createElement, Dispatch, useCallback, useEffect } from 'react' import { Keyboard, Pressable, @@ -22,94 +16,14 @@ import PostSuggestions from './PostSuggestions' import PostEmojis from './PostEmojis' import { useQuery } from 'react-query' import { emojisFetch } from 'src/stacks/common/emojisFetch' +import { PostAction, PostState } from '../PostToot' -export type PostState = { - text: { - count: number - raw: string - formatted: ReactNode - } - selection: { start: number; end: number } - overlay: null | 'suggestions' | 'emojis' - tag: - | { - type: 'url' | 'accounts' | 'hashtags' - text: string - offset: number - } - | undefined - emojis: mastodon.Emoji[] | undefined - height: { - view: number - editor: number - keyboard: number - } +export interface Props { + postState: PostState + postDispatch: Dispatch } -export type PostAction = - | { - type: 'text' - payload: Partial - } - | { - type: 'selection' - payload: PostState['selection'] - } - | { - type: 'overlay' - payload: PostState['overlay'] - } - | { - type: 'tag' - payload: PostState['tag'] - } - | { - type: 'emojis' - payload: PostState['emojis'] - } - | { - type: 'height' - payload: Partial - } - -const postInitialState: PostState = { - text: { - count: 0, - raw: '', - formatted: undefined - }, - selection: { start: 0, end: 0 }, - overlay: null, - tag: undefined, - emojis: undefined, - height: { - view: 0, - editor: 0, - keyboard: 0 - } -} -const postReducer = (state: PostState, action: PostAction): PostState => { - switch (action.type) { - case 'text': - return { ...state, text: { ...state.text, ...action.payload } } - case 'selection': - return { ...state, selection: action.payload } - case 'overlay': - return { ...state, overlay: action.payload } - case 'tag': - return { ...state, tag: action.payload } - case 'emojis': - return { ...state, emojis: action.payload } - case 'height': - return { ...state, height: { ...state.height, ...action.payload } } - default: - throw new Error('Unexpected action') - } -} - -const PostMain = () => { - const [postState, postDispatch] = useReducer(postReducer, postInitialState) - +const PostMain: React.FC = ({ postState, postDispatch }) => { useEffect(() => { Keyboard.addListener('keyboardDidShow', _keyboardDidShow) Keyboard.addListener('keyboardDidHide', _keyboardDidHide) diff --git a/src/stacks/Shared/PostToot/PostSuggestions.tsx b/src/stacks/Shared/PostToot/PostSuggestions.tsx index 1c60e6d0..a50b9e6e 100644 --- a/src/stacks/Shared/PostToot/PostSuggestions.tsx +++ b/src/stacks/Shared/PostToot/PostSuggestions.tsx @@ -4,7 +4,7 @@ import { FlatList } from 'react-native-gesture-handler' import { useQuery } from 'react-query' import { searchFetch } from '../../common/searchFetch' -import { PostAction, PostState } from './PostMain' +import { PostAction, PostState } from '../PostToot' import updateText from './updateText' declare module 'react' { diff --git a/src/stacks/Shared/PostToot/updateText.ts b/src/stacks/Shared/PostToot/updateText.ts index f61b451a..e2122ff6 100644 --- a/src/stacks/Shared/PostToot/updateText.ts +++ b/src/stacks/Shared/PostToot/updateText.ts @@ -1,4 +1,4 @@ -import { PostState } from './PostMain' +import { PostState } from '../PostToot' const updateText = ({ onChangeText,