mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Fxied #353
This commit is contained in:
@@ -47,10 +47,6 @@ const ComposeDraftsListRoot: React.FC<Props> = ({ timestamp }) => {
|
||||
|
||||
const [checkingAttachments, setCheckingAttachments] = useState(false)
|
||||
|
||||
const removeDraft = useCallback(ts => {
|
||||
dispatch(removeInstanceDraft(ts))
|
||||
}, [])
|
||||
|
||||
const renderItem = useCallback(
|
||||
({ item }: { item: ComposeStateDraft }) => {
|
||||
return (
|
||||
@@ -144,7 +140,7 @@ const ComposeDraftsListRoot: React.FC<Props> = ({ timestamp }) => {
|
||||
}}
|
||||
source={{
|
||||
uri:
|
||||
attachment.local?.local_thumbnail ||
|
||||
attachment.local?.thumbnail ||
|
||||
attachment.remote?.preview_url
|
||||
}}
|
||||
/>
|
||||
@@ -157,38 +153,6 @@ const ComposeDraftsListRoot: React.FC<Props> = ({ timestamp }) => {
|
||||
},
|
||||
[theme]
|
||||
)
|
||||
const renderHiddenItem = useCallback(
|
||||
({ item }) => (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
backgroundColor: colors.red
|
||||
}}
|
||||
children={
|
||||
<Pressable
|
||||
style={{
|
||||
flexBasis:
|
||||
StyleConstants.Font.Size.L +
|
||||
StyleConstants.Spacing.Global.PagePadding * 4,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
onPress={() => removeDraft(item.timestamp)}
|
||||
children={
|
||||
<Icon
|
||||
name='Trash'
|
||||
size={StyleConstants.Font.Size.L}
|
||||
color={colors.primaryOverlay}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
[theme]
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -220,7 +184,35 @@ const ComposeDraftsListRoot: React.FC<Props> = ({ timestamp }) => {
|
||||
<SwipeListView
|
||||
data={instanceDrafts}
|
||||
renderItem={renderItem}
|
||||
renderHiddenItem={renderHiddenItem}
|
||||
renderHiddenItem={({ item }) => (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
backgroundColor: colors.red
|
||||
}}
|
||||
children={
|
||||
<Pressable
|
||||
style={{
|
||||
flexBasis:
|
||||
StyleConstants.Font.Size.L +
|
||||
StyleConstants.Spacing.Global.PagePadding * 4,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
onPress={() => dispatch(removeInstanceDraft(item.timestamp))}
|
||||
children={
|
||||
<Icon
|
||||
name='Trash'
|
||||
size={StyleConstants.Font.Size.L}
|
||||
color={colors.primaryOverlay}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
disableRightSwipe={true}
|
||||
rightOpenValue={-actionWidth}
|
||||
// previewRowKey={
|
||||
|
@@ -35,7 +35,7 @@ const ComposeEditAttachmentRoot: React.FC<Props> = ({ index }) => {
|
||||
video.local
|
||||
? ({
|
||||
url: video.local.uri,
|
||||
preview_url: video.local.local_thumbnail,
|
||||
preview_url: video.local.thumbnail,
|
||||
blurhash: video.remote?.blurhash
|
||||
} as Mastodon.AttachmentVideo)
|
||||
: (video.remote as Mastodon.AttachmentVideo)
|
||||
|
@@ -4,13 +4,7 @@ import { useSearchQuery } from '@utils/queryHooks/search'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { chunk, forEach, groupBy, sortBy } from 'lodash'
|
||||
import React, {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef
|
||||
} from 'react'
|
||||
import React, { useContext, useEffect, useMemo, useRef } from 'react'
|
||||
import {
|
||||
AccessibilityInfo,
|
||||
findNodeHandle,
|
||||
@@ -147,35 +141,25 @@ const ComposeRoot = React.memo(
|
||||
}
|
||||
}, [isFetching])
|
||||
|
||||
const listItem = useCallback(
|
||||
({ item }) => (
|
||||
<ComposeRootSuggestion
|
||||
item={item}
|
||||
composeState={composeState}
|
||||
composeDispatch={composeDispatch}
|
||||
/>
|
||||
),
|
||||
[composeState]
|
||||
)
|
||||
|
||||
const ListFooter = useCallback(
|
||||
() => (
|
||||
<ComposeRootFooter
|
||||
accessibleRefAttachments={accessibleRefAttachments}
|
||||
accessibleRefEmojis={accessibleRefEmojis}
|
||||
/>
|
||||
),
|
||||
[]
|
||||
)
|
||||
|
||||
return (
|
||||
<View style={styles.base}>
|
||||
<FlatList
|
||||
renderItem={listItem}
|
||||
renderItem={({ item }) => (
|
||||
<ComposeRootSuggestion
|
||||
item={item}
|
||||
composeState={composeState}
|
||||
composeDispatch={composeDispatch}
|
||||
/>
|
||||
)}
|
||||
ListEmptyComponent={listEmpty}
|
||||
keyboardShouldPersistTaps='always'
|
||||
ListHeaderComponent={ComposeRootHeader}
|
||||
ListFooterComponent={ListFooter}
|
||||
ListFooterComponent={() => (
|
||||
<ComposeRootFooter
|
||||
accessibleRefAttachments={accessibleRefAttachments}
|
||||
accessibleRefEmojis={accessibleRefEmojis}
|
||||
/>
|
||||
)}
|
||||
ItemSeparatorComponent={ComponentSeparator}
|
||||
// @ts-ignore
|
||||
data={data ? data[composeState.tag?.type] : undefined}
|
||||
|
@@ -56,9 +56,12 @@ const ComposeAttachments: React.FC<Props> = ({ accessibleRefAttachments }) => {
|
||||
})
|
||||
}, [composeState.attachments.sensitive])
|
||||
|
||||
const calculateWidth = useCallback(item => {
|
||||
const calculateWidth = useCallback((item: ExtendedAttachment) => {
|
||||
if (item.local) {
|
||||
return (item.local.width / item.local.height) * DEFAULT_HEIGHT
|
||||
return (
|
||||
((item.local.width || 100) / (item.local.height || 100)) *
|
||||
DEFAULT_HEIGHT
|
||||
)
|
||||
} else {
|
||||
if (item.remote) {
|
||||
if (item.remote.meta.original.aspect) {
|
||||
@@ -135,7 +138,7 @@ const ComposeAttachments: React.FC<Props> = ({ accessibleRefAttachments }) => {
|
||||
<FastImage
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
source={{
|
||||
uri: item.local?.local_thumbnail || item.remote?.preview_url
|
||||
uri: item.local?.thumbnail || item.remote?.preview_url
|
||||
}}
|
||||
/>
|
||||
{item.remote?.meta?.original?.duration ? (
|
||||
|
@@ -42,22 +42,6 @@ const ComposeEmojis: React.FC<Props> = ({ accessibleRefEmojis }) => {
|
||||
}
|
||||
}, [composeState.emoji.active])
|
||||
|
||||
const listHeader = useCallback(
|
||||
({ section: { title } }) => (
|
||||
<CustomText
|
||||
fontStyle='S'
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: StyleConstants.Spacing.L,
|
||||
color: colors.secondary
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</CustomText>
|
||||
),
|
||||
[]
|
||||
)
|
||||
|
||||
const listItem = useCallback(
|
||||
({ index, item }: { item: Mastodon.Emoji[]; index: number }) => {
|
||||
return (
|
||||
@@ -155,7 +139,18 @@ const ComposeEmojis: React.FC<Props> = ({ accessibleRefEmojis }) => {
|
||||
keyboardShouldPersistTaps='always'
|
||||
sections={composeState.emoji.emojis || []}
|
||||
keyExtractor={item => item[0].shortcode}
|
||||
renderSectionHeader={listHeader}
|
||||
renderSectionHeader={({ section: { title } }) => (
|
||||
<CustomText
|
||||
fontStyle='S'
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: StyleConstants.Spacing.L,
|
||||
color: colors.secondary
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</CustomText>
|
||||
)}
|
||||
renderItem={listItem}
|
||||
windowSize={2}
|
||||
/>
|
||||
|
@@ -7,7 +7,7 @@ import { ActionSheetOptions } from '@expo/react-native-action-sheet'
|
||||
import i18next from 'i18next'
|
||||
import apiInstance from '@api/instance'
|
||||
import mediaSelector from '@components/mediaSelector'
|
||||
import { ImageOrVideo } from 'react-native-image-crop-picker'
|
||||
import { Asset } from 'react-native-image-picker'
|
||||
|
||||
export interface Props {
|
||||
composeDispatch: Dispatch<ComposeAction>
|
||||
@@ -22,46 +22,40 @@ export const uploadAttachment = async ({
|
||||
media
|
||||
}: {
|
||||
composeDispatch: Dispatch<ComposeAction>
|
||||
media: { uri: string } & Pick<ImageOrVideo, 'mime' | 'width' | 'height'>
|
||||
media: Required<Pick<Asset, 'uri' | 'type' | 'fileName'>>
|
||||
}) => {
|
||||
const hash = await Crypto.digestStringAsync(
|
||||
Crypto.CryptoDigestAlgorithm.SHA256,
|
||||
media.uri + Math.random()
|
||||
)
|
||||
|
||||
switch (media.mime.split('/')[0]) {
|
||||
switch (media.type.split('/')[0]) {
|
||||
case 'image':
|
||||
composeDispatch({
|
||||
type: 'attachment/upload/start',
|
||||
payload: {
|
||||
local: { ...media, type: 'image', local_thumbnail: media.uri, hash },
|
||||
local: { ...media, thumbnail: media.uri, hash },
|
||||
uploading: true
|
||||
}
|
||||
})
|
||||
break
|
||||
case 'video':
|
||||
VideoThumbnails.getThumbnailAsync(media.uri)
|
||||
.then(({ uri, width, height }) =>
|
||||
.then(({ uri, width, height }) => {
|
||||
console.log('new', uri, width, height)
|
||||
composeDispatch({
|
||||
type: 'attachment/upload/start',
|
||||
payload: {
|
||||
local: {
|
||||
...media,
|
||||
type: 'video',
|
||||
local_thumbnail: uri,
|
||||
hash,
|
||||
width,
|
||||
height
|
||||
},
|
||||
local: { ...media, thumbnail: uri, hash, width, height },
|
||||
uploading: true
|
||||
}
|
||||
})
|
||||
)
|
||||
})
|
||||
.catch(() =>
|
||||
composeDispatch({
|
||||
type: 'attachment/upload/start',
|
||||
payload: {
|
||||
local: { ...media, type: 'video', hash },
|
||||
local: { ...media, hash },
|
||||
uploading: true
|
||||
}
|
||||
})
|
||||
@@ -71,7 +65,7 @@ export const uploadAttachment = async ({
|
||||
composeDispatch({
|
||||
type: 'attachment/upload/start',
|
||||
payload: {
|
||||
local: { ...media, type: 'unknown', hash },
|
||||
local: { ...media, hash },
|
||||
uploading: true
|
||||
}
|
||||
})
|
||||
@@ -102,8 +96,8 @@ export const uploadAttachment = async ({
|
||||
const formData = new FormData()
|
||||
formData.append('file', {
|
||||
uri: media.uri,
|
||||
name: media.uri.match(new RegExp(/.*\/(.*)/))?.[1] || 'file.jpg',
|
||||
type: media.mime
|
||||
name: media.fileName,
|
||||
type: media.type
|
||||
} as any)
|
||||
|
||||
return apiInstance<Mastodon.Attachment>({
|
||||
@@ -140,7 +134,8 @@ const chooseAndUploadAttachment = async ({
|
||||
showActionSheetWithOptions
|
||||
})
|
||||
for (const media of result) {
|
||||
uploadAttachment({ composeDispatch, media })
|
||||
const requiredMedia = media as Required<Asset>
|
||||
uploadAttachment({ composeDispatch, media: requiredMedia })
|
||||
await new Promise(res => setTimeout(res, 500))
|
||||
}
|
||||
}
|
||||
|
@@ -101,15 +101,7 @@ const ComposeTextInput: React.FC = () => {
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
uploadAttachment({
|
||||
composeDispatch,
|
||||
media: {
|
||||
uri: file.uri,
|
||||
mime: file.type,
|
||||
width: 100,
|
||||
height: 100
|
||||
}
|
||||
})
|
||||
uploadAttachment({ composeDispatch, media: file })
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
13
src/screens/Compose/utils/types.d.ts
vendored
13
src/screens/Compose/utils/types.d.ts
vendored
@@ -1,12 +1,8 @@
|
||||
import { ImageOrVideo } from 'react-native-image-crop-picker'
|
||||
import { Asset } from 'react-native-image-picker'
|
||||
|
||||
export type ExtendedAttachment = {
|
||||
remote?: Mastodon.Attachment
|
||||
local?: { uri: string } & Pick<ImageOrVideo, 'width' | 'height' | 'mime'> & {
|
||||
type: 'image' | 'video' | 'unknown'
|
||||
local_thumbnail?: string
|
||||
hash?: string
|
||||
}
|
||||
local?: Asset & { thumbnail?: string; hash: string }
|
||||
uploading?: boolean
|
||||
}
|
||||
|
||||
@@ -121,10 +117,7 @@ export type ComposeAction =
|
||||
}
|
||||
| {
|
||||
type: 'attachment/upload/end'
|
||||
payload: {
|
||||
remote: Mastodon.Attachment
|
||||
local: { uri: string } & Pick<ImageOrVideo, 'width' | 'height' | 'mime'>
|
||||
}
|
||||
payload: { remote: Mastodon.Attachment; local: Asset }
|
||||
}
|
||||
| {
|
||||
type: 'attachment/upload/fail'
|
||||
|
Reference in New Issue
Block a user