diff options
| -rw-r--r-- | apps/mobile/app/dashboard/(tabs)/index.tsx | 2 | ||||
| -rw-r--r-- | apps/mobile/app/dashboard/(tabs)/settings.tsx | 42 | ||||
| -rw-r--r-- | apps/mobile/components/ui/PageTitle.tsx | 13 | ||||
| -rw-r--r-- | apps/mobile/lib/settings.ts | 18 | ||||
| -rw-r--r-- | apps/mobile/package.json | 1 | ||||
| -rw-r--r-- | pnpm-lock.yaml | 20 |
6 files changed, 84 insertions, 12 deletions
diff --git a/apps/mobile/app/dashboard/(tabs)/index.tsx b/apps/mobile/app/dashboard/(tabs)/index.tsx index dc9871e7..bd7ce3ea 100644 --- a/apps/mobile/app/dashboard/(tabs)/index.tsx +++ b/apps/mobile/app/dashboard/(tabs)/index.tsx @@ -34,7 +34,7 @@ function HeaderRight({ } else if (nativeEvent.event === "library") { const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, - quality: 0, + quality: settings.imageQuality, allowsMultipleSelection: false, }); if (!result.canceled) { diff --git a/apps/mobile/app/dashboard/(tabs)/settings.tsx b/apps/mobile/app/dashboard/(tabs)/settings.tsx index 73d1b42d..18d3d243 100644 --- a/apps/mobile/app/dashboard/(tabs)/settings.tsx +++ b/apps/mobile/app/dashboard/(tabs)/settings.tsx @@ -1,6 +1,10 @@ +import { useEffect } from "react"; import { Text, View } from "react-native"; +import { Slider } from "react-native-awesome-slider"; +import { useSharedValue } from "react-native-reanimated"; import { Button } from "@/components/ui/Button"; import CustomSafeAreaView from "@/components/ui/CustomSafeAreaView"; +import { Divider } from "@/components/ui/Divider"; import PageTitle from "@/components/ui/PageTitle"; import { useSession } from "@/lib/session"; import useAppSettings from "@/lib/settings"; @@ -8,7 +12,19 @@ import { api } from "@/lib/trpc"; export default function Dashboard() { const { logout } = useSession(); - const { settings, isLoading: isSettingsLoading } = useAppSettings(); + const { + settings, + setSettings, + isLoading: isSettingsLoading, + } = useAppSettings(); + + const imageQuality = useSharedValue(0); + const imageQualityMin = useSharedValue(0); + const imageQualityMax = useSharedValue(100); + + useEffect(() => { + imageQuality.value = settings.imageQuality * 100; + }, [settings]); const { data, error, isLoading } = api.users.whoami.useQuery(); @@ -30,8 +46,30 @@ export default function Dashboard() { {isLoading ? "Loading ..." : data?.email} </Text> </View> - <Button className="w-full" label="Log Out" onPress={logout} /> + <Divider orientation="horizontal" /> + <Text className="w-full p-1 text-2xl font-bold text-foreground"> + Upload Settings + </Text> + <View className="flex w-full flex-row items-center justify-between gap-8 rounded-lg bg-white px-4 py-2 dark:bg-accent"> + <Text className="text-lg text-accent-foreground">Image Quality</Text> + <View className="flex flex-1 flex-row items-center justify-center gap-2"> + <Text className="text-foreground"> + {settings.imageQuality * 100}% + </Text> + <Slider + onSlidingComplete={(value) => + setSettings({ + ...settings, + imageQuality: Math.round(value) / 100, + }) + } + progress={imageQuality} + minimumValue={imageQualityMin} + maximumValue={imageQualityMax} + /> + </View> + </View> </View> </CustomSafeAreaView> ); diff --git a/apps/mobile/components/ui/PageTitle.tsx b/apps/mobile/components/ui/PageTitle.tsx index 1c1543ce..dc712379 100644 --- a/apps/mobile/components/ui/PageTitle.tsx +++ b/apps/mobile/components/ui/PageTitle.tsx @@ -1,7 +1,16 @@ import { Text } from "react-native"; +import { cx } from "class-variance-authority"; -export default function PageTitle({ title }: { title: string }) { +export default function PageTitle({ + title, + className, +}: { + title: string; + className?: string; +}) { return ( - <Text className="p-4 text-4xl font-bold text-foreground">{title}</Text> + <Text className={cx("p-4 text-4xl font-bold text-foreground", className)}> + {title} + </Text> ); } diff --git a/apps/mobile/lib/settings.ts b/apps/mobile/lib/settings.ts index efb5593a..085f4f01 100644 --- a/apps/mobile/lib/settings.ts +++ b/apps/mobile/lib/settings.ts @@ -1,13 +1,17 @@ import * as SecureStore from "expo-secure-store"; +import { z } from "zod"; import { create } from "zustand"; const SETTING_NAME = "settings"; -export interface Settings { - apiKey?: string; - apiKeyId?: string; - address: string; -} +const zSettingsSchema = z.object({ + apiKey: z.string().optional(), + apiKeyId: z.string().optional(), + address: z.string(), + imageQuality: z.number().optional().default(0.2), +}); + +export type Settings = z.infer<typeof zSettingsSchema>; interface AppSettingsState { settings: { isLoading: boolean; settings: Settings }; @@ -18,7 +22,7 @@ interface AppSettingsState { const useSettings = create<AppSettingsState>((set, get) => ({ settings: { isLoading: true, - settings: { address: "" }, + settings: { address: "", imageQuality: 0.2 }, }, setSettings: async (settings) => { await SecureStore.setItemAsync(SETTING_NAME, JSON.stringify(settings)); @@ -36,7 +40,7 @@ const useSettings = create<AppSettingsState>((set, get) => ({ return; } // TODO Wipe the state if invalid - const parsed = JSON.parse(strVal) as Settings; + const parsed = zSettingsSchema.parse(JSON.parse(strVal)); set((_state) => ({ settings: { isLoading: false, settings: parsed } })); }, })); diff --git a/apps/mobile/package.json b/apps/mobile/package.json index da8ef432..6519692e 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -42,6 +42,7 @@ "nativewind": "^4.0.1", "react": "^18.2.0", "react-native": "0.73.4", + "react-native-awesome-slider": "^2.5.3", "react-native-gesture-handler": "~2.14.0", "react-native-markdown-display": "^7.0.2", "react-native-reanimated": "^3.8.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f1fcf9a0..9f0d5901 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -378,6 +378,9 @@ importers: react-native: specifier: 0.73.4 version: 0.73.4(@babel/core@7.23.9)(@babel/preset-env@7.24.0(@babel/core@7.23.9))(react@18.2.0) + react-native-awesome-slider: + specifier: ^2.5.3 + version: 2.5.3(react-native-gesture-handler@2.14.1(react-native@0.73.4(@babel/core@7.23.9)(@babel/preset-env@7.24.0(@babel/core@7.23.9))(react@18.2.0))(react@18.2.0))(react-native-reanimated@3.8.0(@babel/core@7.23.9)(react-native@0.73.4(@babel/core@7.23.9)(@babel/preset-env@7.24.0(@babel/core@7.23.9))(react@18.2.0))(react@18.2.0))(react-native@0.73.4(@babel/core@7.23.9)(@babel/preset-env@7.24.0(@babel/core@7.23.9))(react@18.2.0))(react@18.2.0) react-native-gesture-handler: specifier: ~2.14.0 version: 2.14.1(react-native@0.73.4(@babel/core@7.23.9)(@babel/preset-env@7.24.0(@babel/core@7.23.9))(react@18.2.0))(react@18.2.0) @@ -10340,6 +10343,15 @@ packages: peerDependencies: react: '>=16.0.0' + react-native-awesome-slider@2.5.3: + resolution: {integrity: sha512-dKgwcKz2hobR1xd0FFdxP99vcPhVJK6sZgTyjwbGBK97cnFB8mB5WhIRXzPLnSgrNMABPS6IbdseLRNhpp57gQ==} + engines: {node: '>= 18.0.0'} + peerDependencies: + react: '*' + react-native: '*' + react-native-gesture-handler: '>=2.0.0' + react-native-reanimated: '>=3.0.0' + react-native-css-interop@0.0.36: resolution: {integrity: sha512-ZWoKQlq6XrI5DB4BdPk5ABvJQsX7zls1SQYWuYXOQB8u5QE0KH3OfOGAGRZPekTjgkhjqGO4Bf8G2JTSWAYMSg==} engines: {node: '>=18'} @@ -26521,6 +26533,14 @@ snapshots: react: 18.2.0 dev: false + react-native-awesome-slider@2.5.3(react-native-gesture-handler@2.14.1(react-native@0.73.4(@babel/core@7.23.9)(@babel/preset-env@7.24.0(@babel/core@7.23.9))(react@18.2.0))(react@18.2.0))(react-native-reanimated@3.8.0(@babel/core@7.23.9)(react-native@0.73.4(@babel/core@7.23.9)(@babel/preset-env@7.24.0(@babel/core@7.23.9))(react@18.2.0))(react@18.2.0))(react-native@0.73.4(@babel/core@7.23.9)(@babel/preset-env@7.24.0(@babel/core@7.23.9))(react@18.2.0))(react@18.2.0): + dependencies: + react: 18.2.0 + react-native: 0.73.4(@babel/core@7.23.9)(@babel/preset-env@7.24.0(@babel/core@7.23.9))(react@18.2.0) + react-native-gesture-handler: 2.14.1(react-native@0.73.4(@babel/core@7.23.9)(@babel/preset-env@7.24.0(@babel/core@7.23.9))(react@18.2.0))(react@18.2.0) + react-native-reanimated: 3.8.0(@babel/core@7.23.9)(react-native@0.73.4(@babel/core@7.23.9)(@babel/preset-env@7.24.0(@babel/core@7.23.9))(react@18.2.0))(react@18.2.0) + dev: false + react-native-css-interop@0.0.36(@babel/core@7.23.9)(react-native-reanimated@3.8.0(@babel/core@7.23.9)(react-native@0.73.4(@babel/core@7.23.9)(@babel/preset-env@7.24.0(@babel/core@7.23.9))(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.8.2(react-native@0.73.4(@babel/core@7.23.9)(@babel/preset-env@7.24.0(@babel/core@7.23.9))(react@18.2.0))(react@18.2.0))(react-native-svg@15.1.0(react-native@0.73.4(@babel/core@7.23.9)(@babel/preset-env@7.24.0(@babel/core@7.23.9))(react@18.2.0))(react@18.2.0))(react-native@0.73.4(@babel/core@7.23.9)(@babel/preset-env@7.24.0(@babel/core@7.23.9))(react@18.2.0))(react@18.2.0)(tailwindcss@3.4.1): dependencies: '@babel/helper-module-imports': 7.22.15 |
