1
0
mirror of https://github.com/tooot-app/app synced 2025-06-05 22:19:13 +02:00
This commit is contained in:
Zhiyuan Zheng 2021-01-14 22:53:01 +01:00
parent 95f500ae72
commit d39bc82909
No known key found for this signature in database
GPG Key ID: 078A93AB607D85E0
22 changed files with 300 additions and 163 deletions

View File

@ -14,6 +14,7 @@
"@react-native-community/masked-view": "0.1.10", "@react-native-community/masked-view": "0.1.10",
"@react-native-community/netinfo": "^5.9.7", "@react-native-community/netinfo": "^5.9.7",
"@react-native-community/segmented-control": "2.2.1", "@react-native-community/segmented-control": "2.2.1",
"@react-native-community/viewpager": "4.2.0",
"@react-navigation/bottom-tabs": "^5.11.2", "@react-navigation/bottom-tabs": "^5.11.2",
"@react-navigation/native": "^5.8.10", "@react-navigation/native": "^5.8.10",
"@reduxjs/toolkit": "^1.5.0", "@reduxjs/toolkit": "^1.5.0",
@ -61,6 +62,7 @@
"react-native-shimmer-placeholder": "^2.0.6", "react-native-shimmer-placeholder": "^2.0.6",
"react-native-svg": "12.1.0", "react-native-svg": "12.1.0",
"react-native-tab-view": "^2.15.2", "react-native-tab-view": "^2.15.2",
"react-native-tab-view-viewpager-adapter": "^1.1.0",
"react-native-toast-message": "^1.4.2", "react-native-toast-message": "^1.4.2",
"react-navigation": "^4.4.3", "react-navigation": "^4.4.3",
"react-query": "^3.5.6", "react-query": "^3.5.6",

View File

