diff --git a/apps/mobile/package.json b/apps/mobile/package.json index d12117a724..de885b7117 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -66,6 +66,7 @@ "expo-sharing": "~55.0.21", "expo-splash-screen": "55.0.22", "expo-status-bar": "55.0.6", + "expo-store-review": "~55.0.15", "expo-tracking-transparency": "55.0.15", "expo-web-browser": "55.0.17", "jotai": "2.18.1", diff --git a/apps/mobile/src/components/profile-screen.tsx b/apps/mobile/src/components/profile-screen.tsx index 5cd6dfc67a..10ecfb87f5 100644 --- a/apps/mobile/src/components/profile-screen.tsx +++ b/apps/mobile/src/components/profile-screen.tsx @@ -4,13 +4,13 @@ import { type Href, useRouter } from 'expo-router'; import { GitPullRequest, KeyRound, - LifeBuoy, Lock, LogOut, + MessageSquare, ShieldCheck, Trash2, } from 'lucide-react-native'; -import { Alert, Linking, Platform, Pressable, ScrollView, View } from 'react-native'; +import { Alert, Platform, Pressable, ScrollView, View } from 'react-native'; import { toast } from 'sonner-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import Animated, { FadeIn, FadeOut, LinearTransition } from 'react-native-reanimated'; @@ -23,6 +23,7 @@ import { ConfigureRow } from '@/components/ui/configure-row'; import { Skeleton } from '@/components/ui/skeleton'; import { Text } from '@/components/ui/text'; import { useAuth } from '@/lib/auth/auth-context'; +import { showFeedbackPrompt } from '@/lib/feedback'; import { useCurrentUserId } from '@/lib/hooks/use-current-user-id'; import { useThemeColors } from '@/lib/hooks/use-theme-colors'; import { useOrganization } from '@/lib/organization-context'; @@ -31,8 +32,6 @@ import { getSecurityAgentPath } from '@/lib/security-agent'; import { getTabBarOverlayHeight } from '@/lib/tab-bar-layout'; import { useTRPC } from '@/lib/trpc'; -const SUPPORT_EMAIL = 'hi@kilo.ai'; - function providerIcon(_provider: string) { return KeyRound; } @@ -97,21 +96,6 @@ export function ProfileScreen() { const { bottom } = useSafeAreaInsets(); - const openSupportEmail = async () => { - const envDetails = [ - `User ID: ${userId ?? 'unknown'}`, - `App version: ${Application.nativeApplicationVersion} (${Application.nativeBuildVersion})`, - `OS: ${Platform.OS} ${Platform.Version}`, - ].join('\n'); - const body = `\n\n---\n${envDetails}`; - const url = `mailto:${SUPPORT_EMAIL}?subject=${encodeURIComponent('mobile app feedback')}&body=${encodeURIComponent(body)}`; - try { - await Linking.openURL(url); - } catch { - toast.error(`No email app available. You can reach us at ${SUPPORT_EMAIL}`); - } - }; - const deleteAccount = useMutation( trpc.user.requestAccountDeletion.mutationOptions({ onSuccess: () => { @@ -262,11 +246,11 @@ export function ProfileScreen() { { - void openSupportEmail(); + showFeedbackPrompt(userId); }} /> { + Alert.alert( + "We're glad to hear that!", + 'A store review would help us out immensely. Thanks for using Kilo!', + [ + { text: 'Not now', style: 'cancel' }, + { + text: 'Rate Kilo', + onPress: () => { + void rateApp(); + }, + }, + ] + ); + }, + }, + { + text: 'Needs work', + onPress: () => { + Alert.alert( + "We're sorry to hear that!", + 'Please let us know what needs to be better. The engineer in charge of the app reads every single report!', + [ + { text: 'Not now', style: 'cancel' }, + { + text: 'Email us', + onPress: () => { + void openSupportEmail(userId); + }, + }, + ] + ); + }, + }, + ]); +} diff --git a/apps/mobile/src/lib/storage-keys.ts b/apps/mobile/src/lib/storage-keys.ts index 541feac18f..c33ee361fa 100644 --- a/apps/mobile/src/lib/storage-keys.ts +++ b/apps/mobile/src/lib/storage-keys.ts @@ -14,3 +14,4 @@ export const LAST_ACTIVE_INSTANCE_KEY = 'last-active-chat-instance'; export const CONSENT_USER_KEY_PREFIX = 'consent-accepted-'; export const AGENT_MODEL_PREFERENCE_KEY = 'agent-model-preference'; export const REASONING_DEFAULT_EXPANDED_KEY = 'agent-reasoning-default-expanded'; +export const REVIEW_REQUESTED_AT_KEY = 'store-review-requested-at'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6a6eecf9bd..d636bae5cf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -387,6 +387,9 @@ importers: expo-status-bar: specifier: 55.0.6 version: 55.0.6(react-native@0.83.6(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.0)(utf-8-validate@6.0.6))(react@19.2.0) + expo-store-review: + specifier: ~55.0.15 + version: 55.0.15(expo@55.0.27)(react-native@0.83.6(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.0)(utf-8-validate@6.0.6)) expo-tracking-transparency: specifier: 55.0.15 version: 55.0.15(expo@55.0.27)(react-native@0.83.6(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.0)(utf-8-validate@6.0.6)) @@ -12177,6 +12180,12 @@ packages: react: '*' react-native: '*' + expo-store-review@55.0.15: + resolution: {integrity: sha512-p4hnMPMqP/CzX9rI32dI8xwWzIAXYQgCldUpx/me/xA3SBwQhjC1OzaCvmtHolxpVv19ut9YtxH2JXTAoadrVw==} + peerDependencies: + expo: '*' + react-native: '*' + expo-symbols@55.0.9: resolution: {integrity: sha512-F85C/8ExQjd2gYjasLVKMT8wPj+1+19TVTqg4jAeVjVZklqiQtLO72io9Ji1xAjYNgmDeUI0diVHlFMMTC4Ekg==} peerDependencies: @@ -28883,6 +28892,11 @@ snapshots: react-native: 0.83.6(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.0)(utf-8-validate@6.0.6) react-native-is-edge-to-edge: 1.3.1(react-native@0.83.6(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.0)(utf-8-validate@6.0.6))(react@19.2.0) + expo-store-review@55.0.15(expo@55.0.27)(react-native@0.83.6(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.0)(utf-8-validate@6.0.6)): + dependencies: + expo: 55.0.27(@babel/core@7.29.0)(@expo/dom-webview@55.0.6)(@expo/metro-runtime@55.0.11)(bufferutil@4.1.0)(expo-router@55.0.16)(react-dom@19.2.6(react@19.2.0))(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.6(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.0)(utf-8-validate@6.0.6))(react@19.2.0))(react-native@0.83.6(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.0)(utf-8-validate@6.0.6))(react@19.2.0)(typescript@5.9.3)(utf-8-validate@6.0.6) + react-native: 0.83.6(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.0)(utf-8-validate@6.0.6) + expo-symbols@55.0.9(expo-font@55.0.8)(expo@55.0.27)(react-native@0.83.6(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.0)(utf-8-validate@6.0.6))(react@19.2.0): dependencies: '@expo-google-fonts/material-symbols': 0.4.27