mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Able to edit image as attachment
This commit is contained in:
217
src/@types/mastodon.d.ts
vendored
217
src/@types/mastodon.d.ts
vendored
@ -1,3 +1,137 @@
|
|||||||
|
type AttachmentImage = {
|
||||||
|
// Base
|
||||||
|
id: string
|
||||||
|
type: 'image'
|
||||||
|
url: string
|
||||||
|
preview_url: string
|
||||||
|
|
||||||
|
// Others
|
||||||
|
remote_url?: string
|
||||||
|
text_url?: string
|
||||||
|
meta?: {
|
||||||
|
original?: { width: number; height: number; size: string; aspect: number }
|
||||||
|
small?: { width: number; height: number; size: string; aspect: number }
|
||||||
|
focus?: { x: number; y: number }
|
||||||
|
}
|
||||||
|
description?: string
|
||||||
|
blurhash?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttachmentVideo = {
|
||||||
|
// Base
|
||||||
|
id: string
|
||||||
|
type: 'video'
|
||||||
|
url: string
|
||||||
|
preview_url: string
|
||||||
|
|
||||||
|
// Others
|
||||||
|
remote_url?: string
|
||||||
|
text_url?: string
|
||||||
|
meta?: {
|
||||||
|
length: string
|
||||||
|
duration: number
|
||||||
|
fps: number
|
||||||
|
size: string
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
aspect: number
|
||||||
|
audio_encode: string
|
||||||
|
audio_bitrate: string
|
||||||
|
audio_channels: string
|
||||||
|
original: {
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
frame_rate: string
|
||||||
|
duration: number
|
||||||
|
bitrate: number
|
||||||
|
}
|
||||||
|
small: {
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
size: string
|
||||||
|
aspect: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
description?: string
|
||||||
|
blurhash?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttachmentGifv = {
|
||||||
|
// Base
|
||||||
|
id: string
|
||||||
|
type: 'gifv'
|
||||||
|
url: string
|
||||||
|
preview_url: string
|
||||||
|
|
||||||
|
// Others
|
||||||
|
remote_url?: string
|
||||||
|
text_url?: string
|
||||||
|
meta?: {
|
||||||
|
length: string
|
||||||
|
duration: number
|
||||||
|
fps: number
|
||||||
|
size: string
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
aspect: number
|
||||||
|
original: {
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
frame_rate: string
|
||||||
|
duration: number
|
||||||
|
bitrate: number
|
||||||
|
}
|
||||||
|
small: {
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
size: string
|
||||||
|
aspect: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
description?: string
|
||||||
|
blurhash?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttachmentAudio = {
|
||||||
|
// Base
|
||||||
|
id: string
|
||||||
|
type: 'audio'
|
||||||
|
url: string
|
||||||
|
preview_url: string
|
||||||
|
|
||||||
|
// Others
|
||||||
|
remote_url?: string
|
||||||
|
text_url?: string
|
||||||
|
meta?: {
|
||||||
|
length: string
|
||||||
|
duration: number
|
||||||
|
audio_encode: string
|
||||||
|
audio_bitrate: string
|
||||||
|
audio_channels: string
|
||||||
|
original: {
|
||||||
|
duration: number
|
||||||
|
bitrate: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
description?: string
|
||||||
|
blurhash?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttachmentUnknown = {
|
||||||
|
// Base
|
||||||
|
id: string
|
||||||
|
type: 'unknown'
|
||||||
|
url: string
|
||||||
|
preview_url: string
|
||||||
|
|
||||||
|
// Others
|
||||||
|
remote_url?: string
|
||||||
|
text_url?: string
|
||||||
|
meta?: any
|
||||||
|
description?: string
|
||||||
|
blurhash?: string
|
||||||
|
}
|
||||||
|
|
||||||
declare namespace Mastodon {
|
declare namespace Mastodon {
|
||||||
type Account = {
|
type Account = {
|
||||||
// Base
|
// Base
|
||||||
@ -38,83 +172,12 @@ declare namespace Mastodon {
|
|||||||
vapid_key?: string
|
vapid_key?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Attachment = {
|
type Attachment =
|
||||||
// Base
|
| AttachmentImage
|
||||||
id: string
|
| AttachmentVideo
|
||||||
type: 'unknown' | 'image' | 'gifv' | 'video' | 'audio'
|
| AttachmentGifv
|
||||||
url: string
|
| AttachmentAudio
|
||||||
preview_url?: string
|
| AttachmentUnknown
|
||||||
|
|
||||||
// Others
|
|
||||||
remote_url?: string
|
|
||||||
text_url?: string
|
|
||||||
meta?: {
|
|
||||||
original?: { width: number; height: number; size: string; aspect: number }
|
|
||||||
small?: { width: number; height: number; size: string; aspect: number }
|
|
||||||
focus?:
|
|
||||||
| { x: number; y: number }
|
|
||||||
| {
|
|
||||||
length: string
|
|
||||||
duration: number
|
|
||||||
fps: number
|
|
||||||
size: string
|
|
||||||
width: number
|
|
||||||
height: number
|
|
||||||
aspect: number
|
|
||||||
audio_encode: string
|
|
||||||
audio_bitrate: string
|
|
||||||
audio_channels: string
|
|
||||||
original: {
|
|
||||||
width: number
|
|
||||||
height: number
|
|
||||||
frame_rate: string
|
|
||||||
duration: number
|
|
||||||
bitrate: number
|
|
||||||
}
|
|
||||||
small: {
|
|
||||||
width: number
|
|
||||||
height: number
|
|
||||||
size: string
|
|
||||||
aspect: number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
length: string
|
|
||||||
duration: number
|
|
||||||
fps: number
|
|
||||||
size: string
|
|
||||||
width: number
|
|
||||||
height: number
|
|
||||||
aspect: number
|
|
||||||
original: {
|
|
||||||
width: number
|
|
||||||
height: number
|
|
||||||
frame_rate: string
|
|
||||||
duration: number
|
|
||||||
bitrate: number
|
|
||||||
}
|
|
||||||
small: {
|
|
||||||
width: number
|
|
||||||
height: number
|
|
||||||
size: string
|
|
||||||
aspect: number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
length: string
|
|
||||||
duration: number
|
|
||||||
audio_encode: string
|
|
||||||
audio_bitrate: string
|
|
||||||
audio_channels: string
|
|
||||||
original: {
|
|
||||||
duration: number
|
|
||||||
bitrate: number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
description?: string
|
|
||||||
blurhash?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Card = {
|
type Card = {
|
||||||
// Base
|
// Base
|
||||||
|
@ -84,6 +84,10 @@ export type PostAction =
|
|||||||
type: 'attachmentUploadProgress'
|
type: 'attachmentUploadProgress'
|
||||||
payload: PostState['attachmentUploadProgress']
|
payload: PostState['attachmentUploadProgress']
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
type: 'attachmentEdit'
|
||||||
|
payload: Mastodon.Attachment & { local_url?: string }
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
type: 'visibility'
|
type: 'visibility'
|
||||||
payload: PostState['visibility']
|
payload: PostState['visibility']
|
||||||
@ -133,6 +137,13 @@ const postReducer = (state: PostState, action: PostAction): PostState => {
|
|||||||
return { ...state, attachments: action.payload }
|
return { ...state, attachments: action.payload }
|
||||||
case 'attachmentUploadProgress':
|
case 'attachmentUploadProgress':
|
||||||
return { ...state, attachmentUploadProgress: action.payload }
|
return { ...state, attachmentUploadProgress: action.payload }
|
||||||
|
case 'attachmentEdit':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
attachments: state.attachments.map(attachment =>
|
||||||
|
attachment.id === action.payload.id ? action.payload : attachment
|
||||||
|
)
|
||||||
|
}
|
||||||
case 'visibility':
|
case 'visibility':
|
||||||
return { ...state, visibility: action.payload }
|
return { ...state, visibility: action.payload }
|
||||||
default:
|
default:
|
||||||
|
@ -84,7 +84,8 @@ const ComposeAttachments: React.FC<Props> = ({ postState, postDispatch }) => {
|
|||||||
icon: 'edit',
|
icon: 'edit',
|
||||||
onPress: () =>
|
onPress: () =>
|
||||||
navigation.navigate('Screen-Shared-Compose-EditAttachment', {
|
navigation.navigate('Screen-Shared-Compose-EditAttachment', {
|
||||||
attachment: item
|
attachment: item,
|
||||||
|
postDispatch
|
||||||
})
|
})
|
||||||
})}
|
})}
|
||||||
</View>
|
</View>
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import React, { useCallback, useMemo, useRef, useState } from 'react'
|
import React, {
|
||||||
|
Dispatch,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useState
|
||||||
|
} from 'react'
|
||||||
import {
|
import {
|
||||||
|
Alert,
|
||||||
Animated,
|
Animated,
|
||||||
Dimensions,
|
Dimensions,
|
||||||
Image,
|
Image,
|
||||||
KeyboardAvoidingView,
|
KeyboardAvoidingView,
|
||||||
PanResponder,
|
|
||||||
ScrollView,
|
ScrollView,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
Text,
|
Text,
|
||||||
@ -20,6 +26,8 @@ import { HeaderLeft, HeaderRight } from 'src/components/Header'
|
|||||||
import { StyleConstants } from 'src/utils/styles/constants'
|
import { StyleConstants } from 'src/utils/styles/constants'
|
||||||
import { useTheme } from 'src/utils/styles/ThemeManager'
|
import { useTheme } from 'src/utils/styles/ThemeManager'
|
||||||
import { PanGestureHandler } from 'react-native-gesture-handler'
|
import { PanGestureHandler } from 'react-native-gesture-handler'
|
||||||
|
import { PostAction } from '../Compose'
|
||||||
|
import client from 'src/api/client'
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator()
|
const Stack = createNativeStackNavigator()
|
||||||
|
|
||||||
@ -27,28 +35,63 @@ export interface Props {
|
|||||||
route: {
|
route: {
|
||||||
params: {
|
params: {
|
||||||
attachment: Mastodon.Attachment & { local_url: string }
|
attachment: Mastodon.Attachment & { local_url: string }
|
||||||
|
postDispatch: Dispatch<PostAction>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ComposeEditAttachment: React.FC<Props> = ({
|
const ComposeEditAttachment: React.FC<Props> = ({
|
||||||
route: {
|
route: {
|
||||||
params: { attachment }
|
params: { attachment, postDispatch }
|
||||||
}
|
}
|
||||||
}) => {
|
}) => {
|
||||||
|
const imageDimensionis = {
|
||||||
|
width: Dimensions.get('screen').width,
|
||||||
|
height: Dimensions.get('screen').width / attachment.meta?.original?.aspect!
|
||||||
|
}
|
||||||
|
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
|
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
|
||||||
const [altText, setAltText] = useState<string | undefined>()
|
const [altText, setAltText] = useState<string | undefined>(
|
||||||
|
attachment.description
|
||||||
|
)
|
||||||
|
const focus = useRef({ x: 0, y: 0 })
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const unsubscribe = navigation.addListener('beforeRemove', () => {
|
||||||
|
let needUpdate = false
|
||||||
|
if (altText) {
|
||||||
|
attachment.description = altText
|
||||||
|
needUpdate = true
|
||||||
|
}
|
||||||
|
if (focus.current.x !== 0 || focus.current.y !== 0) {
|
||||||
|
attachment.meta!.focus = {
|
||||||
|
x: focus.current.x > 1 ? 1 : focus.current.x,
|
||||||
|
y: focus.current.y > 1 ? 1 : focus.current.y
|
||||||
|
}
|
||||||
|
needUpdate = true
|
||||||
|
}
|
||||||
|
if (needUpdate) {
|
||||||
|
postDispatch({ type: 'attachmentEdit', payload: attachment })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return unsubscribe
|
||||||
|
}, [navigation, altText])
|
||||||
|
|
||||||
const imageFocus = useCallback(() => {
|
const imageFocus = useCallback(() => {
|
||||||
const imageDimensionis = {
|
const panFocus = useRef(
|
||||||
width: Dimensions.get('screen').width,
|
new Animated.ValueXY(
|
||||||
height:
|
attachment.meta.focus?.x && attachment.meta.focus?.y
|
||||||
Dimensions.get('screen').width / attachment.meta?.original?.aspect!
|
? {
|
||||||
}
|
x: (attachment.meta.focus.x * imageDimensionis.width) / 2,
|
||||||
|
y: (-attachment.meta.focus.y * imageDimensionis.height) / 2
|
||||||
const panFocus = useRef(new Animated.ValueXY({ x: 0, y: 0 })).current
|
}
|
||||||
|
: { x: 0, y: 0 }
|
||||||
|
)
|
||||||
|
).current
|
||||||
const panX = panFocus.x.interpolate({
|
const panX = panFocus.x.interpolate({
|
||||||
inputRange: [-imageDimensionis.width / 2, imageDimensionis.width / 2],
|
inputRange: [-imageDimensionis.width / 2, imageDimensionis.width / 2],
|
||||||
outputRange: [-imageDimensionis.width / 2, imageDimensionis.width / 2],
|
outputRange: [-imageDimensionis.width / 2, imageDimensionis.width / 2],
|
||||||
@ -59,6 +102,12 @@ const ComposeEditAttachment: React.FC<Props> = ({
|
|||||||
outputRange: [-imageDimensionis.height / 2, imageDimensionis.height / 2],
|
outputRange: [-imageDimensionis.height / 2, imageDimensionis.height / 2],
|
||||||
extrapolate: 'clamp'
|
extrapolate: 'clamp'
|
||||||
})
|
})
|
||||||
|
panFocus.addListener(e => {
|
||||||
|
focus.current = {
|
||||||
|
x: e.x / (imageDimensionis.width / 2),
|
||||||
|
y: -e.y / (imageDimensionis.height / 2)
|
||||||
|
}
|
||||||
|
})
|
||||||
const handleGesture = Animated.event(
|
const handleGesture = Animated.event(
|
||||||
[{ nativeEvent: { translationX: panFocus.x, translationY: panFocus.y } }],
|
[{ nativeEvent: { translationX: panFocus.x, translationY: panFocus.y } }],
|
||||||
{ useNativeDriver: true }
|
{ useNativeDriver: true }
|
||||||
@ -68,62 +117,66 @@ const ComposeEditAttachment: React.FC<Props> = ({
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{ overflow: 'hidden' }}>
|
<>
|
||||||
<Image
|
<View style={{ overflow: 'hidden' }}>
|
||||||
style={{
|
<Image
|
||||||
width: imageDimensionis.width,
|
style={{
|
||||||
height: imageDimensionis.height
|
width: imageDimensionis.width,
|
||||||
}}
|
height: imageDimensionis.height
|
||||||
source={{
|
}}
|
||||||
uri: attachment.local_url || attachment.preview_url
|
source={{
|
||||||
}}
|
uri: attachment.local_url || attachment.preview_url
|
||||||
/>
|
}}
|
||||||
<PanGestureHandler
|
/>
|
||||||
onGestureEvent={handleGesture}
|
<PanGestureHandler
|
||||||
onHandlerStateChange={onHandlerStateChange}
|
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'>
|
<Animated.View
|
||||||
<G>
|
style={[
|
||||||
<G id='Mask'>
|
{
|
||||||
<Path
|
position: 'absolute',
|
||||||
d={
|
top: -1000 + imageDimensionis.height / 2,
|
||||||
'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'
|
left: -1000 + imageDimensionis.width / 2,
|
||||||
}
|
transform: [{ translateX: panX }, { translateY: panY }]
|
||||||
fillOpacity='0.35'
|
}
|
||||||
fill='#000000'
|
]}
|
||||||
/>
|
>
|
||||||
<G transform='translate(467.000000, 467.000000)'>
|
<Svg width='2000' height='2000' viewBox='0 0 2000 2000'>
|
||||||
<Circle
|
<G>
|
||||||
stroke='#FFFFFF'
|
<G id='Mask'>
|
||||||
strokeWidth='2'
|
<Path
|
||||||
cx='33'
|
d={
|
||||||
cy='33'
|
'M2000,0 L2000,2000 L0,2000 L0,0 L2000,0 Z M1000,967 C981.774603,967 967,981.774603 967,1000 C967,1018.2254 981.774603,1033 1000,1033 C1018.2254,1033 1033,1018.2254 1033,1000 C1033,981.774603 1018.2254,967 1000,967 Z'
|
||||||
r='33'
|
}
|
||||||
|
fill={theme.backgroundOverlay}
|
||||||
/>
|
/>
|
||||||
<Circle fill='#FFFFFF' cx='33' cy='33' r='2' />
|
<G transform='translate(967, 967)'>
|
||||||
|
<Circle
|
||||||
|
stroke={theme.background}
|
||||||
|
strokeWidth='2'
|
||||||
|
cx='33'
|
||||||
|
cy='33'
|
||||||
|
r='33'
|
||||||
|
/>
|
||||||
|
<Circle fill={theme.background} cx='33' cy='33' r='2' />
|
||||||
|
</G>
|
||||||
</G>
|
</G>
|
||||||
</G>
|
</G>
|
||||||
</G>
|
</Svg>
|
||||||
</Svg>
|
</Animated.View>
|
||||||
</Animated.View>
|
</PanGestureHandler>
|
||||||
</PanGestureHandler>
|
</View>
|
||||||
</View>
|
<Text style={[styles.imageFocusText, { color: theme.primary }]}>
|
||||||
|
在预览图上拖动圆圈,以选择缩略图的焦点。
|
||||||
|
</Text>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const altTextInput = useCallback(() => {
|
const altTextInput = useCallback(() => {
|
||||||
return (
|
return (
|
||||||
<>
|
<View style={styles.altTextContainer}>
|
||||||
<Text style={[styles.altTextInputHeading, { color: theme.primary }]}>
|
<Text style={[styles.altTextInputHeading, { color: theme.primary }]}>
|
||||||
为附件添加文字说明
|
为附件添加文字说明
|
||||||
</Text>
|
</Text>
|
||||||
@ -139,13 +192,14 @@ const ComposeEditAttachment: React.FC<Props> = ({
|
|||||||
}
|
}
|
||||||
placeholderTextColor={theme.secondary}
|
placeholderTextColor={theme.secondary}
|
||||||
scrollEnabled
|
scrollEnabled
|
||||||
|
value={altText}
|
||||||
/>
|
/>
|
||||||
<Text style={[styles.altTextLength, { color: theme.secondary }]}>
|
<Text style={[styles.altTextLength, { color: theme.secondary }]}>
|
||||||
{1500 - (altText?.length || 0)}
|
{altText?.length || 0} / 1500
|
||||||
</Text>
|
</Text>
|
||||||
</>
|
</View>
|
||||||
)
|
)
|
||||||
}, [altText?.length])
|
}, [altText])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyboardAvoidingView behavior='padding' style={{ flex: 1 }}>
|
<KeyboardAvoidingView behavior='padding' style={{ flex: 1 }}>
|
||||||
@ -158,25 +212,76 @@ const ComposeEditAttachment: React.FC<Props> = ({
|
|||||||
headerLeft: () => (
|
headerLeft: () => (
|
||||||
<HeaderLeft text='取消' onPress={() => navigation.goBack()} />
|
<HeaderLeft text='取消' onPress={() => navigation.goBack()} />
|
||||||
),
|
),
|
||||||
headerRight: () => <HeaderRight text='应用' onPress={() => {}} />
|
headerRight: () => (
|
||||||
|
<HeaderRight
|
||||||
|
text='应用'
|
||||||
|
onPress={() => {
|
||||||
|
const formData = new FormData()
|
||||||
|
|
||||||
|
if (altText) {
|
||||||
|
formData.append('description', altText)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (focus.current.x !== 0 || focus.current.y !== 0) {
|
||||||
|
formData.append(
|
||||||
|
'focus',
|
||||||
|
`${focus.current.x},${focus.current.y}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
client({
|
||||||
|
method: 'put',
|
||||||
|
instance: 'local',
|
||||||
|
url: `media/${attachment.id}`,
|
||||||
|
...(formData && { body: formData })
|
||||||
|
})
|
||||||
|
.then(
|
||||||
|
res => {
|
||||||
|
if (res.body.id === attachment.id) {
|
||||||
|
Alert.alert('修改成功', '', [
|
||||||
|
{
|
||||||
|
text: '好的',
|
||||||
|
onPress: () => {
|
||||||
|
navigation.goBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
Alert.alert('修改失败', '', [
|
||||||
|
{
|
||||||
|
text: '返回重试'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
Alert.alert('修改失败', error.body, [
|
||||||
|
{
|
||||||
|
text: '返回重试'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.catch(() => {
|
||||||
|
Alert.alert('修改失败', '', [
|
||||||
|
{
|
||||||
|
text: '返回重试'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{() => {
|
{() => {
|
||||||
switch (attachment.type) {
|
switch (attachment.type) {
|
||||||
case 'image':
|
case 'image':
|
||||||
return (
|
return (
|
||||||
<View style={{ flex: 1 }}>
|
<ScrollView style={{ flex: 1 }}>
|
||||||
{imageFocus()}
|
{imageFocus()}
|
||||||
<Text
|
{altTextInput()}
|
||||||
style={[
|
</ScrollView>
|
||||||
styles.imageFocusText,
|
|
||||||
{ color: theme.primary }
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
在预览图上点击或拖动圆圈,以选择缩略图的焦点。
|
|
||||||
</Text>
|
|
||||||
<View style={styles.editContainer}>{altTextInput()}</View>
|
|
||||||
</View>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
@ -189,14 +294,14 @@ const ComposeEditAttachment: React.FC<Props> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
editContainer: { padding: StyleConstants.Spacing.Global.PagePadding },
|
|
||||||
imageFocusText: {
|
imageFocusText: {
|
||||||
fontSize: StyleConstants.Font.Size.M
|
fontSize: StyleConstants.Font.Size.M,
|
||||||
|
padding: StyleConstants.Spacing.Global.PagePadding
|
||||||
},
|
},
|
||||||
|
altTextContainer: { padding: StyleConstants.Spacing.Global.PagePadding },
|
||||||
altTextInputHeading: {
|
altTextInputHeading: {
|
||||||
fontSize: StyleConstants.Font.Size.M,
|
fontSize: StyleConstants.Font.Size.M,
|
||||||
fontWeight: StyleConstants.Font.Weight.Bold,
|
fontWeight: StyleConstants.Font.Weight.Bold
|
||||||
marginTop: StyleConstants.Spacing.XL
|
|
||||||
},
|
},
|
||||||
altTextInput: {
|
altTextInput: {
|
||||||
height: 200,
|
height: 200,
|
||||||
@ -215,4 +320,4 @@ const styles = StyleSheet.create({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export default ComposeEditAttachment
|
export default React.memo(ComposeEditAttachment, () => true)
|
||||||
|
@ -60,7 +60,7 @@ const sharedScreens = (Stack: any) => {
|
|||||||
name='Screen-Shared-Compose-EditAttachment'
|
name='Screen-Shared-Compose-EditAttachment'
|
||||||
component={ComposeEditAttachment}
|
component={ComposeEditAttachment}
|
||||||
options={{
|
options={{
|
||||||
stackPresentation: 'fullScreenModal'
|
stackPresentation: 'modal',
|
||||||
}}
|
}}
|
||||||
/>,
|
/>,
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
|
Reference in New Issue
Block a user