import { useCallback, useMemo } from "react"; import { ActivityIndicator, Dimensions, Pressable, ScrollView, StyleSheet, Text, View, } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useRouter, useLocalSearchParams } from "expo-router"; import { Canvas, Image as SkImage, Shader, Fill, useImage, } from "@shopify/react-native-skia"; import Animated, { FadeIn, FadeInUp } from "react-native-reanimated"; import { ArrowLeft, Check, Export } from "phosphor-react-native"; import { GlassBottomSheet, GlassButton, GlassCard, GlassPill, GlassSlider } from "@/components/glass"; import { useWallpaperStore } from "@/stores/wallpaper.store"; import { useAnimation } from "@/hooks/useAnimation"; import { useGyroscope } from "@/hooks/useGyroscope"; import { useWallpaperExport } from "@/hooks/useWallpaperExport"; import { shaders } from "@/shaders"; import { ANIMATION_META, type AnimationType, } from "@/types/wallpaper"; import { colors, typography, spacing, layout } from "@/theme"; const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get("window"); const AVAILABLE_ANIMATIONS: AnimationType[] = [ "ken-burns", "color-shift", "particles", "vignette-pulse", "glitch", ]; export default function EditorScreen() { const insets = useSafeAreaInsets(); const router = useRouter(); const { id } = useLocalSearchParams<{ id: string }>(); const { sourceUri, animation, uniforms, particlePreset, setAnimation, setUniform, setParticlePreset, saveWallpaper, } = useWallpaperStore(); const { time } = useAnimation(animation, uniforms); const gyro = useGyroscope(animation === "depth-parallax"); const { status, exportAsWallpaper } = useWallpaperExport(); const image = useImage(sourceUri); const shader = useMemo(() => shaders[animation], [animation]); const handleApply = useCallback(async () => { const config = saveWallpaper(); if (config) { await exportAsWallpaper(config); } }, [saveWallpaper, exportAsWallpaper]); const handleSaveOnly = useCallback(() => { saveWallpaper(); router.back(); }, [saveWallpaper, router]); // No image selected — redirect back if (!sourceUri) { return ( Aucune image sélectionnée Retournez à la galerie pour choisir une photo router.back()} variant="primary" size="md" /> ); } // Image loading if (!image) { return ( Chargement de l'image... ); } return ( {/* Full-screen animated preview */} {shader && ( )} {/* Top bar overlay */} router.back()} style={styles.iconButton}> {/* Bottom sheet with controls */} {/* Animation selector */} Animation {AVAILABLE_ANIMATIONS.map((type) => ( setAnimation(type)} /> ))} {/* Particle preset selector (only for particles animation) */} {animation === "particles" && ( <> Style {(["snow", "rain", "bokeh"] as const).map((preset) => ( setParticlePreset(preset)} /> ))} )} {/* Uniform controls */} setUniform("intensity", v)} /> setUniform("speed", v)} /> {(animation === "ken-burns" || animation === "glitch") && ( setUniform("direction", v)} formatValue={(v) => `${Math.round((v * 180) / Math.PI)}°`} /> )} {/* Export button */} ) : undefined } /> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: colors.background, }, centered: { justifyContent: "center", alignItems: "center", paddingHorizontal: layout.screenPadding, }, canvas: { position: "absolute", top: 0, left: 0, width: SCREEN_WIDTH, height: SCREEN_HEIGHT, }, topBar: { position: "absolute", left: 16, right: 16, flexDirection: "row", justifyContent: "space-between", zIndex: 10, }, iconButton: { width: 40, height: 40, borderRadius: 20, backgroundColor: "rgba(0, 0, 0, 0.4)", borderWidth: 1, borderColor: colors.glass.border, alignItems: "center", justifyContent: "center", }, animationScroll: { marginBottom: spacing.lg, }, controls: { marginBottom: spacing.lg, }, ctaContainer: { marginTop: spacing.sm, }, });