mirror of
https://github.com/tooot-app/app
synced 2025-02-19 21:30:40 +01:00
Fixed #108
This commit is contained in:
parent
d6768c0f6f
commit
fc8fdec12f
@ -41,9 +41,6 @@ PODS:
|
|||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- ExpoHaptics (11.2.0):
|
- ExpoHaptics (11.2.0):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- ExpoImageManipulator (10.3.1):
|
|
||||||
- EXImageLoader
|
|
||||||
- ExpoModulesCore
|
|
||||||
- ExpoImagePicker (13.1.1):
|
- ExpoImagePicker (13.1.1):
|
||||||
- ExpoModulesCore
|
- ExpoModulesCore
|
||||||
- ExpoKeepAwake (10.1.1):
|
- ExpoKeepAwake (10.1.1):
|
||||||
@ -531,6 +528,15 @@ PODS:
|
|||||||
- SDWebImageWebPCoder (~> 0.8.4)
|
- SDWebImageWebPCoder (~> 0.8.4)
|
||||||
- RNGestureHandler (2.4.2):
|
- RNGestureHandler (2.4.2):
|
||||||
- React-Core
|
- React-Core
|
||||||
|
- RNImageCropPicker (0.37.3):
|
||||||
|
- React-Core
|
||||||
|
- React-RCTImage
|
||||||
|
- RNImageCropPicker/QBImagePickerController (= 0.37.3)
|
||||||
|
- TOCropViewController
|
||||||
|
- RNImageCropPicker/QBImagePickerController (0.37.3):
|
||||||
|
- React-Core
|
||||||
|
- React-RCTImage
|
||||||
|
- TOCropViewController
|
||||||
- RNReanimated (2.8.0):
|
- RNReanimated (2.8.0):
|
||||||
- DoubleConversion
|
- DoubleConversion
|
||||||
- FBLazyVector
|
- FBLazyVector
|
||||||
@ -577,6 +583,7 @@ PODS:
|
|||||||
- Sentry (7.11.0):
|
- Sentry (7.11.0):
|
||||||
- Sentry/Core (= 7.11.0)
|
- Sentry/Core (= 7.11.0)
|
||||||
- Sentry/Core (7.11.0)
|
- Sentry/Core (7.11.0)
|
||||||
|
- TOCropViewController (2.6.1)
|
||||||
- Yoga (1.14.0)
|
- Yoga (1.14.0)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
@ -599,7 +606,6 @@ DEPENDENCIES:
|
|||||||
- Expo (from `../node_modules/expo/ios`)
|
- Expo (from `../node_modules/expo/ios`)
|
||||||
- ExpoCrypto (from `../node_modules/expo-crypto/ios`)
|
- ExpoCrypto (from `../node_modules/expo-crypto/ios`)
|
||||||
- ExpoHaptics (from `../node_modules/expo-haptics/ios`)
|
- ExpoHaptics (from `../node_modules/expo-haptics/ios`)
|
||||||
- ExpoImageManipulator (from `../node_modules/expo-image-manipulator/ios`)
|
|
||||||
- ExpoImagePicker (from `../node_modules/expo-image-picker/ios`)
|
- ExpoImagePicker (from `../node_modules/expo-image-picker/ios`)
|
||||||
- ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`)
|
- ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`)
|
||||||
- ExpoLocalization (from `../node_modules/expo-localization/ios`)
|
- ExpoLocalization (from `../node_modules/expo-localization/ios`)
|
||||||
@ -658,6 +664,7 @@ DEPENDENCIES:
|
|||||||
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
|
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
|
||||||
- RNFastImage (from `../node_modules/react-native-fast-image`)
|
- RNFastImage (from `../node_modules/react-native-fast-image`)
|
||||||
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
|
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
|
||||||
|
- RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`)
|
||||||
- RNReanimated (from `../node_modules/react-native-reanimated`)
|
- RNReanimated (from `../node_modules/react-native-reanimated`)
|
||||||
- RNScreens (from `../node_modules/react-native-screens`)
|
- RNScreens (from `../node_modules/react-native-screens`)
|
||||||
- "RNSentry (from `../node_modules/@sentry/react-native`)"
|
- "RNSentry (from `../node_modules/@sentry/react-native`)"
|
||||||
@ -685,6 +692,7 @@ SPEC REPOS:
|
|||||||
- SDWebImage
|
- SDWebImage
|
||||||
- SDWebImageWebPCoder
|
- SDWebImageWebPCoder
|
||||||
- Sentry
|
- Sentry
|
||||||
|
- TOCropViewController
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
boost:
|
boost:
|
||||||
@ -725,8 +733,6 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/expo-crypto/ios"
|
:path: "../node_modules/expo-crypto/ios"
|
||||||
ExpoHaptics:
|
ExpoHaptics:
|
||||||
:path: "../node_modules/expo-haptics/ios"
|
:path: "../node_modules/expo-haptics/ios"
|
||||||
ExpoImageManipulator:
|
|
||||||
:path: "../node_modules/expo-image-manipulator/ios"
|
|
||||||
ExpoImagePicker:
|
ExpoImagePicker:
|
||||||
:path: "../node_modules/expo-image-picker/ios"
|
:path: "../node_modules/expo-image-picker/ios"
|
||||||
ExpoKeepAwake:
|
ExpoKeepAwake:
|
||||||
@ -835,6 +841,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../node_modules/react-native-fast-image"
|
:path: "../node_modules/react-native-fast-image"
|
||||||
RNGestureHandler:
|
RNGestureHandler:
|
||||||
:path: "../node_modules/react-native-gesture-handler"
|
:path: "../node_modules/react-native-gesture-handler"
|
||||||
|
RNImageCropPicker:
|
||||||
|
:path: "../node_modules/react-native-image-crop-picker"
|
||||||
RNReanimated:
|
RNReanimated:
|
||||||
:path: "../node_modules/react-native-reanimated"
|
:path: "../node_modules/react-native-reanimated"
|
||||||
RNScreens:
|
RNScreens:
|
||||||
@ -869,7 +877,6 @@ SPEC CHECKSUMS:
|
|||||||
Expo: 64d52669fa3b9342919b5b44b2b4f15f19b0cf76
|
Expo: 64d52669fa3b9342919b5b44b2b4f15f19b0cf76
|
||||||
ExpoCrypto: d0d0f3e20875dc450b4ec88f0fb608da5c2c6c17
|
ExpoCrypto: d0d0f3e20875dc450b4ec88f0fb608da5c2c6c17
|
||||||
ExpoHaptics: ad58ec96a25e57579c14a47c7d71f0de0de8656a
|
ExpoHaptics: ad58ec96a25e57579c14a47c7d71f0de0de8656a
|
||||||
ExpoImageManipulator: b55580bbc7b10099c7707949903e7176a8542ee8
|
|
||||||
ExpoImagePicker: d9d6b4f29db437fc7796f13cee5f133f5b4b5f7c
|
ExpoImagePicker: d9d6b4f29db437fc7796f13cee5f133f5b4b5f7c
|
||||||
ExpoKeepAwake: c0c494b442ecd8122974c13b93ccfb57bd408e88
|
ExpoKeepAwake: c0c494b442ecd8122974c13b93ccfb57bd408e88
|
||||||
ExpoLocalization: 8f619bb6eec64575cd5220bfabbd7b4e2d6f33f8
|
ExpoLocalization: 8f619bb6eec64575cd5220bfabbd7b4e2d6f33f8
|
||||||
@ -938,6 +945,7 @@ SPEC CHECKSUMS:
|
|||||||
RNCAsyncStorage: 466b9df1a14bccda91da86e0b7d9a345d78e1673
|
RNCAsyncStorage: 466b9df1a14bccda91da86e0b7d9a345d78e1673
|
||||||
RNFastImage: 945abf54742505d790d9024d230c69b1e866bc88
|
RNFastImage: 945abf54742505d790d9024d230c69b1e866bc88
|
||||||
RNGestureHandler: 61628a2c859172551aa2100d3e73d1e57878392f
|
RNGestureHandler: 61628a2c859172551aa2100d3e73d1e57878392f
|
||||||
|
RNImageCropPicker: 44e2807bc410741f35d4c45b6586aedfe3da39d2
|
||||||
RNReanimated: 64573e25e078ae6bec03b891586d50b9ec284393
|
RNReanimated: 64573e25e078ae6bec03b891586d50b9ec284393
|
||||||
RNScreens: 40a2cb40a02a609938137a1e0acfbf8fc9eebf19
|
RNScreens: 40a2cb40a02a609938137a1e0acfbf8fc9eebf19
|
||||||
RNSentry: 85f6525b5fe8d2ada065858026b338605b3c09da
|
RNSentry: 85f6525b5fe8d2ada065858026b338605b3c09da
|
||||||
@ -946,6 +954,7 @@ SPEC CHECKSUMS:
|
|||||||
SDWebImage: 0905f1b7760fc8ac4198cae0036600d67478751e
|
SDWebImage: 0905f1b7760fc8ac4198cae0036600d67478751e
|
||||||
SDWebImageWebPCoder: f93010f3f6c031e2f8fb3081ca4ee6966c539815
|
SDWebImageWebPCoder: f93010f3f6c031e2f8fb3081ca4ee6966c539815
|
||||||
Sentry: 0c5cd63d714187b4a39c331c1f0eb04ba7868341
|
Sentry: 0c5cd63d714187b4a39c331c1f0eb04ba7868341
|
||||||
|
TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863
|
||||||
Yoga: 99652481fcd320aefa4a7ef90095b95acd181952
|
Yoga: 99652481fcd320aefa4a7ef90095b95acd181952
|
||||||
|
|
||||||
PODFILE CHECKSUM: d6d20fa7c51228cebc309aed987ed7d8f4274844
|
PODFILE CHECKSUM: d6d20fa7c51228cebc309aed987ed7d8f4274844
|
||||||
|
@ -6,6 +6,5 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
"NSCameraUsageDescription" = "Allow tooot to capture photo or video and attach it to your toot";
|
|
||||||
"NSPhotoLibraryAddUsageDescription" = "Allow tooot to save an image to your camera roll";
|
"NSPhotoLibraryAddUsageDescription" = "Allow tooot to save an image to your camera roll";
|
||||||
"NSPhotoLibraryUsageDescription" = "Allow tooot to access your camera roll to attach photos or videos to your toot";
|
"NSPhotoLibraryUsageDescription" = "Allow tooot to access your camera roll to attach photos or videos to your toot";
|
||||||
|
@ -386,13 +386,17 @@
|
|||||||
"${PODS_ROOT}/Target Support Files/Pods-tooot/Pods-tooot-resources.sh",
|
"${PODS_ROOT}/Target Support Files/Pods-tooot/Pods-tooot-resources.sh",
|
||||||
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
|
||||||
"${PODS_CONFIGURATION_BUILD_DIR}/EXUpdates/EXUpdates.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/EXUpdates/EXUpdates.bundle",
|
||||||
|
"${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle",
|
||||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
|
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
|
||||||
|
"${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController/TOCropViewControllerBundle.bundle",
|
||||||
);
|
);
|
||||||
name = "[CP] Copy Pods Resources";
|
name = "[CP] Copy Pods Resources";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXUpdates.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXUpdates.bundle",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle",
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
|
||||||
|
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/TOCropViewControllerBundle.bundle",
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
|
@ -55,8 +55,6 @@
|
|||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>NSCameraUsageDescription</key>
|
|
||||||
<string>Allow $(PRODUCT_NAME) to capture photo or video and attach it to your toot</string>
|
|
||||||
<key>NSLocationWhenInUseUsageDescription</key>
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
<string></string>
|
<string></string>
|
||||||
<key>NSMainNibFile</key>
|
<key>NSMainNibFile</key>
|
||||||
|
@ -6,6 +6,5 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
"NSCameraUsageDescription" = "允许tooot拍摄图片或视频,以添加嘟文附件";
|
|
||||||
"NSPhotoLibraryAddUsageDescription" = "允许tooot保存图片至相册";
|
"NSPhotoLibraryAddUsageDescription" = "允许tooot保存图片至相册";
|
||||||
"NSPhotoLibraryUsageDescription" = "允许tooot读取相册图片或视频,以添加嘟文附件";
|
"NSPhotoLibraryUsageDescription" = "允许tooot读取相册图片或视频,以添加嘟文附件";
|
||||||
|
@ -55,7 +55,6 @@
|
|||||||
"expo-file-system": "14.0.0",
|
"expo-file-system": "14.0.0",
|
||||||
"expo-firebase-analytics": "7.0.0",
|
"expo-firebase-analytics": "7.0.0",
|
||||||
"expo-haptics": "11.2.0",
|
"expo-haptics": "11.2.0",
|
||||||
"expo-image-manipulator": "10.3.1",
|
|
||||||
"expo-image-picker": "13.1.1",
|
"expo-image-picker": "13.1.1",
|
||||||
"expo-linking": "3.1.0",
|
"expo-linking": "3.1.0",
|
||||||
"expo-localization": "13.0.0",
|
"expo-localization": "13.0.0",
|
||||||
@ -84,6 +83,7 @@
|
|||||||
"react-native-flash-message": "0.2.1",
|
"react-native-flash-message": "0.2.1",
|
||||||
"react-native-gesture-handler": "2.4.2",
|
"react-native-gesture-handler": "2.4.2",
|
||||||
"react-native-htmlview": "0.16.0",
|
"react-native-htmlview": "0.16.0",
|
||||||
|
"react-native-image-crop-picker": "^0.37.3",
|
||||||
"react-native-image-keyboard": "^2.2.0",
|
"react-native-image-keyboard": "^2.2.0",
|
||||||
"react-native-pager-view": "5.4.11",
|
"react-native-pager-view": "5.4.11",
|
||||||
"react-native-reanimated": "2.8.0",
|
"react-native-reanimated": "2.8.0",
|
||||||
|
7
src/@types/app.d.ts
vendored
7
src/@types/app.d.ts
vendored
@ -13,11 +13,4 @@ declare namespace App {
|
|||||||
| 'Conversations'
|
| 'Conversations'
|
||||||
| 'Bookmarks'
|
| 'Bookmarks'
|
||||||
| 'Favourites'
|
| 'Favourites'
|
||||||
|
|
||||||
interface IImageInfo {
|
|
||||||
uri: string
|
|
||||||
width: number
|
|
||||||
height: number
|
|
||||||
type?: 'image' | 'video'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
156
src/Screens.tsx
156
src/Screens.tsx
@ -178,8 +178,49 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let text: string | undefined = undefined
|
let text: string | undefined = undefined
|
||||||
let images: { type: string; uri: string }[] = []
|
let media: { path: string; mime: string }[] = []
|
||||||
let video: { type: string; uri: string } | undefined = undefined
|
|
||||||
|
const typesImage = ['png', 'jpg', 'jpeg', 'gif']
|
||||||
|
const typesVideo = ['mp4', 'm4v', 'mov', 'webm', 'mpeg']
|
||||||
|
const filterMedia = ({ path, mime }: { path: string; mime: string }) => {
|
||||||
|
if (mime.startsWith('image/')) {
|
||||||
|
if (!typesImage.includes(mime.split('/')[1])) {
|
||||||
|
console.warn('Image type not supported:', mime.split('/')[1])
|
||||||
|
displayMessage({
|
||||||
|
message: t('shareError.imageNotSupported', {
|
||||||
|
type: mime.split('/')[1]
|
||||||
|
}),
|
||||||
|
type: 'error',
|
||||||
|
theme
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
media.push({ path, mime })
|
||||||
|
} else if (mime.startsWith('video/')) {
|
||||||
|
if (!typesVideo.includes(mime.split('/')[1])) {
|
||||||
|
console.warn('Video type not supported:', mime.split('/')[1])
|
||||||
|
displayMessage({
|
||||||
|
message: t('shareError.videoNotSupported', {
|
||||||
|
type: mime.split('/')[1]
|
||||||
|
}),
|
||||||
|
type: 'error',
|
||||||
|
theme
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
media.push({ path, mime })
|
||||||
|
} else {
|
||||||
|
if (typesImage.includes(path.split('.').pop() || '')) {
|
||||||
|
media.push({ path: path, mime: 'image/jpg' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (typesVideo.includes(path.split('.').pop() || '')) {
|
||||||
|
media.push({ path: path, mime: 'video/mp4' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
text = !text ? path : text.concat(text, `\n${path}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (Platform.OS) {
|
switch (Platform.OS) {
|
||||||
case 'ios':
|
case 'ios':
|
||||||
@ -187,55 +228,11 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
item.data.forEach(d => {
|
for (const d of item.data) {
|
||||||
if (typeof d === 'string') return
|
if (typeof d !== 'string') {
|
||||||
const typesImage = ['png', 'jpg', 'jpeg', 'gif']
|
filterMedia({ path: d.data, mime: d.mimeType })
|
||||||
const typesVideo = ['mp4', 'm4v', 'mov', 'webm']
|
|
||||||
const { mimeType, data } = d
|
|
||||||
if (mimeType.startsWith('image/')) {
|
|
||||||
if (!typesImage.includes(mimeType.split('/')[1])) {
|
|
||||||
console.warn(
|
|
||||||
'Image type not supported:',
|
|
||||||
mimeType.split('/')[1]
|
|
||||||
)
|
|
||||||
displayMessage({
|
|
||||||
message: t('shareError.imageNotSupported', {
|
|
||||||
type: mimeType.split('/')[1]
|
|
||||||
}),
|
|
||||||
type: 'error',
|
|
||||||
theme
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
images.push({ type: mimeType.split('/')[1], uri: data })
|
|
||||||
} else if (mimeType.startsWith('video/')) {
|
|
||||||
if (!typesVideo.includes(mimeType.split('/')[1])) {
|
|
||||||
console.warn(
|
|
||||||
'Video type not supported:',
|
|
||||||
mimeType.split('/')[1]
|
|
||||||
)
|
|
||||||
displayMessage({
|
|
||||||
message: t('shareError.videoNotSupported', {
|
|
||||||
type: mimeType.split('/')[1]
|
|
||||||
}),
|
|
||||||
type: 'error',
|
|
||||||
theme
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
video = { type: mimeType.split('/')[1], uri: data }
|
|
||||||
} else {
|
|
||||||
if (typesImage.includes(data.split('.').pop() || '')) {
|
|
||||||
images.push({ type: data.split('.').pop()!, uri: data })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (typesVideo.includes(data.split('.').pop() || '')) {
|
|
||||||
video = { type: data.split('.').pop()!, uri: data }
|
|
||||||
return
|
|
||||||
}
|
|
||||||
text = !text ? data : text.concat(text, `\n${data}`)
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
break
|
break
|
||||||
case 'android':
|
case 'android':
|
||||||
if (!item.mimeType) {
|
if (!item.mimeType) {
|
||||||
@ -247,65 +244,16 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
|||||||
} else {
|
} else {
|
||||||
tempData = item.data
|
tempData = item.data
|
||||||
}
|
}
|
||||||
tempData.forEach(d => {
|
for (const d of item.data) {
|
||||||
const typesImage = ['png', 'jpg', 'jpeg', 'gif']
|
filterMedia({ path: d, mime: item.mimeType })
|
||||||
const typesVideo = ['mp4', 'm4v', 'mov', 'webm', 'mpeg']
|
}
|
||||||
if (item.mimeType!.startsWith('image/')) {
|
|
||||||
if (!typesImage.includes(item.mimeType.split('/')[1])) {
|
|
||||||
console.warn(
|
|
||||||
'Image type not supported:',
|
|
||||||
item.mimeType.split('/')[1]
|
|
||||||
)
|
|
||||||
displayMessage({
|
|
||||||
message: t('shareError.imageNotSupported', {
|
|
||||||
type: item.mimeType.split('/')[1]
|
|
||||||
}),
|
|
||||||
type: 'error',
|
|
||||||
theme
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
images.push({ type: item.mimeType.split('/')[1], uri: d })
|
|
||||||
} else if (item.mimeType.startsWith('video/')) {
|
|
||||||
if (!typesVideo.includes(item.mimeType.split('/')[1])) {
|
|
||||||
console.warn(
|
|
||||||
'Video type not supported:',
|
|
||||||
item.mimeType.split('/')[1]
|
|
||||||
)
|
|
||||||
displayMessage({
|
|
||||||
message: t('shareError.videoNotSupported', {
|
|
||||||
type: item.mimeType.split('/')[1]
|
|
||||||
}),
|
|
||||||
type: 'error',
|
|
||||||
theme
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
video = { type: item.mimeType.split('/')[1], uri: d }
|
|
||||||
} else {
|
|
||||||
if (typesImage.includes(d.split('.').pop() || '')) {
|
|
||||||
images.push({ type: d.split('.').pop()!, uri: d })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (typesVideo.includes(d.split('.').pop() || '')) {
|
|
||||||
video = { type: d.split('.').pop()!, uri: d }
|
|
||||||
return
|
|
||||||
}
|
|
||||||
text = !text ? d : text.concat(text, `\n${d}`)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!text && (!images || !images.length) && !video) {
|
if (!text && !media.length) {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
navigationRef.navigate('Screen-Compose', {
|
navigationRef.navigate('Screen-Compose', { type: 'share', text, media })
|
||||||
type: 'share',
|
|
||||||
text,
|
|
||||||
images,
|
|
||||||
video
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[instanceActive]
|
[instanceActive]
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
import { ActionSheetOptions } from '@expo/react-native-action-sheet'
|
import { ActionSheetOptions } from '@expo/react-native-action-sheet'
|
||||||
import * as ImageManipulator from 'expo-image-manipulator'
|
import { store } from '@root/store'
|
||||||
import * as ImagePicker from 'expo-image-picker'
|
import { getInstanceConfigurationStatusMaxAttachments } from '@utils/slices/instancesSlice'
|
||||||
import {
|
import * as ExpoImagePicker from 'expo-image-picker'
|
||||||
ImageInfo,
|
|
||||||
UIImagePickerPresentationStyle
|
|
||||||
} from 'expo-image-picker/build/ImagePicker.types'
|
|
||||||
import i18next from 'i18next'
|
import i18next from 'i18next'
|
||||||
import { Alert, Linking, Platform } from 'react-native'
|
import { Alert, Linking } from 'react-native'
|
||||||
|
import ImagePicker, {
|
||||||
|
Image,
|
||||||
|
ImageOrVideo
|
||||||
|
} from 'react-native-image-crop-picker'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
mediaTypes?: ImagePicker.MediaTypeOptions
|
mediaType?: 'photo' | 'video'
|
||||||
resize?: { width?: number; height?: number } // Resize mode contain
|
resize?: { width?: number; height?: number }
|
||||||
|
maximum?: number
|
||||||
|
indicateMaximum?: boolean
|
||||||
showActionSheetWithOptions: (
|
showActionSheetWithOptions: (
|
||||||
options: ActionSheetOptions,
|
options: ActionSheetOptions,
|
||||||
callback: (i?: number | undefined) => void | Promise<void>
|
callback: (i?: number | undefined) => void | Promise<void>
|
||||||
@ -19,134 +22,157 @@ export interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mediaSelector = async ({
|
const mediaSelector = async ({
|
||||||
mediaTypes = ImagePicker.MediaTypeOptions.All,
|
mediaType,
|
||||||
resize,
|
resize,
|
||||||
|
maximum,
|
||||||
|
indicateMaximum = false,
|
||||||
showActionSheetWithOptions
|
showActionSheetWithOptions
|
||||||
}: Props): Promise<ImageInfo> => {
|
}: Props): Promise<ImageOrVideo[]> => {
|
||||||
return new Promise((resolve, reject) => {
|
const checkLibraryPermission = async (): Promise<boolean> => {
|
||||||
const resolveResult = async (result: ImageInfo) => {
|
const { status } =
|
||||||
if (resize && result.type === 'image') {
|
await ExpoImagePicker.requestMediaLibraryPermissionsAsync()
|
||||||
let newResult: ImageManipulator.ImageResult
|
if (status !== 'granted') {
|
||||||
if (resize.width && resize.height) {
|
Alert.alert(
|
||||||
if (resize.width / resize.height > result.width / result.height) {
|
i18next.t('componentMediaSelector:library.alert.title'),
|
||||||
newResult = await ImageManipulator.manipulateAsync(result.uri, [
|
i18next.t('componentMediaSelector:library.alert.message'),
|
||||||
{ resize: { width: resize.width } }
|
[
|
||||||
])
|
{
|
||||||
} else {
|
text: i18next.t('common:buttons.cancel'),
|
||||||
newResult = await ImageManipulator.manipulateAsync(result.uri, [
|
style: 'cancel',
|
||||||
{ resize: { height: resize.height } }
|
onPress: () =>
|
||||||
])
|
analytics('mediaSelector_nopermission', {
|
||||||
|
action: 'cancel'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: i18next.t(
|
||||||
|
'componentMediaSelector:library.alert.buttons.settings'
|
||||||
|
),
|
||||||
|
style: 'default',
|
||||||
|
onPress: () => {
|
||||||
|
analytics('mediaSelector_nopermission', {
|
||||||
|
action: 'settings'
|
||||||
|
})
|
||||||
|
Linking.openURL('app-settings:')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
]
|
||||||
newResult = await ImageManipulator.manipulateAsync(result.uri, [
|
)
|
||||||
{ resize }
|
return false
|
||||||
])
|
} else {
|
||||||
}
|
return true
|
||||||
resolve({ ...newResult, cancelled: false })
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const _maximum =
|
||||||
|
maximum ||
|
||||||
|
getInstanceConfigurationStatusMaxAttachments(store.getState()) ||
|
||||||
|
4
|
||||||
|
|
||||||
|
const options = () => {
|
||||||
|
switch (mediaType) {
|
||||||
|
case 'photo':
|
||||||
|
return [
|
||||||
|
i18next.t(
|
||||||
|
'componentMediaSelector:options.image',
|
||||||
|
indicateMaximum ? { context: 'max', max: _maximum } : undefined
|
||||||
|
),
|
||||||
|
i18next.t('common:buttons.cancel')
|
||||||
|
]
|
||||||
|
case 'video':
|
||||||
|
return [
|
||||||
|
i18next.t(
|
||||||
|
'componentMediaSelector:options.video',
|
||||||
|
indicateMaximum ? { context: 'max', max: 1 } : undefined
|
||||||
|
),
|
||||||
|
i18next.t('common:buttons.cancel')
|
||||||
|
]
|
||||||
|
default:
|
||||||
|
return [
|
||||||
|
i18next.t(
|
||||||
|
'componentMediaSelector:options.image',
|
||||||
|
indicateMaximum ? { context: 'max', max: _maximum } : undefined
|
||||||
|
),
|
||||||
|
i18next.t(
|
||||||
|
'componentMediaSelector:options.video',
|
||||||
|
indicateMaximum ? { context: 'max', max: 1 } : undefined
|
||||||
|
),
|
||||||
|
i18next.t('common:buttons.cancel')
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const selectImage = async () => {
|
||||||
|
const images = await ImagePicker.openPicker({
|
||||||
|
mediaType: 'photo',
|
||||||
|
includeExif: false,
|
||||||
|
multiple: true,
|
||||||
|
minFiles: 1,
|
||||||
|
maxFiles: _maximum
|
||||||
|
}).catch(() => {})
|
||||||
|
|
||||||
|
if (!images) {
|
||||||
|
return reject()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resize) {
|
||||||
|
return resolve(images)
|
||||||
} else {
|
} else {
|
||||||
resolve(result)
|
const croppedImages: Image[] = []
|
||||||
|
for (const image of images) {
|
||||||
|
const croppedImage = await ImagePicker.openCropper({
|
||||||
|
mediaType: 'photo',
|
||||||
|
path: image.path,
|
||||||
|
width: resize.width,
|
||||||
|
height: resize.height
|
||||||
|
}).catch(() => {})
|
||||||
|
croppedImage && croppedImages.push(croppedImage)
|
||||||
|
}
|
||||||
|
return resolve(croppedImages)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const selectVideo = async () => {
|
||||||
|
const video = await ImagePicker.openPicker({
|
||||||
|
mediaType: 'video',
|
||||||
|
includeExif: false
|
||||||
|
}).catch(() => {})
|
||||||
|
|
||||||
|
if (video) {
|
||||||
|
return resolve([video])
|
||||||
|
} else {
|
||||||
|
return reject()
|
||||||
|
}
|
||||||
|
}
|
||||||
showActionSheetWithOptions(
|
showActionSheetWithOptions(
|
||||||
{
|
{
|
||||||
title: i18next.t('componentMediaSelector:title'),
|
title: i18next.t('componentMediaSelector:title'),
|
||||||
options: [
|
options: options(),
|
||||||
i18next.t('componentMediaSelector:options.library'),
|
cancelButtonIndex: mediaType ? 1 : 2
|
||||||
i18next.t('componentMediaSelector:options.photo'),
|
|
||||||
i18next.t('componentMediaSelector:options.cancel')
|
|
||||||
],
|
|
||||||
cancelButtonIndex: 2
|
|
||||||
},
|
},
|
||||||
async buttonIndex => {
|
async buttonIndex => {
|
||||||
if (buttonIndex === 0) {
|
if (!(await checkLibraryPermission())) {
|
||||||
const { status } =
|
return reject()
|
||||||
await ImagePicker.requestMediaLibraryPermissionsAsync()
|
}
|
||||||
if (status !== 'granted') {
|
|
||||||
Alert.alert(
|
|
||||||
i18next.t('componentMediaSelector:library.alert.title'),
|
|
||||||
i18next.t('componentMediaSelector:library.alert.message'),
|
|
||||||
[
|
|
||||||
{
|
|
||||||
text: i18next.t(
|
|
||||||
'componentMediaSelector:library.alert.buttons.cancel'
|
|
||||||
),
|
|
||||||
style: 'cancel',
|
|
||||||
onPress: () =>
|
|
||||||
analytics('mediaSelector_nopermission', {
|
|
||||||
action: 'cancel'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18next.t(
|
|
||||||
'componentMediaSelector:library.alert.buttons.settings'
|
|
||||||
),
|
|
||||||
style: 'default',
|
|
||||||
onPress: () => {
|
|
||||||
analytics('mediaSelector_nopermission', {
|
|
||||||
action: 'settings'
|
|
||||||
})
|
|
||||||
Linking.openURL('app-settings:')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
const result = await ImagePicker.launchImageLibraryAsync({
|
|
||||||
mediaTypes,
|
|
||||||
exif: false,
|
|
||||||
presentationStyle:
|
|
||||||
Platform.OS === 'ios' && parseInt(Platform.Version) < 13
|
|
||||||
? UIImagePickerPresentationStyle.FULL_SCREEN
|
|
||||||
: UIImagePickerPresentationStyle.AUTOMATIC
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!result.cancelled) {
|
switch (mediaType) {
|
||||||
await resolveResult(result)
|
case 'photo':
|
||||||
|
if (buttonIndex === 0) {
|
||||||
|
await selectImage()
|
||||||
}
|
}
|
||||||
}
|
break
|
||||||
} else if (buttonIndex === 1) {
|
case 'video':
|
||||||
const { status } = await ImagePicker.requestCameraPermissionsAsync()
|
if (buttonIndex === 0) {
|
||||||
if (status !== 'granted') {
|
await selectVideo()
|
||||||
Alert.alert(
|
|
||||||
i18next.t('componentMediaSelector:photo.alert.title'),
|
|
||||||
i18next.t('componentMediaSelector:photo.alert.message'),
|
|
||||||
[
|
|
||||||
{
|
|
||||||
text: i18next.t(
|
|
||||||
'componentMediaSelector:photo.alert.buttons.cancel'
|
|
||||||
),
|
|
||||||
style: 'cancel',
|
|
||||||
onPress: () => {
|
|
||||||
analytics('compose_addattachment_camera_nopermission', {
|
|
||||||
action: 'cancel'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: i18next.t(
|
|
||||||
'componentMediaSelector:photo.alert.buttons.settings'
|
|
||||||
),
|
|
||||||
style: 'default',
|
|
||||||
onPress: () => {
|
|
||||||
analytics('compose_addattachment_camera_nopermission', {
|
|
||||||
action: 'settings'
|
|
||||||
})
|
|
||||||
Linking.openURL('app-settings:')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
const result = await ImagePicker.launchCameraAsync({
|
|
||||||
mediaTypes,
|
|
||||||
exif: false
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!result.cancelled) {
|
|
||||||
await resolveResult(result)
|
|
||||||
}
|
}
|
||||||
}
|
break
|
||||||
|
default:
|
||||||
|
if (buttonIndex === 0) {
|
||||||
|
await selectImage()
|
||||||
|
} else if (buttonIndex === 1) {
|
||||||
|
await selectVideo()
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,27 +1,17 @@
|
|||||||
{
|
{
|
||||||
"title": "Select media source",
|
"title": "Select media source",
|
||||||
"options": {
|
"options": {
|
||||||
"library": "Upload from library",
|
"image": "Upload photos",
|
||||||
"photo": "Take a photo",
|
"image_max": "Upload photos (max {{max}})",
|
||||||
"cancel": "$t(common:buttons.cancel)"
|
"video": "Upload video",
|
||||||
|
"video_max": "Upload video (max {{max}})"
|
||||||
},
|
},
|
||||||
"library": {
|
"library": {
|
||||||
"alert": {
|
"alert": {
|
||||||
"title": "No permission",
|
"title": "No permission",
|
||||||
"message": "Require photo library read permission to upload",
|
"message": "Require photo library read permission to upload",
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"settings": "Update setting",
|
"settings": "Update setting"
|
||||||
"cancel": "$t(common:buttons.cancel)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"photo": {
|
|
||||||
"alert": {
|
|
||||||
"title": "No permission",
|
|
||||||
"message": "Require camera usage permission to upload",
|
|
||||||
"buttons": {
|
|
||||||
"settings": "Update setting",
|
|
||||||
"cancel": "$t(common:buttons.cancel)"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,18 +136,6 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
|||||||
])
|
])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const uploadImage = async ({
|
|
||||||
type,
|
|
||||||
uri
|
|
||||||
}: {
|
|
||||||
type: 'image' | 'video'
|
|
||||||
uri: string
|
|
||||||
}) => {
|
|
||||||
await uploadAttachment({
|
|
||||||
composeDispatch,
|
|
||||||
imageInfo: { type, uri, width: 100, height: 100 }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
switch (params?.type) {
|
switch (params?.type) {
|
||||||
case 'share':
|
case 'share':
|
||||||
if (params.text) {
|
if (params.text) {
|
||||||
@ -158,12 +146,13 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
|||||||
disableDebounce: true
|
disableDebounce: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (params.images?.length) {
|
if (params.media?.length) {
|
||||||
params.images.forEach(image => {
|
for (const m of params.media) {
|
||||||
uploadImage({ type: 'image', uri: image.uri })
|
uploadAttachment({
|
||||||
})
|
composeDispatch,
|
||||||
} else if (params.video) {
|
media: { ...m, width: 100, height: 100 }
|
||||||
uploadImage({ type: 'video', uri: params.video.uri })
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'edit':
|
case 'edit':
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import * as Crypto from 'expo-crypto'
|
import * as Crypto from 'expo-crypto'
|
||||||
import { ImageInfo } from 'expo-image-picker/build/ImagePicker.types'
|
|
||||||
import * as VideoThumbnails from 'expo-video-thumbnails'
|
import * as VideoThumbnails from 'expo-video-thumbnails'
|
||||||
import { Dispatch } from 'react'
|
import { Dispatch } from 'react'
|
||||||
import { Alert } from 'react-native'
|
import { Alert } from 'react-native'
|
||||||
@ -8,6 +7,7 @@ import { ActionSheetOptions } from '@expo/react-native-action-sheet'
|
|||||||
import i18next from 'i18next'
|
import i18next from 'i18next'
|
||||||
import apiInstance from '@api/instance'
|
import apiInstance from '@api/instance'
|
||||||
import mediaSelector from '@components/mediaSelector'
|
import mediaSelector from '@components/mediaSelector'
|
||||||
|
import { ImageOrVideo } from 'react-native-image-crop-picker'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
composeDispatch: Dispatch<ComposeAction>
|
composeDispatch: Dispatch<ComposeAction>
|
||||||
@ -19,39 +19,38 @@ export interface Props {
|
|||||||
|
|
||||||
export const uploadAttachment = async ({
|
export const uploadAttachment = async ({
|
||||||
composeDispatch,
|
composeDispatch,
|
||||||
imageInfo
|
media
|
||||||
}: {
|
}: {
|
||||||
composeDispatch: Dispatch<ComposeAction>
|
composeDispatch: Dispatch<ComposeAction>
|
||||||
imageInfo: Pick<ImageInfo, 'type' | 'uri' | 'width' | 'height'>
|
media: Pick<ImageOrVideo, 'path' | 'mime' | 'width' | 'height'>
|
||||||
}) => {
|
}) => {
|
||||||
const hash = await Crypto.digestStringAsync(
|
const hash = await Crypto.digestStringAsync(
|
||||||
Crypto.CryptoDigestAlgorithm.SHA256,
|
Crypto.CryptoDigestAlgorithm.SHA256,
|
||||||
imageInfo.uri + Math.random()
|
media.path + Math.random()
|
||||||
)
|
)
|
||||||
|
|
||||||
let attachmentType: string
|
let attachmentType: string
|
||||||
|
|
||||||
switch (imageInfo.type) {
|
switch (media.mime.split('/')[0]) {
|
||||||
case 'image':
|
case 'image':
|
||||||
console.log('uri', imageInfo.uri)
|
attachmentType = `image/${media.path.split('.')[1]}`
|
||||||
attachmentType = `image/${imageInfo.uri.split('.')[1]}`
|
|
||||||
composeDispatch({
|
composeDispatch({
|
||||||
type: 'attachment/upload/start',
|
type: 'attachment/upload/start',
|
||||||
payload: {
|
payload: {
|
||||||
local: { ...imageInfo, local_thumbnail: imageInfo.uri, hash },
|
local: { ...media, type: 'image', local_thumbnail: media.path, hash },
|
||||||
uploading: true
|
uploading: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
case 'video':
|
case 'video':
|
||||||
attachmentType = `video/${imageInfo.uri.split('.')[1]}`
|
attachmentType = `video/${media.path.split('.')[1]}`
|
||||||
VideoThumbnails.getThumbnailAsync(imageInfo.uri)
|
VideoThumbnails.getThumbnailAsync(media.path)
|
||||||
.then(({ uri, width, height }) =>
|
.then(({ uri, width, height }) =>
|
||||||
composeDispatch({
|
composeDispatch({
|
||||||
type: 'attachment/upload/start',
|
type: 'attachment/upload/start',
|
||||||
payload: {
|
payload: {
|
||||||
local: {
|
local: {
|
||||||
...imageInfo,
|
...media,
|
||||||
|
type: 'video',
|
||||||
local_thumbnail: uri,
|
local_thumbnail: uri,
|
||||||
hash,
|
hash,
|
||||||
width,
|
width,
|
||||||
@ -65,7 +64,7 @@ export const uploadAttachment = async ({
|
|||||||
composeDispatch({
|
composeDispatch({
|
||||||
type: 'attachment/upload/start',
|
type: 'attachment/upload/start',
|
||||||
payload: {
|
payload: {
|
||||||
local: { ...imageInfo, hash },
|
local: { ...media, type: 'video', hash },
|
||||||
uploading: true
|
uploading: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -76,7 +75,7 @@ export const uploadAttachment = async ({
|
|||||||
composeDispatch({
|
composeDispatch({
|
||||||
type: 'attachment/upload/start',
|
type: 'attachment/upload/start',
|
||||||
payload: {
|
payload: {
|
||||||
local: { ...imageInfo, hash },
|
local: { ...media, type: 'unknown', hash },
|
||||||
uploading: true
|
uploading: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -106,14 +105,14 @@ export const uploadAttachment = async ({
|
|||||||
|
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('file', {
|
formData.append('file', {
|
||||||
// @ts-ignore
|
uri: `file://${media.path}`,
|
||||||
uri: imageInfo.uri,
|
|
||||||
name: attachmentType,
|
name: attachmentType,
|
||||||
type: attachmentType
|
type: attachmentType
|
||||||
})
|
} as any)
|
||||||
|
|
||||||
return apiInstance<Mastodon.Attachment>({
|
return apiInstance<Mastodon.Attachment>({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
|
version: 'v2',
|
||||||
url: 'media',
|
url: 'media',
|
||||||
body: formData
|
body: formData
|
||||||
})
|
})
|
||||||
@ -121,7 +120,7 @@ export const uploadAttachment = async ({
|
|||||||
if (res.body.id) {
|
if (res.body.id) {
|
||||||
composeDispatch({
|
composeDispatch({
|
||||||
type: 'attachment/upload/end',
|
type: 'attachment/upload/end',
|
||||||
payload: { remote: res.body, local: imageInfo }
|
payload: { remote: res.body, local: media }
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
uploadFailed()
|
uploadFailed()
|
||||||
@ -136,8 +135,13 @@ const chooseAndUploadAttachment = async ({
|
|||||||
composeDispatch,
|
composeDispatch,
|
||||||
showActionSheetWithOptions
|
showActionSheetWithOptions
|
||||||
}: Props): Promise<any> => {
|
}: Props): Promise<any> => {
|
||||||
const result = await mediaSelector({ showActionSheetWithOptions })
|
const result = await mediaSelector({
|
||||||
await uploadAttachment({ composeDispatch, imageInfo: result })
|
indicateMaximum: true,
|
||||||
|
showActionSheetWithOptions
|
||||||
|
})
|
||||||
|
for (const media of result) {
|
||||||
|
uploadAttachment({ composeDispatch, media })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default chooseAndUploadAttachment
|
export default chooseAndUploadAttachment
|
||||||
|
@ -83,9 +83,9 @@ const ComposeTextInput: React.FC = () => {
|
|||||||
if (nativeEvent.linkUri) {
|
if (nativeEvent.linkUri) {
|
||||||
uploadAttachment({
|
uploadAttachment({
|
||||||
composeDispatch,
|
composeDispatch,
|
||||||
imageInfo: {
|
media: {
|
||||||
uri: nativeEvent.linkUri,
|
path: nativeEvent.linkUri,
|
||||||
type: 'image',
|
mime: 'image/gif',
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 100
|
height: 100
|
||||||
}
|
}
|
||||||
|
8
src/screens/Compose/utils/types.d.ts
vendored
8
src/screens/Compose/utils/types.d.ts
vendored
@ -1,6 +1,12 @@
|
|||||||
|
import { ImageOrVideo } from 'react-native-image-crop-picker'
|
||||||
|
|
||||||
export type ExtendedAttachment = {
|
export type ExtendedAttachment = {
|
||||||
remote?: Mastodon.Attachment
|
remote?: Mastodon.Attachment
|
||||||
local?: App.IImageInfo & { local_thumbnail?: string; hash?: string }
|
local?: Pick<ImageOrVideo, 'path' | 'width' | 'height'> & {
|
||||||
|
type: 'image' | 'video' | 'unknown'
|
||||||
|
local_thumbnail?: string
|
||||||
|
hash?: string
|
||||||
|
}
|
||||||
uploading?: boolean
|
uploading?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ import { MenuRow } from '@components/Menu'
|
|||||||
import { useActionSheet } from '@expo/react-native-action-sheet'
|
import { useActionSheet } from '@expo/react-native-action-sheet'
|
||||||
import { useProfileMutation, useProfileQuery } from '@utils/queryHooks/profile'
|
import { useProfileMutation, useProfileQuery } from '@utils/queryHooks/profile'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import * as ImagePicker from 'expo-image-picker'
|
|
||||||
import React, { RefObject } from 'react'
|
import React, { RefObject } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import FlashMessage from 'react-native-flash-message'
|
import FlashMessage from 'react-native-flash-message'
|
||||||
@ -30,9 +29,13 @@ const ProfileAvatarHeader: React.FC<Props> = ({ type, messageRef }) => {
|
|||||||
iconBack='ChevronRight'
|
iconBack='ChevronRight'
|
||||||
onPress={async () => {
|
onPress={async () => {
|
||||||
const image = await mediaSelector({
|
const image = await mediaSelector({
|
||||||
|
mediaType: 'photo',
|
||||||
|
maximum: 1,
|
||||||
showActionSheetWithOptions,
|
showActionSheetWithOptions,
|
||||||
mediaTypes: ImagePicker.MediaTypeOptions.Images,
|
resize:
|
||||||
resize: { width: 400, height: 400 }
|
type === 'avatar'
|
||||||
|
? { width: 400, height: 400 }
|
||||||
|
: { width: 1500, height: 500 }
|
||||||
})
|
})
|
||||||
mutation.mutate({
|
mutation.mutate({
|
||||||
theme,
|
theme,
|
||||||
@ -43,7 +46,7 @@ const ProfileAvatarHeader: React.FC<Props> = ({ type, messageRef }) => {
|
|||||||
failed: true
|
failed: true
|
||||||
},
|
},
|
||||||
type,
|
type,
|
||||||
data: image.uri
|
data: image[0].path
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -52,8 +52,7 @@ export type RootStackParamList = {
|
|||||||
| {
|
| {
|
||||||
type: 'share'
|
type: 'share'
|
||||||
text?: string
|
text?: string
|
||||||
images?: { type: string; uri: string }[]
|
media?: { path: string; mime: string }[]
|
||||||
video?: { type: string; uri: string }
|
|
||||||
}
|
}
|
||||||
| undefined
|
| undefined
|
||||||
'Screen-ImagesViewer': {
|
'Screen-ImagesViewer': {
|
||||||
|
@ -71,11 +71,10 @@ const mutationFunction = async ({ type, data }: MutationVarsProfile) => {
|
|||||||
})
|
})
|
||||||
} else if (type === 'avatar' || type === 'header') {
|
} else if (type === 'avatar' || type === 'header') {
|
||||||
formData.append(type, {
|
formData.append(type, {
|
||||||
// @ts-ignore
|
uri: `file://${data}`,
|
||||||
uri: data,
|
|
||||||
name: 'image/jpeg',
|
name: 'image/jpeg',
|
||||||
type: 'image/jpeg'
|
type: 'image/jpeg'
|
||||||
})
|
} as any)
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
formData.append(type, data)
|
formData.append(type, data)
|
||||||
|
12
yarn.lock
12
yarn.lock
@ -4421,13 +4421,6 @@ expo-image-loader@~3.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/expo-image-loader/-/expo-image-loader-3.2.0.tgz#d98b021660edef7243f7c5ec011b8d0545626d41"
|
resolved "https://registry.yarnpkg.com/expo-image-loader/-/expo-image-loader-3.2.0.tgz#d98b021660edef7243f7c5ec011b8d0545626d41"
|
||||||
integrity sha512-LU3Q2prn64/HxdToDmxgMIRXS1ZvD9Q3iCxRVTZn1fPQNNDciIQFE5okaa74Ogx20DFHs90r6WoUd7w9Af1OGQ==
|
integrity sha512-LU3Q2prn64/HxdToDmxgMIRXS1ZvD9Q3iCxRVTZn1fPQNNDciIQFE5okaa74Ogx20DFHs90r6WoUd7w9Af1OGQ==
|
||||||
|
|
||||||
expo-image-manipulator@10.3.1:
|
|
||||||
version "10.3.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/expo-image-manipulator/-/expo-image-manipulator-10.3.1.tgz#e16dd76a550c7f5d653a2a666f26429eba311a6b"
|
|
||||||
integrity sha512-D08dMD6MerxBu23DmCIhurySQih+eLP7VxKvY5mWqYz9payjDOS1cAGs3HvXPrEDusPQFQ0uIfqc+oqeMNFBIA==
|
|
||||||
dependencies:
|
|
||||||
expo-image-loader "~3.2.0"
|
|
||||||
|
|
||||||
expo-image-picker@13.1.1:
|
expo-image-picker@13.1.1:
|
||||||
version "13.1.1"
|
version "13.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/expo-image-picker/-/expo-image-picker-13.1.1.tgz#e039bf9748ccb7b89370ff2969c3ef07cc949192"
|
resolved "https://registry.yarnpkg.com/expo-image-picker/-/expo-image-picker-13.1.1.tgz#e039bf9748ccb7b89370ff2969c3ef07cc949192"
|
||||||
@ -7517,6 +7510,11 @@ react-native-htmlview@0.16.0:
|
|||||||
entities "^1.1.1"
|
entities "^1.1.1"
|
||||||
htmlparser2-without-node-native "^3.9.2"
|
htmlparser2-without-node-native "^3.9.2"
|
||||||
|
|
||||||
|
react-native-image-crop-picker@^0.37.3:
|
||||||
|
version "0.37.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-native-image-crop-picker/-/react-native-image-crop-picker-0.37.3.tgz#f260e40b6a6ba8e98f4db3dde25a8f09e0936385"
|
||||||
|
integrity sha512-ih+0pWWRUNEFQyaHwGbH9rqJNOb7EBYMwKJhTY0VmsKIA9E+usfwMmQXAFIfOnee7fTn0A2vOXkBCPQZwyvnQw==
|
||||||
|
|
||||||
react-native-image-keyboard@^2.2.0:
|
react-native-image-keyboard@^2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-native-image-keyboard/-/react-native-image-keyboard-2.2.0.tgz#dc3f90aaaac20a79315015a330e62e85547e0674"
|
resolved "https://registry.yarnpkg.com/react-native-image-keyboard/-/react-native-image-keyboard-2.2.0.tgz#dc3f90aaaac20a79315015a330e62e85547e0674"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user