From eef23656f93ee0236284320d6729701d7030f074 Mon Sep 17 00:00:00 2001 From: Kenneth Date: Tue, 24 Sep 2024 23:40:27 +0100 Subject: [PATCH] initial ui --- .gitignore | 6 ++ README.md | 51 +--------- app/(tabs)/_layout.tsx | 71 +++++++------ app/(tabs)/index.tsx | 211 +++++++++++++++++++++++++++------------ app/_layout.tsx | 59 ++++++----- app/wallpaper.tsx | 124 +++++++++++++++++++++++ cloudinary/cloudinary.ts | 142 ++++++++++++++++++++++++++ package-lock.json | 168 ++++++++++++++++++++++++++++++- package.json | 8 +- store/store.ts | 16 +++ 10 files changed, 685 insertions(+), 171 deletions(-) create mode 100644 app/wallpaper.tsx create mode 100644 cloudinary/cloudinary.ts create mode 100644 store/store.ts diff --git a/.gitignore b/.gitignore index ec8a36a..6623142 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,9 @@ web-build/ # macOS .DS_Store + +# @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb +# The following patterns were generated by expo-cli + +expo-env.d.ts +# @end expo-cli \ No newline at end of file diff --git a/README.md b/README.md index cd4feb8..19d33af 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,7 @@ -# Welcome to your Expo app 👋 +# doors -This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app). +doors is a free open-source alternative to MKBHD's [panels.art] wallpaper app. -## Get started +## wallpaper request -1. Install dependencies - - ```bash - npm install - ``` - -2. Start the app - - ```bash - npx expo start - ``` - -In the output, you'll find options to open the app in a - -- [development build](https://docs.expo.dev/develop/development-builds/introduction/) -- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/) -- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/) -- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo - -You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction). - -## Get a fresh project - -When you're ready, run: - -```bash -npm run reset-project -``` - -This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing. - -## Learn more - -To learn more about developing your project with Expo, look at the following resources: - -- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides). -- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web. - -## Join the community - -Join our community of developers creating universal apps. - -- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute. -- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions. +please file an issue if you wish to upload your own wallpapers to the app. all wallpapers are hosted on cloudinary. diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index 22a49b6..dfa7c15 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -1,37 +1,44 @@ -import { Tabs } from 'expo-router'; -import React from 'react'; +import { Tabs } from "expo-router"; +import React from "react"; -import { TabBarIcon } from '@/components/navigation/TabBarIcon'; -import { Colors } from '@/constants/Colors'; -import { useColorScheme } from '@/hooks/useColorScheme'; +import { TabBarIcon } from "@/components/navigation/TabBarIcon"; +import { Colors } from "@/constants/Colors"; +import { useColorScheme } from "@/hooks/useColorScheme"; export default function TabLayout() { - const colorScheme = useColorScheme(); + const colorScheme = useColorScheme(); - return ( - - ( - - ), - }} - /> - ( - - ), - }} - /> - - ); + return ( + + ( + + ), + }} + /> + ( + + ), + }} + /> + + ); } diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index 324aeb7..449e3c5 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -1,70 +1,155 @@ -import { Image, StyleSheet, Platform } from 'react-native'; +import { + View, + Image, + StyleSheet, + Platform, + Text, + TouchableOpacity, +} from "react-native"; -import { HelloWave } from '@/components/HelloWave'; -import ParallaxScrollView from '@/components/ParallaxScrollView'; -import { ThemedText } from '@/components/ThemedText'; -import { ThemedView } from '@/components/ThemedView'; +import { HelloWave } from "@/components/HelloWave"; +import ParallaxScrollView from "@/components/ParallaxScrollView"; +import { ThemedText } from "@/components/ThemedText"; +import { ThemedView } from "@/components/ThemedView"; +import { useEffect, useState } from "react"; +import { FAKE_DATA, type ImageAsset } from "@/cloudinary/cloudinary"; +import { MasonryFlashList } from "@shopify/flash-list"; +import { useRouter } from "expo-router"; +import { useStore } from "@/store/store"; export default function HomeScreen() { - return ( - - }> - - Welcome! - - - - Step 1: Try it - - Edit app/(tabs)/index.tsx to see changes. - Press{' '} - - {Platform.select({ ios: 'cmd + d', android: 'cmd + m' })} - {' '} - to open developer tools. - - - - Step 2: Explore - - Tap the Explore tab to learn more about what's included in this starter app. - - - - Step 3: Get a fresh start - - When you're ready, run{' '} - npm run reset-project to get a fresh{' '} - app directory. This will move the current{' '} - app to{' '} - app-example. - - - - ); + const router = useRouter(); + const setSelectedWallpaper = useStore((store) => store.setSelectedWallpaper); + const [images, setImages] = useState(null); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + async function fetchWallpapers() { + setIsLoading(true); + const res = await fetch("http://localhost:8080/wallpapers"); + const images: ImageAsset[] = await res.json(); + setImages(images); + setIsLoading(false); + } + fetchWallpapers(); + }, []); + + return ( + + ( + + Wallpapers + + )} + renderItem={({ item, target }) => ( + + { + setSelectedWallpaper(item); + router.push("/wallpaper"); + }} + > + + + + + {item.public_id.replace("wallpaper/", "")} + + + + )} + estimatedItemSize={200} + /> + + ); + + return ( + + } + > + + Welcome! + + + + Step 1: Try it + + Edit{" "} + app/(tabs)/index.tsx{" "} + to see changes. Press{" "} + + {Platform.select({ ios: "cmd + d", android: "cmd + m" })} + {" "} + to open developer tools. + + + + Step 2: Explore + + Tap the Explore tab to learn more about what's included in this + starter app. + + + + Step 3: Get a fresh start + + When you're ready, run{" "} + npm run reset-project{" "} + to get a fresh app{" "} + directory. This will move the current{" "} + app to{" "} + app-example. + + + + ); } const styles = StyleSheet.create({ - titleContainer: { - flexDirection: 'row', - alignItems: 'center', - gap: 8, - }, - stepContainer: { - gap: 8, - marginBottom: 8, - }, - reactLogo: { - height: 178, - width: 290, - bottom: 0, - left: 0, - position: 'absolute', - }, + titleContainer: { + flexDirection: "row", + alignItems: "center", + gap: 8, + }, + stepContainer: { + gap: 8, + marginBottom: 8, + }, + reactLogo: { + height: 178, + width: 290, + bottom: 0, + left: 0, + position: "absolute", + }, }); diff --git a/app/_layout.tsx b/app/_layout.tsx index 2e37cdd..ba91f86 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -1,37 +1,42 @@ -import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native'; -import { useFonts } from 'expo-font'; -import { Stack } from 'expo-router'; -import * as SplashScreen from 'expo-splash-screen'; -import { useEffect } from 'react'; -import 'react-native-reanimated'; +import { + DarkTheme, + DefaultTheme, + ThemeProvider, +} from "@react-navigation/native"; +import { useFonts } from "expo-font"; +import { Stack } from "expo-router"; +import * as SplashScreen from "expo-splash-screen"; +import { useEffect } from "react"; +import "react-native-reanimated"; -import { useColorScheme } from '@/hooks/useColorScheme'; +import { useColorScheme } from "@/hooks/useColorScheme"; // Prevent the splash screen from auto-hiding before asset loading is complete. SplashScreen.preventAutoHideAsync(); export default function RootLayout() { - const colorScheme = useColorScheme(); - const [loaded] = useFonts({ - SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'), - }); + const colorScheme = useColorScheme(); + const [loaded] = useFonts({ + SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"), + }); - useEffect(() => { - if (loaded) { - SplashScreen.hideAsync(); - } - }, [loaded]); + useEffect(() => { + if (loaded) { + SplashScreen.hideAsync(); + } + }, [loaded]); - if (!loaded) { - return null; - } + if (!loaded) { + return null; + } - return ( - - - - - - - ); + return ( + + + + + + + + ); } diff --git a/app/wallpaper.tsx b/app/wallpaper.tsx new file mode 100644 index 0000000..cefb554 --- /dev/null +++ b/app/wallpaper.tsx @@ -0,0 +1,124 @@ +import { useStore } from "@/store/store"; +import * as FileSystem from "expo-file-system"; +import * as MediaLibrary from "expo-media-library"; +import { Ionicons } from "@expo/vector-icons"; +import { useRouter } from "expo-router"; +import { Image, Text, View, TouchableOpacity, Alert } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; + +export default function WallpaperPage() { + const selectedWallpaper = useStore((store) => store.selectedWallpaper); + const router = useRouter(); + + if (!selectedWallpaper) { + return null; + } + + const imageName = selectedWallpaper.public_id.replace("wallpaper/", ""); + + async function downloadWallpaper() { + if (!selectedWallpaper) { + return; + } + + const permStatus = await MediaLibrary.requestPermissionsAsync(); + if (permStatus.status != MediaLibrary.PermissionStatus.GRANTED) { + Alert.alert( + "Media library access required", + "Doors need access to your media library in order to save wallpapers.", + ); + return; + } + + console.log( + `${FileSystem.documentDirectory}${selectedWallpaper.asset_id}.${selectedWallpaper.format}`, + ); + + const result = await FileSystem.downloadAsync( + selectedWallpaper.secure_url, + `${FileSystem.documentDirectory}${selectedWallpaper.asset_id}.${selectedWallpaper.format}`, + ); + await MediaLibrary.createAssetAsync(result.uri); + Alert.alert( + "Wallpaper saved successfully", + "You can change the wallpaper in Settings.", + ); + } + + return ( + + + + { + router.dismiss(); + }} + > + + + + + + + + {imageName} + + { + downloadWallpaper(); + }} + > + + Download + + + + + ); +} diff --git a/cloudinary/cloudinary.ts b/cloudinary/cloudinary.ts new file mode 100644 index 0000000..8768b14 --- /dev/null +++ b/cloudinary/cloudinary.ts @@ -0,0 +1,142 @@ +interface ImageAsset { + asset_id: string; + public_id: string; + format: "jpg" | "png" | "heic"; + version: number; + resource_type: "image"; + type: "upload"; + bytes: number; + width: number; + height: number; + secure_url: string; +} + +const FAKE_DATA: ImageAsset[] = [ + { + asset_id: "0bc266a5c13b32b7f391743e6190e64b", + public_id: "wallpaper/My Wife", + format: "png", + version: 1727208372, + resource_type: "image", + type: "upload", + bytes: 7298274, + width: 7680, + height: 4320, + asset_folder: "wallpaper", + display_name: "wtu9aapnvev6l3u4wxnn", + url: "http://res.cloudinary.com/duyynotn0/image/upload/v1727208372/wallpaper/My%20Wife.png", + secure_url: + "https://res.cloudinary.com/duyynotn0/image/upload/v1727208372/wallpaper/My%20Wife.png", + last_updated: { + public_id_updated_at: "2024-09-24T21:10:00+00:00", + updated_at: "2024-09-24T21:10:00+00:00", + }, + }, + { + asset_id: "054f3880e1b0ae0e1caf7353a3b5887f", + public_id: "wallpaper/Lycoris Recoil", + format: "jpg", + version: 1727208371, + resource_type: "image", + type: "upload", + bytes: 4393245, + width: 3840, + height: 2160, + asset_folder: "wallpaper", + display_name: "bcnwr6vgi6l7biqn10gx", + url: "http://res.cloudinary.com/duyynotn0/image/upload/v1727208371/wallpaper/Lycoris%20Recoil.jpg", + secure_url: + "https://res.cloudinary.com/duyynotn0/image/upload/v1727208371/wallpaper/Lycoris%20Recoil.jpg", + last_updated: { + public_id_updated_at: "2024-09-24T21:10:07+00:00", + updated_at: "2024-09-24T21:10:07+00:00", + }, + }, + { + asset_id: "c9f71873b584b74a5dce33c0a65319ac", + public_id: "wallpaper/Tokyo", + format: "jpg", + version: 1727208369, + resource_type: "image", + type: "upload", + created_at: "2024-09-24T20:06:09Z", + bytes: 7752039, + width: 6720, + height: 4480, + asset_folder: "wallpaper", + display_name: "v59wmbyztuy3hpinwsgo", + url: "http://res.cloudinary.com/duyynotn0/image/upload/v1727208369/wallpaper/Tokyo.jpg", + secure_url: + "https://res.cloudinary.com/duyynotn0/image/upload/v1727208369/wallpaper/Tokyo.jpg", + last_updated: { + public_id_updated_at: "2024-09-24T21:09:28+00:00", + updated_at: "2024-09-24T21:09:28+00:00", + }, + }, + { + asset_id: "7ff8cb0616c8091dc37aadec52a30658", + public_id: "wallpaper/Galaxy", + format: "jpg", + version: 1727208369, + resource_type: "image", + type: "upload", + created_at: "2024-09-24T20:06:09Z", + bytes: 4068457, + width: 7042, + height: 4699, + asset_folder: "wallpaper", + display_name: "xghgm5fp8icre4kmvsx5", + url: "http://res.cloudinary.com/duyynotn0/image/upload/v1727208369/wallpaper/Galaxy.jpg", + secure_url: + "https://res.cloudinary.com/duyynotn0/image/upload/v1727208369/wallpaper/Galaxy.jpg", + last_updated: { + public_id_updated_at: "2024-09-24T21:09:13+00:00", + updated_at: "2024-09-24T21:09:13+00:00", + }, + }, + { + asset_id: "8b1b17423a410a4b2f7bea13957f7de1", + public_id: "wallpaper/Raydrop", + format: "heic", + version: 1727208361, + resource_type: "image", + type: "upload", + created_at: "2024-09-24T20:06:01Z", + bytes: 1034763, + width: 6016, + height: 3388, + asset_folder: "wallpaper", + display_name: "yjcsdlty4pwqaich0fl4", + url: "http://res.cloudinary.com/duyynotn0/image/upload/v1727208361/wallpaper/Raydrop.heic", + secure_url: + "https://res.cloudinary.com/duyynotn0/image/upload/v1727208361/wallpaper/Raydrop.heic", + last_updated: { + public_id_updated_at: "2024-09-24T21:08:56+00:00", + updated_at: "2024-09-24T21:08:56+00:00", + }, + }, + { + asset_id: "7d95c3294cd84e64b004f54a002d3865", + public_id: "wallpaper/Alisa", + format: "jpg", + version: 1727208357, + resource_type: "image", + type: "upload", + created_at: "2024-09-24T20:05:57Z", + bytes: 1556534, + width: 5120, + height: 2880, + asset_folder: "wallpaper", + display_name: "qjbbkz8kn1rtsmvthdb5", + url: "http://res.cloudinary.com/duyynotn0/image/upload/v1727208357/wallpaper/Alisa.jpg", + secure_url: + "https://res.cloudinary.com/duyynotn0/image/upload/v1727208357/wallpaper/Alisa.jpg", + last_updated: { + public_id_updated_at: "2024-09-24T21:08:48+00:00", + updated_at: "2024-09-24T21:08:48+00:00", + }, + }, +]; + +export type { ImageAsset }; +export { FAKE_DATA }; diff --git a/package-lock.json b/package-lock.json index ab64766..1edebed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,12 +8,17 @@ "name": "doors", "version": "1.0.0", "dependencies": { + "@cloudinary/react": "^1.13.0", + "@cloudinary/url-gen": "^1.21.0", "@expo/vector-icons": "^14.0.2", "@react-navigation/native": "^6.0.2", + "@shopify/flash-list": "^1.7.1", "expo": "~51.0.28", "expo-constants": "~16.0.2", + "expo-file-system": "^17.0.1", "expo-font": "~12.0.9", "expo-linking": "~6.3.1", + "expo-media-library": "^16.0.5", "expo-router": "~3.5.23", "expo-splash-screen": "~0.27.5", "expo-status-bar": "~1.12.1", @@ -26,7 +31,8 @@ "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "4.10.5", "react-native-screens": "3.31.1", - "react-native-web": "~0.19.10" + "react-native-web": "~0.19.10", + "zustand": "^5.0.0-rc.2" }, "devDependencies": { "@babel/core": "^7.20.0", @@ -2136,6 +2142,66 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@cloudinary/html": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@cloudinary/html/-/html-1.13.0.tgz", + "integrity": "sha512-kOE54EbAyxSH4k3Z9HzYDSlsfpXSDxckAdyyNfxsnC23u+wfxY0Gt8yBG0s3tiqdTkL6IDd1Momsn/OlYStObg==", + "dependencies": { + "@types/lodash.clonedeep": "^4.5.6", + "@types/lodash.debounce": "^4.0.6", + "@types/node": "^14.14.10", + "lodash.clonedeep": "^4.5.0", + "lodash.debounce": "^4.0.8", + "typescript": "^4.1.2" + } + }, + "node_modules/@cloudinary/html/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==" + }, + "node_modules/@cloudinary/html/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/@cloudinary/react": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@cloudinary/react/-/react-1.13.0.tgz", + "integrity": "sha512-S+Wmdmg4+pjWWlRLUcaHlQ0d59njecja6wGGWGJwVsQioBF/E/KUxNl09Q6NemXuJJAVW/hSW9kT/diJmI7eQQ==", + "dependencies": { + "@cloudinary/html": "^1.13.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.3.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@cloudinary/transformation-builder-sdk": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@cloudinary/transformation-builder-sdk/-/transformation-builder-sdk-1.15.1.tgz", + "integrity": "sha512-Dvm5DzGPq6NtXRLhpubu7SVfD+TLEWbU9q/FOxpGelWpn3NaTSOH/b+jRbpTL+EgYSHeaTeK6kHMraousPkNQg==", + "dependencies": { + "@cloudinary/url-gen": "^1.7.0" + } + }, + "node_modules/@cloudinary/url-gen": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@cloudinary/url-gen/-/url-gen-1.21.0.tgz", + "integrity": "sha512-ctYcCzX3G3vcgnESTU2ET3K1XsBiXcEnBddCGV0QbR3fJhLLrIShjSMEwZoepgh4LAFOHJu9DzvLFr+E8R7c7g==", + "dependencies": { + "@cloudinary/transformation-builder-sdk": "^1.15.1" + } + }, "node_modules/@egjs/hammerjs": { "version": "2.0.17", "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", @@ -6549,6 +6615,25 @@ "join-component": "^1.1.0" } }, + "node_modules/@shopify/flash-list": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@shopify/flash-list/-/flash-list-1.7.1.tgz", + "integrity": "sha512-sUYl7h8ydJutufA26E42Hj7cLvaBTpkMIyNJiFrxUspkcANb6jnFiLt9rEwAuDjvGk/C0lHau+WyT6ZOxqVPwg==", + "dependencies": { + "recyclerlistview": "4.2.1", + "tslib": "2.6.3" + }, + "peerDependencies": { + "@babel/runtime": "*", + "react": "*", + "react-native": "*" + } + }, + "node_modules/@shopify/flash-list/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, "node_modules/@sideway/address": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", @@ -6704,6 +6789,27 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, + "node_modules/@types/lodash": { + "version": "4.17.9", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.9.tgz", + "integrity": "sha512-w9iWudx1XWOHW5lQRS9iKpK/XuRhnN+0T7HvdCCd802FYkT1AMTnxndJHGrNJwRoRHkslGr4S29tjm1cT7x/7w==" + }, + "node_modules/@types/lodash.clonedeep": { + "version": "4.5.9", + "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.9.tgz", + "integrity": "sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q==", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/lodash.debounce": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.9.tgz", + "integrity": "sha512-Ma5JcgTREwpLRwMM+XwBR7DaWe96nC38uCBDFKZWbNKD+osjVzdpnUSwBcqCptrp16sSOLBAUb50Car5I0TCsQ==", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/node": { "version": "18.19.50", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.50.tgz", @@ -9348,6 +9454,14 @@ "invariant": "^2.2.4" } }, + "node_modules/expo-media-library": { + "version": "16.0.5", + "resolved": "https://registry.npmjs.org/expo-media-library/-/expo-media-library-16.0.5.tgz", + "integrity": "sha512-O9RUqBWgJVRF0mO6EiLSBFyfb5wR1/ZqovbT43V0TAo5sgcjrHRs+0NID/U6BWDRuiFeX2AU516JgNDutNUFSw==", + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-modules-autolinking": { "version": "1.11.2", "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.11.2.tgz", @@ -13677,6 +13791,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -15985,6 +16104,20 @@ "node": ">=0.10.0" } }, + "node_modules/recyclerlistview": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/recyclerlistview/-/recyclerlistview-4.2.1.tgz", + "integrity": "sha512-NtVYjofwgUCt1rEsTp6jHQg/47TWjnO92TU2kTVgJ9wsc/ely4HnizHHa+f/dI7qaw4+zcSogElrLjhMltN2/g==", + "dependencies": { + "lodash.debounce": "4.0.8", + "prop-types": "15.8.1", + "ts-object-utils": "0.0.5" + }, + "peerDependencies": { + "react": ">= 15.2.1", + "react-native": ">= 0.30.0" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -17507,6 +17640,11 @@ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" }, + "node_modules/ts-object-utils": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/ts-object-utils/-/ts-object-utils-0.0.5.tgz", + "integrity": "sha512-iV0GvHqOmilbIKJsfyfJY9/dNHCs969z3so90dQWsO1eMMozvTpnB1MEaUbb3FYtZTGjv5sIy/xmslEz0Rg2TA==" + }, "node_modules/tslib": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", @@ -18354,6 +18492,34 @@ "peerDependencies": { "zod": "^3.18.0" } + }, + "node_modules/zustand": { + "version": "5.0.0-rc.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.0-rc.2.tgz", + "integrity": "sha512-o2Nwuvnk8vQBX7CcHL8WfFkZNJdxB/VKeWw0tNglw8p4cypsZ3tRT7rTRTDNeUPFS0qaMBRSKe+fVwL5xpcE3A==", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index 7097e22..abc602e 100644 --- a/package.json +++ b/package.json @@ -15,12 +15,17 @@ "preset": "jest-expo" }, "dependencies": { + "@cloudinary/react": "^1.13.0", + "@cloudinary/url-gen": "^1.21.0", "@expo/vector-icons": "^14.0.2", "@react-navigation/native": "^6.0.2", + "@shopify/flash-list": "^1.7.1", "expo": "~51.0.28", "expo-constants": "~16.0.2", + "expo-file-system": "^17.0.1", "expo-font": "~12.0.9", "expo-linking": "~6.3.1", + "expo-media-library": "^16.0.5", "expo-router": "~3.5.23", "expo-splash-screen": "~0.27.5", "expo-status-bar": "~1.12.1", @@ -33,7 +38,8 @@ "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "4.10.5", "react-native-screens": "3.31.1", - "react-native-web": "~0.19.10" + "react-native-web": "~0.19.10", + "zustand": "^5.0.0-rc.2" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/store/store.ts b/store/store.ts new file mode 100644 index 0000000..bae3253 --- /dev/null +++ b/store/store.ts @@ -0,0 +1,16 @@ +import type { ImageAsset } from "@/cloudinary/cloudinary"; +import { create } from "zustand"; + +interface Store { + selectedWallpaper: ImageAsset | null; + setSelectedWallpaper(image: ImageAsset): void; +} + +const useStore = create((set) => ({ + selectedWallpaper: null, + + setSelectedWallpaper: (image) => + set((store) => ({ ...store, selectedWallpaper: image })), +})); + +export { useStore };