|
1 | 1 | import { useState, type FC, useRef } from "react"; |
2 | 2 | import type { ScrollView as RNScrollVIew } from "react-native"; |
3 | | -import { InteractionManager, StyleSheet } from "react-native"; |
4 | | -import { TouchableWithoutFeedback } from "react-native-gesture-handler"; |
| 3 | +import { StyleSheet } from "react-native"; |
| 4 | +import { PanGestureHandler, TouchableWithoutFeedback } from "react-native-gesture-handler"; |
5 | 5 | import Animated, { Easing, runOnJS, interpolate, interpolateColor, useAnimatedStyle, useSharedValue, withTiming, LinearTransition, FadeInLeft } from "react-native-reanimated"; |
6 | 6 | import { Camera, useCameraPermission, useCameraDevice } from "react-native-vision-camera"; |
7 | 7 |
|
8 | | -import { Album, Image as ImageIcon, Maximize2, Plus, X } from "@tamagui/lucide-icons"; |
| 8 | +import { Image as ImageIcon, Maximize2, Plus, X } from "@tamagui/lucide-icons"; |
9 | 9 | import { BlurView } from "expo-blur"; |
10 | 10 | import { Image } from "expo-image"; |
11 | 11 | import * as MediaLibrary from "expo-media-library"; |
12 | 12 | import { Button, ScrollView, Stack, Text, View, XStack, YStack, useWindowDimensions } from "tamagui"; |
13 | 13 |
|
14 | 14 | import { IS_ANDROID } from "@/constants"; |
15 | 15 | import { useColors } from "@/hooks/use-colors"; |
16 | | -import { useCreateShots } from "@/hooks/use-create-shots"; |
17 | 16 | import { useIsLogin } from "@/hooks/use-is-login"; |
18 | 17 | import { useRootNavigation } from "@/hooks/use-navigation"; |
19 | 18 | import { usePickImages } from "@/hooks/use-pick-images"; |
@@ -195,150 +194,167 @@ export const CreateShortsButton: FC = () => { |
195 | 194 | }; |
196 | 195 |
|
197 | 196 | return ( |
198 | | - <Stack width={buttonSize} height={buttonSize} marginHorizontal={15} overflow="visible" zIndex={999}> |
199 | | - <Animated.View |
200 | | - style={[{ |
201 | | - borderRadius: 10, |
202 | | - alignSelf: "center", |
203 | | - position: "absolute", |
204 | | - alignItems: "center", |
205 | | - justifyContent: "center", |
206 | | - overflow: "hidden", |
207 | | - flex: 1, |
208 | | - }, containerAnimStyle]} |
209 | | - > |
210 | | - <Animated.View style={[actionsContainerAnimStyle, { width: targetWidth, height: targetHeight, position: "absolute", borderRadius: 10, padding: 12 }]}> |
211 | | - <BlurView tint="dark" intensity={IS_ANDROID ? 10 : 30} style={StyleSheet.absoluteFillObject}/> |
212 | | - <XStack flex={1} gap={6}> |
213 | | - <XStack flex={1} > |
214 | | - <ScrollView ref={scrollViewRef} flex={1}> |
215 | | - <XStack flexWrap="wrap" justifyContent="space-between"> |
216 | | - <Animated.View layout={LinearTransition.duration(150)} entering={FadeInLeft.duration(150)}> |
217 | | - <TouchableWithoutFeedback |
218 | | - style={{ marginBottom: 4 }} |
219 | | - onPress={selectImageFromAlbum} |
220 | | - > |
221 | | - <View width={photoSize} height={photoSize} backgroundColor={"$primary"} borderRadius={10} alignItems="center" justifyContent="center"> |
222 | | - <ImageIcon size={"$3"}/> |
223 | | - </View> |
224 | | - </TouchableWithoutFeedback> |
225 | | - </Animated.View> |
226 | | - { |
227 | | - mediaPermissionResponse?.granted && photos.map((item) => { |
228 | | - const isSelected = selectedPhotos.some(photo => photo.uri === item.uri); |
229 | | - |
230 | | - return ( |
231 | | - <Animated.View key={item.uri} layout={LinearTransition.duration(150)} entering={FadeInLeft.duration(150)}> |
232 | | - <TouchableWithoutFeedback |
233 | | - style={{ marginBottom: 4 }} |
234 | | - onPress={() => { |
235 | | - if (isSelected) { |
236 | | - setSelectedPhotos(selectedPhotos.filter(({ uri }) => uri !== item.uri)); |
237 | | - return; |
238 | | - } |
239 | | - |
240 | | - setSelectedPhotos([...selectedPhotos, item]); |
241 | | - }} |
242 | | - > |
243 | | - <View width={photoSize} height={photoSize}> |
244 | | - <Image source={{ uri: item.uri }} style={{ width: "100%", height: "100%", borderRadius: 10 }}/> |
| 197 | + <PanGestureHandler> |
| 198 | + <Stack width={buttonSize} height={buttonSize} marginHorizontal={15} overflow="visible" zIndex={999}> |
| 199 | + <Animated.View |
| 200 | + style={[{ |
| 201 | + borderRadius: 10, |
| 202 | + alignSelf: "center", |
| 203 | + position: "absolute", |
| 204 | + alignItems: "center", |
| 205 | + justifyContent: "center", |
| 206 | + overflow: "hidden", |
| 207 | + flex: 1, |
| 208 | + }, containerAnimStyle]} |
| 209 | + > |
| 210 | + <Animated.View |
| 211 | + style={[ |
| 212 | + actionsContainerAnimStyle, { |
| 213 | + width: targetWidth, |
| 214 | + height: targetHeight, |
| 215 | + position: "absolute", |
| 216 | + borderRadius: 10, |
| 217 | + padding: 12, |
| 218 | + }] |
| 219 | + } |
| 220 | + > |
| 221 | + <BlurView |
| 222 | + tint="dark" |
| 223 | + intensity={30} |
| 224 | + experimentalBlurMethod="dimezisBlurView" |
| 225 | + style={StyleSheet.absoluteFillObject} |
| 226 | + /> |
| 227 | + <XStack flex={1} gap={6}> |
| 228 | + <XStack flex={1} > |
| 229 | + <ScrollView ref={scrollViewRef} flex={1}> |
| 230 | + <XStack flexWrap="wrap" justifyContent="space-between"> |
| 231 | + <Animated.View layout={LinearTransition.duration(150)} entering={FadeInLeft.duration(150)}> |
| 232 | + <TouchableWithoutFeedback |
| 233 | + style={{ marginBottom: 4 }} |
| 234 | + onPress={selectImageFromAlbum} |
| 235 | + > |
| 236 | + <View width={photoSize} height={photoSize} backgroundColor={"$primary"} borderRadius={10} alignItems="center" justifyContent="center"> |
| 237 | + <ImageIcon size={"$3"}/> |
| 238 | + </View> |
| 239 | + </TouchableWithoutFeedback> |
| 240 | + </Animated.View> |
| 241 | + { |
| 242 | + mediaPermissionResponse?.granted && photos.map((item) => { |
| 243 | + const isSelected = selectedPhotos.some(photo => photo.uri === item.uri); |
| 244 | + |
| 245 | + return ( |
| 246 | + <Animated.View key={item.uri} layout={LinearTransition.duration(150)} entering={FadeInLeft.duration(150)}> |
| 247 | + <TouchableWithoutFeedback |
| 248 | + style={{ marginBottom: 4 }} |
| 249 | + onPress={() => { |
| 250 | + if (isSelected) { |
| 251 | + setSelectedPhotos(selectedPhotos.filter(({ uri }) => uri !== item.uri)); |
| 252 | + return; |
| 253 | + } |
| 254 | + |
| 255 | + setSelectedPhotos([...selectedPhotos, item]); |
| 256 | + }} |
| 257 | + > |
| 258 | + <View width={photoSize} height={photoSize}> |
| 259 | + <Image source={{ uri: item.uri }} style={{ width: "100%", height: "100%", borderRadius: 10 }}/> |
| 260 | + <Stack |
| 261 | + position="absolute" |
| 262 | + right="$2" |
| 263 | + bottom="$2" |
| 264 | + width={15} |
| 265 | + height={15} |
| 266 | + borderRadius={50} |
| 267 | + alignItems="center" |
| 268 | + justifyContent="center" |
| 269 | + borderWidth={1} |
| 270 | + borderColor={"white"} |
| 271 | + > |
| 272 | + {isSelected && <Stack width={10} height={10} borderRadius={50} backgroundColor={"$primary"}/>} |
| 273 | + </Stack> |
| 274 | + </View> |
| 275 | + </TouchableWithoutFeedback> |
| 276 | + </Animated.View> |
| 277 | + ); |
| 278 | + }) |
| 279 | + } |
| 280 | + </XStack> |
| 281 | + </ScrollView> |
| 282 | + </XStack> |
| 283 | + <YStack flex={1} gap={8} borderRadius={10}> |
| 284 | + <Stack flex={1} borderRadius={10} overflow="hidden"> |
| 285 | + {hasPermission && ( |
| 286 | + device |
| 287 | + ? ( |
| 288 | + <Stack flex={1}> |
| 289 | + {device && ( |
| 290 | + <Camera |
| 291 | + ref={cameraRef} |
| 292 | + device={device} |
| 293 | + isActive={expanded} |
| 294 | + photo={true} |
| 295 | + video={false} |
| 296 | + audio={false} |
| 297 | + style={{ flex: 1 }} |
| 298 | + /> |
| 299 | + )} |
| 300 | + <XTouch onPress={handleOpenCamera} enableHaptics containerStyle={{ |
| 301 | + position: "absolute", |
| 302 | + right: 12, |
| 303 | + top: 12, |
| 304 | + }}> |
| 305 | + <Maximize2 color="white" size={25}/> |
| 306 | + </XTouch> |
| 307 | + |
| 308 | + <XTouch onPress={takePhoto} enableHaptics containerStyle={{ |
| 309 | + position: "absolute", |
| 310 | + left: "50%", |
| 311 | + bottom: 12, |
| 312 | + transform: [{ translateX: -15 }], |
| 313 | + }}> |
| 314 | + <Stack borderRadius={50} borderWidth={1} borderColor={"white"} width={30} height={30} alignItems="center" justifyContent="center"> |
245 | 315 | <Stack |
246 | | - position="absolute" |
247 | | - right="$2" |
248 | | - bottom="$2" |
249 | | - width={15} |
250 | | - height={15} |
251 | 316 | borderRadius={50} |
252 | | - alignItems="center" |
253 | | - justifyContent="center" |
254 | | - borderWidth={1} |
255 | | - borderColor={"white"} |
256 | | - > |
257 | | - {isSelected && <Stack width={10} height={10} borderRadius={50} backgroundColor={"$primary"}/>} |
258 | | - </Stack> |
259 | | - </View> |
260 | | - </TouchableWithoutFeedback> |
261 | | - </Animated.View> |
262 | | - ); |
263 | | - }) |
264 | | - } |
265 | | - </XStack> |
266 | | - </ScrollView> |
267 | | - </XStack> |
268 | | - <YStack flex={1} gap={8} borderRadius={10}> |
269 | | - <Stack flex={1} borderRadius={10} overflow="hidden"> |
270 | | - {hasPermission && ( |
271 | | - device |
272 | | - ? ( |
273 | | - <Stack flex={1}> |
274 | | - {device && ( |
275 | | - <Camera |
276 | | - ref={cameraRef} |
277 | | - device={device} |
278 | | - isActive={expanded} |
279 | | - photo={true} |
280 | | - video={false} |
281 | | - audio={false} |
282 | | - style={{ flex: 1 }} |
283 | | - /> |
284 | | - )} |
285 | | - <XTouch onPress={handleOpenCamera} enableHaptics containerStyle={{ |
286 | | - position: "absolute", |
287 | | - right: 12, |
288 | | - top: 12, |
289 | | - }}> |
290 | | - <Maximize2 color="white" size={25}/> |
291 | | - </XTouch> |
292 | | - |
293 | | - <XTouch onPress={takePhoto} enableHaptics containerStyle={{ |
294 | | - position: "absolute", |
295 | | - left: "50%", |
296 | | - bottom: 12, |
297 | | - transform: [{ translateX: -15 }], |
298 | | - }}> |
299 | | - <Stack borderRadius={50} borderWidth={1} borderColor={"white"} width={30} height={30} alignItems="center" justifyContent="center"> |
300 | | - <Stack |
301 | | - borderRadius={50} |
302 | | - backgroundColor={"white"} |
303 | | - width={25} |
304 | | - height={25} |
305 | | - /> |
306 | | - </Stack> |
307 | | - </XTouch> |
308 | | - </Stack> |
309 | | - ) |
310 | | - : ( |
311 | | - <View flex={1} alignItems="center" justifyContent="center"> |
312 | | - <Text textAlign="center" fontSize={"$1"}> |
| 317 | + backgroundColor={"white"} |
| 318 | + width={25} |
| 319 | + height={25} |
| 320 | + /> |
| 321 | + </Stack> |
| 322 | + </XTouch> |
| 323 | + </Stack> |
| 324 | + ) |
| 325 | + : ( |
| 326 | + <View flex={1} alignItems="center" justifyContent="center"> |
| 327 | + <Text textAlign="center" fontSize={"$1"}> |
313 | 328 | Please use physical device, this feature is not supported on simulator. |
314 | | - </Text> |
315 | | - </View> |
316 | | - ) |
317 | | - )} |
318 | | - </Stack> |
319 | | - <XStack alignItems="center" justifyContent="space-between" width={"100%"} gap={8}> |
320 | | - <Button |
321 | | - flex={1} |
322 | | - disabled={selectedPhotos.length === 0} |
323 | | - backgroundColor={selectedPhotos.length === 0 ? "$backgroundHover" : "$primary"} |
324 | | - onPress={onHandleNext} |
325 | | - > |
| 329 | + </Text> |
| 330 | + </View> |
| 331 | + ) |
| 332 | + )} |
| 333 | + </Stack> |
| 334 | + <XStack alignItems="center" justifyContent="space-between" width={"100%"} gap={8}> |
| 335 | + <Button |
| 336 | + flex={1} |
| 337 | + disabled={selectedPhotos.length === 0} |
| 338 | + backgroundColor={selectedPhotos.length === 0 ? "$backgroundHover" : "$primary"} |
| 339 | + onPress={onHandleNext} |
| 340 | + > |
326 | 341 | Next |
327 | | - </Button> |
328 | | - <Button onPress={toggle} backgroundColor={"$backgroundHover"} icon={<X size={22}/>} padding={12}/> |
329 | | - </XStack> |
330 | | - </YStack> |
331 | | - </XStack> |
332 | | - </Animated.View> |
| 342 | + </Button> |
| 343 | + <Button onPress={toggle} backgroundColor={"$backgroundHover"} icon={<X size={22}/>} padding={12}/> |
| 344 | + </XStack> |
| 345 | + </YStack> |
| 346 | + </XStack> |
| 347 | + </Animated.View> |
333 | 348 |
|
334 | | - <Animated.View style={[buttonAnimStyle, { flex: 1, justifyContent: "center", alignItems: "center" }]}> |
335 | | - <TouchableWithoutFeedback onPress={handleOnPress}> |
336 | | - <Stack flex={1} alignItems="center" justifyContent="center"> |
337 | | - <Plus color="white"/> |
338 | | - </Stack> |
339 | | - </TouchableWithoutFeedback> |
| 349 | + <Animated.View style={[buttonAnimStyle, { flex: 1, justifyContent: "center", alignItems: "center" }]}> |
| 350 | + <TouchableWithoutFeedback onPress={handleOnPress}> |
| 351 | + <Stack flex={1} alignItems="center" justifyContent="center"> |
| 352 | + <Plus color="white"/> |
| 353 | + </Stack> |
| 354 | + </TouchableWithoutFeedback> |
| 355 | + </Animated.View> |
340 | 356 | </Animated.View> |
341 | | - </Animated.View> |
342 | | - </Stack> |
| 357 | + </Stack> |
| 358 | + </PanGestureHandler> |
343 | 359 | ); |
344 | 360 | }; |
0 commit comments