@ -14,8 +14,18 @@ declare namespace Nav {
'Screen-Shared-Announcements': { showAll?: boolean } 'Screen-Shared-Announcements': { showAll?: boolean }
'Screen-Shared-Compose': 'Screen-Shared-Compose':
| { | {
type?: 'reply' | 'conversation' | 'edit' type: 'reply' | 'conversation' | 'edit'
incomingStatus: Mastodon.Status incomingStatus: Mastodon.Status
queryKey?: [
'Timeline',
{
page: App.Pages
hashtag?: Mastodon.Tag['name']
list?: Mastodon.List['id']
toot?: Mastodon.Status['id']
account?: Mastodon.Account['id']
}
]
} }
| undefined | undefined
'Screen-Shared-Hashtag': { 'Screen-Shared-Hashtag': {

View File

@ -1,10 +1,14 @@
import React from 'react' import React, { useRef } from 'react'
import { Dimensions, Modal, StyleSheet, View } from 'react-native' import { Dimensions, Modal, StyleSheet, View } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context' import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { useTheme } from '@utils/styles/ThemeManager' import { useTheme } from '@utils/styles/ThemeManager'
import { StyleConstants } from '@utils/styles/constants' import { StyleConstants } from '@utils/styles/constants'
import Button from '@components/Button' import Button from '@components/Button'
import { PanGestureHandler } from 'react-native-gesture-handler' import {
PanGestureHandler,
State,
TapGestureHandler
} from 'react-native-gesture-handler'
import Animated, { import Animated, {
Extrapolate, Extrapolate,
interpolate, interpolate,
@ -55,33 +59,44 @@ const BottomSheet: React.FC<Props> = ({ children, visible, handleDismiss }) => {
return ( return (
<Modal animated animationType='fade' visible={visible} transparent> <Modal animated animationType='fade' visible={visible} transparent>
<PanGestureHandler onGestureEvent={onGestureEvent}> <TapGestureHandler
onHandlerStateChange={({ nativeEvent }) => {
if (nativeEvent.state === State.ACTIVE) {
callDismiss()
}
}}
>
<Animated.View <Animated.View
style={[styles.overlay, { backgroundColor: theme.backgroundOverlay }]} style={[styles.overlay, { backgroundColor: theme.backgroundOverlay }]}
> >
<Animated.View <PanGestureHandler onGestureEvent={onGestureEvent}>
style={[ <Animated.View
styles.container, style={[
styleTop, styles.container,
{ styleTop,
backgroundColor: theme.background, {
paddingBottom: insets.bottom || StyleConstants.Spacing.L backgroundColor: theme.background,
} paddingBottom: insets.bottom || StyleConstants.Spacing.L
]} }
> ]}
<View >
style={[styles.handle, { backgroundColor: theme.primaryOverlay }]} <View
/> style={[
{children} styles.handle,
<Button { backgroundColor: theme.primaryOverlay }
type='text' ]}
content='取消' />
onPress={() => handleDismiss()} {children}
style={styles.button} <Button
/> type='text'
</Animated.View> content='取消'
onPress={() => handleDismiss()}
style={styles.button}
/>
</Animated.View>
</PanGestureHandler>
</Animated.View> </Animated.View>
</PanGestureHandler> </TapGestureHandler>
</Modal> </Modal>
) )
} }

View File

@ -1,7 +1,8 @@
import Icon from '@components/Icon' import Icon from '@components/Icon'
import { StyleConstants } from '@utils/styles/constants' import { StyleConstants } from '@utils/styles/constants'
import layoutAnimation from '@utils/styles/layoutAnimation'
import { useTheme } from '@utils/styles/ThemeManager' import { useTheme } from '@utils/styles/ThemeManager'
import React, { useMemo } from 'react' import React, { useEffect, useMemo, useRef } from 'react'
import { Pressable, StyleSheet, Text, View } from 'react-native' import { Pressable, StyleSheet, Text, View } from 'react-native'
import { Chase } from 'react-native-animated-spinkit' import { Chase } from 'react-native-animated-spinkit'
@ -24,6 +25,15 @@ const HeaderRight: React.FC<Props> = ({
}) => { }) => {
const { theme } = useTheme() const { theme } = useTheme()
const mounted = useRef(false)
useEffect(() => {
if (mounted.current) {
layoutAnimation()
} else {
mounted.current = true
}
}, [content, loading, disabled])
const loadingSpinkit = useMemo( const loadingSpinkit = useMemo(
() => ( () => (
<View style={{ position: 'absolute' }}> <View style={{ position: 'absolute' }}>

View File

@ -3,8 +3,9 @@ import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager' import { useTheme } from '@utils/styles/ThemeManager'
import { ColorDefinitions } from '@utils/styles/themes' import { ColorDefinitions } from '@utils/styles/themes'
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { Pressable, StyleSheet, Switch, Text, View } from 'react-native' import { StyleSheet, Switch, Text, View } from 'react-native'
import { Chase } from 'react-native-animated-spinkit' import { Chase } from 'react-native-animated-spinkit'
import { State, TapGestureHandler } from 'react-native-gesture-handler'
export interface Props { export interface Props {
iconFront?: any iconFront?: any
@ -54,81 +55,86 @@ const MenuRow: React.FC<Props> = ({
) )
return ( return (
<Pressable <View style={styles.base}>
style={styles.base} <TapGestureHandler
onPress={onPress} onHandlerStateChange={({ nativeEvent }) => {
disabled={loading} if (nativeEvent.state === State.ACTIVE) {
testID='base' if (!loading) {
> onPress && onPress()
<View style={styles.core}> }
<View style={styles.front}> }
{iconFront && ( }}
<Icon >
name={iconFront} <View style={styles.core}>
size={StyleConstants.Font.Size.L} <View style={styles.front}>
color={theme[iconFrontColor]} {iconFront && (
style={styles.iconFront} <Icon
/> name={iconFront}
)} size={StyleConstants.Font.Size.L}
<View style={styles.main}> color={theme[iconFrontColor]}
<Text style={styles.iconFront}
style={[styles.title, { color: theme.primary }]}
numberOfLines={1}
>
{title}
</Text>
{description ? (
<Text style={[styles.description, { color: theme.secondary }]}>
{description}
</Text>
) : null}
</View>
</View>
{(content && content.length) ||
switchValue !== undefined ||
iconBack ? (
<View style={styles.back}>
{content && content.length ? (
<>
<Text
style={[
styles.content,
{
color: theme.secondary,
opacity: !iconBack && loading ? 0 : 1
}
]}
numberOfLines={1}
>
{content}
</Text>
{loading && !iconBack && loadingSpinkit}
</>
) : null}
{switchValue !== undefined ? (
<Switch
value={switchValue}
onValueChange={switchOnValueChange}
disabled={switchDisabled}
trackColor={{ true: theme.blue, false: theme.disabled }}
/> />
) : null} )}
{iconBack ? ( <View style={styles.main}>
<> <Text
<Icon style={[styles.title, { color: theme.primary }]}
name={iconBack} numberOfLines={1}
size={StyleConstants.Font.Size.L} >
color={theme[iconBackColor]} {title}
style={[styles.iconBack, { opacity: loading ? 0 : 1 }]} </Text>
/> {description ? (
{loading && loadingSpinkit} <Text style={[styles.description, { color: theme.secondary }]}>
</> {description}
) : null} </Text>
) : null}
</View>
</View> </View>
) : null}
</View> {(content && content.length) ||
</Pressable> switchValue !== undefined ||
iconBack ? (
<View style={styles.back}>
{content && content.length ? (
<>
<Text
style={[
styles.content,
{
color: theme.secondary,
opacity: !iconBack && loading ? 0 : 1
}
]}
numberOfLines={1}
>
{content}
</Text>
{loading && !iconBack && loadingSpinkit}
</>
) : null}
{switchValue !== undefined ? (
<Switch
value={switchValue}
onValueChange={switchOnValueChange}
disabled={switchDisabled}
trackColor={{ true: theme.blue, false: theme.disabled }}
/>
) : null}
{iconBack ? (
<>
<Icon
name={iconBack}
size={StyleConstants.Font.Size.L}
color={theme[iconBackColor]}
style={[styles.iconBack, { opacity: loading ? 0 : 1 }]}
/>
{loading && loadingSpinkit}
</>
) : null}
</View>
) : null}
</View>
</TapGestureHandler>
</View>
) )
} }

View File

@ -9,6 +9,7 @@ import React, { useCallback, useMemo, useState } from 'react'
import { Dimensions, Platform, StyleSheet, View } from 'react-native' import { Dimensions, Platform, StyleSheet, View } from 'react-native'
import { createNativeStackNavigator } from 'react-native-screens/native-stack' import { createNativeStackNavigator } from 'react-native-screens/native-stack'
import { TabView } from 'react-native-tab-view' import { TabView } from 'react-native-tab-view'
import ViewPagerAdapter from 'react-native-tab-view-viewpager-adapter'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
const Stack = createNativeStackNavigator< const Stack = createNativeStackNavigator<
@ -53,21 +54,6 @@ const Timelines: React.FC<Props> = ({ name, content }) => {
[localActiveIndex] [localActiveIndex]
) )
const screenComponent = useCallback(
() => (
<TabView
lazy
swipeEnabled
renderScene={renderScene}
renderTabBar={() => null}
onIndexChange={index => setSegment(index)}
navigationState={{ index: segment, routes }}
initialLayout={{ width: Dimensions.get('window').width }}
/>
),
[segment, localActiveIndex]
)
const screenOptions = useMemo(() => { const screenOptions = useMemo(() => {
if (localActiveIndex === null) { if (localActiveIndex === null) {
if (name === 'Public') { if (name === 'Public') {
@ -102,6 +88,8 @@ const Timelines: React.FC<Props> = ({ name, content }) => {
} }
}, [localActiveIndex, mode, segment]) }, [localActiveIndex, mode, segment])
const renderPager = useCallback(props => <ViewPagerAdapter {...props} />, [])
return ( return (
<Stack.Navigator <Stack.Navigator
screenOptions={{ headerHideShadow: true, headerTopInsetEnabled: false }} screenOptions={{ headerHideShadow: true, headerTopInsetEnabled: false }}
@ -109,9 +97,21 @@ const Timelines: React.FC<Props> = ({ name, content }) => {
<Stack.Screen <Stack.Screen
// @ts-ignore // @ts-ignore
name={`Screen-${name}-Root`} name={`Screen-${name}-Root`}
component={screenComponent}
options={screenOptions} options={screenOptions}
/> >
{() => (
<TabView
lazy
swipeEnabled
renderPager={renderPager}
renderScene={renderScene}
renderTabBar={() => null}
onIndexChange={index => setSegment(index)}
navigationState={{ index: segment, routes }}
initialLayout={{ width: Dimensions.get('window').width }}
/>
)}
</Stack.Screen>
{sharedScreens(Stack)} {sharedScreens(Stack)}
</Stack.Navigator> </Stack.Navigator>

View File

@ -14,9 +14,7 @@ import { FlatList } from 'react-native-gesture-handler'
import { useDispatch } from 'react-redux' import { useDispatch } from 'react-redux'
import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline' import { QueryKeyTimeline, useTimelineQuery } from '@utils/queryHooks/timeline'
import { findIndex } from 'lodash' import { findIndex } from 'lodash'
import CustomRefreshControl from '@components/CustomRefreshControl'
import { InfiniteData, useQueryClient } from 'react-query' import { InfiniteData, useQueryClient } from 'react-query'
import { useTheme } from '@utils/styles/ThemeManager'
export interface Props { export interface Props {
page: App.Pages page: App.Pages
@ -37,8 +35,6 @@ const Timeline: React.FC<Props> = ({
disableRefresh = false, disableRefresh = false,
disableInfinity = false disableInfinity = false
}) => { }) => {
const { theme } = useTheme()
const queryKeyParams = { const queryKeyParams = {
page, page,
...(hashtag && { hashtag }), ...(hashtag && { hashtag }),
@ -212,6 +208,7 @@ const Timeline: React.FC<Props> = ({
return ( return (
<FlatList <FlatList
bounces={!disableRefresh}
ref={flRef} ref={flRef}
windowSize={11} windowSize={11}
data={flattenData} data={flattenData}

View File

@ -103,7 +103,8 @@ const TimelineActions: React.FC<Props> = ({ queryKey, status, reblog }) => {
() => () =>
navigation.navigate('Screen-Shared-Compose', { navigation.navigate('Screen-Shared-Compose', {
type: 'reply', type: 'reply',
incomingStatus: status incomingStatus: status,
queryKey
}), }),
[] []
) )

View File

@ -10,6 +10,32 @@ export interface Props {
card: Mastodon.Card card: Mastodon.Card
} }
type CancelPromise = ((reason?: Error) => void) | undefined
type ImageSize = { width: number; height: number }
interface ImageSizeOperation {
start: () => Promise<ImageSize>
cancel: CancelPromise
}
const getImageSize = (uri: string): ImageSizeOperation => {
let cancel: CancelPromise
const start = (): Promise<ImageSize> =>
new Promise<{ width: number; height: number }>((resolve, reject) => {
cancel = reject
Image.getSize(
uri,
(width, height) => {
cancel = undefined
resolve({ width, height })
},
error => {
reject(error)
}
)
})
return { start, cancel }
}
const TimelineCard: React.FC<Props> = ({ card }) => { const TimelineCard: React.FC<Props> = ({ card }) => {
const { theme } = useTheme() const { theme } = useTheme()
@ -19,6 +45,26 @@ const TimelineCard: React.FC<Props> = ({ card }) => {
Image.getSize(card.image, () => setImageLoaded(true)) Image.getSize(card.image, () => setImageLoaded(true))
} }
}, []) }, [])
useEffect(() => {
let cancel: CancelPromise
const sideEffect = async (): Promise<void> => {
try {
const operation = getImageSize(card.image)
cancel = operation.cancel
await operation.start()
} catch (error) {
if (__DEV__) console.warn(error)
}
}
if (card.image) {
sideEffect()
}
return () => {
if (cancel) {
cancel()
}
}
})
const cardVisual = useMemo(() => { const cardVisual = useMemo(() => {
if (imageLoaded) { if (imageLoaded) {
return <Image source={{ uri: card.image }} style={styles.image} /> return <Image source={{ uri: card.image }} style={styles.image} />

View File

@ -96,7 +96,8 @@ const HeaderActionsStatus: React.FC<Props> = ({
if (res.id) { if (res.id) {
navigation.navigate('Screen-Shared-Compose', { navigation.navigate('Screen-Shared-Compose', {
type: 'edit', type: 'edit',
incomingStatus: res incomingStatus: res,
queryKey
}) })
} }
} }

View File

@ -23,7 +23,6 @@ const ScreenMeRoot: React.FC<StackScreenProps<
>> = ({ route: { params }, navigation }) => { >> = ({ route: { params }, navigation }) => {
useEffect(() => { useEffect(() => {
if (params && params.navigateAway) { if (params && params.navigateAway) {
console.log('oops')
navigation.navigate(params.navigateAway) navigation.navigate(params.navigateAway)
} }
}, [params]) }, [params])

View File

@ -43,8 +43,8 @@ const AccountHeader: React.FC<Props> = ({ account, limitHeight = false }) => {
}, [account]) }, [account])
return ( return (
<Animated.Image <Animated.View
source={{ uri: account?.header }} // source={{ uri: account?.header }}
style={[styleHeight, { backgroundColor: theme.disabled }]} style={[styleHeight, { backgroundColor: theme.disabled }]}
/> />
) )

View File

@ -1,10 +1,11 @@
import Timeline from '@components/Timelines/Timeline' import Timeline from '@components/Timelines/Timeline'
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs' import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs'
import { StyleConstants } from '@utils/styles/constants' import { StyleConstants } from '@utils/styles/constants'
import React, { useContext } from 'react' import React, { useCallback, useContext } from 'react'
import { Dimensions, StyleSheet } from 'react-native' import { Dimensions, StyleSheet } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context' import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { TabView } from 'react-native-tab-view' import { TabView } from 'react-native-tab-view'
import ViewPagerAdapter from 'react-native-tab-view-viewpager-adapter'
import AccountContext from './utils/createContext' import AccountContext from './utils/createContext'
export interface Props { export interface Props {
@ -32,10 +33,13 @@ const AccountToots: React.FC<Props> = ({ id }) => {
return <Timeline page={route.key} account={id} disableRefresh /> return <Timeline page={route.key} account={id} disableRefresh />
} }
const renderPager = useCallback(props => <ViewPagerAdapter {...props} />, [])
return ( return (
<TabView <TabView
lazy lazy
swipeEnabled swipeEnabled
renderPager={renderPager}
renderScene={renderScene} renderScene={renderScene}
renderTabBar={() => null} renderTabBar={() => null}
initialLayout={{ width: Dimensions.get('window').width }} initialLayout={{ width: Dimensions.get('window').width }}

View File

@ -1,7 +1,6 @@
import { HeaderLeft, HeaderRight } from '@components/Header' import { HeaderLeft, HeaderRight } from '@components/Header'
import haptics from '@root/components/haptics' import haptics from '@root/components/haptics'
import { store } from '@root/store' import { store } from '@root/store'
import layoutAnimation from '@root/utils/styles/layoutAnimation'
import formatText from '@screens/Shared/Compose/formatText' import formatText from '@screens/Shared/Compose/formatText'
import ComposeRoot from '@screens/Shared/Compose/Root' import ComposeRoot from '@screens/Shared/Compose/Root'
import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
@ -58,11 +57,8 @@ const Compose: React.FC<SharedComposeProp> = ({
const localAccount = getLocalAccount(store.getState()) const localAccount = getLocalAccount(store.getState())
const [composeState, composeDispatch] = useReducer( const [composeState, composeDispatch] = useReducer(
composeReducer, composeReducer,
params?.type && params?.incomingStatus params
? composeParseState({ ? composeParseState(params)
type: params.type,
incomingStatus: params.incomingStatus
})
: { : {
...composeInitialState, ...composeInitialState,
visibility: visibility:
@ -72,7 +68,6 @@ const Compose: React.FC<SharedComposeProp> = ({
: 'public' : 'public'
} }
) )
const [isSubmitting, setIsSubmitting] = useState(false)
useEffect(() => { useEffect(() => {
switch (params?.type) { switch (params?.type) {
@ -163,8 +158,8 @@ const Compose: React.FC<SharedComposeProp> = ({
type='text' type='text'
content={params?.type ? postButtonText[params.type] : '发嘟嘟'} content={params?.type ? postButtonText[params.type] : '发嘟嘟'}
onPress={() => { onPress={() => {
layoutAnimation() composeDispatch({ type: 'posting', payload: true })
setIsSubmitting(true)
composePost(params, composeState) composePost(params, composeState)
.then(() => { .then(() => {
haptics('Success') haptics('Success')
@ -173,27 +168,15 @@ const Compose: React.FC<SharedComposeProp> = ({
{ page: 'Following' } { page: 'Following' }
] ]
queryClient.invalidateQueries(queryKey) queryClient.invalidateQueries(queryKey)
if (
params?.type && if (params?.queryKey && params.queryKey[1].page === 'Toot') {
(params.type === 'reply' || params.type === 'conversation') queryClient.invalidateQueries(params.queryKey)
) {
queryClient.invalidateQueries(
[
'Toot',
{
toot: params.incomingStatus.reblog
? params.incomingStatus.reblog.id
: params.incomingStatus.id
}
],
{ exact: true, active: true }
)
} }
navigation.goBack() navigation.goBack()
}) })
.catch(() => { .catch(() => {
haptics('Error') haptics('Error')
setIsSubmitting(false) composeDispatch({ type: 'posting', payload: false })
Alert.alert('发布失败', '', [ Alert.alert('发布失败', '', [
{ {
text: '返回重试' text: '返回重试'
@ -201,11 +184,11 @@ const Compose: React.FC<SharedComposeProp> = ({
]) ])
}) })
}} }}
loading={isSubmitting} loading={composeState.posting}
disabled={composeState.text.raw.length < 1 || totalTextCount > 500} disabled={composeState.text.raw.length < 1 || totalTextCount > 500}
/> />
), ),
[isSubmitting, totalTextCount, composeState] [totalTextCount, composeState]
) )
return ( return (

View File

@ -0,0 +1,43 @@
import React, { useContext } from 'react'
import { Modal, StyleSheet, View } from 'react-native'
import { useTheme } from '@utils/styles/ThemeManager'
import ComposeContext from './utils/createContext'
import { Chase } from 'react-native-animated-spinkit'
import { StyleConstants } from '@utils/styles/constants'
const ComposePosting = React.memo(
() => {
const { composeState } = useContext(ComposeContext)
const { theme } = useTheme()
return (
<Modal
transparent
animationType='fade'
visible={composeState.posting}
children={
<View
style={[styles.base, { backgroundColor: theme.backgroundOverlay }]}
children={
<Chase
size={StyleConstants.Font.Size.L * 2}
color={theme.primaryOverlay}
/>
}
/>
}
/>
)
},
() => true
)
const styles = StyleSheet.create({
base: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
})
export default ComposePosting

View File

@ -7,6 +7,7 @@ import React, { useCallback, useContext, useEffect, useMemo } from 'react'
import { View, FlatList, StyleSheet } from 'react-native' import { View, FlatList, StyleSheet } from 'react-native'
import { Chase } from 'react-native-animated-spinkit' import { Chase } from 'react-native-animated-spinkit'
import ComposeActions from './Actions' import ComposeActions from './Actions'
import ComposePosting from './Posting'
import ComposeRootFooter from './Root/Footer' import ComposeRootFooter from './Root/Footer'
import ComposeRootHeader from './Root/Header' import ComposeRootHeader from './Root/Header'
import ComposeRootSuggestion from './Root/Suggestion' import ComposeRootSuggestion from './Root/Suggestion'
@ -89,6 +90,7 @@ const ComposeRoot: React.FC = () => {
keyExtractor={({ item }) => item.acct || item.name} keyExtractor={({ item }) => item.acct || item.name}
/> />
<ComposeActions /> <ComposeActions />
<ComposePosting />
</View> </View>
) )
} }

View File

@ -1,7 +1,8 @@
import { createRef } from "react" import { createRef } from 'react'
import { ComposeState } from "./types" import { ComposeState } from './types'
const composeInitialState: ComposeState = { const composeInitialState: ComposeState = {
posting: false,
spoiler: { spoiler: {
active: false, active: false,
count: 0, count: 0,

View File

@ -1,10 +1,10 @@
import client from '@root/api/client' import client from '@root/api/client'
import { Props } from '@screens/Shared/Compose'
import { ComposeState } from '@screens/Shared/Compose/utils/types' import { ComposeState } from '@screens/Shared/Compose/utils/types'
import { SharedComposeProp } from '@screens/Shared/sharedScreens'
import * as Crypto from 'expo-crypto' import * as Crypto from 'expo-crypto'
const composePost = async ( const composePost = async (
params: Props['route']['params'], params: SharedComposeProp['route']['params'],
composeState: ComposeState composeState: ComposeState
) => { ) => {
const formData = new FormData() const formData = new FormData()
@ -43,7 +43,7 @@ const composePost = async (
return client<Mastodon.Status>({ return client<Mastodon.Status>({
method: 'post', method: 'post',
instance: 'local', instance: 'local',
url: 'statuses', url: 'statusess',
headers: { headers: {
'Idempotency-Key': await Crypto.digestStringAsync( 'Idempotency-Key': await Crypto.digestStringAsync(
Crypto.CryptoDigestAlgorithm.SHA256, Crypto.CryptoDigestAlgorithm.SHA256,

View File

@ -5,6 +5,8 @@ const composeReducer = (
action: ComposeAction action: ComposeAction
): ComposeState => { ): ComposeState => {
switch (action.type) { switch (action.type) {
case 'posting':
return { ...state, posting: action.payload }
case 'spoiler': case 'spoiler':
return { ...state, spoiler: { ...state.spoiler, ...action.payload } } return { ...state, spoiler: { ...state.spoiler, ...action.payload } }
case 'text': case 'text':

View File

@ -5,6 +5,7 @@ export type ExtendedAttachment = {
} }
export type ComposeState = { export type ComposeState = {
posting: boolean
spoiler: { spoiler: {
active: boolean active: boolean
count: number count: number
@ -62,6 +63,10 @@ export type ComposeState = {
} }
export type ComposeAction = export type ComposeAction =
| {
type: 'posting'
payload: ComposeState['posting']
}
| { | {
type: 'spoiler' type: 'spoiler'
payload: Partial<ComposeState['spoiler']> payload: Partial<ComposeState['spoiler']>

View File

@ -33,7 +33,7 @@ const ScreenSharedRelationships: React.FC<SharedRelationshipsProp> = ({
) )
}) })
return updateHeaderRight() return updateHeaderRight()
}, []) }, [segment, mode])
const routes: { const routes: {
key: SharedRelationshipsProp['route']['params']['initialType'] key: SharedRelationshipsProp['route']['params']['initialType']
@ -55,9 +55,9 @@ const ScreenSharedRelationships: React.FC<SharedRelationshipsProp> = ({
swipeEnabled swipeEnabled
renderScene={renderScene} renderScene={renderScene}
renderTabBar={() => null} renderTabBar={() => null}
initialLayout={{ width: Dimensions.get('window').width }}
navigationState={{ index: segment, routes }}
onIndexChange={index => setSegment(index)} onIndexChange={index => setSegment(index)}
navigationState={{ index: segment, routes }}
initialLayout={{ width: Dimensions.get('window').width }}
/> />
) )
} }

View File

@ -1727,6 +1727,11 @@
resolved "https://registry.yarnpkg.com/@react-native-community/segmented-control/-/segmented-control-2.2.1.tgz#5ca418d78c5f6051353c9586918458713b88a83c" resolved "https://registry.yarnpkg.com/@react-native-community/segmented-control/-/segmented-control-2.2.1.tgz#5ca418d78c5f6051353c9586918458713b88a83c"
integrity sha512-BzxFbI9Iqv+31yVqEvCTzJYmwb8jOMTf/UPuC4Hj176tmEPqBpuDaGH+rkAFg1miOco3/43RQxiAZO+mkY40Fg== integrity sha512-BzxFbI9Iqv+31yVqEvCTzJYmwb8jOMTf/UPuC4Hj176tmEPqBpuDaGH+rkAFg1miOco3/43RQxiAZO+mkY40Fg==
"@react-native-community/viewpager@4.2.0":
version "4.2.0"
resolved "https://registry.yarnpkg.com/@react-native-community/viewpager/-/viewpager-4.2.0.tgz#11a4453715eb45cb0f732c98666ce168b800dfa4"
integrity sha512-tptvkyStulE9Jv/LVYSldvydAq3DVOwsfqmy3mTh3NWl1LZwE4gZwZ455jiRmW5StsJ3Q/Od/GGsN0FI8gHAXQ==
"@react-navigation/bottom-tabs@^5.11.2": "@react-navigation/bottom-tabs@^5.11.2":
version "5.11.2" version "5.11.2"
resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-5.11.2.tgz#5b541612fcecdea2a5024a4028da35e4a727bde6" resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-5.11.2.tgz#5b541612fcecdea2a5024a4028da35e4a727bde6"
@ -8280,6 +8285,11 @@ react-native-svg@12.1.0:
css-select "^2.1.0" css-select "^2.1.0"
css-tree "^1.0.0-alpha.39" css-tree "^1.0.0-alpha.39"
react-native-tab-view-viewpager-adapter@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/react-native-tab-view-viewpager-adapter/-/react-native-tab-view-viewpager-adapter-1.1.0.tgz#d6e085ed1c91a13e714d87395d428f8afc2b3377"
integrity sha512-KLKw+Ay41xAl5F2eRscJd7DHN8JLx06E2lTQJrYAxtBcNKDKfrcYC3wmq7t72DAOBe/6niF0h44c/t/D/AoXsA==
react-native-tab-view@^2.15.2: react-native-tab-view@^2.15.2:
version "2.15.2" version "2.15.2"
resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-2.15.2.tgz#4bc7832d33a119306614efee667509672a7ee64e" resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-2.15.2.tgz#4bc7832d33a119306614efee667509672a7ee64e"