diff --git a/frontends/react-native-ios/app.json b/frontends/react-native-ios/app.json
index ed4a0b9..054e63a 100644
--- a/frontends/react-native-ios/app.json
+++ b/frontends/react-native-ios/app.json
@@ -10,7 +10,7 @@
"newArchEnabled": true,
"splash": {
"image": "./assets/splash-icon.png",
- "resizeMode": "contain",
+ "resizeMode": "cover",
"backgroundColor": "#0a0a0a"
},
"ios": {
@@ -29,7 +29,7 @@
{
"backgroundColor": "#0a0a0a",
"image": "./assets/splash-icon.png",
- "imageWidth": 200
+ "resizeMode": "cover"
}
]
],
diff --git a/frontends/react-native-ios/app/(app)/(tabs)/home.tsx b/frontends/react-native-ios/app/(app)/(tabs)/home.tsx
index ca9de4f..17eb9fc 100644
--- a/frontends/react-native-ios/app/(app)/(tabs)/home.tsx
+++ b/frontends/react-native-ios/app/(app)/(tabs)/home.tsx
@@ -3,21 +3,19 @@
* home.tsx
*/
+import { useCurrentUser } from '@/api/hooks'
+import { DottedBackground } from '@/shared/components'
import type React from 'react'
import { SafeAreaView } from 'react-native-safe-area-context'
import { Stack, Text, YStack } from 'tamagui'
-import { useCurrentUser } from '@/api/hooks'
-import { colors } from '@/theme/tokens'
export default function HomeScreen(): React.ReactElement {
const { data: user } = useCurrentUser()
return (
-
-
+
+
+
-
-
+
+
+
)
}
diff --git a/frontends/react-native-ios/app/(app)/(tabs)/settings.tsx b/frontends/react-native-ios/app/(app)/(tabs)/settings.tsx
index d8f19dc..f8784c3 100644
--- a/frontends/react-native-ios/app/(app)/(tabs)/settings.tsx
+++ b/frontends/react-native-ios/app/(app)/(tabs)/settings.tsx
@@ -3,15 +3,17 @@
* settings.tsx
*/
-import { ChevronRight, LogOut, Shield, User } from 'lucide-react-native'
-import type React from 'react'
-import { SafeAreaView } from 'react-native-safe-area-context'
-import { Stack, Switch, Text, YStack } from 'tamagui'
import { useCurrentUser, useLogout } from '@/api/hooks'
import { useBiometricsEnabled, useUIStore } from '@/core/lib'
+import { DottedBackground } from '@/shared/components'
import { getBiometricLabel, useBiometrics } from '@/shared/hooks'
import { haptics } from '@/shared/utils'
import { colors } from '@/theme/tokens'
+import { router } from 'expo-router'
+import { ChevronRight, LogOut, Shield, User } from 'lucide-react-native'
+import type React from 'react'
+import { SafeAreaView } from 'react-native-safe-area-context'
+import { Stack, Switch, Text, YStack } from 'tamagui'
function SettingsRow({
icon,
@@ -67,11 +69,9 @@ export default function SettingsScreen(): React.ReactElement {
const canUseBiometrics = isAvailable && isEnrolled
return (
-
-
+
+
+
}
label="Profile"
- onPress={() => {}}
+ onPress={() => router.push('/(app)/profile')}
/>
{canUseBiometrics && (
}
- label={getBiometricLabel(biometryType)}
+ label={`${getBiometricLabel(biometryType)} Lock`}
rightElement={
-
+
}
/>
@@ -133,7 +140,8 @@ export default function SettingsScreen(): React.ReactElement {
onPress={handleLogout}
/>
-
-
+
+
+
)
}
diff --git a/frontends/react-native-ios/app/(app)/profile.tsx b/frontends/react-native-ios/app/(app)/profile.tsx
new file mode 100644
index 0000000..a78cc5e
--- /dev/null
+++ b/frontends/react-native-ios/app/(app)/profile.tsx
@@ -0,0 +1,224 @@
+/**
+ * @AngelaMos | 2026
+ * profile.tsx
+ */
+
+import { useChangePassword, useCurrentUser } from '@/api/hooks'
+import { useUpdateProfile } from '@/api/hooks/useUsers'
+import { Button, DottedBackground, Input, PasswordInput } from '@/shared/components'
+import { haptics } from '@/shared/utils'
+import { router } from 'expo-router'
+import { ArrowLeft } from 'lucide-react-native'
+import type React from 'react'
+import { useEffect, useState } from 'react'
+import { Alert, KeyboardAvoidingView, Platform, ScrollView } from 'react-native'
+import { SafeAreaView } from 'react-native-safe-area-context'
+import { Stack, Text, YStack } from 'tamagui'
+import { colors } from '@/theme/tokens'
+
+export default function ProfileScreen(): React.ReactElement {
+ const { data: user } = useCurrentUser()
+ const updateProfile = useUpdateProfile()
+ const changePassword = useChangePassword()
+
+ const [fullName, setFullName] = useState('')
+ const [currentPassword, setCurrentPassword] = useState('')
+ const [newPassword, setNewPassword] = useState('')
+ const [confirmPassword, setConfirmPassword] = useState('')
+
+ useEffect(() => {
+ if (user) {
+ setFullName(user.full_name ?? '')
+ }
+ }, [user])
+
+ const handleSaveProfile = (): void => {
+ if (!fullName.trim()) {
+ haptics.error()
+ Alert.alert('Error', 'Please enter your name')
+ return
+ }
+
+ updateProfile.mutate(
+ { full_name: fullName.trim() },
+ {
+ onSuccess: () => {
+ haptics.success()
+ Alert.alert('Success', 'Profile updated')
+ },
+ onError: (err) => {
+ haptics.error()
+ Alert.alert('Error', err.message)
+ },
+ }
+ )
+ }
+
+ const handleChangePassword = (): void => {
+ if (!currentPassword || !newPassword || !confirmPassword) {
+ haptics.error()
+ Alert.alert('Error', 'Please fill in all password fields')
+ return
+ }
+
+ if (newPassword !== confirmPassword) {
+ haptics.error()
+ Alert.alert('Error', 'New passwords do not match')
+ return
+ }
+
+ if (newPassword.length < 8) {
+ haptics.error()
+ Alert.alert('Error', 'Password must be at least 8 characters')
+ return
+ }
+
+ changePassword.mutate(
+ { current_password: currentPassword, new_password: newPassword },
+ {
+ onSuccess: () => {
+ haptics.success()
+ Alert.alert('Success', 'Password changed')
+ setCurrentPassword('')
+ setNewPassword('')
+ setConfirmPassword('')
+ },
+ onError: (err) => {
+ haptics.error()
+ Alert.alert('Error', err.message)
+ },
+ }
+ )
+ }
+
+ return (
+
+
+
+
+ router.back()}
+ >
+
+
+ Back
+
+
+
+
+ Profile
+
+
+
+
+
+ Account Info
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Change Password
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/frontends/react-native-ios/app/(auth)/login.tsx b/frontends/react-native-ios/app/(auth)/login.tsx
index bd1290b..af3689e 100644
--- a/frontends/react-native-ios/app/(auth)/login.tsx
+++ b/frontends/react-native-ios/app/(auth)/login.tsx
@@ -3,12 +3,6 @@
* login.tsx
*/
-import { Link, router } from 'expo-router'
-import { ArrowLeft } from 'lucide-react-native'
-import type React from 'react'
-import { useState } from 'react'
-import { KeyboardAvoidingView, Platform, ScrollView } from 'react-native'
-import { Stack, Text, YStack } from 'tamagui'
import { useLogin } from '@/api/hooks'
import { loginRequestSchema } from '@/api/types'
import {
@@ -21,11 +15,16 @@ import {
CardHeader,
CardSubtitle,
CardTitle,
+ DottedBackground,
Input,
PasswordInput,
} from '@/shared/components'
import { haptics } from '@/shared/utils'
-import { colors } from '@/theme/tokens'
+import { Link, router } from 'expo-router'
+import type React from 'react'
+import { useState } from 'react'
+import { KeyboardAvoidingView, Platform, ScrollView } from 'react-native'
+import { Stack, Text, YStack } from 'tamagui'
export default function LoginScreen(): React.ReactElement {
const login = useLogin()
@@ -62,36 +61,21 @@ export default function LoginScreen(): React.ReactElement {
}
return (
-
-
+
- router.back()}
+
-
-
- Home
-
-
-
-
+
Login
@@ -143,8 +127,9 @@ export default function LoginScreen(): React.ReactElement {
-
-
-
+
+
+
+
)
}
diff --git a/frontends/react-native-ios/app/(auth)/register.tsx b/frontends/react-native-ios/app/(auth)/register.tsx
index caa78d3..7c79965 100644
--- a/frontends/react-native-ios/app/(auth)/register.tsx
+++ b/frontends/react-native-ios/app/(auth)/register.tsx
@@ -3,13 +3,6 @@
* register.tsx
*/
-import { Link, router } from 'expo-router'
-import { ArrowLeft } from 'lucide-react-native'
-import type React from 'react'
-import { useState } from 'react'
-import { KeyboardAvoidingView, Platform, ScrollView } from 'react-native'
-import { Stack, Text, YStack } from 'tamagui'
-import { z } from 'zod'
import { useRegister } from '@/api/hooks'
import { userCreateRequestSchema } from '@/api/types'
import { PASSWORD_CONSTRAINTS } from '@/core/config'
@@ -23,11 +16,17 @@ import {
CardHeader,
CardSubtitle,
CardTitle,
+ DottedBackground,
Input,
PasswordInput,
} from '@/shared/components'
import { haptics } from '@/shared/utils'
-import { colors } from '@/theme/tokens'
+import { Link, router } from 'expo-router'
+import type React from 'react'
+import { useState } from 'react'
+import { KeyboardAvoidingView, Platform, ScrollView } from 'react-native'
+import { Stack, Text, YStack } from 'tamagui'
+import { z } from 'zod'
const registerFormSchema = userCreateRequestSchema
.extend({
@@ -84,37 +83,22 @@ export default function RegisterScreen(): React.ReactElement {
}
return (
-
-
+
- router.back()}
+
-
-
- Home
-
-
-
-
-
+
+
Sign up
Create a new account
@@ -171,9 +155,10 @@ export default function RegisterScreen(): React.ReactElement {
-
-
-
-
+
+
+
+
+
)
}
diff --git a/frontends/react-native-ios/eas.json b/frontends/react-native-ios/eas.json
index 78fb43d..0d60262 100644
--- a/frontends/react-native-ios/eas.json
+++ b/frontends/react-native-ios/eas.json
@@ -1,6 +1,7 @@
{
"cli": {
- "version": ">= 5.0.0"
+ "version": ">= 5.0.0",
+ "appVersionSource": "remote"
},
"build": {
"development": {
diff --git a/frontends/react-native-ios/package.json b/frontends/react-native-ios/package.json
index eba160b..a9bebf5 100644
--- a/frontends/react-native-ios/package.json
+++ b/frontends/react-native-ios/package.json
@@ -38,12 +38,11 @@
"react": "19.1.0",
"react-native": "0.81.5",
"react-native-gesture-handler": "~2.28.0",
- "react-native-mmkv": "^4.1.1",
"react-native-reanimated": "~4.1.1",
"react-native-safe-area-context": "~5.6.2",
- "react-native-worklets": "^0.7.1",
"react-native-screens": "~4.16.0",
"react-native-svg": "15.12.1",
+ "react-native-worklets": "^0.7.1",
"tamagui": "^1.144.1",
"zod": "^4.3.5",
"zustand": "^5.0.9"
diff --git a/frontends/react-native-ios/pnpm-lock.yaml b/frontends/react-native-ios/pnpm-lock.yaml
index adcec29..3c799b0 100644
--- a/frontends/react-native-ios/pnpm-lock.yaml
+++ b/frontends/react-native-ios/pnpm-lock.yaml
@@ -84,9 +84,6 @@ importers:
react-native-gesture-handler:
specifier: ~2.28.0
version: 2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
- react-native-mmkv:
- specifier: ^4.1.1
- version: 4.1.1(react-native-nitro-modules@0.32.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
react-native-reanimated:
specifier: ~4.1.1
version: 4.1.6(@babel/core@7.28.5)(react-native-worklets@0.7.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
@@ -3776,19 +3773,6 @@ packages:
react: 19.1.0
react-native: '*'
- react-native-mmkv@4.1.1:
- resolution: {integrity: sha512-nYFjM27l7zVhIiyAqWEFRagGASecb13JMIlzAuOeakRRz9GMJ49hCQntUBE2aeuZRE4u4ehSqTOomB0mTF56Ew==}
- peerDependencies:
- react: 19.1.0
- react-native: '*'
- react-native-nitro-modules: '*'
-
- react-native-nitro-modules@0.32.1:
- resolution: {integrity: sha512-V+Vy76e4fxRxgVGu5Uh3cBPvuFQW8fM1OUKk1mqEA/JawjhX+hxHtBhpfuvNjV0BnV/uXCIg8/eK+rTpB6tqFg==}
- peerDependencies:
- react: 19.1.0
- react-native: '*'
-
react-native-reanimated@4.1.6:
resolution: {integrity: sha512-F+ZJBYiok/6Jzp1re75F/9aLzkgoQCOh4yxrnwATa8392RvM3kx+fiXXFvwcgE59v48lMwd9q0nzF1oJLXpfxQ==}
peerDependencies:
@@ -9251,17 +9235,6 @@ snapshots:
react: 19.1.0
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)
- react-native-mmkv@4.1.1(react-native-nitro-modules@0.32.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0):
- dependencies:
- react: 19.1.0
- react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)
- react-native-nitro-modules: 0.32.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
-
- react-native-nitro-modules@0.32.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0):
- dependencies:
- react: 19.1.0
- react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)
-
react-native-reanimated@4.1.6(@babel/core@7.28.5)(react-native-worklets@0.7.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0):
dependencies:
'@babel/core': 7.28.5
diff --git a/frontends/react-native-ios/src/api/types/auth.types.ts b/frontends/react-native-ios/src/api/types/auth.types.ts
index ae046d6..1957ac3 100644
--- a/frontends/react-native-ios/src/api/types/auth.types.ts
+++ b/frontends/react-native-ios/src/api/types/auth.types.ts
@@ -42,7 +42,7 @@ export const tokenWithUserResponseSchema = tokenResponseSchema.extend({
export const mobileLoginResponseSchema = z.object({
access_token: z.string(),
- refresh_token: z.string(),
+ refresh_token: z.string().optional(),
token_type: z.string(),
user: userResponseSchema,
})
diff --git a/frontends/react-native-ios/src/core/api/api.config.ts b/frontends/react-native-ios/src/core/api/api.config.ts
index 7412ff9..9234a65 100644
--- a/frontends/react-native-ios/src/core/api/api.config.ts
+++ b/frontends/react-native-ios/src/core/api/api.config.ts
@@ -148,9 +148,12 @@ apiClient.interceptors.response.use(
const isUnauthorized = error.response?.status === HTTP_STATUS.UNAUTHORIZED
const isNotRetried = originalRequest._retry !== true
- const isNotRefreshEndpoint = originalRequest.url?.includes('refresh') !== true
+ const isNotAuthEndpoint =
+ !originalRequest.url?.includes('refresh') &&
+ !originalRequest.url?.includes('login') &&
+ !originalRequest.url?.includes('register')
- if (isUnauthorized && isNotRetried && isNotRefreshEndpoint) {
+ if (isUnauthorized && isNotRetried && isNotAuthEndpoint) {
if (isRefreshing) {
return new Promise((resolve, reject) => {
addRefreshSubscriber(
diff --git a/frontends/react-native-ios/src/core/api/query.config.ts b/frontends/react-native-ios/src/core/api/query.config.ts
index e0a839f..66519c1 100644
--- a/frontends/react-native-ios/src/core/api/query.config.ts
+++ b/frontends/react-native-ios/src/core/api/query.config.ts
@@ -6,7 +6,7 @@
import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister'
import { MutationCache, QueryCache, QueryClient } from '@tanstack/react-query'
import { QUERY_CONFIG } from '@/core/config'
-import { queryClientMMKVStorage } from '@/core/storage'
+import { queryClientStorage } from '@/core/storage'
import { ApiError, ApiErrorCode } from './errors'
const NO_RETRY_ERROR_CODES: readonly ApiErrorCode[] = [
@@ -111,6 +111,6 @@ export const queryClient = new QueryClient({
})
export const queryClientPersister = createAsyncStoragePersister({
- storage: queryClientMMKVStorage,
+ storage: queryClientStorage,
key: 'query-cache',
})
diff --git a/frontends/react-native-ios/src/core/config.ts b/frontends/react-native-ios/src/core/config.ts
index 0b2eb90..f58d0c9 100644
--- a/frontends/react-native-ios/src/core/config.ts
+++ b/frontends/react-native-ios/src/core/config.ts
@@ -10,7 +10,6 @@ export const APP_CONFIG = {
KEYCHAIN_SERVICE: 'com.yourcompany.app.auth',
AUTH_STORAGE_KEY: 'app-auth',
- MMKV_STORAGE_ID: 'app-storage',
ACCENT_COLOR: '#C15F3C',
} as const
diff --git a/frontends/react-native-ios/src/core/lib/auth.store.ts b/frontends/react-native-ios/src/core/lib/auth.store.ts
index ee6c12b..4635937 100644
--- a/frontends/react-native-ios/src/core/lib/auth.store.ts
+++ b/frontends/react-native-ios/src/core/lib/auth.store.ts
@@ -7,7 +7,7 @@ import { create } from 'zustand'
import { createJSONStorage, persist } from 'zustand/middleware'
import { type UserResponse, UserRole } from '@/api/types'
import { APP_CONFIG } from '@/core/config'
-import { zustandMMKVStorage } from '@/core/storage'
+import { zustandStorage } from '@/core/storage'
interface AuthState {
user: UserResponse | null
@@ -59,7 +59,7 @@ export const useAuthStore = create()(
}),
{
name: APP_CONFIG.AUTH_STORAGE_KEY,
- storage: createJSONStorage(() => zustandMMKVStorage),
+ storage: createJSONStorage(() => zustandStorage),
partialize: (state) =>
({
user: state.user,
@@ -67,6 +67,7 @@ export const useAuthStore = create()(
}) as AuthStore,
onRehydrateStorage: () => (state) => {
state?.setHasHydrated(true)
+ state?.setLoading(false)
},
}
)
diff --git a/frontends/react-native-ios/src/core/lib/ui.store.ts b/frontends/react-native-ios/src/core/lib/ui.store.ts
index d7f227e..9456c29 100644
--- a/frontends/react-native-ios/src/core/lib/ui.store.ts
+++ b/frontends/react-native-ios/src/core/lib/ui.store.ts
@@ -5,7 +5,7 @@
import { create } from 'zustand'
import { createJSONStorage, persist } from 'zustand/middleware'
-import { zustandMMKVStorage } from '@/core/storage'
+import { zustandStorage } from '@/core/storage'
interface UIState {
biometricsEnabled: boolean
@@ -26,7 +26,7 @@ export const useUIStore = create()(
}),
{
name: 'ui-storage',
- storage: createJSONStorage(() => zustandMMKVStorage),
+ storage: createJSONStorage(() => zustandStorage),
}
)
)
diff --git a/frontends/react-native-ios/src/core/storage/app.storage.ts b/frontends/react-native-ios/src/core/storage/app.storage.ts
new file mode 100644
index 0000000..f74eba4
--- /dev/null
+++ b/frontends/react-native-ios/src/core/storage/app.storage.ts
@@ -0,0 +1,35 @@
+// ===================
+// © AngelaMos | 2026
+// app.storage.ts
+// ===================
+
+import * as SecureStore from 'expo-secure-store'
+import type { StateStorage } from 'zustand/middleware'
+
+export const zustandStorage: StateStorage = {
+ getItem: (name: string): string | null => {
+ return SecureStore.getItem(name)
+ },
+
+ setItem: (name: string, value: string): void => {
+ SecureStore.setItem(name, value)
+ },
+
+ removeItem: (name: string): void => {
+ SecureStore.deleteItemAsync(name)
+ },
+}
+
+export const queryClientStorage = {
+ getItem: (key: string): string | null => {
+ return SecureStore.getItem(key)
+ },
+
+ setItem: (key: string, value: string): void => {
+ SecureStore.setItem(key, value)
+ },
+
+ removeItem: (key: string): void => {
+ SecureStore.deleteItemAsync(key)
+ },
+}
diff --git a/frontends/react-native-ios/src/core/storage/index.ts b/frontends/react-native-ios/src/core/storage/index.ts
index 77fcded..c494275 100644
--- a/frontends/react-native-ios/src/core/storage/index.ts
+++ b/frontends/react-native-ios/src/core/storage/index.ts
@@ -1,7 +1,7 @@
-/**
- * @AngelaMos | 2026
- * index.ts
- */
+// ===================
+// © AngelaMos | 2026
+// index.ts
+// ===================
-export * from './mmkv.storage'
export * from './secure.storage'
+export * from './app.storage'
diff --git a/frontends/react-native-ios/src/core/storage/mmkv.storage.ts b/frontends/react-native-ios/src/core/storage/mmkv.storage.ts
deleted file mode 100644
index 0251a8b..0000000
--- a/frontends/react-native-ios/src/core/storage/mmkv.storage.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-// ===================
-// © AngelaMos | 2026
-// mmkv.storage.ts
-// ===================
-
-import { MMKV } from 'react-native-mmkv'
-import type { StateStorage } from 'zustand/middleware'
-import { APP_CONFIG } from '@/core/config'
-
-export const mmkv = new MMKV({
- id: APP_CONFIG.MMKV_STORAGE_ID,
-})
-
-export const zustandMMKVStorage: StateStorage = {
- getItem: (name: string): string | null => {
- const value = mmkv.getString(name)
- return value ?? null
- },
-
- setItem: (name: string, value: string): void => {
- mmkv.set(name, value)
- },
-
- removeItem: (name: string): void => {
- mmkv.delete(name)
- },
-}
-
-export const queryClientMMKVStorage = {
- getItem: (key: string): string | null => {
- const value = mmkv.getString(key)
- return value ?? null
- },
-
- setItem: (key: string, value: string): void => {
- mmkv.set(key, value)
- },
-
- removeItem: (key: string): void => {
- mmkv.delete(key)
- },
-}
diff --git a/frontends/react-native-ios/src/shared/components/ui/Button.tsx b/frontends/react-native-ios/src/shared/components/ui/Button.tsx
index fee9716..79508e6 100644
--- a/frontends/react-native-ios/src/shared/components/ui/Button.tsx
+++ b/frontends/react-native-ios/src/shared/components/ui/Button.tsx
@@ -3,9 +3,10 @@
* Button.tsx
*/
-import type React from 'react'
-import { type GetProps, Stack, styled, Text } from 'tamagui'
import { haptics } from '@/shared/utils'
+import type React from 'react'
+import { type GetProps, styled } from 'tamagui'
+import { Stack, Text } from 'tamagui'
const ButtonFrame = styled(Stack, {
name: 'Button',
@@ -14,9 +15,9 @@ const ButtonFrame = styled(Stack, {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
- height: 48,
+ height: 44,
paddingHorizontal: '$4',
- borderRadius: '$3',
+ borderRadius: '$2',
cursor: 'pointer',
pressStyle: {
opacity: 0.9,
@@ -64,7 +65,7 @@ const ButtonFrame = styled(Stack, {
const ButtonText = styled(Text, {
name: 'ButtonText',
- fontSize: 16,
+ fontSize: 14,
fontWeight: '500',
textAlign: 'center',
diff --git a/frontends/react-native-ios/src/shared/components/ui/Card.tsx b/frontends/react-native-ios/src/shared/components/ui/Card.tsx
index ea1b8b1..dc50af6 100644
--- a/frontends/react-native-ios/src/shared/components/ui/Card.tsx
+++ b/frontends/react-native-ios/src/shared/components/ui/Card.tsx
@@ -3,17 +3,18 @@
* Card.tsx
*/
-import { type GetProps, Stack, styled, Text } from 'tamagui'
+import { type GetProps, styled } from 'tamagui'
+import { Stack, Text } from 'tamagui'
export const Card = styled(Stack, {
name: 'Card',
backgroundColor: '$black',
borderWidth: 1,
borderColor: '$borderDefault',
- borderRadius: '$4',
- padding: '$8',
+ borderRadius: '$3',
+ padding: '$6',
width: '100%',
- maxWidth: 400,
+ maxWidth: 340,
variants: {
variant: {
@@ -32,27 +33,27 @@ export const Card = styled(Stack, {
export const CardHeader = styled(Stack, {
name: 'CardHeader',
flexDirection: 'column',
- marginBottom: '$6',
+ marginBottom: '$4',
})
export const CardTitle = styled(Text, {
name: 'CardTitle',
- fontSize: 24,
+ fontSize: 22,
fontWeight: '600',
color: '$white',
- marginBottom: '$2',
+ marginBottom: '$1',
})
export const CardSubtitle = styled(Text, {
name: 'CardSubtitle',
- fontSize: 14,
+ fontSize: 12,
color: '$textLight',
})
export const CardContent = styled(Stack, {
name: 'CardContent',
flexDirection: 'column',
- gap: '$5',
+ gap: '$4',
})
export const CardFooter = styled(Stack, {
@@ -60,19 +61,19 @@ export const CardFooter = styled(Stack, {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
- marginTop: '$6',
+ marginTop: '$4',
})
export const CardFooterText = styled(Text, {
name: 'CardFooterText',
- fontSize: 14,
+ fontSize: 12,
color: '$textLight',
textAlign: 'center',
})
export const CardFooterLink = styled(Text, {
name: 'CardFooterLink',
- fontSize: 14,
+ fontSize: 12,
color: '$accent',
textDecorationLine: 'underline',
})
diff --git a/frontends/react-native-ios/src/shared/components/ui/DottedBackground.tsx b/frontends/react-native-ios/src/shared/components/ui/DottedBackground.tsx
new file mode 100644
index 0000000..c410bdd
--- /dev/null
+++ b/frontends/react-native-ios/src/shared/components/ui/DottedBackground.tsx
@@ -0,0 +1,65 @@
+// ===================
+// © AngelaMos | 2026
+// DottedBackground.tsx
+// ===================
+
+import { colors } from '@/theme/tokens'
+import type React from 'react'
+import { Dimensions, StyleSheet, View } from 'react-native'
+import Svg, { Circle, Defs, Pattern, Rect } from 'react-native-svg'
+
+interface DottedBackgroundProps {
+ children: React.ReactNode
+ dotColor?: string
+ dotSize?: number
+ dotSpacing?: number
+}
+
+export function DottedBackground({
+ children,
+ dotColor = colors.bgShellDot.val,
+ dotSize = 1,
+ dotSpacing = 16,
+}: DottedBackgroundProps): React.ReactElement {
+ const { width, height } = Dimensions.get('window')
+
+ return (
+
+
+ {children}
+
+ )
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ content: {
+ flex: 1,
+ },
+})
diff --git a/frontends/react-native-ios/src/shared/components/ui/Input.tsx b/frontends/react-native-ios/src/shared/components/ui/Input.tsx
index 888b9c4..7911186 100644
--- a/frontends/react-native-ios/src/shared/components/ui/Input.tsx
+++ b/frontends/react-native-ios/src/shared/components/ui/Input.tsx
@@ -3,25 +3,20 @@
* Input.tsx
*/
-import { forwardRef, type Ref } from 'react'
+import { type Ref, forwardRef } from 'react'
import type { TextInput as RNTextInput } from 'react-native'
-import {
- type GetProps,
- Stack,
- styled,
- Input as TamaguiInput,
- Text,
-} from 'tamagui'
+import { type GetProps, styled } from 'tamagui'
+import { Stack, Input as TamaguiInput, Text } from 'tamagui'
const InputFrame = styled(TamaguiInput, {
name: 'Input',
- height: 48,
- paddingHorizontal: '$4',
+ height: 44,
+ paddingHorizontal: '$3',
backgroundColor: 'transparent',
borderWidth: 1,
borderColor: '$borderDefault',
- borderRadius: '$3',
- fontSize: 16,
+ borderRadius: '$2',
+ fontSize: 14,
color: '$white',
placeholderTextColor: '$textMuted',
outlineWidth: 0,
@@ -50,7 +45,7 @@ const InputContainer = styled(Stack, {
const Label = styled(Text, {
name: 'Label',
- fontSize: 14,
+ fontSize: 12,
fontWeight: '500',
color: '$white',
})
diff --git a/frontends/react-native-ios/src/shared/components/ui/index.ts b/frontends/react-native-ios/src/shared/components/ui/index.ts
index a5d296f..afdfda7 100644
--- a/frontends/react-native-ios/src/shared/components/ui/index.ts
+++ b/frontends/react-native-ios/src/shared/components/ui/index.ts
@@ -4,6 +4,7 @@
*/
export * from './Button'
-export * from './Card'
export * from './Input'
+export * from './Card'
export * from './PasswordInput'
+export * from './DottedBackground'