diff --git a/modules/expo-bluesky-swiss-army/ios/ExpoBlueskySwissArmy.podspec b/modules/expo-bluesky-swiss-army/ios/ExpoBlueskySwissArmy.podspec
index be4b0eae45..b4af69e4de 100644
--- a/modules/expo-bluesky-swiss-army/ios/ExpoBlueskySwissArmy.podspec
+++ b/modules/expo-bluesky-swiss-army/ios/ExpoBlueskySwissArmy.podspec
@@ -10,7 +10,9 @@ Pod::Spec.new do |s|
s.static_framework = true
s.dependency 'ExpoModulesCore'
-
+ s.dependency 'SDWebImage', '~> 5.19.1'
+ s.dependency 'SDWebImageWebPCoder', '~> 0.14.6'
+
# Swift/Objective-C compatibility
s.pod_target_xcconfig = {
'DEFINES_MODULE' => 'YES',
diff --git a/modules/expo-bluesky-swiss-army/ios/PlatformInfo/ExpoPlatformInfoModule.swift b/modules/expo-bluesky-swiss-army/ios/PlatformInfo/ExpoPlatformInfoModule.swift
index 02bf5c6628..684d55785f 100644
--- a/modules/expo-bluesky-swiss-army/ios/PlatformInfo/ExpoPlatformInfoModule.swift
+++ b/modules/expo-bluesky-swiss-army/ios/PlatformInfo/ExpoPlatformInfoModule.swift
@@ -42,5 +42,9 @@ public class ExpoPlatformInfoModule: Module {
}
}
}
+
+ AsyncFunction("isAnimatedImage") { (url: URL) in
+ return ImageInfo.isAnimatedImage(url: url)
+ }
}
}
diff --git a/modules/expo-bluesky-swiss-army/ios/PlatformInfo/ImageInfo.swift b/modules/expo-bluesky-swiss-army/ios/PlatformInfo/ImageInfo.swift
new file mode 100644
index 0000000000..1156d534b6
--- /dev/null
+++ b/modules/expo-bluesky-swiss-army/ios/PlatformInfo/ImageInfo.swift
@@ -0,0 +1,22 @@
+//
+// IsAnimatedImage.swift
+// Pods
+//
+// Created by Hailey on 10/27/24.
+//
+
+import SDWebImage
+
+class ImageInfo {
+ static func isAnimatedImage(url: URL) -> Bool {
+ var path: String
+ if #available(iOS 16.0, *) {
+ path = url.path()
+ } else {
+ path = url.path
+ }
+
+ let image = SDAnimatedImage(contentsOfFile: path)
+ return image?.sd_isAnimated ?? false
+ }
+}
diff --git a/src/view/com/composer/photos/SelectPhotoBtn.tsx b/src/view/com/composer/photos/SelectPhotoBtn.tsx
index 37bfbafe60..da6556f16f 100644
--- a/src/view/com/composer/photos/SelectPhotoBtn.tsx
+++ b/src/view/com/composer/photos/SelectPhotoBtn.tsx
@@ -1,6 +1,11 @@
/* eslint-disable react-native-a11y/has-valid-accessibility-ignores-invert-colors */
import React, {useCallback} from 'react'
-import {msg} from '@lingui/macro'
+import {Dimensions, View} from 'react-native'
+import * as FS from 'expo-file-system'
+import {Image} from 'expo-image'
+import {Asset, AssetInfo, MediaType, PagedInfo} from 'expo-media-library'
+import * as MediaLibrary from 'expo-media-library'
+import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {usePhotoLibraryPermission} from '#/lib/hooks/usePermissions'
@@ -8,9 +13,13 @@ import {openPicker} from '#/lib/media/picker'
import {isNative} from '#/platform/detection'
import {ComposerImage, createComposerImage} from '#/state/gallery'
import {atoms as a, useTheme} from '#/alf'
-import {Button} from '#/components/Button'
+import {Button, ButtonText} from '#/components/Button'
+import * as Dialog from '#/components/Dialog'
+import {useDialogControl} from '#/components/Dialog'
import {useSheetWrapper} from '#/components/Dialog/sheet-wrapper'
-import {Image_Stroke2_Corner0_Rounded as Image} from '#/components/icons/Image'
+import {Image_Stroke2_Corner0_Rounded as ImageIcon} from '#/components/icons/Image'
+import {Play_Filled_Corner0_Rounded as PlayIcon} from '#/components/icons/Play'
+import {Text} from '#/components/Typography'
type Props = {
size: number
@@ -24,24 +33,32 @@ export function SelectPhotoBtn({size, disabled, onAdd}: Props) {
const t = useTheme()
const sheetWrapper = useSheetWrapper()
+ const control = useDialogControl()
+
const onPressSelectPhotos = useCallback(async () => {
- if (isNative && !(await requestPhotoAccessIfNeeded())) {
- return
- }
+ control.open()
- const images = await sheetWrapper(
- openPicker({
- selectionLimit: 4 - size,
- allowsMultipleSelection: true,
- }),
- )
-
- const results = await Promise.all(
- images.map(img => createComposerImage(img)),
- )
-
- onAdd(results)
- }, [requestPhotoAccessIfNeeded, size, onAdd, sheetWrapper])
+ // if (isNative && !(await requestPhotoAccessIfNeeded())) {
+ // return
+ // }
+ //
+ // const images = await sheetWrapper(
+ // openPicker({
+ // selectionLimit: 4 - size,
+ // allowsMultipleSelection: true,
+ // }),
+ // )
+ //
+ // if (images.length === 0) {
+ // return
+ // }
+ //
+ // const results = await Promise.all(
+ // images.map(img => createComposerImage(img)),
+ // )
+ //
+ // onAdd(results)
+ }, [control])
return (
+ )
+}
+
+function ImagePickerView({control}: {control: Dialog.DialogControlProps}) {
+ const [isLoading, setIsLoading] = React.useState(true)
+ const [pages, setPages] = React.useState[]>([])
+ const [selections, setSelections] = React.useState([])
+
+ const canPickMore = React.useMemo(() => {
+ if (selections.length === 0) {
+ return true
+ }
+ if (selections[0].mediaType === MediaType.video) {
+ return false
+ }
+ if (selections[0].mediaType === MediaType.photo && selections[0]) {
+ return selections.length < 4
+ }
+ }, [selections])
+
+ React.useEffect(() => {
+ ;(async () => {
+ const items = await MediaLibrary.getAssetsAsync({
+ first: 30,
+ mediaType: [MediaType.video, MediaType.photo],
+ })
+
+ setPages([items])
+ setIsLoading(false)
+ })()
+ }, [])
+
+ const items = pages.flatMap(page => page.assets)
+ const slices = items.reduce((acc, item, i) => {
+ if (i % 3 === 0) {
+ acc.push([item])
+ } else {
+ acc[acc.length - 1].push(item)
+ }
+ return acc
+ }, [])
+
+ return (
+ control.close()} />}
+ renderRight={() => {}} disabled={true} />}
+ style={[a.pb_sm]}>
+
+ Edit profile
+
+
+ }>
+
+ {slices.map((slice, i) => (
+
+ ))}
+
+
+ )
+}
+
+function ImagePickerRow({
+ items,
+ isFirstRow,
+}: {
+ items: Asset[]
+ isFirstRow: boolean
+}) {
+ return (
+
+ {items.map((item, i) => (
+
+ ))}
+
+ )
+}
+
+const IMAGE_WIDTH = Dimensions.get('window').width / 3 - 8
+
+function ImagePickerItem({
+ item,
+ index,
+ isFirstRow,
+}: {
+ item: Asset
+ index: number
+ isFirstRow: boolean
+}) {
+ const isVideo = item.mediaType === MediaType.video
+
+ return (
+
+
+ {isVideo ? (
+
+
+
+
+ {Math.floor(item.duration / 60)}:
+ {Math.floor(item.duration % 60)
+ .toString()
+ .padStart(2, '0')}
+
+
+
+ ) : null}
+
+ )
+}
+
+function SaveButton({
+ onPress,
+ disabled,
+}: {
+ onPress: () => void
+ disabled: boolean
+}) {
+ const {_} = useLingui()
+
+ return (
+
+ )
+}
+
+function CancelButton({onPress}: {onPress: () => void}) {
+ const {_} = useLingui()
+
+ return (
+
)
}