From 3ef867d89447b9dd0f0a736f180d774042660324 Mon Sep 17 00:00:00 2001 From: Hailey Date: Sun, 27 Oct 2024 00:29:38 -0700 Subject: [PATCH] implement base of image picker --- .../ios/ExpoBlueskySwissArmy.podspec | 4 +- .../PlatformInfo/ExpoPlatformInfoModule.swift | 4 + .../ios/PlatformInfo/ImageInfo.swift | 22 ++ .../com/composer/photos/SelectPhotoBtn.tsx | 254 ++++++++++++++++-- 4 files changed, 263 insertions(+), 21 deletions(-) create mode 100644 modules/expo-bluesky-swiss-army/ios/PlatformInfo/ImageInfo.swift 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 ( + ) }