mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Focus animation working
This commit is contained in:
@ -40,6 +40,7 @@
|
|||||||
"react-native-safe-area-context": "3.1.4",
|
"react-native-safe-area-context": "3.1.4",
|
||||||
"react-native-screens": "~2.10.1",
|
"react-native-screens": "~2.10.1",
|
||||||
"react-native-shimmer-placeholder": "^2.0.6",
|
"react-native-shimmer-placeholder": "^2.0.6",
|
||||||
|
"react-native-svg": "12.1.0",
|
||||||
"react-native-toast-message": "^1.3.4",
|
"react-native-toast-message": "^1.3.4",
|
||||||
"react-native-webview": "10.7.0",
|
"react-native-webview": "10.7.0",
|
||||||
"react-navigation": "^4.4.3",
|
"react-navigation": "^4.4.3",
|
||||||
|
@ -4,9 +4,7 @@ import {
|
|||||||
Dimensions,
|
Dimensions,
|
||||||
Modal,
|
Modal,
|
||||||
PanResponder,
|
PanResponder,
|
||||||
Pressable,
|
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
Text,
|
|
||||||
View
|
View
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||||
|
@ -56,8 +56,10 @@ const ComposeActions: React.FC<Props> = ({
|
|||||||
size={24}
|
size={24}
|
||||||
color={
|
color={
|
||||||
postState.poll.active || postState.attachments.length >= 4
|
postState.poll.active || postState.attachments.length >= 4
|
||||||
? theme.secondary
|
? theme.disabled
|
||||||
: theme.primary
|
: postState.attachments.length
|
||||||
|
? theme.primary
|
||||||
|
: theme.secondary
|
||||||
}
|
}
|
||||||
onPress={async () => {
|
onPress={async () => {
|
||||||
if (!postState.poll.active && postState.attachments.length < 4) {
|
if (!postState.poll.active && postState.attachments.length < 4) {
|
||||||
@ -70,8 +72,10 @@ const ComposeActions: React.FC<Props> = ({
|
|||||||
size={24}
|
size={24}
|
||||||
color={
|
color={
|
||||||
postState.attachments.length || postState.attachmentUploadProgress
|
postState.attachments.length || postState.attachmentUploadProgress
|
||||||
? theme.secondary
|
? theme.disabled
|
||||||
: theme.primary
|
: postState.poll.active
|
||||||
|
? theme.primary
|
||||||
|
: theme.secondary
|
||||||
}
|
}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (
|
if (
|
||||||
@ -91,7 +95,7 @@ const ComposeActions: React.FC<Props> = ({
|
|||||||
<Feather
|
<Feather
|
||||||
name={getVisibilityIcon()}
|
name={getVisibilityIcon()}
|
||||||
size={24}
|
size={24}
|
||||||
color={theme.primary}
|
color={theme.secondary}
|
||||||
onPress={() =>
|
onPress={() =>
|
||||||
ActionSheetIOS.showActionSheetWithOptions(
|
ActionSheetIOS.showActionSheetWithOptions(
|
||||||
{
|
{
|
||||||
@ -120,7 +124,7 @@ const ComposeActions: React.FC<Props> = ({
|
|||||||
<Feather
|
<Feather
|
||||||
name='smile'
|
name='smile'
|
||||||
size={24}
|
size={24}
|
||||||
color={postState.emoji.emojis?.length ? theme.primary : theme.secondary}
|
color={postState.emoji.emojis?.length ? theme.secondary : theme.disabled}
|
||||||
{...(postState.emoji.emojis && {
|
{...(postState.emoji.emojis && {
|
||||||
onPress: () => {
|
onPress: () => {
|
||||||
if (postState.emoji.active) {
|
if (postState.emoji.active) {
|
||||||
@ -141,7 +145,7 @@ const ComposeActions: React.FC<Props> = ({
|
|||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.count,
|
styles.count,
|
||||||
{ color: postState.text.count < 0 ? theme.error : theme.primary }
|
{ color: postState.text.count < 0 ? theme.error : theme.secondary }
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{postState.text.count}
|
{postState.text.count}
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
import React, { Dispatch, useCallback } from 'react'
|
import React, { Dispatch, useCallback } from 'react'
|
||||||
import {
|
import { FlatList, Image, Pressable, StyleSheet, View } from 'react-native'
|
||||||
ActivityIndicator,
|
|
||||||
FlatList,
|
|
||||||
Image,
|
|
||||||
StyleSheet,
|
|
||||||
View
|
|
||||||
} from 'react-native'
|
|
||||||
import { Feather } from '@expo/vector-icons'
|
import { Feather } from '@expo/vector-icons'
|
||||||
|
|
||||||
import { PostAction, PostState } from '../Compose'
|
import { PostAction, PostState } from '../Compose'
|
||||||
import { StyleConstants } from 'src/utils/styles/constants'
|
import { StyleConstants } from 'src/utils/styles/constants'
|
||||||
|
import { useTheme } from 'src/utils/styles/ThemeManager'
|
||||||
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import ShimmerPlaceholder from 'react-native-shimmer-placeholder'
|
||||||
|
|
||||||
|
const DEFAULT_HEIGHT = 200
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
postState: PostState
|
postState: PostState
|
||||||
@ -17,57 +16,82 @@ export interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ComposeAttachments: React.FC<Props> = ({ postState, postDispatch }) => {
|
const ComposeAttachments: React.FC<Props> = ({ postState, postDispatch }) => {
|
||||||
const renderImage = useCallback(({ item, index }) => {
|
const { theme } = useTheme()
|
||||||
return (
|
const navigation = useNavigation()
|
||||||
<View key={index}>
|
|
||||||
<Image
|
|
||||||
style={[
|
|
||||||
styles.image,
|
|
||||||
{
|
|
||||||
width: (item.meta?.original?.aspect || 1) * 200
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
source={{ uri: item!.preview_url }}
|
|
||||||
/>
|
|
||||||
<Feather
|
|
||||||
name='edit'
|
|
||||||
size={24}
|
|
||||||
color='white'
|
|
||||||
style={styles.buttonEdit}
|
|
||||||
/>
|
|
||||||
<Feather
|
|
||||||
name='trash-2'
|
|
||||||
size={24}
|
|
||||||
color='white'
|
|
||||||
style={styles.buttonRemove}
|
|
||||||
onPress={() =>
|
|
||||||
postDispatch({
|
|
||||||
type: 'attachments',
|
|
||||||
payload: postState.attachments.filter(e => e.id !== item.id)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const listFooter = useCallback(() => {
|
const imageActions = ({
|
||||||
return postState.attachmentUploadProgress ? (
|
type,
|
||||||
<View
|
icon,
|
||||||
style={{
|
onPress
|
||||||
width: postState.attachmentUploadProgress.aspect * 200,
|
}: {
|
||||||
height: 200,
|
type: 'edit' | 'delete'
|
||||||
flex: 1,
|
icon: string
|
||||||
backgroundColor: 'gray',
|
onPress: () => void
|
||||||
marginLeft: StyleConstants.Spacing.Global.PagePadding,
|
}) => {
|
||||||
marginTop: StyleConstants.Spacing.Global.PagePadding,
|
return (
|
||||||
marginBottom: StyleConstants.Spacing.Global.PagePadding
|
<Pressable
|
||||||
}}
|
style={[
|
||||||
|
styles.actions,
|
||||||
|
styles[type],
|
||||||
|
{ backgroundColor: theme.backgroundOverlay }
|
||||||
|
]}
|
||||||
|
onPress={onPress}
|
||||||
>
|
>
|
||||||
<ActivityIndicator />
|
<Feather
|
||||||
</View>
|
name={icon}
|
||||||
) : null
|
size={StyleConstants.Font.Size.L}
|
||||||
}, [postState.attachmentUploadProgress])
|
color={theme.primaryOverlay}
|
||||||
|
/>
|
||||||
|
</Pressable>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderImage = useCallback(
|
||||||
|
({
|
||||||
|
item,
|
||||||
|
index
|
||||||
|
}: {
|
||||||
|
item: Mastodon.Attachment & { local_url?: string }
|
||||||
|
index: number
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<View key={index}>
|
||||||
|
<Image
|
||||||
|
style={[
|
||||||
|
styles.image,
|
||||||
|
{
|
||||||
|
width: (item.meta?.original?.aspect || 1) * DEFAULT_HEIGHT
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
source={{
|
||||||
|
uri:
|
||||||
|
item.type === 'image'
|
||||||
|
? item.local_url || item.preview_url
|
||||||
|
: item.preview_url
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{imageActions({
|
||||||
|
type: 'delete',
|
||||||
|
icon: 'x',
|
||||||
|
onPress: () =>
|
||||||
|
postDispatch({
|
||||||
|
type: 'attachments',
|
||||||
|
payload: postState.attachments.filter(e => e.id !== item.id)
|
||||||
|
})
|
||||||
|
})}
|
||||||
|
{imageActions({
|
||||||
|
type: 'edit',
|
||||||
|
icon: 'edit',
|
||||||
|
onPress: () =>
|
||||||
|
navigation.navigate('Screen-Shared-Compose-EditAttachment', {
|
||||||
|
attachment: item
|
||||||
|
})
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.base}>
|
<View style={styles.base}>
|
||||||
@ -75,7 +99,18 @@ const ComposeAttachments: React.FC<Props> = ({ postState, postDispatch }) => {
|
|||||||
horizontal
|
horizontal
|
||||||
data={postState.attachments}
|
data={postState.attachments}
|
||||||
renderItem={renderImage}
|
renderItem={renderImage}
|
||||||
ListFooterComponent={listFooter}
|
ListFooterComponent={
|
||||||
|
<ShimmerPlaceholder
|
||||||
|
style={styles.progressContainer}
|
||||||
|
visible={postState.attachmentUploadProgress === undefined}
|
||||||
|
width={
|
||||||
|
(postState.attachmentUploadProgress?.aspect || 16 / 9) *
|
||||||
|
DEFAULT_HEIGHT
|
||||||
|
}
|
||||||
|
height={200}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
showsHorizontalScrollIndicator={false}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
@ -86,7 +121,7 @@ const styles = StyleSheet.create({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
marginRight: StyleConstants.Spacing.Global.PagePadding,
|
marginRight: StyleConstants.Spacing.Global.PagePadding,
|
||||||
height: 200
|
height: DEFAULT_HEIGHT
|
||||||
},
|
},
|
||||||
image: {
|
image: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@ -94,15 +129,28 @@ const styles = StyleSheet.create({
|
|||||||
marginTop: StyleConstants.Spacing.Global.PagePadding,
|
marginTop: StyleConstants.Spacing.Global.PagePadding,
|
||||||
marginBottom: StyleConstants.Spacing.Global.PagePadding
|
marginBottom: StyleConstants.Spacing.Global.PagePadding
|
||||||
},
|
},
|
||||||
buttonEdit: {
|
actions: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 0,
|
padding: StyleConstants.Spacing.S * 1.5,
|
||||||
left: 0
|
borderRadius: StyleConstants.Spacing.XL
|
||||||
},
|
},
|
||||||
buttonRemove: {
|
edit: {
|
||||||
position: 'absolute',
|
bottom:
|
||||||
top: 0,
|
StyleConstants.Spacing.Global.PagePadding + StyleConstants.Spacing.S,
|
||||||
right: 0
|
right: StyleConstants.Spacing.S
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
top: StyleConstants.Spacing.Global.PagePadding + StyleConstants.Spacing.S,
|
||||||
|
right: StyleConstants.Spacing.S
|
||||||
|
},
|
||||||
|
progressContainer: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: DEFAULT_HEIGHT,
|
||||||
|
marginLeft: StyleConstants.Spacing.Global.PagePadding,
|
||||||
|
marginTop: StyleConstants.Spacing.Global.PagePadding,
|
||||||
|
marginBottom: StyleConstants.Spacing.Global.PagePadding
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
218
src/screens/Shared/Compose/EditAttachment.tsx
Normal file
218
src/screens/Shared/Compose/EditAttachment.tsx
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import React, { useCallback, useMemo, useRef, useState } from 'react'
|
||||||
|
import {
|
||||||
|
Animated,
|
||||||
|
Dimensions,
|
||||||
|
Image,
|
||||||
|
KeyboardAvoidingView,
|
||||||
|
PanResponder,
|
||||||
|
ScrollView,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
View
|
||||||
|
} from 'react-native'
|
||||||
|
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||||
|
import Svg, { Circle, G, Path } from 'react-native-svg'
|
||||||
|
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
||||||
|
|
||||||
|
import { HeaderLeft, HeaderRight } from 'src/components/Header'
|
||||||
|
import { StyleConstants } from 'src/utils/styles/constants'
|
||||||
|
import { useTheme } from 'src/utils/styles/ThemeManager'
|
||||||
|
import { PanGestureHandler } from 'react-native-gesture-handler'
|
||||||
|
|
||||||
|
const Stack = createNativeStackNavigator()
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
route: {
|
||||||
|
params: {
|
||||||
|
attachment: Mastodon.Attachment & { local_url: string }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ComposeEditAttachment: React.FC<Props> = ({
|
||||||
|
route: {
|
||||||
|
params: { attachment }
|
||||||
|
}
|
||||||
|
}) => {
|
||||||
|
const navigation = useNavigation()
|
||||||
|
const { theme } = useTheme()
|
||||||
|
|
||||||
|
const [altText, setAltText] = useState<string | undefined>()
|
||||||
|
|
||||||
|
const imageFocus = useCallback(() => {
|
||||||
|
const imageDimensionis = {
|
||||||
|
width: Dimensions.get('screen').width,
|
||||||
|
height:
|
||||||
|
Dimensions.get('screen').width / attachment.meta?.original?.aspect!
|
||||||
|
}
|
||||||
|
|
||||||
|
const panFocus = useRef(new Animated.ValueXY({ x: 0, y: 0 })).current
|
||||||
|
const panX = panFocus.x.interpolate({
|
||||||
|
inputRange: [-imageDimensionis.width / 2, imageDimensionis.width / 2],
|
||||||
|
outputRange: [-imageDimensionis.width / 2, imageDimensionis.width / 2],
|
||||||
|
extrapolate: 'clamp'
|
||||||
|
})
|
||||||
|
const panY = panFocus.y.interpolate({
|
||||||
|
inputRange: [-imageDimensionis.height / 2, imageDimensionis.height / 2],
|
||||||
|
outputRange: [-imageDimensionis.height / 2, imageDimensionis.height / 2],
|
||||||
|
extrapolate: 'clamp'
|
||||||
|
})
|
||||||
|
const handleGesture = Animated.event(
|
||||||
|
[{ nativeEvent: { translationX: panFocus.x, translationY: panFocus.y } }],
|
||||||
|
{ useNativeDriver: true }
|
||||||
|
)
|
||||||
|
const onHandlerStateChange = useCallback(() => {
|
||||||
|
panFocus.extractOffset()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={{ overflow: 'hidden' }}>
|
||||||
|
<Image
|
||||||
|
style={{
|
||||||
|
width: imageDimensionis.width,
|
||||||
|
height: imageDimensionis.height
|
||||||
|
}}
|
||||||
|
source={{
|
||||||
|
uri: attachment.local_url || attachment.preview_url
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<PanGestureHandler
|
||||||
|
onGestureEvent={handleGesture}
|
||||||
|
onHandlerStateChange={onHandlerStateChange}
|
||||||
|
>
|
||||||
|
<Animated.View
|
||||||
|
style={[
|
||||||
|
{
|
||||||
|
position: 'absolute',
|
||||||
|
top: -500 + imageDimensionis.height / 2,
|
||||||
|
left: -500 + imageDimensionis.width / 2,
|
||||||
|
transform: [{ translateX: panX }, { translateY: panY }]
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Svg width='1000px' height='1000px' viewBox='0 0 1000 1000'>
|
||||||
|
<G>
|
||||||
|
<G id='Mask'>
|
||||||
|
<Path
|
||||||
|
d={
|
||||||
|
'M1000,0 L1000,1000 L0,1000 L0,0 L1000,0 Z M500,467 C481.774603,467 467,481.774603 467,500 C467,518.225397 481.774603,533 500,533 C518.225397,533 533,518.225397 533,500 C533,481.774603 518.225397,467 500,467 Z'
|
||||||
|
}
|
||||||
|
fillOpacity='0.35'
|
||||||
|
fill='#000000'
|
||||||
|
/>
|
||||||
|
<G transform='translate(467.000000, 467.000000)'>
|
||||||
|
<Circle
|
||||||
|
stroke='#FFFFFF'
|
||||||
|
strokeWidth='2'
|
||||||
|
cx='33'
|
||||||
|
cy='33'
|
||||||
|
r='33'
|
||||||
|
/>
|
||||||
|
<Circle fill='#FFFFFF' cx='33' cy='33' r='2' />
|
||||||
|
</G>
|
||||||
|
</G>
|
||||||
|
</G>
|
||||||
|
</Svg>
|
||||||
|
</Animated.View>
|
||||||
|
</PanGestureHandler>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const altTextInput = useCallback(() => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Text style={[styles.altTextInputHeading, { color: theme.primary }]}>
|
||||||
|
为附件添加文字说明
|
||||||
|
</Text>
|
||||||
|
<TextInput
|
||||||
|
style={[styles.altTextInput, { borderColor: theme.border }]}
|
||||||
|
autoCapitalize='none'
|
||||||
|
autoCorrect={false}
|
||||||
|
maxLength={1500}
|
||||||
|
multiline
|
||||||
|
onChangeText={e => setAltText(e)}
|
||||||
|
placeholder={
|
||||||
|
'你可以为附件添加文字说明,以便更多人可以查看他们(包括视力障碍或视力受损人士)。\n\n优质的描述应该简洁明了,但要准确地描述照片中的内容,以便用户理解其含义。'
|
||||||
|
}
|
||||||
|
placeholderTextColor={theme.secondary}
|
||||||
|
scrollEnabled
|
||||||
|
/>
|
||||||
|
<Text style={[styles.altTextLength, { color: theme.secondary }]}>
|
||||||
|
{1500 - (altText?.length || 0)}
|
||||||
|
</Text>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}, [altText?.length])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<KeyboardAvoidingView behavior='padding' style={{ flex: 1 }}>
|
||||||
|
<SafeAreaView style={{ flex: 1 }} edges={['right', 'bottom', 'left']}>
|
||||||
|
<Stack.Navigator>
|
||||||
|
<Stack.Screen
|
||||||
|
name='Screen-Shared-Compose-EditAttachment-Root'
|
||||||
|
options={{
|
||||||
|
title: '编辑附件',
|
||||||
|
headerLeft: () => (
|
||||||
|
<HeaderLeft text='取消' onPress={() => navigation.goBack()} />
|
||||||
|
),
|
||||||
|
headerRight: () => <HeaderRight text='应用' onPress={() => {}} />
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{() => {
|
||||||
|
switch (attachment.type) {
|
||||||
|
case 'image':
|
||||||
|
return (
|
||||||
|
<View style={{ flex: 1 }}>
|
||||||
|
{imageFocus()}
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.imageFocusText,
|
||||||
|
{ color: theme.primary }
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
在预览图上点击或拖动圆圈,以选择缩略图的焦点。
|
||||||
|
</Text>
|
||||||
|
<View style={styles.editContainer}>{altTextInput()}</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}}
|
||||||
|
</Stack.Screen>
|
||||||
|
</Stack.Navigator>
|
||||||
|
</SafeAreaView>
|
||||||
|
</KeyboardAvoidingView>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
editContainer: { padding: StyleConstants.Spacing.Global.PagePadding },
|
||||||
|
imageFocusText: {
|
||||||
|
fontSize: StyleConstants.Font.Size.M
|
||||||
|
},
|
||||||
|
altTextInputHeading: {
|
||||||
|
fontSize: StyleConstants.Font.Size.M,
|
||||||
|
fontWeight: StyleConstants.Font.Weight.Bold,
|
||||||
|
marginTop: StyleConstants.Spacing.XL
|
||||||
|
},
|
||||||
|
altTextInput: {
|
||||||
|
height: 200,
|
||||||
|
fontSize: StyleConstants.Font.Size.M,
|
||||||
|
marginTop: StyleConstants.Spacing.M,
|
||||||
|
marginBottom: StyleConstants.Spacing.S,
|
||||||
|
padding: StyleConstants.Spacing.Global.PagePadding,
|
||||||
|
paddingTop: StyleConstants.Spacing.S * 1.5,
|
||||||
|
borderWidth: StyleSheet.hairlineWidth
|
||||||
|
},
|
||||||
|
altTextLength: {
|
||||||
|
textAlign: 'right',
|
||||||
|
marginRight: StyleConstants.Spacing.S,
|
||||||
|
fontSize: StyleConstants.Font.Size.S,
|
||||||
|
marginBottom: StyleConstants.Spacing.M
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default ComposeEditAttachment
|
@ -6,6 +6,7 @@ import {
|
|||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
FlatList,
|
FlatList,
|
||||||
Pressable,
|
Pressable,
|
||||||
|
ProgressViewIOS,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
@ -112,6 +113,10 @@ const ComposeRoot: React.FC<Props> = ({ postState, postDispatch }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.base}>
|
<View style={styles.base}>
|
||||||
|
<ProgressViewIOS
|
||||||
|
progress={postState.attachmentUploadProgress?.progress || 0}
|
||||||
|
progressViewStyle='bar'
|
||||||
|
/>
|
||||||
<FlatList
|
<FlatList
|
||||||
ListHeaderComponent={
|
ListHeaderComponent={
|
||||||
<ComposeTextInput
|
<ComposeTextInput
|
||||||
|
@ -39,12 +39,13 @@ const uploadAttachment = async ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(({ body }: { body: Mastodon.Attachment }) => {
|
.then(({ body }: { body: Mastodon.Attachment & { local_url: string } }) => {
|
||||||
postDispatch({
|
postDispatch({
|
||||||
type: 'attachmentUploadProgress',
|
type: 'attachmentUploadProgress',
|
||||||
payload: undefined
|
payload: undefined
|
||||||
})
|
})
|
||||||
if (body.id) {
|
if (body.id) {
|
||||||
|
body.local_url = result.uri
|
||||||
postDispatch({
|
postDispatch({
|
||||||
type: 'attachments',
|
type: 'attachments',
|
||||||
payload: postState.attachments.concat([body])
|
payload: postState.attachments.concat([body])
|
||||||
|
@ -5,6 +5,7 @@ import ScreenSharedHashtag from 'src/screens/Shared/Hashtag'
|
|||||||
import ScreenSharedToot from 'src/screens/Shared/Toot'
|
import ScreenSharedToot from 'src/screens/Shared/Toot'
|
||||||
import ScreenSharedWebview from 'src/screens/Shared/Webview'
|
import ScreenSharedWebview from 'src/screens/Shared/Webview'
|
||||||
import Compose from 'src/screens/Shared/Compose'
|
import Compose from 'src/screens/Shared/Compose'
|
||||||
|
import ComposeEditAttachment from './Compose/EditAttachment'
|
||||||
import ScreenSharedSearch from './Search'
|
import ScreenSharedSearch from './Search'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
@ -54,6 +55,14 @@ const sharedScreens = (Stack: any) => {
|
|||||||
stackPresentation: 'fullScreenModal'
|
stackPresentation: 'fullScreenModal'
|
||||||
}}
|
}}
|
||||||
/>,
|
/>,
|
||||||
|
<Stack.Screen
|
||||||
|
key='Screen-Shared-Compose-EditAttachment'
|
||||||
|
name='Screen-Shared-Compose-EditAttachment'
|
||||||
|
component={ComposeEditAttachment}
|
||||||
|
options={{
|
||||||
|
stackPresentation: 'fullScreenModal'
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
key='Screen-Shared-Search'
|
key='Screen-Shared-Search'
|
||||||
name='Screen-Shared-Search'
|
name='Screen-Shared-Search'
|
||||||
|
57
yarn.lock
57
yarn.lock
@ -1875,6 +1875,11 @@ blueimp-md5@^2.10.0:
|
|||||||
resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.18.0.tgz#1152be1335f0c6b3911ed9e36db54f3e6ac52935"
|
resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.18.0.tgz#1152be1335f0c6b3911ed9e36db54f3e6ac52935"
|
||||||
integrity sha512-vE52okJvzsVWhcgUHOv+69OG3Mdg151xyn41aVQN/5W5S+S43qZhxECtYLAEHMSFWX6Mv5IZrzj3T5+JqXfj5Q==
|
integrity sha512-vE52okJvzsVWhcgUHOv+69OG3Mdg151xyn41aVQN/5W5S+S43qZhxECtYLAEHMSFWX6Mv5IZrzj3T5+JqXfj5Q==
|
||||||
|
|
||||||
|
boolbase@^1.0.0, boolbase@~1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||||
|
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
|
||||||
|
|
||||||
bplist-creator@0.0.8:
|
bplist-creator@0.0.8:
|
||||||
version "0.0.8"
|
version "0.0.8"
|
||||||
resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.0.8.tgz#56b2a6e79e9aec3fc33bf831d09347d73794e79c"
|
resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.0.8.tgz#56b2a6e79e9aec3fc33bf831d09347d73794e79c"
|
||||||
@ -2320,6 +2325,29 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5:
|
|||||||
shebang-command "^1.2.0"
|
shebang-command "^1.2.0"
|
||||||
which "^1.2.9"
|
which "^1.2.9"
|
||||||
|
|
||||||
|
css-select@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef"
|
||||||
|
integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==
|
||||||
|
dependencies:
|
||||||
|
boolbase "^1.0.0"
|
||||||
|
css-what "^3.2.1"
|
||||||
|
domutils "^1.7.0"
|
||||||
|
nth-check "^1.0.2"
|
||||||
|
|
||||||
|
css-tree@^1.0.0-alpha.39:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.2.tgz#9ae393b5dafd7dae8a622475caec78d3d8fbd7b5"
|
||||||
|
integrity sha512-wCoWush5Aeo48GLhfHPbmvZs59Z+M7k5+B1xDnXbdWNcEF423DoFdqSWE0PM5aNk5nI5cp1q7ms36zGApY/sKQ==
|
||||||
|
dependencies:
|
||||||
|
mdn-data "2.0.14"
|
||||||
|
source-map "^0.6.1"
|
||||||
|
|
||||||
|
css-what@^3.2.1:
|
||||||
|
version "3.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4"
|
||||||
|
integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==
|
||||||
|
|
||||||
csstype@^3.0.2:
|
csstype@^3.0.2:
|
||||||
version "3.0.5"
|
version "3.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.5.tgz#7fdec6a28a67ae18647c51668a9ff95bb2fa7bb8"
|
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.5.tgz#7fdec6a28a67ae18647c51668a9ff95bb2fa7bb8"
|
||||||
@ -2455,7 +2483,7 @@ domhandler@^2.3.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
domelementtype "1"
|
domelementtype "1"
|
||||||
|
|
||||||
domutils@^1.5.1:
|
domutils@^1.5.1, domutils@^1.7.0:
|
||||||
version "1.7.0"
|
version "1.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
|
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
|
||||||
integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
|
integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
|
||||||
@ -3886,11 +3914,6 @@ klaw@^1.0.0:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
graceful-fs "^4.1.9"
|
graceful-fs "^4.1.9"
|
||||||
|
|
||||||
ky@^0.24.0:
|
|
||||||
version "0.24.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/ky/-/ky-0.24.0.tgz#337e534a7f47c12476988eef3cb968daef318349"
|
|
||||||
integrity sha512-/vpuQguwV30jErrqLpoaU/YJAFALrUkqqWLILnSoBOj5/O/LKzro/pPNtxbLgY6m4w5XNM6YZ3v7/or8qLlFuw==
|
|
||||||
|
|
||||||
leven@^3.1.0:
|
leven@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
|
resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
|
||||||
@ -3994,6 +4017,11 @@ md5-file@^3.2.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
buffer-alloc "^1.1.0"
|
buffer-alloc "^1.1.0"
|
||||||
|
|
||||||
|
mdn-data@2.0.14:
|
||||||
|
version "2.0.14"
|
||||||
|
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
|
||||||
|
integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
|
||||||
|
|
||||||
merge-stream@^1.0.1:
|
merge-stream@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1"
|
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1"
|
||||||
@ -4541,6 +4569,13 @@ npm-run-path@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
path-key "^2.0.0"
|
path-key "^2.0.0"
|
||||||
|
|
||||||
|
nth-check@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
|
||||||
|
integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
|
||||||
|
dependencies:
|
||||||
|
boolbase "~1.0.0"
|
||||||
|
|
||||||
nullthrows@^1.1.0, nullthrows@^1.1.1:
|
nullthrows@^1.1.0, nullthrows@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1"
|
resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1"
|
||||||
@ -5068,6 +5103,14 @@ react-native-shimmer-placeholder@^2.0.6:
|
|||||||
resolved "https://registry.yarnpkg.com/react-native-shimmer-placeholder/-/react-native-shimmer-placeholder-2.0.6.tgz#a6626d955945edb1aa01f8863f3e039a738d53d1"
|
resolved "https://registry.yarnpkg.com/react-native-shimmer-placeholder/-/react-native-shimmer-placeholder-2.0.6.tgz#a6626d955945edb1aa01f8863f3e039a738d53d1"
|
||||||
integrity sha512-eq0Jxi/j/WseijfSeNjoAsaz1164XUCDvKpG/+My+c5YeVMfjTnl9SwoVAIr9uOpuDXXia67j8xME+eFJZvBXw==
|
integrity sha512-eq0Jxi/j/WseijfSeNjoAsaz1164XUCDvKpG/+My+c5YeVMfjTnl9SwoVAIr9uOpuDXXia67j8xME+eFJZvBXw==
|
||||||
|
|
||||||
|
react-native-svg@12.1.0:
|
||||||
|
version "12.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-12.1.0.tgz#acfe48c35cd5fca3d5fd767abae0560c36cfc03d"
|
||||||
|
integrity sha512-1g9qBRci7man8QsHoXn6tP3DhCDiypGgc6+AOWq+Sy+PmP6yiyf8VmvKuoqrPam/tf5x+ZaBT2KI0gl7bptZ7w==
|
||||||
|
dependencies:
|
||||||
|
css-select "^2.1.0"
|
||||||
|
css-tree "^1.0.0-alpha.39"
|
||||||
|
|
||||||
react-native-toast-message@^1.3.4:
|
react-native-toast-message@^1.3.4:
|
||||||
version "1.3.6"
|
version "1.3.6"
|
||||||
resolved "https://registry.yarnpkg.com/react-native-toast-message/-/react-native-toast-message-1.3.6.tgz#78f90f78bbd8c97ce987f5dabc99ecc4a2d5eaef"
|
resolved "https://registry.yarnpkg.com/react-native-toast-message/-/react-native-toast-message-1.3.6.tgz#78f90f78bbd8c97ce987f5dabc99ecc4a2d5eaef"
|
||||||
@ -5655,7 +5698,7 @@ source-map@^0.5.0, source-map@^0.5.6:
|
|||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||||
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
|
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
|
||||||
|
|
||||||
source-map@^0.6.0, source-map@~0.6.1:
|
source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
|
||||||
version "0.6.1"
|
version "0.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||||
|
Reference in New Issue
Block a user