vybekiit 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +840 -296
- package/dist/global-skills/add-ai/SKILL.md +53 -0
- package/dist/global-skills/add-analytics/SKILL.md +65 -0
- package/dist/global-skills/add-app-clip/SKILL.md +280 -0
- package/dist/global-skills/add-app-clip/agents/openai.yaml +4 -0
- package/dist/global-skills/add-app-clip/references/native-module.md +96 -0
- package/dist/global-skills/add-blog/SKILL.md +49 -0
- package/dist/global-skills/add-crud/SKILL.md +28 -0
- package/dist/global-skills/add-files/SKILL.md +65 -0
- package/dist/global-skills/add-images/SKILL.md +64 -0
- package/dist/global-skills/add-language/SKILL.md +44 -0
- package/dist/global-skills/add-notifications/SKILL.md +61 -0
- package/dist/global-skills/add-realtime/SKILL.md +49 -0
- package/dist/global-skills/add-route/SKILL.md +28 -0
- package/dist/global-skills/add-search/SKILL.md +43 -0
- package/dist/global-skills/add-signin/SKILL.md +78 -0
- package/dist/global-skills/add-teams/SKILL.md +64 -0
- package/dist/global-skills/add-upload/SKILL.md +27 -0
- package/dist/global-skills/aws-cdk/SKILL.md +72 -0
- package/dist/global-skills/aws-cdk/references/bootstrap-and-project-setup.md +257 -0
- package/dist/global-skills/aws-cdk/references/compliance-and-drift.md +193 -0
- package/dist/global-skills/aws-cdk/references/construct-patterns.md +315 -0
- package/dist/global-skills/aws-cdk/references/import-and-migrate.md +184 -0
- package/dist/global-skills/aws-cdk/references/refactor-and-prevent-replacement.md +198 -0
- package/dist/global-skills/aws-cdk/references/troubleshooting-credentials.md +183 -0
- package/dist/global-skills/aws-cdk/references/troubleshooting-deployment.md +293 -0
- package/dist/global-skills/aws-cdk/references/troubleshooting-synth.md +274 -0
- package/dist/global-skills/aws-cdk/references/v1-to-v2-migration.md +119 -0
- package/dist/global-skills/aws-cloudformation/SKILL.md +88 -0
- package/dist/global-skills/aws-cloudformation/references/author-cloudformation-best-practices.script.md +179 -0
- package/dist/global-skills/aws-cloudformation/references/check-cloudformation-template-compliance.script.md +159 -0
- package/dist/global-skills/aws-cloudformation/references/cloudformation-pre-deploy-validation.script.md +217 -0
- package/dist/global-skills/aws-cloudformation/references/deploy-with-express-mode.script.md +271 -0
- package/dist/global-skills/aws-cloudformation/references/lookup-resource-properties.script.md +130 -0
- package/dist/global-skills/aws-cloudformation/references/troubleshoot-deployment.script.md +200 -0
- package/dist/global-skills/aws-cloudformation/references/validate-cloudformation-template.script.md +135 -0
- package/dist/global-skills/aws-serverless/SKILL.md +51 -0
- package/dist/global-skills/aws-serverless/assets/powertools-handler.py +49 -0
- package/dist/global-skills/aws-serverless/references/api-gateway.md +553 -0
- package/dist/global-skills/aws-serverless/references/architecture.md +262 -0
- package/dist/global-skills/aws-serverless/references/concurrency.md +200 -0
- package/dist/global-skills/aws-serverless/references/deployment.md +94 -0
- package/dist/global-skills/aws-serverless/references/event-sources.md +484 -0
- package/dist/global-skills/aws-serverless/references/lambda.md +548 -0
- package/dist/global-skills/aws-serverless/references/orchestration.md +449 -0
- package/dist/global-skills/aws-serverless/references/production.md +493 -0
- package/dist/global-skills/aws-serverless/references/troubleshooting.md +711 -0
- package/dist/global-skills/back-up-my-code/SKILL.md +48 -0
- package/dist/global-skills/better-auth-best-practices/SKILL.md +182 -0
- package/dist/global-skills/better-auth-security-best-practices/SKILL.md +432 -0
- package/dist/global-skills/building-native-ui/SKILL.md +377 -0
- package/dist/global-skills/building-native-ui/agents/openai.yaml +4 -0
- package/dist/global-skills/building-native-ui/references/animations.md +220 -0
- package/dist/global-skills/building-native-ui/references/controls.md +270 -0
- package/dist/global-skills/building-native-ui/references/form-sheet.md +253 -0
- package/dist/global-skills/building-native-ui/references/gradients.md +106 -0
- package/dist/global-skills/building-native-ui/references/icons.md +213 -0
- package/dist/global-skills/building-native-ui/references/media.md +198 -0
- package/dist/global-skills/building-native-ui/references/route-structure.md +229 -0
- package/dist/global-skills/building-native-ui/references/search.md +248 -0
- package/dist/global-skills/building-native-ui/references/storage.md +121 -0
- package/dist/global-skills/building-native-ui/references/tabs.md +433 -0
- package/dist/global-skills/building-native-ui/references/toolbar-and-headers.md +284 -0
- package/dist/global-skills/building-native-ui/references/visual-effects.md +197 -0
- package/dist/global-skills/building-native-ui/references/webgpu-three.md +605 -0
- package/dist/global-skills/building-native-ui/references/zoom-transitions.md +158 -0
- package/dist/global-skills/buy-domain/SKILL.md +64 -0
- package/dist/global-skills/check-safety/SKILL.md +94 -0
- package/dist/global-skills/claimable-postgres/SKILL.md +249 -0
- package/dist/global-skills/configure-capabilities/SKILL.md +38 -0
- package/dist/global-skills/connect-account/SKILL.md +64 -0
- package/dist/global-skills/connect-account-backend/SKILL.md +41 -0
- package/dist/global-skills/create-auth-skill/SKILL.md +321 -0
- package/dist/global-skills/design-my-data/SKILL.md +48 -0
- package/dist/global-skills/doctor/SKILL.md +84 -0
- package/dist/global-skills/eas-app-stores/SKILL.md +152 -0
- package/dist/global-skills/eas-app-stores/agents/openai.yaml +4 -0
- package/dist/global-skills/eas-app-stores/references/app-store-metadata.md +497 -0
- package/dist/global-skills/eas-app-stores/references/ios-app-store.md +371 -0
- package/dist/global-skills/eas-app-stores/references/play-store.md +246 -0
- package/dist/global-skills/eas-app-stores/references/testflight.md +58 -0
- package/dist/global-skills/eas-app-stores/references/workflows.md +120 -0
- package/dist/global-skills/eas-hosting/SKILL.md +423 -0
- package/dist/global-skills/eas-hosting/agents/openai.yaml +4 -0
- package/dist/global-skills/eas-observe/SKILL.md +32 -0
- package/dist/global-skills/eas-observe/agents/openai.yaml +4 -0
- package/dist/global-skills/eas-observe/references/metrics.md +47 -0
- package/dist/global-skills/eas-observe/references/queries.md +342 -0
- package/dist/global-skills/eas-observe/references/setup.md +317 -0
- package/dist/global-skills/eas-simulator/SKILL.md +156 -0
- package/dist/global-skills/eas-simulator/agents/openai.yaml +4 -0
- package/dist/global-skills/eas-simulator/references/controllers.md +67 -0
- package/dist/global-skills/eas-simulator/references/run-your-app.md +185 -0
- package/dist/global-skills/eas-simulator/references/troubleshooting.md +38 -0
- package/dist/global-skills/eas-update-insights/SKILL.md +230 -0
- package/dist/global-skills/eas-update-insights/agents/openai.yaml +4 -0
- package/dist/global-skills/eas-update-insights/references/channel-insights-schema.md +47 -0
- package/dist/global-skills/eas-update-insights/references/update-insights-schema.md +69 -0
- package/dist/global-skills/eas-workflows/SKILL.md +94 -0
- package/dist/global-skills/eas-workflows/agents/openai.yaml +4 -0
- package/dist/global-skills/eas-workflows/scripts/fetch.js +109 -0
- package/dist/global-skills/eas-workflows/scripts/package.json +11 -0
- package/dist/global-skills/eas-workflows/scripts/validate.js +84 -0
- package/dist/global-skills/email-best-practices/SKILL.md +73 -0
- package/dist/global-skills/expo-api-routes/SKILL.md +368 -0
- package/dist/global-skills/expo-api-routes/agents/openai.yaml +4 -0
- package/dist/global-skills/expo-app-clip/SKILL.md +282 -0
- package/dist/global-skills/expo-app-clip/agents/openai.yaml +4 -0
- package/dist/global-skills/expo-app-clip/references/native-module.md +96 -0
- package/dist/global-skills/expo-brownfield/SKILL.md +54 -0
- package/dist/global-skills/expo-brownfield/agents/openai.yaml +4 -0
- package/dist/global-skills/expo-brownfield/references/brownfield-integrated.md +526 -0
- package/dist/global-skills/expo-brownfield/references/brownfield-isolated.md +402 -0
- package/dist/global-skills/expo-brownfield/references/comparison.md +63 -0
- package/dist/global-skills/expo-brownfield/references/troubleshooting.md +88 -0
- package/dist/global-skills/expo-cicd-workflows/SKILL.md +92 -0
- package/dist/global-skills/expo-cicd-workflows/agents/openai.yaml +4 -0
- package/dist/global-skills/expo-cicd-workflows/scripts/fetch.js +109 -0
- package/dist/global-skills/expo-cicd-workflows/scripts/package.json +11 -0
- package/dist/global-skills/expo-cicd-workflows/scripts/validate.js +84 -0
- package/dist/global-skills/expo-data-fetching/SKILL.md +449 -0
- package/dist/global-skills/expo-data-fetching/agents/openai.yaml +4 -0
- package/dist/global-skills/expo-data-fetching/references/expo-router-loaders.md +341 -0
- package/dist/global-skills/expo-data-fetching/references/offline-and-cancellation.md +68 -0
- package/dist/global-skills/expo-deployment/SKILL.md +167 -0
- package/dist/global-skills/expo-deployment/agents/openai.yaml +4 -0
- package/dist/global-skills/expo-deployment/references/app-store-metadata.md +497 -0
- package/dist/global-skills/expo-deployment/references/ios-app-store.md +371 -0
- package/dist/global-skills/expo-deployment/references/play-store.md +246 -0
- package/dist/global-skills/expo-deployment/references/testflight.md +58 -0
- package/dist/global-skills/expo-deployment/references/workflows.md +160 -0
- package/dist/global-skills/expo-dev-client/SKILL.md +174 -0
- package/dist/global-skills/expo-dev-client/agents/openai.yaml +4 -0
- package/dist/global-skills/expo-dom/SKILL.md +417 -0
- package/dist/global-skills/expo-dom/agents/openai.yaml +4 -0
- package/dist/global-skills/expo-examples/SKILL.md +99 -0
- package/dist/global-skills/expo-examples/agents/openai.yaml +4 -0
- package/dist/global-skills/expo-examples/references/catalog.md +105 -0
- package/dist/global-skills/expo-module/SKILL.md +141 -0
- package/dist/global-skills/expo-module/agents/openai.yaml +4 -0
- package/dist/global-skills/expo-module/references/config-plugin.md +90 -0
- package/dist/global-skills/expo-module/references/create-expo-module.md +206 -0
- package/dist/global-skills/expo-module/references/lifecycle.md +127 -0
- package/dist/global-skills/expo-module/references/module-config.md +48 -0
- package/dist/global-skills/expo-module/references/native-module.md +286 -0
- package/dist/global-skills/expo-module/references/native-view.md +171 -0
- package/dist/global-skills/expo-native-ui/SKILL.md +181 -0
- package/dist/global-skills/expo-native-ui/agents/openai.yaml +4 -0
- package/dist/global-skills/expo-native-ui/references/animations.md +220 -0
- package/dist/global-skills/expo-native-ui/references/controls.md +270 -0
- package/dist/global-skills/expo-native-ui/references/gradients.md +106 -0
- package/dist/global-skills/expo-native-ui/references/icons.md +213 -0
- package/dist/global-skills/expo-native-ui/references/media.md +198 -0
- package/dist/global-skills/expo-native-ui/references/storage.md +121 -0
- package/dist/global-skills/expo-native-ui/references/visual-effects.md +197 -0
- package/dist/global-skills/expo-native-ui/references/webgpu-three.md +605 -0
- package/dist/global-skills/expo-observe/SKILL.md +30 -0
- package/dist/global-skills/expo-observe/references/metrics.md +47 -0
- package/dist/global-skills/expo-observe/references/queries.md +342 -0
- package/dist/global-skills/expo-observe/references/setup.md +317 -0
- package/dist/global-skills/expo-router/SKILL.md +230 -0
- package/dist/global-skills/expo-router/agents/openai.yaml +4 -0
- package/dist/global-skills/expo-router/references/form-sheet.md +253 -0
- package/dist/global-skills/expo-router/references/route-structure.md +229 -0
- package/dist/global-skills/expo-router/references/search.md +248 -0
- package/dist/global-skills/expo-router/references/tabs.md +433 -0
- package/dist/global-skills/expo-router/references/toolbar-and-headers.md +284 -0
- package/dist/global-skills/expo-router/references/zoom-transitions.md +158 -0
- package/dist/global-skills/expo-skill-eval/SKILL.md +314 -0
- package/dist/global-skills/expo-skill-eval/agents/visual-grader.md +52 -0
- package/dist/global-skills/expo-skill-eval/references/design-rubric.md +45 -0
- package/dist/global-skills/expo-skill-eval/references/runtime-matrix.md +34 -0
- package/dist/global-skills/expo-skill-eval/scripts/check-static.sh +64 -0
- package/dist/global-skills/expo-skill-eval/scripts/clean-fixture.sh +56 -0
- package/dist/global-skills/expo-skill-eval/scripts/generate_viewer.py +450 -0
- package/dist/global-skills/expo-skill-eval/scripts/latest-sdk.sh +46 -0
- package/dist/global-skills/expo-skill-eval/scripts/make-fixture.sh +95 -0
- package/dist/global-skills/expo-skill-eval/scripts/make-workspace.sh +31 -0
- package/dist/global-skills/expo-skill-eval/scripts/snapshot-android.sh +284 -0
- package/dist/global-skills/expo-skill-eval/scripts/snapshot-ios.sh +132 -0
- package/dist/global-skills/expo-skill-eval/scripts/snapshot-web.sh +61 -0
- package/dist/global-skills/expo-skill-feedback/SKILL.md +54 -0
- package/dist/global-skills/expo-skill-feedback/agents/openai.yaml +4 -0
- package/dist/global-skills/expo-skill-feedback/scripts/skill-event.cjs +180 -0
- package/dist/global-skills/expo-skill-feedback/scripts/skill-feedback.cjs +118 -0
- package/dist/global-skills/expo-skill-feedback/scripts/telemetry.cjs +65 -0
- package/dist/global-skills/expo-skill-feedback/scripts/telemetry_common.cjs +171 -0
- package/dist/global-skills/expo-tailwind-setup/SKILL.md +480 -0
- package/dist/global-skills/expo-tailwind-setup/agents/openai.yaml +4 -0
- package/dist/global-skills/expo-ui/SKILL.md +47 -0
- package/dist/global-skills/expo-ui/agents/openai.yaml +4 -0
- package/dist/global-skills/expo-ui/references/drop-in-replacements.md +27 -0
- package/dist/global-skills/expo-ui/references/jetpack-compose.md +73 -0
- package/dist/global-skills/expo-ui/references/swift-ui.md +73 -0
- package/dist/global-skills/expo-ui/references/universal.md +73 -0
- package/dist/global-skills/expo-ui/scripts/list-components.js +193 -0
- package/dist/global-skills/expo-upgrade/SKILL.md +134 -0
- package/dist/global-skills/expo-upgrade/agents/openai.yaml +4 -0
- package/dist/global-skills/expo-upgrade/references/expo-av-to-audio.md +132 -0
- package/dist/global-skills/expo-upgrade/references/expo-av-to-video.md +160 -0
- package/dist/global-skills/expo-upgrade/references/native-tabs.md +124 -0
- package/dist/global-skills/expo-upgrade/references/new-architecture.md +79 -0
- package/dist/global-skills/expo-upgrade/references/react-19.md +79 -0
- package/dist/global-skills/expo-upgrade/references/react-compiler.md +59 -0
- package/dist/global-skills/expo-upgrade/references/react-navigation-to-expo-router.md +61 -0
- package/dist/global-skills/expo-web-to-native/SKILL.md +83 -0
- package/dist/global-skills/expo-web-to-native/agents/openai.yaml +4 -0
- package/dist/global-skills/expo-web-to-native/references/false-friends.md +119 -0
- package/dist/global-skills/expo-web-to-native/references/native-patterns.md +39 -0
- package/dist/global-skills/expo-web-to-native/references/run-as-goal.md +48 -0
- package/dist/global-skills/expo-web-to-native/references/verify-on-device.md +45 -0
- package/dist/global-skills/firebase-ai-logic-basics/SKILL.md +165 -0
- package/dist/global-skills/firebase-ai-logic-basics/references/flutter_setup.md +95 -0
- package/dist/global-skills/firebase-ai-logic-basics/references/ios_setup.md +156 -0
- package/dist/global-skills/firebase-ai-logic-basics/references/usage_patterns_android.md +157 -0
- package/dist/global-skills/firebase-ai-logic-basics/references/usage_patterns_web.md +186 -0
- package/dist/global-skills/firebase-app-hosting-basics/SKILL.md +73 -0
- package/dist/global-skills/firebase-app-hosting-basics/references/cli_commands.md +85 -0
- package/dist/global-skills/firebase-app-hosting-basics/references/configuration.md +59 -0
- package/dist/global-skills/firebase-app-hosting-basics/references/emulation.md +59 -0
- package/dist/global-skills/firebase-auth-basics/SKILL.md +118 -0
- package/dist/global-skills/firebase-auth-basics/references/client_sdk_android.md +160 -0
- package/dist/global-skills/firebase-auth-basics/references/client_sdk_web.md +301 -0
- package/dist/global-skills/firebase-auth-basics/references/flutter_setup.md +149 -0
- package/dist/global-skills/firebase-auth-basics/references/ios_setup.md +87 -0
- package/dist/global-skills/firebase-auth-basics/references/security_rules.md +49 -0
- package/dist/global-skills/firebase-basics/SKILL.md +148 -0
- package/dist/global-skills/firebase-basics/references/android_setup.md +41 -0
- package/dist/global-skills/firebase-basics/references/firebase-cli-guide.md +18 -0
- package/dist/global-skills/firebase-basics/references/firebase-service-init.md +20 -0
- package/dist/global-skills/firebase-basics/references/flutter_setup.md +143 -0
- package/dist/global-skills/firebase-basics/references/ios_setup.md +113 -0
- package/dist/global-skills/firebase-basics/references/local-env-setup.md +80 -0
- package/dist/global-skills/firebase-basics/references/refresh/android_studio.md +41 -0
- package/dist/global-skills/firebase-basics/references/refresh/antigravity.md +64 -0
- package/dist/global-skills/firebase-basics/references/refresh/claude.md +12 -0
- package/dist/global-skills/firebase-basics/references/refresh/gemini-cli.md +13 -0
- package/dist/global-skills/firebase-basics/references/refresh/other-agents.md +67 -0
- package/dist/global-skills/firebase-basics/references/setup/android_studio.md +23 -0
- package/dist/global-skills/firebase-basics/references/setup/antigravity.md +98 -0
- package/dist/global-skills/firebase-basics/references/setup/claude_code.md +45 -0
- package/dist/global-skills/firebase-basics/references/setup/cursor.md +93 -0
- package/dist/global-skills/firebase-basics/references/setup/gemini_cli.md +53 -0
- package/dist/global-skills/firebase-basics/references/setup/github_copilot.md +104 -0
- package/dist/global-skills/firebase-basics/references/setup/other_agents.md +99 -0
- package/dist/global-skills/firebase-basics/references/web_setup.md +77 -0
- package/dist/global-skills/firebase-crashlytics/SKILL.md +42 -0
- package/dist/global-skills/firebase-crashlytics/references/android_setup.md +152 -0
- package/dist/global-skills/firebase-crashlytics/references/ios_setup.md +115 -0
- package/dist/global-skills/firebase-data-connect/SKILL.md +191 -0
- package/dist/global-skills/firebase-data-connect/examples.md +638 -0
- package/dist/global-skills/firebase-data-connect/reference/cloud_functions.md +184 -0
- package/dist/global-skills/firebase-data-connect/reference/config.md +271 -0
- package/dist/global-skills/firebase-data-connect/reference/data_seeding.md +185 -0
- package/dist/global-skills/firebase-data-connect/reference/native_sql.md +170 -0
- package/dist/global-skills/firebase-data-connect/reference/operations.md +385 -0
- package/dist/global-skills/firebase-data-connect/reference/realtime.md +210 -0
- package/dist/global-skills/firebase-data-connect/reference/schema.md +290 -0
- package/dist/global-skills/firebase-data-connect/reference/sdk_admin_node.md +141 -0
- package/dist/global-skills/firebase-data-connect/reference/sdk_android.md +126 -0
- package/dist/global-skills/firebase-data-connect/reference/sdk_flutter.md +134 -0
- package/dist/global-skills/firebase-data-connect/reference/sdk_ios.md +157 -0
- package/dist/global-skills/firebase-data-connect/reference/sdk_web.md +146 -0
- package/dist/global-skills/firebase-data-connect/reference/search.md +264 -0
- package/dist/global-skills/firebase-data-connect/reference/security.md +295 -0
- package/dist/global-skills/firebase-data-connect/templates.md +318 -0
- package/dist/global-skills/firebase-firestore/SKILL.md +89 -0
- package/dist/global-skills/firebase-firestore/references/enterprise/android_sdk_usage.md +230 -0
- package/dist/global-skills/firebase-firestore/references/enterprise/data_model.md +66 -0
- package/dist/global-skills/firebase-firestore/references/enterprise/flutter_setup.md +180 -0
- package/dist/global-skills/firebase-firestore/references/enterprise/indexes.md +135 -0
- package/dist/global-skills/firebase-firestore/references/enterprise/ios_setup.md +189 -0
- package/dist/global-skills/firebase-firestore/references/enterprise/provisioning.md +118 -0
- package/dist/global-skills/firebase-firestore/references/enterprise/python_sdk_usage.md +138 -0
- package/dist/global-skills/firebase-firestore/references/enterprise/security_rules.md +577 -0
- package/dist/global-skills/firebase-firestore/references/enterprise/web_sdk_usage.md +127 -0
- package/dist/global-skills/firebase-firestore/references/standard/android_sdk_usage.md +193 -0
- package/dist/global-skills/firebase-firestore/references/standard/flutter_setup.md +176 -0
- package/dist/global-skills/firebase-firestore/references/standard/indexes.md +113 -0
- package/dist/global-skills/firebase-firestore/references/standard/ios_setup.md +174 -0
- package/dist/global-skills/firebase-firestore/references/standard/provisioning.md +110 -0
- package/dist/global-skills/firebase-firestore/references/standard/security_rules.md +577 -0
- package/dist/global-skills/firebase-firestore/references/standard/web_sdk_usage.md +192 -0
- package/dist/global-skills/firebase-hosting-basics/SKILL.md +64 -0
- package/dist/global-skills/firebase-hosting-basics/references/configuration.md +115 -0
- package/dist/global-skills/firebase-hosting-basics/references/deploying.md +48 -0
- package/dist/global-skills/firebase-remote-config-basics/SKILL.md +124 -0
- package/dist/global-skills/firebase-remote-config-basics/references/android_setup.md +91 -0
- package/dist/global-skills/firebase-remote-config-basics/references/ios_setup.md +89 -0
- package/dist/global-skills/firebase-security-rules-auditor/SKILL.md +70 -0
- package/dist/global-skills/go-live/SKILL.md +90 -0
- package/dist/global-skills/harden/SKILL.md +101 -0
- package/dist/global-skills/instrument-feature-flags/SKILL.md +88 -0
- package/dist/global-skills/instrument-feature-flags/references/adding-feature-flag-code.md +3578 -0
- package/dist/global-skills/instrument-feature-flags/references/android.md +146 -0
- package/dist/global-skills/instrument-feature-flags/references/api.md +194 -0
- package/dist/global-skills/instrument-feature-flags/references/best-practices.md +231 -0
- package/dist/global-skills/instrument-feature-flags/references/django.md +263 -0
- package/dist/global-skills/instrument-feature-flags/references/dotnet.md +763 -0
- package/dist/global-skills/instrument-feature-flags/references/elixir.md +47 -0
- package/dist/global-skills/instrument-feature-flags/references/flask.md +131 -0
- package/dist/global-skills/instrument-feature-flags/references/flutter.md +212 -0
- package/dist/global-skills/instrument-feature-flags/references/go.md +208 -0
- package/dist/global-skills/instrument-feature-flags/references/ios.md +146 -0
- package/dist/global-skills/instrument-feature-flags/references/java.md +94 -0
- package/dist/global-skills/instrument-feature-flags/references/laravel.md +170 -0
- package/dist/global-skills/instrument-feature-flags/references/next-js.md +383 -0
- package/dist/global-skills/instrument-feature-flags/references/nodejs.md +216 -0
- package/dist/global-skills/instrument-feature-flags/references/php.md +187 -0
- package/dist/global-skills/instrument-feature-flags/references/python.md +191 -0
- package/dist/global-skills/instrument-feature-flags/references/react-native.md +166 -0
- package/dist/global-skills/instrument-feature-flags/references/react.md +302 -0
- package/dist/global-skills/instrument-feature-flags/references/ruby-on-rails.md +604 -0
- package/dist/global-skills/instrument-feature-flags/references/ruby.md +200 -0
- package/dist/global-skills/instrument-feature-flags/references/rust.md +222 -0
- package/dist/global-skills/instrument-feature-flags/references/usage.md +600 -0
- package/dist/global-skills/instrument-feature-flags/references/web.md +193 -0
- package/dist/global-skills/instrument-product-analytics/SKILL.md +139 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-android.md +1639 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-angular.md +944 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-astro-hybrid.md +1001 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-astro-ssr.md +1004 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-astro-static.md +720 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-astro-view-transitions.md +810 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-django.md +1167 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-expo.md +1412 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-fastapi.md +1585 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-flask.md +1208 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-laravel.md +2267 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-next-app-router.md +712 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-next-pages-router.md +767 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-nuxt-3-6.md +939 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-nuxt-4.md +1078 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-php.md +527 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-python.md +481 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-react-native.md +1823 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-react-react-router-6.md +582 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-react-react-router-7-data.md +892 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-react-react-router-7-declarative.md +649 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-react-react-router-7-framework.md +1218 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-react-tanstack-router-code-based.md +793 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-react-tanstack-router-file-based.md +780 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-ruby-on-rails.md +1320 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-ruby.md +452 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-sveltekit.md +854 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-swift.md +665 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-tanstack-start.md +1199 -0
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-vue-3.md +910 -0
- package/dist/global-skills/instrument-product-analytics/references/android.md +811 -0
- package/dist/global-skills/instrument-product-analytics/references/angular.md +376 -0
- package/dist/global-skills/instrument-product-analytics/references/astro.md +165 -0
- package/dist/global-skills/instrument-product-analytics/references/configuration.md +299 -0
- package/dist/global-skills/instrument-product-analytics/references/django.md +263 -0
- package/dist/global-skills/instrument-product-analytics/references/dotnet.md +763 -0
- package/dist/global-skills/instrument-product-analytics/references/elixir.md +414 -0
- package/dist/global-skills/instrument-product-analytics/references/flask.md +131 -0
- package/dist/global-skills/instrument-product-analytics/references/flutter.md +845 -0
- package/dist/global-skills/instrument-product-analytics/references/go.md +567 -0
- package/dist/global-skills/instrument-product-analytics/references/identify-users.md +272 -0
- package/dist/global-skills/instrument-product-analytics/references/ios.md +154 -0
- package/dist/global-skills/instrument-product-analytics/references/laravel.md +170 -0
- package/dist/global-skills/instrument-product-analytics/references/next-js.md +383 -0
- package/dist/global-skills/instrument-product-analytics/references/nuxt-js-3-6.md +244 -0
- package/dist/global-skills/instrument-product-analytics/references/nuxt-js.md +253 -0
- package/dist/global-skills/instrument-product-analytics/references/php.md +646 -0
- package/dist/global-skills/instrument-product-analytics/references/posthog-python.md +1675 -0
- package/dist/global-skills/instrument-product-analytics/references/python.md +898 -0
- package/dist/global-skills/instrument-product-analytics/references/react-native.md +1338 -0
- package/dist/global-skills/instrument-product-analytics/references/react-router-v6.md +376 -0
- package/dist/global-skills/instrument-product-analytics/references/react-router-v7-data-mode.md +364 -0
- package/dist/global-skills/instrument-product-analytics/references/react-router-v7-declarative-mode.md +376 -0
- package/dist/global-skills/instrument-product-analytics/references/react-router-v7-framework-mode.md +479 -0
- package/dist/global-skills/instrument-product-analytics/references/ruby-on-rails.md +604 -0
- package/dist/global-skills/instrument-product-analytics/references/ruby.md +759 -0
- package/dist/global-skills/instrument-product-analytics/references/svelte.md +234 -0
- package/dist/global-skills/instrument-product-analytics/references/tanstack-start.md +193 -0
- package/dist/global-skills/instrument-product-analytics/references/usage.md +600 -0
- package/dist/global-skills/instrument-product-analytics/references/vue-js.md +306 -0
- package/dist/global-skills/manifest.json +125 -0
- package/dist/global-skills/mongodb-atlas-stream-processing/SKILL.md +284 -0
- package/dist/global-skills/mongodb-atlas-stream-processing/references/connection-configs.md +298 -0
- package/dist/global-skills/mongodb-atlas-stream-processing/references/development-workflow.md +304 -0
- package/dist/global-skills/mongodb-atlas-stream-processing/references/mcp-troubleshooting.md +55 -0
- package/dist/global-skills/mongodb-atlas-stream-processing/references/output-diagnostics.md +150 -0
- package/dist/global-skills/mongodb-atlas-stream-processing/references/pipeline-patterns.md +457 -0
- package/dist/global-skills/mongodb-atlas-stream-processing/references/sizing-and-parallelism.md +178 -0
- package/dist/global-skills/mongodb-connection/SKILL.md +201 -0
- package/dist/global-skills/mongodb-connection/references/monitoring-guide.md +191 -0
- package/dist/global-skills/mongodb-mcp-setup/SKILL.md +324 -0
- package/dist/global-skills/mongodb-natural-language-querying/SKILL.md +195 -0
- package/dist/global-skills/mongodb-query-optimizer/SKILL.md +150 -0
- package/dist/global-skills/mongodb-query-optimizer/references/aggregation-optimization.md +210 -0
- package/dist/global-skills/mongodb-query-optimizer/references/antipattern-examples.md +74 -0
- package/dist/global-skills/mongodb-query-optimizer/references/core-indexing-principles.md +134 -0
- package/dist/global-skills/mongodb-query-optimizer/references/update-query-examples.md +39 -0
- package/dist/global-skills/mongodb-schema-design/SKILL.md +181 -0
- package/dist/global-skills/mongodb-schema-design/references/antipattern-excessive-lookups.md +84 -0
- package/dist/global-skills/mongodb-schema-design/references/antipattern-unnecessary-collections.md +91 -0
- package/dist/global-skills/mongodb-schema-design/references/antipattern-unnecessary-indexes.md +95 -0
- package/dist/global-skills/mongodb-schema-design/references/fundamental-document-model.md +91 -0
- package/dist/global-skills/mongodb-schema-design/references/fundamental-document-size.md +226 -0
- package/dist/global-skills/mongodb-schema-design/references/fundamental-embed-vs-reference.md +418 -0
- package/dist/global-skills/mongodb-schema-design/references/fundamental-schema-validation.md +131 -0
- package/dist/global-skills/mongodb-schema-design/references/pattern-approximation.md +80 -0
- package/dist/global-skills/mongodb-schema-design/references/pattern-archive.md +143 -0
- package/dist/global-skills/mongodb-schema-design/references/pattern-attribute.md +77 -0
- package/dist/global-skills/mongodb-schema-design/references/pattern-bucket.md +102 -0
- package/dist/global-skills/mongodb-schema-design/references/pattern-computed.md +162 -0
- package/dist/global-skills/mongodb-schema-design/references/pattern-document-versioning.md +166 -0
- package/dist/global-skills/mongodb-schema-design/references/pattern-extended-reference.md +72 -0
- package/dist/global-skills/mongodb-schema-design/references/pattern-outlier.md +159 -0
- package/dist/global-skills/mongodb-schema-design/references/pattern-polymorphic.md +167 -0
- package/dist/global-skills/mongodb-schema-design/references/pattern-schema-versioning.md +273 -0
- package/dist/global-skills/mongodb-schema-design/references/pattern-time-series-collections.md +190 -0
- package/dist/global-skills/mongodb-schema-design/references/source-query-stats.md +123 -0
- package/dist/global-skills/mongodb-schema-design/references/source-slow-query-logs.md +63 -0
- package/dist/global-skills/mongodb-search-and-ai/SKILL.md +142 -0
- package/dist/global-skills/mongodb-search-and-ai/references/hybrid-search.md +697 -0
- package/dist/global-skills/mongodb-search-and-ai/references/lexical-search-indexing.md +584 -0
- package/dist/global-skills/mongodb-search-and-ai/references/lexical-search-querying.md +636 -0
- package/dist/global-skills/mongodb-search-and-ai/references/vector-search.md +746 -0
- package/dist/global-skills/native-data-fetching/SKILL.md +505 -0
- package/dist/global-skills/native-data-fetching/agents/openai.yaml +4 -0
- package/dist/global-skills/native-data-fetching/references/expo-router-loaders.md +341 -0
- package/dist/global-skills/neon/SKILL.md +343 -0
- package/dist/global-skills/neon-ai-gateway/SKILL.md +236 -0
- package/dist/global-skills/neon-functions/SKILL.md +490 -0
- package/dist/global-skills/neon-functions/references/ai-sdk.md +139 -0
- package/dist/global-skills/neon-functions/references/hono-websockets.md +145 -0
- package/dist/global-skills/neon-functions/references/mastra-studio.md +127 -0
- package/dist/global-skills/neon-functions/references/mcp.md +137 -0
- package/dist/global-skills/neon-functions/references/sentry.md +131 -0
- package/dist/global-skills/neon-functions/references/sse.md +143 -0
- package/dist/global-skills/neon-object-storage/SKILL.md +197 -0
- package/dist/global-skills/neon-postgres/SKILL.md +376 -0
- package/dist/global-skills/neon-postgres-branches/SKILL.md +281 -0
- package/dist/global-skills/neon-postgres-egress-optimizer/SKILL.md +247 -0
- package/dist/global-skills/onboarding/SKILL.md +101 -0
- package/dist/global-skills/plan-my-idea/SKILL.md +67 -0
- package/dist/global-skills/publish-app/SKILL.md +72 -0
- package/dist/global-skills/publish-extension/SKILL.md +47 -0
- package/dist/global-skills/resend/SKILL.md +294 -0
- package/dist/global-skills/resend/references/api-keys.md +98 -0
- package/dist/global-skills/resend/references/automations.md +228 -0
- package/dist/global-skills/resend/references/broadcasts.md +125 -0
- package/dist/global-skills/resend/references/contact-properties.md +111 -0
- package/dist/global-skills/resend/references/contacts.md +174 -0
- package/dist/global-skills/resend/references/domains.md +179 -0
- package/dist/global-skills/resend/references/events.md +119 -0
- package/dist/global-skills/resend/references/fetch-all-templates.mjs +37 -0
- package/dist/global-skills/resend/references/installation.md +142 -0
- package/dist/global-skills/resend/references/logs.md +161 -0
- package/dist/global-skills/resend/references/receiving.md +287 -0
- package/dist/global-skills/resend/references/segments.md +77 -0
- package/dist/global-skills/resend/references/sending/batch-email-examples.md +807 -0
- package/dist/global-skills/resend/references/sending/best-practices.md +453 -0
- package/dist/global-skills/resend/references/sending/email-management.md +135 -0
- package/dist/global-skills/resend/references/sending/overview.md +209 -0
- package/dist/global-skills/resend/references/sending/single-email-examples.md +470 -0
- package/dist/global-skills/resend/references/templates.md +202 -0
- package/dist/global-skills/resend/references/topics.md +104 -0
- package/dist/global-skills/resend/references/webhooks.md +317 -0
- package/dist/global-skills/reset-password/SKILL.md +59 -0
- package/dist/global-skills/save-data/SKILL.md +70 -0
- package/dist/global-skills/sentry-sdk-setup/SKILL.md +116 -0
- package/dist/global-skills/sentry-workflow/SKILL.md +32 -0
- package/dist/global-skills/setup-email/SKILL.md +69 -0
- package/dist/global-skills/setup-payments/SKILL.md +82 -0
- package/dist/global-skills/setup-sms/SKILL.md +51 -0
- package/dist/global-skills/sign-in-with-email-link/SKILL.md +53 -0
- package/dist/global-skills/sign-in-with-google/SKILL.md +68 -0
- package/dist/global-skills/sign-in-with-phone/SKILL.md +53 -0
- package/dist/global-skills/stripe-best-practices/SKILL.md +48 -0
- package/dist/global-skills/stripe-best-practices/references/billing.md +58 -0
- package/dist/global-skills/stripe-best-practices/references/connect.md +173 -0
- package/dist/global-skills/stripe-best-practices/references/payments.md +79 -0
- package/dist/global-skills/stripe-best-practices/references/security.md +109 -0
- package/dist/global-skills/stripe-best-practices/references/tax.md +37 -0
- package/dist/global-skills/stripe-best-practices/references/treasury.md +16 -0
- package/dist/global-skills/supabase/CHANGELOG.md +53 -0
- package/dist/global-skills/supabase/SKILL.md +145 -0
- package/dist/global-skills/supabase/assets/feedback-issue-template.md +17 -0
- package/dist/global-skills/supabase/references/skill-feedback.md +17 -0
- package/dist/global-skills/supabase-postgres-best-practices/CHANGELOG.md +43 -0
- package/dist/global-skills/supabase-postgres-best-practices/SKILL.md +64 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/_contributing.md +170 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/_sections.md +39 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/_template.md +34 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +55 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +49 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +46 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/conn-limits.md +44 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/conn-pooling.md +41 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +46 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/data-batch-inserts.md +54 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/data-n-plus-one.md +53 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/data-pagination.md +50 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/data-upsert.md +50 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/lock-advisory.md +56 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +68 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/lock-short-transactions.md +50 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/lock-skip-locked.md +54 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +45 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +55 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +55 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/query-composite-indexes.md +44 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/query-covering-indexes.md +40 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/query-index-types.md +48 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/query-missing-indexes.md +43 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/query-partial-indexes.md +45 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/schema-constraints.md +80 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/schema-data-types.md +46 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +59 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +55 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/schema-partitioning.md +55 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/schema-primary-keys.md +61 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/security-privileges.md +54 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/security-rls-basics.md +50 -0
- package/dist/global-skills/supabase-postgres-best-practices/references/security-rls-performance.md +63 -0
- package/dist/global-skills/track-errors/SKILL.md +67 -0
- package/dist/global-skills/update-kit/SKILL.md +75 -0
- package/dist/global-skills/upgrading-expo/SKILL.md +134 -0
- package/dist/global-skills/upgrading-expo/agents/openai.yaml +4 -0
- package/dist/global-skills/upgrading-expo/references/expo-av-to-audio.md +132 -0
- package/dist/global-skills/upgrading-expo/references/expo-av-to-video.md +160 -0
- package/dist/global-skills/upgrading-expo/references/native-tabs.md +124 -0
- package/dist/global-skills/upgrading-expo/references/new-architecture.md +79 -0
- package/dist/global-skills/upgrading-expo/references/react-19.md +79 -0
- package/dist/global-skills/upgrading-expo/references/react-compiler.md +59 -0
- package/dist/global-skills/upgrading-expo/references/react-navigation-to-expo-router.md +61 -0
- package/dist/global-skills/use-dom/SKILL.md +417 -0
- package/dist/global-skills/use-dom/agents/openai.yaml +4 -0
- package/dist/global-skills/use-kit-mcp/SKILL.md +100 -0
- package/dist/global-skills/use-railway/SKILL.md +357 -0
- package/dist/global-skills/use-railway/references/analyze-db-mongo.md +84 -0
- package/dist/global-skills/use-railway/references/analyze-db-mysql.md +254 -0
- package/dist/global-skills/use-railway/references/analyze-db-postgres.md +479 -0
- package/dist/global-skills/use-railway/references/analyze-db-redis.md +208 -0
- package/dist/global-skills/use-railway/references/analyze-db.md +344 -0
- package/dist/global-skills/use-railway/references/configure.md +359 -0
- package/dist/global-skills/use-railway/references/deploy.md +225 -0
- package/dist/global-skills/use-railway/references/feature-flags.md +89 -0
- package/dist/global-skills/use-railway/references/iac.md +108 -0
- package/dist/global-skills/use-railway/references/operate.md +273 -0
- package/dist/global-skills/use-railway/references/request.md +262 -0
- package/dist/global-skills/use-railway/references/sandbox.md +167 -0
- package/dist/global-skills/use-railway/references/setup.md +364 -0
- package/dist/global-skills/use-railway/scripts/analyze-mongo.py +1549 -0
- package/dist/global-skills/use-railway/scripts/analyze-mysql.py +1195 -0
- package/dist/global-skills/use-railway/scripts/analyze-postgres.py +3058 -0
- package/dist/global-skills/use-railway/scripts/analyze-redis.py +1090 -0
- package/dist/global-skills/use-railway/scripts/dal.py +671 -0
- package/dist/global-skills/use-railway/scripts/enable-pg-stats.py +170 -0
- package/dist/global-skills/use-railway/scripts/pg-extensions.py +370 -0
- package/dist/global-skills/use-railway/scripts/railway-api.sh +52 -0
- package/dist/global-skills/vercel-composition-patterns/AGENTS.md +946 -0
- package/dist/global-skills/vercel-composition-patterns/README.md +60 -0
- package/dist/global-skills/vercel-composition-patterns/SKILL.md +89 -0
- package/dist/global-skills/vercel-composition-patterns/metadata.json +11 -0
- package/dist/global-skills/vercel-composition-patterns/rules/_sections.md +29 -0
- package/dist/global-skills/vercel-composition-patterns/rules/_template.md +24 -0
- package/dist/global-skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
- package/dist/global-skills/vercel-composition-patterns/rules/architecture-compound-components.md +112 -0
- package/dist/global-skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md +87 -0
- package/dist/global-skills/vercel-composition-patterns/rules/patterns-explicit-variants.md +100 -0
- package/dist/global-skills/vercel-composition-patterns/rules/react19-no-forwardref.md +42 -0
- package/dist/global-skills/vercel-composition-patterns/rules/state-context-interface.md +191 -0
- package/dist/global-skills/vercel-composition-patterns/rules/state-decouple-implementation.md +113 -0
- package/dist/global-skills/vercel-composition-patterns/rules/state-lift-state.md +125 -0
- package/dist/global-skills/vercel-react-best-practices/AGENTS.md +3810 -0
- package/dist/global-skills/vercel-react-best-practices/README.md +123 -0
- package/dist/global-skills/vercel-react-best-practices/SKILL.md +149 -0
- package/dist/global-skills/vercel-react-best-practices/metadata.json +15 -0
- package/dist/global-skills/vercel-react-best-practices/rules/_sections.md +46 -0
- package/dist/global-skills/vercel-react-best-practices/rules/_template.md +28 -0
- package/dist/global-skills/vercel-react-best-practices/rules/advanced-effect-event-deps.md +56 -0
- package/dist/global-skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/dist/global-skills/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
- package/dist/global-skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
- package/dist/global-skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/dist/global-skills/vercel-react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
- package/dist/global-skills/vercel-react-best-practices/rules/async-defer-await.md +82 -0
- package/dist/global-skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
- package/dist/global-skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/dist/global-skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/dist/global-skills/vercel-react-best-practices/rules/bundle-analyzable-paths.md +63 -0
- package/dist/global-skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +60 -0
- package/dist/global-skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/dist/global-skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/dist/global-skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/dist/global-skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/dist/global-skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/dist/global-skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/dist/global-skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/dist/global-skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/dist/global-skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/dist/global-skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/dist/global-skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/dist/global-skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/dist/global-skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/dist/global-skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/dist/global-skills/vercel-react-best-practices/rules/js-flatmap-filter.md +60 -0
- package/dist/global-skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/dist/global-skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/dist/global-skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/dist/global-skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/dist/global-skills/vercel-react-best-practices/rules/js-request-idle-callback.md +105 -0
- package/dist/global-skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/dist/global-skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rendering-resource-hints.md +85 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rendering-script-defer-async.md +68 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rerender-no-inline-components.md +82 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md +59 -0
- package/dist/global-skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/dist/global-skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/dist/global-skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
- package/dist/global-skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/dist/global-skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
- package/dist/global-skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
- package/dist/global-skills/vercel-react-best-practices/rules/server-hoist-static-io.md +149 -0
- package/dist/global-skills/vercel-react-best-practices/rules/server-no-shared-module-state.md +50 -0
- package/dist/global-skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/dist/global-skills/vercel-react-best-practices/rules/server-parallel-nested-fetching.md +34 -0
- package/dist/global-skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/dist/global-skills/web-to-native/SKILL.md +83 -0
- package/dist/global-skills/web-to-native/references/false-friends.md +119 -0
- package/dist/global-skills/web-to-native/references/native-patterns.md +39 -0
- package/dist/global-skills/web-to-native/references/run-as-goal.md +48 -0
- package/dist/global-skills/web-to-native/references/verify-on-device.md +45 -0
- package/dist/global-skills/wire-auth/SKILL.md +28 -0
- package/dist/global-skills/wire-database/SKILL.md +28 -0
- package/dist/global-skills/wire-email/SKILL.md +27 -0
- package/dist/global-skills/wire-payments/SKILL.md +27 -0
- package/dist/global-skills/workers-best-practices/SKILL.md +127 -0
- package/dist/global-skills/workers-best-practices/references/review.md +174 -0
- package/dist/global-skills/workers-best-practices/references/rules.md +463 -0
- package/dist/global-skills/wrangler/SKILL.md +922 -0
- package/dist/global-skills/xcode-project-setup/SKILL.md +140 -0
- package/dist/global-skills/xcode-project-setup/scripts/xcode_spm_setup/Package.resolved +41 -0
- package/dist/global-skills/xcode-project-setup/scripts/xcode_spm_setup/Package.swift +17 -0
- package/dist/global-skills/xcode-project-setup/scripts/xcode_spm_setup/Sources/main.swift +232 -0
- package/package.json +21 -20
- package/LICENSE.md +0 -47
- package/dist/index.js +0 -21593
|
@@ -0,0 +1,1823 @@
|
|
|
1
|
+
# PostHog react-native Example Project
|
|
2
|
+
|
|
3
|
+
Repository: https://github.com/PostHog/context-mill
|
|
4
|
+
Path: example-apps/react-native
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## README.md
|
|
9
|
+
|
|
10
|
+
# PostHog React Native example
|
|
11
|
+
|
|
12
|
+
This is a bare [React Native](https://reactnative.dev/) example (no Expo) demonstrating PostHog integration with product analytics, user identification, autocapture, and error tracking.
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- **Product analytics**: Track user events and behaviors
|
|
17
|
+
- **Autocapture**: Automatic touch event and screen view tracking
|
|
18
|
+
- **Error tracking**: Capture and track errors manually
|
|
19
|
+
- **User authentication**: Demo login system with PostHog user identification
|
|
20
|
+
- **Session persistence**: AsyncStorage for maintaining user sessions across app restarts
|
|
21
|
+
- **Native navigation**: React Navigation v7 with native stack navigator
|
|
22
|
+
|
|
23
|
+
## Prerequisites
|
|
24
|
+
|
|
25
|
+
### For iOS Development
|
|
26
|
+
|
|
27
|
+
You need a Mac with the following installed:
|
|
28
|
+
|
|
29
|
+
1. **Xcode** (from the Mac App Store)
|
|
30
|
+
- Open App Store and search for "Xcode"
|
|
31
|
+
- Install it (~12GB download)
|
|
32
|
+
- After installing, open Xcode once to accept the license agreement
|
|
33
|
+
|
|
34
|
+
2. **Xcode Command Line Tools**
|
|
35
|
+
```bash
|
|
36
|
+
xcode-select --install
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
3. **CocoaPods** (iOS dependency manager)
|
|
40
|
+
```bash
|
|
41
|
+
brew install cocoapods
|
|
42
|
+
```
|
|
43
|
+
Or without Homebrew:
|
|
44
|
+
```bash
|
|
45
|
+
sudo gem install cocoapods
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### For Android Development
|
|
49
|
+
|
|
50
|
+
1. **Android Studio** (the Android IDE)
|
|
51
|
+
```bash
|
|
52
|
+
brew install --cask android-studio
|
|
53
|
+
```
|
|
54
|
+
Or download from: https://developer.android.com/studio
|
|
55
|
+
|
|
56
|
+
2. **First-time Android Studio Setup**
|
|
57
|
+
- Open Android Studio
|
|
58
|
+
- Complete the setup wizard (downloads Android SDK automatically)
|
|
59
|
+
- Go to **Settings → Languages & Frameworks → Android SDK**
|
|
60
|
+
- Ensure "Android SDK Platform 34" (or latest) is installed
|
|
61
|
+
|
|
62
|
+
3. **Create an Android Emulator**
|
|
63
|
+
- In Android Studio: **Tools → Device Manager**
|
|
64
|
+
- Click **Create Device**
|
|
65
|
+
- Select a phone (e.g., "Pixel 7")
|
|
66
|
+
- Download a system image (e.g., API 34)
|
|
67
|
+
- Finish and click the **Play** button to launch
|
|
68
|
+
|
|
69
|
+
4. **Environment Variables** (add to `~/.zshrc` or `~/.bashrc`)
|
|
70
|
+
```bash
|
|
71
|
+
# Android SDK
|
|
72
|
+
export ANDROID_HOME=$HOME/Library/Android/sdk
|
|
73
|
+
export PATH=$PATH:$ANDROID_HOME/emulator
|
|
74
|
+
export PATH=$PATH:$ANDROID_HOME/platform-tools
|
|
75
|
+
|
|
76
|
+
# Java from Android Studio (required for Gradle)
|
|
77
|
+
export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"
|
|
78
|
+
export PATH=$JAVA_HOME/bin:$PATH
|
|
79
|
+
```
|
|
80
|
+
Then run `source ~/.zshrc` to apply.
|
|
81
|
+
|
|
82
|
+
5. **Create local.properties file** (if SDK location is not detected)
|
|
83
|
+
Create `android/local.properties` with:
|
|
84
|
+
```
|
|
85
|
+
sdk.dir=$HOME/Library/Android/sdk
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
6. **Clear Gradle cache** (required when jumping between different versions of Gradle)
|
|
89
|
+
```bash
|
|
90
|
+
rm -rf ~/.gradle/caches/modules-2/files-2.1/org.gradle.toolchains/foojay-resolver
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Getting started
|
|
94
|
+
|
|
95
|
+
### 1. Install dependencies
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npm install
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 2. Configure environment variables
|
|
102
|
+
|
|
103
|
+
Create a `.env` file:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
cp .env.example .env
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Edit `.env` and add your PostHog project token:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
POSTHOG_PROJECT_TOKEN=phc_your_project_token_here
|
|
113
|
+
POSTHOG_HOST=https://us.i.posthog.com
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Get your PostHog project token from your [PostHog project settings](https://app.posthog.com/project/settings).
|
|
117
|
+
|
|
118
|
+
> **Note:** The app will still run without a PostHog project token - analytics will simply be disabled.
|
|
119
|
+
|
|
120
|
+
### 3. Run on iOS
|
|
121
|
+
|
|
122
|
+
Install iOS dependencies (first time only):
|
|
123
|
+
```bash
|
|
124
|
+
cd ios && pod install && cd ..
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Run the app:
|
|
128
|
+
```bash
|
|
129
|
+
npm run ios
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
> **Note:** First build takes 5-10 minutes. Subsequent builds are much faster.
|
|
133
|
+
|
|
134
|
+
### 4. Run on Android
|
|
135
|
+
|
|
136
|
+
Make sure an Android emulator is running (from Android Studio Device Manager), then:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npm run android
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
> **Note:** First build takes 3-5 minutes.
|
|
143
|
+
|
|
144
|
+
## Troubleshooting
|
|
145
|
+
|
|
146
|
+
### iOS Issues
|
|
147
|
+
|
|
148
|
+
**"No `Podfile' found"**
|
|
149
|
+
- Make sure you're in the `ios` directory: `cd ios && pod install`
|
|
150
|
+
|
|
151
|
+
**Build fails with signing errors**
|
|
152
|
+
- Open `ios/BurritoApp.xcworkspace` in Xcode
|
|
153
|
+
- Select the project → Signing & Capabilities
|
|
154
|
+
- Select your development team
|
|
155
|
+
|
|
156
|
+
**Simulator not launching**
|
|
157
|
+
- Open Xcode → Open Developer Tool → Simulator
|
|
158
|
+
- Or run: `open -a Simulator`
|
|
159
|
+
|
|
160
|
+
### Android Issues
|
|
161
|
+
|
|
162
|
+
**"SDK location not found"**
|
|
163
|
+
- Ensure `ANDROID_HOME` is set in your shell profile
|
|
164
|
+
- Run `source ~/.zshrc` after adding it
|
|
165
|
+
|
|
166
|
+
**"No connected devices"**
|
|
167
|
+
- Launch an emulator from Android Studio Device Manager
|
|
168
|
+
- Or connect a physical device with USB debugging enabled
|
|
169
|
+
|
|
170
|
+
**Gradle build fails**
|
|
171
|
+
- Try: `cd android && ./gradlew clean && cd ..`
|
|
172
|
+
- Then: `npm run android`
|
|
173
|
+
|
|
174
|
+
## Project structure
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
src/
|
|
178
|
+
├── config/
|
|
179
|
+
│ └── posthog.ts # PostHog client configuration
|
|
180
|
+
├── contexts/
|
|
181
|
+
│ └── AuthContext.tsx # Authentication context with PostHog integration
|
|
182
|
+
├── navigation/
|
|
183
|
+
│ └── RootNavigator.tsx # React Navigation stack navigator
|
|
184
|
+
├── screens/
|
|
185
|
+
│ ├── HomeScreen.tsx # Home/login screen
|
|
186
|
+
│ ├── BurritoScreen.tsx # Demo feature screen with event tracking
|
|
187
|
+
│ └── ProfileScreen.tsx # User profile with error tracking demo
|
|
188
|
+
├── services/
|
|
189
|
+
│ └── storage.ts # AsyncStorage wrapper for persistence
|
|
190
|
+
├── styles/
|
|
191
|
+
│ └── theme.ts # Shared style constants
|
|
192
|
+
└── types/
|
|
193
|
+
└── env.d.ts # Type declarations for environment variables
|
|
194
|
+
|
|
195
|
+
App.tsx # Root component with PostHogProvider
|
|
196
|
+
index.js # App entry point
|
|
197
|
+
.env # Environment variables (create from .env.example)
|
|
198
|
+
ios/ # Native iOS project (Xcode)
|
|
199
|
+
android/ # Native Android project (Android Studio)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Key integration points
|
|
203
|
+
|
|
204
|
+
### PostHog client setup (config/posthog.ts)
|
|
205
|
+
|
|
206
|
+
The PostHog client is configured with V4 SDK options. If no project token is provided, analytics are disabled gracefully:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import PostHog from 'posthog-react-native'
|
|
210
|
+
import Config from 'react-native-config'
|
|
211
|
+
|
|
212
|
+
const projectToken = Config.POSTHOG_PROJECT_TOKEN
|
|
213
|
+
const isPostHogConfigured = projectToken && projectToken !== 'phc_your_project_token_here'
|
|
214
|
+
|
|
215
|
+
export const posthog = new PostHog(projectToken || 'placeholder_key', {
|
|
216
|
+
host: Config.POSTHOG_HOST || 'https://us.i.posthog.com',
|
|
217
|
+
disabled: !isPostHogConfigured, // Disable if no project token
|
|
218
|
+
captureAppLifecycleEvents: true,
|
|
219
|
+
debug: __DEV__,
|
|
220
|
+
flushAt: 20,
|
|
221
|
+
flushInterval: 10000,
|
|
222
|
+
preloadFeatureFlags: true,
|
|
223
|
+
})
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Provider setup with React Navigation v7 (App.tsx)
|
|
227
|
+
|
|
228
|
+
For React Navigation v7, `PostHogProvider` must be placed **inside** `NavigationContainer`, and screen tracking must be done manually:
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
import { NavigationContainer, NavigationContainerRef } from '@react-navigation/native'
|
|
232
|
+
import { PostHogProvider } from 'posthog-react-native'
|
|
233
|
+
import { posthog } from './src/config/posthog'
|
|
234
|
+
|
|
235
|
+
export default function App() {
|
|
236
|
+
const navigationRef = useRef<NavigationContainerRef<RootStackParamList>>(null)
|
|
237
|
+
const routeNameRef = useRef<string | undefined>()
|
|
238
|
+
|
|
239
|
+
return (
|
|
240
|
+
<NavigationContainer
|
|
241
|
+
ref={navigationRef}
|
|
242
|
+
onReady={() => {
|
|
243
|
+
routeNameRef.current = navigationRef.current?.getCurrentRoute()?.name
|
|
244
|
+
}}
|
|
245
|
+
onStateChange={() => {
|
|
246
|
+
// Manual screen tracking for React Navigation v7
|
|
247
|
+
const previousRouteName = routeNameRef.current
|
|
248
|
+
const currentRouteName = navigationRef.current?.getCurrentRoute()?.name
|
|
249
|
+
|
|
250
|
+
if (previousRouteName !== currentRouteName && currentRouteName) {
|
|
251
|
+
posthog.screen(currentRouteName, {
|
|
252
|
+
previous_screen: previousRouteName,
|
|
253
|
+
})
|
|
254
|
+
}
|
|
255
|
+
routeNameRef.current = currentRouteName
|
|
256
|
+
}}
|
|
257
|
+
>
|
|
258
|
+
<PostHogProvider
|
|
259
|
+
client={posthog}
|
|
260
|
+
autocapture={{
|
|
261
|
+
captureScreens: false, // Disabled for React Navigation v7
|
|
262
|
+
captureTouches: true, // Enable touch event autocapture
|
|
263
|
+
propsToCapture: ['testID'],
|
|
264
|
+
}}
|
|
265
|
+
>
|
|
266
|
+
<AuthProvider>
|
|
267
|
+
<RootNavigator />
|
|
268
|
+
</AuthProvider>
|
|
269
|
+
</PostHogProvider>
|
|
270
|
+
</NavigationContainer>
|
|
271
|
+
)
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Autocapture
|
|
276
|
+
|
|
277
|
+
PostHog autocapture automatically tracks:
|
|
278
|
+
|
|
279
|
+
- **Touch events**: When users interact with the screen
|
|
280
|
+
- **App lifecycle events**: Application Installed, Updated, Opened, Became Active, Backgrounded
|
|
281
|
+
|
|
282
|
+
Use `testID` prop on components to help identify them in analytics:
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
<TouchableOpacity testID="consider-burrito-button" onPress={handlePress}>
|
|
286
|
+
<Text>Consider Burrito</Text>
|
|
287
|
+
</TouchableOpacity>
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### User identification (contexts/AuthContext.tsx)
|
|
291
|
+
|
|
292
|
+
Use `$set` and `$set_once` for person properties:
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
import { usePostHog } from 'posthog-react-native'
|
|
296
|
+
|
|
297
|
+
const posthog = usePostHog()
|
|
298
|
+
|
|
299
|
+
// On login - identify with person properties
|
|
300
|
+
posthog.identify(username, {
|
|
301
|
+
$set: {
|
|
302
|
+
username: username,
|
|
303
|
+
},
|
|
304
|
+
$set_once: {
|
|
305
|
+
first_login_date: new Date().toISOString(),
|
|
306
|
+
},
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
// Capture login event
|
|
310
|
+
posthog.capture('user_logged_in', {
|
|
311
|
+
username: username,
|
|
312
|
+
is_new_user: isNewUser,
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
// On logout - reset clears distinct ID and anonymous ID
|
|
316
|
+
posthog.capture('user_logged_out')
|
|
317
|
+
posthog.reset()
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Event tracking (screens/BurritoScreen.tsx)
|
|
321
|
+
|
|
322
|
+
Capture custom events with properties:
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
import { usePostHog } from 'posthog-react-native'
|
|
326
|
+
|
|
327
|
+
const posthog = usePostHog()
|
|
328
|
+
|
|
329
|
+
// We recommend using a [object] [verb] format for event names
|
|
330
|
+
posthog.capture('burrito_considered', {
|
|
331
|
+
total_considerations: user.burritoConsiderations + 1,
|
|
332
|
+
username: user.username,
|
|
333
|
+
})
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Error tracking (screens/ProfileScreen.tsx)
|
|
337
|
+
|
|
338
|
+
Capture exceptions using `captureException`:
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
import { usePostHog } from 'posthog-react-native'
|
|
342
|
+
|
|
343
|
+
const posthog = usePostHog()
|
|
344
|
+
|
|
345
|
+
try {
|
|
346
|
+
throw new Error('Test error for PostHog error tracking')
|
|
347
|
+
} catch (err) {
|
|
348
|
+
posthog.captureException(err)
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Session persistence (services/storage.ts)
|
|
353
|
+
|
|
354
|
+
AsyncStorage replaces localStorage for persisting user sessions:
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
import AsyncStorage from '@react-native-async-storage/async-storage'
|
|
358
|
+
|
|
359
|
+
export const storage = {
|
|
360
|
+
getCurrentUser: async (): Promise<string | null> => {
|
|
361
|
+
return await AsyncStorage.getItem('currentUser')
|
|
362
|
+
},
|
|
363
|
+
|
|
364
|
+
setCurrentUser: async (username: string): Promise<void> => {
|
|
365
|
+
await AsyncStorage.setItem('currentUser', username)
|
|
366
|
+
},
|
|
367
|
+
|
|
368
|
+
saveUser: async (user: User): Promise<void> => {
|
|
369
|
+
const users = await storage.getUsers()
|
|
370
|
+
users[user.username] = user
|
|
371
|
+
await AsyncStorage.setItem('users', JSON.stringify(users))
|
|
372
|
+
},
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
## Learn more
|
|
377
|
+
|
|
378
|
+
- [PostHog documentation](https://posthog.com/docs)
|
|
379
|
+
- [PostHog React Native integration](https://posthog.com/docs/libraries/react-native)
|
|
380
|
+
- [PostHog React Native autocapture](https://posthog.com/docs/libraries/react-native#autocapture)
|
|
381
|
+
- [PostHog React Native screen tracking](https://posthog.com/docs/libraries/react-native#capturing-screen-views)
|
|
382
|
+
- [React Native documentation](https://reactnative.dev/docs/getting-started)
|
|
383
|
+
- [React Native environment setup](https://reactnative.dev/docs/set-up-your-environment)
|
|
384
|
+
- [React Navigation documentation](https://reactnavigation.org/docs/getting-started)
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## __tests__/App.test.tsx
|
|
389
|
+
|
|
390
|
+
```tsx
|
|
391
|
+
/**
|
|
392
|
+
* @format
|
|
393
|
+
*/
|
|
394
|
+
|
|
395
|
+
import React from 'react';
|
|
396
|
+
import ReactTestRenderer from 'react-test-renderer';
|
|
397
|
+
import App from '../App';
|
|
398
|
+
|
|
399
|
+
test('renders correctly', async () => {
|
|
400
|
+
await ReactTestRenderer.act(() => {
|
|
401
|
+
ReactTestRenderer.create(<App />);
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## .env.example
|
|
410
|
+
|
|
411
|
+
```example
|
|
412
|
+
POSTHOG_PROJECT_TOKEN=phc_your_project_token_here
|
|
413
|
+
POSTHOG_HOST=https://us.i.posthog.com
|
|
414
|
+
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## .prettierrc.js
|
|
420
|
+
|
|
421
|
+
```js
|
|
422
|
+
module.exports = {
|
|
423
|
+
arrowParens: 'avoid',
|
|
424
|
+
singleQuote: true,
|
|
425
|
+
trailingComma: 'all',
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## App.tsx
|
|
433
|
+
|
|
434
|
+
```tsx
|
|
435
|
+
import React, { useRef } from 'react'
|
|
436
|
+
import { StatusBar } from 'react-native'
|
|
437
|
+
import { SafeAreaProvider } from 'react-native-safe-area-context'
|
|
438
|
+
import {
|
|
439
|
+
NavigationContainer,
|
|
440
|
+
NavigationContainerRef,
|
|
441
|
+
} from '@react-navigation/native'
|
|
442
|
+
import { PostHogProvider } from 'posthog-react-native'
|
|
443
|
+
|
|
444
|
+
import { AuthProvider } from './src/contexts/AuthContext'
|
|
445
|
+
import { RootNavigator, RootStackParamList } from './src/navigation/RootNavigator'
|
|
446
|
+
import { posthog } from './src/config/posthog'
|
|
447
|
+
import { colors } from './src/styles/theme'
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Burrito Consideration App
|
|
451
|
+
*
|
|
452
|
+
* A demo React Native application showcasing PostHog analytics integration.
|
|
453
|
+
*
|
|
454
|
+
* Features:
|
|
455
|
+
* - User authentication (demo mode - accepts any credentials)
|
|
456
|
+
* - Burrito consideration counter with event tracking
|
|
457
|
+
* - User profile with statistics
|
|
458
|
+
* - Error tracking demonstration
|
|
459
|
+
*
|
|
460
|
+
* @see https://posthog.com/docs/libraries/react-native
|
|
461
|
+
*/
|
|
462
|
+
export default function App() {
|
|
463
|
+
const navigationRef = useRef<NavigationContainerRef<RootStackParamList>>(null)
|
|
464
|
+
const routeNameRef = useRef<string | undefined>()
|
|
465
|
+
|
|
466
|
+
return (
|
|
467
|
+
<SafeAreaProvider>
|
|
468
|
+
<StatusBar
|
|
469
|
+
barStyle="light-content"
|
|
470
|
+
backgroundColor={colors.headerBackground}
|
|
471
|
+
/>
|
|
472
|
+
<NavigationContainer
|
|
473
|
+
ref={navigationRef}
|
|
474
|
+
onReady={() => {
|
|
475
|
+
// Store the initial route name
|
|
476
|
+
routeNameRef.current = navigationRef.current?.getCurrentRoute()?.name
|
|
477
|
+
}}
|
|
478
|
+
onStateChange={() => {
|
|
479
|
+
// Track screen views manually for React Navigation v7
|
|
480
|
+
const previousRouteName = routeNameRef.current
|
|
481
|
+
const currentRouteName = navigationRef.current?.getCurrentRoute()?.name
|
|
482
|
+
|
|
483
|
+
if (previousRouteName !== currentRouteName && currentRouteName) {
|
|
484
|
+
// Capture screen view event
|
|
485
|
+
posthog.screen(currentRouteName, {
|
|
486
|
+
previous_screen: previousRouteName,
|
|
487
|
+
})
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Update the stored route name
|
|
491
|
+
routeNameRef.current = currentRouteName
|
|
492
|
+
}}
|
|
493
|
+
>
|
|
494
|
+
{/*
|
|
495
|
+
PostHogProvider is placed INSIDE NavigationContainer for React Navigation v7.
|
|
496
|
+
|
|
497
|
+
For React Navigation v7, we disable automatic screen capture and handle it
|
|
498
|
+
manually via onStateChange above. Touch event autocapture is still enabled.
|
|
499
|
+
|
|
500
|
+
@see https://posthog.com/docs/libraries/react-native#with-react-navigationnative-and-autocapture
|
|
501
|
+
*/}
|
|
502
|
+
<PostHogProvider
|
|
503
|
+
client={posthog}
|
|
504
|
+
autocapture={{
|
|
505
|
+
// Disable automatic screen capture for React Navigation v7
|
|
506
|
+
// We handle screen tracking manually via NavigationContainer.onStateChange
|
|
507
|
+
captureScreens: false,
|
|
508
|
+
// Enable touch event autocapture
|
|
509
|
+
captureTouches: true,
|
|
510
|
+
// Limit which props are captured for touch events
|
|
511
|
+
propsToCapture: ['testID'],
|
|
512
|
+
// Maximum number of elements captured in touch event hierarchy
|
|
513
|
+
maxElementsCaptured: 20,
|
|
514
|
+
}}
|
|
515
|
+
>
|
|
516
|
+
<AuthProvider>
|
|
517
|
+
<RootNavigator />
|
|
518
|
+
</AuthProvider>
|
|
519
|
+
</PostHogProvider>
|
|
520
|
+
</NavigationContainer>
|
|
521
|
+
</SafeAreaProvider>
|
|
522
|
+
)
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## babel.config.js
|
|
530
|
+
|
|
531
|
+
```js
|
|
532
|
+
module.exports = {
|
|
533
|
+
presets: ['module:@react-native/babel-preset'],
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
---
|
|
539
|
+
|
|
540
|
+
## Gemfile
|
|
541
|
+
|
|
542
|
+
```
|
|
543
|
+
source 'https://rubygems.org'
|
|
544
|
+
|
|
545
|
+
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
|
|
546
|
+
ruby ">= 2.6.10"
|
|
547
|
+
|
|
548
|
+
# Exclude problematic versions of cocoapods and activesupport that causes build failures.
|
|
549
|
+
gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1'
|
|
550
|
+
gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0'
|
|
551
|
+
gem 'xcodeproj', '< 1.26.0'
|
|
552
|
+
gem 'concurrent-ruby', '< 1.3.4'
|
|
553
|
+
|
|
554
|
+
# Ruby 3.4.0 has removed some libraries from the standard library.
|
|
555
|
+
gem 'bigdecimal'
|
|
556
|
+
gem 'logger'
|
|
557
|
+
gem 'benchmark'
|
|
558
|
+
gem 'mutex_m'
|
|
559
|
+
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
---
|
|
563
|
+
|
|
564
|
+
## index.js
|
|
565
|
+
|
|
566
|
+
```js
|
|
567
|
+
/**
|
|
568
|
+
* @format
|
|
569
|
+
*/
|
|
570
|
+
|
|
571
|
+
import { AppRegistry } from 'react-native';
|
|
572
|
+
import App from './App';
|
|
573
|
+
import { name as appName } from './app.json';
|
|
574
|
+
|
|
575
|
+
AppRegistry.registerComponent(appName, () => App);
|
|
576
|
+
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
## jest.config.js
|
|
582
|
+
|
|
583
|
+
```js
|
|
584
|
+
module.exports = {
|
|
585
|
+
preset: 'react-native',
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
---
|
|
591
|
+
|
|
592
|
+
## metro.config.js
|
|
593
|
+
|
|
594
|
+
```js
|
|
595
|
+
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Metro configuration
|
|
599
|
+
* https://reactnative.dev/docs/metro
|
|
600
|
+
*
|
|
601
|
+
* @type {import('@react-native/metro-config').MetroConfig}
|
|
602
|
+
*/
|
|
603
|
+
const config = {};
|
|
604
|
+
|
|
605
|
+
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
|
|
606
|
+
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
## src/config/posthog.ts
|
|
612
|
+
|
|
613
|
+
```ts
|
|
614
|
+
import PostHog from 'posthog-react-native'
|
|
615
|
+
import Config from 'react-native-config'
|
|
616
|
+
|
|
617
|
+
// Environment variables are embedded at build time via react-native-config
|
|
618
|
+
// Ensure .env file exists with POSTHOG_PROJECT_TOKEN and POSTHOG_HOST
|
|
619
|
+
const projectToken = Config.POSTHOG_PROJECT_TOKEN
|
|
620
|
+
const host = Config.POSTHOG_HOST || 'https://us.i.posthog.com'
|
|
621
|
+
const isPostHogConfigured = projectToken && projectToken !== 'phc_your_project_token_here'
|
|
622
|
+
|
|
623
|
+
if (!isPostHogConfigured) {
|
|
624
|
+
console.warn(
|
|
625
|
+
'PostHog project token not configured. Analytics will be disabled. ' +
|
|
626
|
+
'Set POSTHOG_PROJECT_TOKEN in your .env file to enable analytics.'
|
|
627
|
+
)
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* PostHog client instance for bare React Native
|
|
632
|
+
*
|
|
633
|
+
* Configuration loaded from .env via react-native-config (embedded at build time).
|
|
634
|
+
* Required peer dependencies: @react-native-async-storage/async-storage,
|
|
635
|
+
* react-native-device-info, react-native-localize
|
|
636
|
+
*
|
|
637
|
+
* @see https://posthog.com/docs/libraries/react-native
|
|
638
|
+
*/
|
|
639
|
+
export const posthog = new PostHog(projectToken || 'placeholder_key', {
|
|
640
|
+
// PostHog API host (usually 'https://us.i.posthog.com' or 'https://eu.i.posthog.com')
|
|
641
|
+
host,
|
|
642
|
+
|
|
643
|
+
// Enable PostHog only when a project token is configured
|
|
644
|
+
disabled: !isPostHogConfigured,
|
|
645
|
+
|
|
646
|
+
// Capture app lifecycle events:
|
|
647
|
+
// - Application Installed, Application Updated
|
|
648
|
+
// - Application Opened, Application Became Active, Application Backgrounded
|
|
649
|
+
captureAppLifecycleEvents: true,
|
|
650
|
+
|
|
651
|
+
// Enable debug mode in development for verbose logging
|
|
652
|
+
debug: __DEV__,
|
|
653
|
+
|
|
654
|
+
// Batching: queue events and flush periodically to optimize battery usage
|
|
655
|
+
flushAt: 20, // Number of events to queue before sending
|
|
656
|
+
flushInterval: 10000, // Interval in ms between periodic flushes
|
|
657
|
+
maxBatchSize: 100, // Maximum events per batch
|
|
658
|
+
maxQueueSize: 1000, // Maximum queued events (oldest dropped when full)
|
|
659
|
+
|
|
660
|
+
// Feature flags
|
|
661
|
+
preloadFeatureFlags: true, // Load flags on initialization
|
|
662
|
+
sendFeatureFlagEvent: true, // Track getFeatureFlag calls for experiments
|
|
663
|
+
featureFlagsRequestTimeoutMs: 10000, // Timeout for flag requests (prevents blocking)
|
|
664
|
+
|
|
665
|
+
// Network settings
|
|
666
|
+
requestTimeout: 10000, // General request timeout in ms
|
|
667
|
+
fetchRetryCount: 3, // Number of retry attempts for failed requests
|
|
668
|
+
fetchRetryDelay: 3000, // Delay between retries in ms
|
|
669
|
+
})
|
|
670
|
+
|
|
671
|
+
// Export helper to check if PostHog is enabled
|
|
672
|
+
export const isPostHogEnabled = isPostHogConfigured
|
|
673
|
+
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
---
|
|
677
|
+
|
|
678
|
+
## src/contexts/AuthContext.tsx
|
|
679
|
+
|
|
680
|
+
```tsx
|
|
681
|
+
import React, {
|
|
682
|
+
createContext,
|
|
683
|
+
useContext,
|
|
684
|
+
useState,
|
|
685
|
+
useEffect,
|
|
686
|
+
ReactNode,
|
|
687
|
+
useCallback,
|
|
688
|
+
} from 'react'
|
|
689
|
+
import { usePostHog } from 'posthog-react-native'
|
|
690
|
+
import { storage, User } from '../services/storage'
|
|
691
|
+
|
|
692
|
+
interface AuthContextType {
|
|
693
|
+
user: User | null
|
|
694
|
+
isLoading: boolean
|
|
695
|
+
login: (username: string, password: string) => Promise<boolean>
|
|
696
|
+
logout: () => Promise<void>
|
|
697
|
+
incrementBurritoConsiderations: () => Promise<void>
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const AuthContext = createContext<AuthContextType | undefined>(undefined)
|
|
701
|
+
|
|
702
|
+
interface AuthProviderProps {
|
|
703
|
+
children: ReactNode
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Authentication Provider with PostHog integration
|
|
708
|
+
*
|
|
709
|
+
* Manages user authentication state and integrates with PostHog for:
|
|
710
|
+
* - User identification (posthog.identify)
|
|
711
|
+
* - Login/logout event tracking
|
|
712
|
+
* - Session reset on logout
|
|
713
|
+
*
|
|
714
|
+
* @see https://posthog.com/docs/libraries/react-native#identifying-users
|
|
715
|
+
*/
|
|
716
|
+
export function AuthProvider({ children }: AuthProviderProps) {
|
|
717
|
+
const posthog = usePostHog()
|
|
718
|
+
const [user, setUser] = useState<User | null>(null)
|
|
719
|
+
const [isLoading, setIsLoading] = useState(true)
|
|
720
|
+
|
|
721
|
+
// Restore session on app launch
|
|
722
|
+
useEffect(() => {
|
|
723
|
+
restoreSession()
|
|
724
|
+
}, [])
|
|
725
|
+
|
|
726
|
+
const restoreSession = async () => {
|
|
727
|
+
try {
|
|
728
|
+
const storedUsername = await storage.getCurrentUser()
|
|
729
|
+
if (storedUsername) {
|
|
730
|
+
const existingUser = await storage.getUser(storedUsername)
|
|
731
|
+
if (existingUser) {
|
|
732
|
+
setUser(existingUser)
|
|
733
|
+
|
|
734
|
+
// Re-identify user in PostHog on session restore
|
|
735
|
+
// This ensures events are correctly attributed after app restart
|
|
736
|
+
posthog.identify(storedUsername, {
|
|
737
|
+
$set: {
|
|
738
|
+
username: storedUsername,
|
|
739
|
+
},
|
|
740
|
+
})
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
} catch (error) {
|
|
744
|
+
console.error('Failed to restore session:', error)
|
|
745
|
+
} finally {
|
|
746
|
+
setIsLoading(false)
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
const login = useCallback(
|
|
751
|
+
async (username: string, password: string): Promise<boolean> => {
|
|
752
|
+
// Simple validation (demo app accepts any username/password)
|
|
753
|
+
if (!username.trim() || !password.trim()) {
|
|
754
|
+
return false
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
try {
|
|
758
|
+
// Check if user exists or create new
|
|
759
|
+
const existingUser = await storage.getUser(username)
|
|
760
|
+
const isNewUser = !existingUser
|
|
761
|
+
|
|
762
|
+
const userData: User = existingUser || {
|
|
763
|
+
username,
|
|
764
|
+
burritoConsiderations: 0,
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// Save user data
|
|
768
|
+
await storage.saveUser(userData)
|
|
769
|
+
await storage.setCurrentUser(username)
|
|
770
|
+
setUser(userData)
|
|
771
|
+
|
|
772
|
+
// PostHog identify - use username as distinct ID
|
|
773
|
+
// $set updates properties every time, $set_once only sets if not already set
|
|
774
|
+
// @see https://posthog.com/docs/libraries/react-native#identifying-users
|
|
775
|
+
posthog.identify(username, {
|
|
776
|
+
$set: {
|
|
777
|
+
username: username,
|
|
778
|
+
},
|
|
779
|
+
$set_once: {
|
|
780
|
+
first_login_date: new Date().toISOString(),
|
|
781
|
+
},
|
|
782
|
+
})
|
|
783
|
+
|
|
784
|
+
// Capture login event with properties
|
|
785
|
+
// @see https://posthog.com/docs/libraries/react-native#capturing-events
|
|
786
|
+
posthog.capture('user_logged_in', {
|
|
787
|
+
username: username,
|
|
788
|
+
is_new_user: isNewUser,
|
|
789
|
+
})
|
|
790
|
+
|
|
791
|
+
return true
|
|
792
|
+
} catch (error) {
|
|
793
|
+
console.error('Login error:', error)
|
|
794
|
+
return false
|
|
795
|
+
}
|
|
796
|
+
},
|
|
797
|
+
[posthog],
|
|
798
|
+
)
|
|
799
|
+
|
|
800
|
+
const logout = useCallback(async () => {
|
|
801
|
+
// Capture logout event before reset
|
|
802
|
+
posthog.capture('user_logged_out')
|
|
803
|
+
|
|
804
|
+
// Reset PostHog - clears the current user's distinct ID and anonymous ID
|
|
805
|
+
// This should be called when the user logs out
|
|
806
|
+
// @see https://posthog.com/docs/libraries/react-native#reset-after-logout
|
|
807
|
+
posthog.reset()
|
|
808
|
+
|
|
809
|
+
await storage.removeCurrentUser()
|
|
810
|
+
setUser(null)
|
|
811
|
+
}, [posthog])
|
|
812
|
+
|
|
813
|
+
const incrementBurritoConsiderations = useCallback(async () => {
|
|
814
|
+
if (user) {
|
|
815
|
+
const updatedUser: User = {
|
|
816
|
+
...user,
|
|
817
|
+
burritoConsiderations: user.burritoConsiderations + 1,
|
|
818
|
+
}
|
|
819
|
+
setUser(updatedUser)
|
|
820
|
+
await storage.saveUser(updatedUser)
|
|
821
|
+
}
|
|
822
|
+
}, [user])
|
|
823
|
+
|
|
824
|
+
return (
|
|
825
|
+
<AuthContext.Provider
|
|
826
|
+
value={{
|
|
827
|
+
user,
|
|
828
|
+
isLoading,
|
|
829
|
+
login,
|
|
830
|
+
logout,
|
|
831
|
+
incrementBurritoConsiderations,
|
|
832
|
+
}}
|
|
833
|
+
>
|
|
834
|
+
{children}
|
|
835
|
+
</AuthContext.Provider>
|
|
836
|
+
)
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
export function useAuth() {
|
|
840
|
+
const context = useContext(AuthContext)
|
|
841
|
+
if (context === undefined) {
|
|
842
|
+
throw new Error('useAuth must be used within an AuthProvider')
|
|
843
|
+
}
|
|
844
|
+
return context
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
---
|
|
850
|
+
|
|
851
|
+
## src/navigation/RootNavigator.tsx
|
|
852
|
+
|
|
853
|
+
```tsx
|
|
854
|
+
import React from 'react'
|
|
855
|
+
import { ActivityIndicator, View, StyleSheet } from 'react-native'
|
|
856
|
+
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
|
857
|
+
import { useAuth } from '../contexts/AuthContext'
|
|
858
|
+
import { colors } from '../styles/theme'
|
|
859
|
+
|
|
860
|
+
import HomeScreen from '../screens/HomeScreen'
|
|
861
|
+
import BurritoScreen from '../screens/BurritoScreen'
|
|
862
|
+
import ProfileScreen from '../screens/ProfileScreen'
|
|
863
|
+
|
|
864
|
+
// Type definitions for navigation
|
|
865
|
+
export type RootStackParamList = {
|
|
866
|
+
Home: undefined
|
|
867
|
+
Burrito: undefined
|
|
868
|
+
Profile: undefined
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
const Stack = createNativeStackNavigator<RootStackParamList>()
|
|
872
|
+
|
|
873
|
+
export function RootNavigator() {
|
|
874
|
+
const { isLoading } = useAuth()
|
|
875
|
+
|
|
876
|
+
// Show loading indicator while restoring session
|
|
877
|
+
if (isLoading) {
|
|
878
|
+
return (
|
|
879
|
+
<View style={styles.loadingContainer}>
|
|
880
|
+
<ActivityIndicator size="large" color={colors.primary} />
|
|
881
|
+
</View>
|
|
882
|
+
)
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
return (
|
|
886
|
+
<Stack.Navigator
|
|
887
|
+
screenOptions={{
|
|
888
|
+
headerStyle: {
|
|
889
|
+
backgroundColor: colors.headerBackground,
|
|
890
|
+
},
|
|
891
|
+
headerTintColor: colors.headerText,
|
|
892
|
+
headerTitleStyle: {
|
|
893
|
+
fontWeight: 'bold',
|
|
894
|
+
},
|
|
895
|
+
headerBackTitleVisible: false,
|
|
896
|
+
animation: 'slide_from_right',
|
|
897
|
+
}}
|
|
898
|
+
>
|
|
899
|
+
<Stack.Screen
|
|
900
|
+
name="Home"
|
|
901
|
+
component={HomeScreen}
|
|
902
|
+
options={{
|
|
903
|
+
title: 'Burrito App',
|
|
904
|
+
}}
|
|
905
|
+
/>
|
|
906
|
+
<Stack.Screen
|
|
907
|
+
name="Burrito"
|
|
908
|
+
component={BurritoScreen}
|
|
909
|
+
options={{
|
|
910
|
+
title: 'Burrito Consideration',
|
|
911
|
+
}}
|
|
912
|
+
/>
|
|
913
|
+
<Stack.Screen
|
|
914
|
+
name="Profile"
|
|
915
|
+
component={ProfileScreen}
|
|
916
|
+
options={{
|
|
917
|
+
title: 'Profile',
|
|
918
|
+
}}
|
|
919
|
+
/>
|
|
920
|
+
</Stack.Navigator>
|
|
921
|
+
)
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
const styles = StyleSheet.create({
|
|
925
|
+
loadingContainer: {
|
|
926
|
+
flex: 1,
|
|
927
|
+
justifyContent: 'center',
|
|
928
|
+
alignItems: 'center',
|
|
929
|
+
backgroundColor: colors.background,
|
|
930
|
+
},
|
|
931
|
+
})
|
|
932
|
+
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
---
|
|
936
|
+
|
|
937
|
+
## src/screens/BurritoScreen.tsx
|
|
938
|
+
|
|
939
|
+
```tsx
|
|
940
|
+
import React, { useState, useEffect } from 'react'
|
|
941
|
+
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'
|
|
942
|
+
import { useNavigation } from '@react-navigation/native'
|
|
943
|
+
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
|
|
944
|
+
import { usePostHog } from 'posthog-react-native'
|
|
945
|
+
import { useAuth } from '../contexts/AuthContext'
|
|
946
|
+
import { RootStackParamList } from '../navigation/RootNavigator'
|
|
947
|
+
import {
|
|
948
|
+
colors,
|
|
949
|
+
spacing,
|
|
950
|
+
typography,
|
|
951
|
+
borderRadius,
|
|
952
|
+
shadows,
|
|
953
|
+
} from '../styles/theme'
|
|
954
|
+
|
|
955
|
+
type BurritoScreenNavigationProp = NativeStackNavigationProp<
|
|
956
|
+
RootStackParamList,
|
|
957
|
+
'Burrito'
|
|
958
|
+
>
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* Burrito Consideration Screen
|
|
962
|
+
*
|
|
963
|
+
* Demonstrates PostHog event tracking with custom properties.
|
|
964
|
+
* Each time the user considers a burrito, an event is captured.
|
|
965
|
+
*
|
|
966
|
+
* @see https://posthog.com/docs/libraries/react-native#capturing-events
|
|
967
|
+
*/
|
|
968
|
+
export default function BurritoScreen() {
|
|
969
|
+
const { user, incrementBurritoConsiderations } = useAuth()
|
|
970
|
+
const navigation = useNavigation<BurritoScreenNavigationProp>()
|
|
971
|
+
const posthog = usePostHog()
|
|
972
|
+
const [hasConsidered, setHasConsidered] = useState(false)
|
|
973
|
+
|
|
974
|
+
// Redirect to home if not logged in
|
|
975
|
+
useEffect(() => {
|
|
976
|
+
if (!user) {
|
|
977
|
+
navigation.navigate('Home')
|
|
978
|
+
}
|
|
979
|
+
}, [user, navigation])
|
|
980
|
+
|
|
981
|
+
if (!user) {
|
|
982
|
+
return null
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
const handleConsideration = async () => {
|
|
986
|
+
const newCount = user.burritoConsiderations + 1
|
|
987
|
+
|
|
988
|
+
// Update state first for immediate feedback
|
|
989
|
+
await incrementBurritoConsiderations()
|
|
990
|
+
setHasConsidered(true)
|
|
991
|
+
|
|
992
|
+
// Hide success message after 2 seconds
|
|
993
|
+
setTimeout(() => setHasConsidered(false), 2000)
|
|
994
|
+
|
|
995
|
+
// Capture custom event in PostHog with properties
|
|
996
|
+
// We recommend using a [object] [verb] format for event names
|
|
997
|
+
// @see https://posthog.com/docs/libraries/react-native#capturing-events
|
|
998
|
+
posthog.capture('burrito_considered', {
|
|
999
|
+
total_considerations: newCount,
|
|
1000
|
+
username: user.username,
|
|
1001
|
+
})
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
return (
|
|
1005
|
+
<View style={styles.container}>
|
|
1006
|
+
<View style={styles.card}>
|
|
1007
|
+
<Text style={styles.title}>Burrito Consideration Zone</Text>
|
|
1008
|
+
<Text style={styles.text}>
|
|
1009
|
+
Take a moment to truly consider the potential of burritos.
|
|
1010
|
+
</Text>
|
|
1011
|
+
|
|
1012
|
+
{/*
|
|
1013
|
+
testID is captured by PostHog autocapture for touch events
|
|
1014
|
+
This helps identify the button in analytics
|
|
1015
|
+
@see https://posthog.com/docs/libraries/react-native#autocapture
|
|
1016
|
+
*/}
|
|
1017
|
+
<TouchableOpacity
|
|
1018
|
+
style={styles.burritoButton}
|
|
1019
|
+
onPress={handleConsideration}
|
|
1020
|
+
activeOpacity={0.8}
|
|
1021
|
+
testID="consider-burrito-button"
|
|
1022
|
+
>
|
|
1023
|
+
<Text style={styles.burritoButtonText}>Consider Burrito</Text>
|
|
1024
|
+
</TouchableOpacity>
|
|
1025
|
+
|
|
1026
|
+
{hasConsidered && (
|
|
1027
|
+
<View style={styles.successContainer}>
|
|
1028
|
+
<Text style={styles.success}>
|
|
1029
|
+
Thank you for your consideration!
|
|
1030
|
+
</Text>
|
|
1031
|
+
<Text style={styles.successCount}>
|
|
1032
|
+
Count: {user.burritoConsiderations}
|
|
1033
|
+
</Text>
|
|
1034
|
+
</View>
|
|
1035
|
+
)}
|
|
1036
|
+
|
|
1037
|
+
<View style={styles.stats}>
|
|
1038
|
+
<Text style={styles.statsTitle}>Consideration Stats</Text>
|
|
1039
|
+
<Text style={styles.statsText}>
|
|
1040
|
+
Total considerations: {user.burritoConsiderations}
|
|
1041
|
+
</Text>
|
|
1042
|
+
</View>
|
|
1043
|
+
</View>
|
|
1044
|
+
</View>
|
|
1045
|
+
)
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
const styles = StyleSheet.create({
|
|
1049
|
+
container: {
|
|
1050
|
+
flex: 1,
|
|
1051
|
+
backgroundColor: colors.background,
|
|
1052
|
+
padding: spacing.md,
|
|
1053
|
+
},
|
|
1054
|
+
card: {
|
|
1055
|
+
backgroundColor: colors.cardBackground,
|
|
1056
|
+
borderRadius: borderRadius.md,
|
|
1057
|
+
padding: spacing.lg,
|
|
1058
|
+
...shadows.md,
|
|
1059
|
+
},
|
|
1060
|
+
title: {
|
|
1061
|
+
fontSize: typography.sizes.xl,
|
|
1062
|
+
fontWeight: typography.weights.bold,
|
|
1063
|
+
color: colors.text,
|
|
1064
|
+
marginBottom: spacing.sm,
|
|
1065
|
+
},
|
|
1066
|
+
text: {
|
|
1067
|
+
fontSize: typography.sizes.md,
|
|
1068
|
+
color: colors.text,
|
|
1069
|
+
marginBottom: spacing.lg,
|
|
1070
|
+
lineHeight: 24,
|
|
1071
|
+
},
|
|
1072
|
+
burritoButton: {
|
|
1073
|
+
backgroundColor: colors.burrito,
|
|
1074
|
+
borderRadius: borderRadius.sm,
|
|
1075
|
+
padding: spacing.lg,
|
|
1076
|
+
alignItems: 'center',
|
|
1077
|
+
marginVertical: spacing.md,
|
|
1078
|
+
...shadows.sm,
|
|
1079
|
+
},
|
|
1080
|
+
burritoButtonText: {
|
|
1081
|
+
color: colors.white,
|
|
1082
|
+
fontSize: typography.sizes.lg,
|
|
1083
|
+
fontWeight: typography.weights.bold,
|
|
1084
|
+
},
|
|
1085
|
+
successContainer: {
|
|
1086
|
+
alignItems: 'center',
|
|
1087
|
+
marginVertical: spacing.sm,
|
|
1088
|
+
},
|
|
1089
|
+
success: {
|
|
1090
|
+
color: colors.success,
|
|
1091
|
+
fontSize: typography.sizes.md,
|
|
1092
|
+
fontWeight: typography.weights.medium,
|
|
1093
|
+
},
|
|
1094
|
+
successCount: {
|
|
1095
|
+
color: colors.success,
|
|
1096
|
+
fontSize: typography.sizes.lg,
|
|
1097
|
+
fontWeight: typography.weights.bold,
|
|
1098
|
+
marginTop: spacing.xs,
|
|
1099
|
+
},
|
|
1100
|
+
stats: {
|
|
1101
|
+
backgroundColor: colors.statsBackground,
|
|
1102
|
+
padding: spacing.md,
|
|
1103
|
+
borderRadius: borderRadius.sm,
|
|
1104
|
+
marginTop: spacing.lg,
|
|
1105
|
+
},
|
|
1106
|
+
statsTitle: {
|
|
1107
|
+
fontSize: typography.sizes.lg,
|
|
1108
|
+
fontWeight: typography.weights.semibold,
|
|
1109
|
+
color: colors.text,
|
|
1110
|
+
marginBottom: spacing.xs,
|
|
1111
|
+
},
|
|
1112
|
+
statsText: {
|
|
1113
|
+
fontSize: typography.sizes.md,
|
|
1114
|
+
color: colors.text,
|
|
1115
|
+
},
|
|
1116
|
+
})
|
|
1117
|
+
|
|
1118
|
+
```
|
|
1119
|
+
|
|
1120
|
+
---
|
|
1121
|
+
|
|
1122
|
+
## src/screens/HomeScreen.tsx
|
|
1123
|
+
|
|
1124
|
+
```tsx
|
|
1125
|
+
import React, { useState } from 'react'
|
|
1126
|
+
import {
|
|
1127
|
+
View,
|
|
1128
|
+
Text,
|
|
1129
|
+
TextInput,
|
|
1130
|
+
TouchableOpacity,
|
|
1131
|
+
StyleSheet,
|
|
1132
|
+
ScrollView,
|
|
1133
|
+
KeyboardAvoidingView,
|
|
1134
|
+
Platform,
|
|
1135
|
+
} from 'react-native'
|
|
1136
|
+
import { useNavigation } from '@react-navigation/native'
|
|
1137
|
+
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
|
|
1138
|
+
import { useAuth } from '../contexts/AuthContext'
|
|
1139
|
+
import { RootStackParamList } from '../navigation/RootNavigator'
|
|
1140
|
+
import {
|
|
1141
|
+
colors,
|
|
1142
|
+
spacing,
|
|
1143
|
+
typography,
|
|
1144
|
+
borderRadius,
|
|
1145
|
+
shadows,
|
|
1146
|
+
} from '../styles/theme'
|
|
1147
|
+
|
|
1148
|
+
type HomeScreenNavigationProp = NativeStackNavigationProp<
|
|
1149
|
+
RootStackParamList,
|
|
1150
|
+
'Home'
|
|
1151
|
+
>
|
|
1152
|
+
|
|
1153
|
+
export default function HomeScreen() {
|
|
1154
|
+
const { user, login, logout } = useAuth()
|
|
1155
|
+
const navigation = useNavigation<HomeScreenNavigationProp>()
|
|
1156
|
+
const [username, setUsername] = useState('')
|
|
1157
|
+
const [password, setPassword] = useState('')
|
|
1158
|
+
const [error, setError] = useState('')
|
|
1159
|
+
const [isSubmitting, setIsSubmitting] = useState(false)
|
|
1160
|
+
|
|
1161
|
+
const handleSubmit = async () => {
|
|
1162
|
+
setError('')
|
|
1163
|
+
|
|
1164
|
+
if (!username.trim() || !password.trim()) {
|
|
1165
|
+
setError('Please provide both username and password')
|
|
1166
|
+
return
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
setIsSubmitting(true)
|
|
1170
|
+
try {
|
|
1171
|
+
const success = await login(username, password)
|
|
1172
|
+
if (success) {
|
|
1173
|
+
setUsername('')
|
|
1174
|
+
setPassword('')
|
|
1175
|
+
} else {
|
|
1176
|
+
setError('An error occurred during login')
|
|
1177
|
+
}
|
|
1178
|
+
} catch {
|
|
1179
|
+
setError('An error occurred during login')
|
|
1180
|
+
} finally {
|
|
1181
|
+
setIsSubmitting(false)
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
// Logged in view
|
|
1186
|
+
if (user) {
|
|
1187
|
+
return (
|
|
1188
|
+
<ScrollView
|
|
1189
|
+
style={styles.scrollView}
|
|
1190
|
+
contentContainerStyle={styles.scrollContent}
|
|
1191
|
+
>
|
|
1192
|
+
<View style={styles.card}>
|
|
1193
|
+
<Text style={styles.title}>Welcome back, {user.username}!</Text>
|
|
1194
|
+
<Text style={styles.text}>
|
|
1195
|
+
You are logged in. Feel free to explore:
|
|
1196
|
+
</Text>
|
|
1197
|
+
|
|
1198
|
+
<View style={styles.buttonGroup}>
|
|
1199
|
+
<TouchableOpacity
|
|
1200
|
+
style={[styles.button, styles.burritoButton]}
|
|
1201
|
+
onPress={() => navigation.navigate('Burrito')}
|
|
1202
|
+
activeOpacity={0.8}
|
|
1203
|
+
>
|
|
1204
|
+
<Text style={styles.buttonText}>Consider Burritos</Text>
|
|
1205
|
+
</TouchableOpacity>
|
|
1206
|
+
|
|
1207
|
+
<TouchableOpacity
|
|
1208
|
+
style={[styles.button, styles.primaryButton]}
|
|
1209
|
+
onPress={() => navigation.navigate('Profile')}
|
|
1210
|
+
activeOpacity={0.8}
|
|
1211
|
+
>
|
|
1212
|
+
<Text style={styles.buttonText}>View Profile</Text>
|
|
1213
|
+
</TouchableOpacity>
|
|
1214
|
+
|
|
1215
|
+
<TouchableOpacity
|
|
1216
|
+
style={[styles.button, styles.logoutButton]}
|
|
1217
|
+
onPress={logout}
|
|
1218
|
+
activeOpacity={0.8}
|
|
1219
|
+
>
|
|
1220
|
+
<Text style={styles.buttonText}>Logout</Text>
|
|
1221
|
+
</TouchableOpacity>
|
|
1222
|
+
</View>
|
|
1223
|
+
</View>
|
|
1224
|
+
</ScrollView>
|
|
1225
|
+
)
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
// Login view
|
|
1229
|
+
return (
|
|
1230
|
+
<KeyboardAvoidingView
|
|
1231
|
+
style={styles.container}
|
|
1232
|
+
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
1233
|
+
>
|
|
1234
|
+
<ScrollView
|
|
1235
|
+
style={styles.scrollView}
|
|
1236
|
+
contentContainerStyle={styles.scrollContent}
|
|
1237
|
+
keyboardShouldPersistTaps="handled"
|
|
1238
|
+
>
|
|
1239
|
+
<View style={styles.card}>
|
|
1240
|
+
<Text style={styles.title}>Welcome to Burrito Consideration App</Text>
|
|
1241
|
+
<Text style={styles.text}>
|
|
1242
|
+
Please sign in to begin your burrito journey
|
|
1243
|
+
</Text>
|
|
1244
|
+
|
|
1245
|
+
<View style={styles.form}>
|
|
1246
|
+
<Text style={styles.label}>Username:</Text>
|
|
1247
|
+
<TextInput
|
|
1248
|
+
style={styles.input}
|
|
1249
|
+
value={username}
|
|
1250
|
+
onChangeText={setUsername}
|
|
1251
|
+
placeholder="Enter any username"
|
|
1252
|
+
placeholderTextColor={colors.textLight}
|
|
1253
|
+
autoCapitalize="none"
|
|
1254
|
+
autoCorrect={false}
|
|
1255
|
+
autoComplete="username"
|
|
1256
|
+
editable={!isSubmitting}
|
|
1257
|
+
/>
|
|
1258
|
+
|
|
1259
|
+
<Text style={styles.label}>Password:</Text>
|
|
1260
|
+
<TextInput
|
|
1261
|
+
style={styles.input}
|
|
1262
|
+
value={password}
|
|
1263
|
+
onChangeText={setPassword}
|
|
1264
|
+
placeholder="Enter any password"
|
|
1265
|
+
placeholderTextColor={colors.textLight}
|
|
1266
|
+
secureTextEntry
|
|
1267
|
+
autoComplete="password"
|
|
1268
|
+
editable={!isSubmitting}
|
|
1269
|
+
/>
|
|
1270
|
+
|
|
1271
|
+
{error ? <Text style={styles.error}>{error}</Text> : null}
|
|
1272
|
+
|
|
1273
|
+
<TouchableOpacity
|
|
1274
|
+
style={[
|
|
1275
|
+
styles.button,
|
|
1276
|
+
styles.primaryButton,
|
|
1277
|
+
isSubmitting && styles.buttonDisabled,
|
|
1278
|
+
]}
|
|
1279
|
+
onPress={handleSubmit}
|
|
1280
|
+
disabled={isSubmitting}
|
|
1281
|
+
activeOpacity={0.8}
|
|
1282
|
+
>
|
|
1283
|
+
<Text style={styles.buttonText}>
|
|
1284
|
+
{isSubmitting ? 'Signing In...' : 'Sign In'}
|
|
1285
|
+
</Text>
|
|
1286
|
+
</TouchableOpacity>
|
|
1287
|
+
</View>
|
|
1288
|
+
|
|
1289
|
+
<Text style={styles.note}>
|
|
1290
|
+
Note: This is a demo app. Use any username and password to sign in.
|
|
1291
|
+
</Text>
|
|
1292
|
+
</View>
|
|
1293
|
+
</ScrollView>
|
|
1294
|
+
</KeyboardAvoidingView>
|
|
1295
|
+
)
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
const styles = StyleSheet.create({
|
|
1299
|
+
container: {
|
|
1300
|
+
flex: 1,
|
|
1301
|
+
backgroundColor: colors.background,
|
|
1302
|
+
},
|
|
1303
|
+
scrollView: {
|
|
1304
|
+
flex: 1,
|
|
1305
|
+
backgroundColor: colors.background,
|
|
1306
|
+
},
|
|
1307
|
+
scrollContent: {
|
|
1308
|
+
flexGrow: 1,
|
|
1309
|
+
padding: spacing.md,
|
|
1310
|
+
justifyContent: 'center',
|
|
1311
|
+
},
|
|
1312
|
+
card: {
|
|
1313
|
+
backgroundColor: colors.cardBackground,
|
|
1314
|
+
borderRadius: borderRadius.md,
|
|
1315
|
+
padding: spacing.lg,
|
|
1316
|
+
...shadows.md,
|
|
1317
|
+
},
|
|
1318
|
+
title: {
|
|
1319
|
+
fontSize: typography.sizes.xl,
|
|
1320
|
+
fontWeight: typography.weights.bold,
|
|
1321
|
+
color: colors.text,
|
|
1322
|
+
marginBottom: spacing.sm,
|
|
1323
|
+
},
|
|
1324
|
+
text: {
|
|
1325
|
+
fontSize: typography.sizes.md,
|
|
1326
|
+
color: colors.text,
|
|
1327
|
+
marginBottom: spacing.md,
|
|
1328
|
+
lineHeight: 24,
|
|
1329
|
+
},
|
|
1330
|
+
form: {
|
|
1331
|
+
marginTop: spacing.md,
|
|
1332
|
+
},
|
|
1333
|
+
label: {
|
|
1334
|
+
fontSize: typography.sizes.md,
|
|
1335
|
+
fontWeight: typography.weights.medium,
|
|
1336
|
+
color: colors.text,
|
|
1337
|
+
marginBottom: spacing.xs,
|
|
1338
|
+
},
|
|
1339
|
+
input: {
|
|
1340
|
+
backgroundColor: colors.inputBackground,
|
|
1341
|
+
borderWidth: 1,
|
|
1342
|
+
borderColor: colors.border,
|
|
1343
|
+
borderRadius: borderRadius.sm,
|
|
1344
|
+
padding: spacing.sm,
|
|
1345
|
+
fontSize: typography.sizes.md,
|
|
1346
|
+
color: colors.text,
|
|
1347
|
+
marginBottom: spacing.md,
|
|
1348
|
+
},
|
|
1349
|
+
buttonGroup: {
|
|
1350
|
+
marginTop: spacing.md,
|
|
1351
|
+
gap: spacing.sm,
|
|
1352
|
+
},
|
|
1353
|
+
button: {
|
|
1354
|
+
borderRadius: borderRadius.sm,
|
|
1355
|
+
padding: spacing.md,
|
|
1356
|
+
alignItems: 'center',
|
|
1357
|
+
marginTop: spacing.sm,
|
|
1358
|
+
},
|
|
1359
|
+
primaryButton: {
|
|
1360
|
+
backgroundColor: colors.primary,
|
|
1361
|
+
},
|
|
1362
|
+
burritoButton: {
|
|
1363
|
+
backgroundColor: colors.burrito,
|
|
1364
|
+
},
|
|
1365
|
+
logoutButton: {
|
|
1366
|
+
backgroundColor: colors.danger,
|
|
1367
|
+
},
|
|
1368
|
+
buttonDisabled: {
|
|
1369
|
+
opacity: 0.6,
|
|
1370
|
+
},
|
|
1371
|
+
buttonText: {
|
|
1372
|
+
color: colors.white,
|
|
1373
|
+
fontSize: typography.sizes.md,
|
|
1374
|
+
fontWeight: typography.weights.semibold,
|
|
1375
|
+
},
|
|
1376
|
+
error: {
|
|
1377
|
+
color: colors.danger,
|
|
1378
|
+
marginBottom: spacing.sm,
|
|
1379
|
+
fontSize: typography.sizes.sm,
|
|
1380
|
+
},
|
|
1381
|
+
note: {
|
|
1382
|
+
marginTop: spacing.lg,
|
|
1383
|
+
color: colors.textSecondary,
|
|
1384
|
+
fontSize: typography.sizes.sm,
|
|
1385
|
+
textAlign: 'center',
|
|
1386
|
+
lineHeight: 20,
|
|
1387
|
+
},
|
|
1388
|
+
})
|
|
1389
|
+
|
|
1390
|
+
```
|
|
1391
|
+
|
|
1392
|
+
---
|
|
1393
|
+
|
|
1394
|
+
## src/screens/ProfileScreen.tsx
|
|
1395
|
+
|
|
1396
|
+
```tsx
|
|
1397
|
+
import React, { useEffect } from 'react'
|
|
1398
|
+
import { View, Text, TouchableOpacity, StyleSheet, Alert } from 'react-native'
|
|
1399
|
+
import { useNavigation } from '@react-navigation/native'
|
|
1400
|
+
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
|
|
1401
|
+
import { usePostHog } from 'posthog-react-native'
|
|
1402
|
+
import { useAuth } from '../contexts/AuthContext'
|
|
1403
|
+
import { RootStackParamList } from '../navigation/RootNavigator'
|
|
1404
|
+
import {
|
|
1405
|
+
colors,
|
|
1406
|
+
spacing,
|
|
1407
|
+
typography,
|
|
1408
|
+
borderRadius,
|
|
1409
|
+
shadows,
|
|
1410
|
+
} from '../styles/theme'
|
|
1411
|
+
|
|
1412
|
+
type ProfileScreenNavigationProp = NativeStackNavigationProp<
|
|
1413
|
+
RootStackParamList,
|
|
1414
|
+
'Profile'
|
|
1415
|
+
>
|
|
1416
|
+
|
|
1417
|
+
/**
|
|
1418
|
+
* Profile Screen
|
|
1419
|
+
*
|
|
1420
|
+
* Displays user information and demonstrates PostHog error tracking.
|
|
1421
|
+
* The test error button shows how to capture exceptions manually.
|
|
1422
|
+
*
|
|
1423
|
+
* @see https://posthog.com/docs/libraries/react-native#error-tracking
|
|
1424
|
+
*/
|
|
1425
|
+
export default function ProfileScreen() {
|
|
1426
|
+
const { user } = useAuth()
|
|
1427
|
+
const navigation = useNavigation<ProfileScreenNavigationProp>()
|
|
1428
|
+
const posthog = usePostHog()
|
|
1429
|
+
|
|
1430
|
+
// Redirect to home if not logged in
|
|
1431
|
+
useEffect(() => {
|
|
1432
|
+
if (!user) {
|
|
1433
|
+
navigation.navigate('Home')
|
|
1434
|
+
}
|
|
1435
|
+
}, [user, navigation])
|
|
1436
|
+
|
|
1437
|
+
if (!user) {
|
|
1438
|
+
return null
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
/**
|
|
1442
|
+
* Triggers a test error and captures it in PostHog
|
|
1443
|
+
*
|
|
1444
|
+
* This demonstrates manual exception capture via captureException.
|
|
1445
|
+
* In production, you would typically set up automatic exception capture
|
|
1446
|
+
* or use the before_send callback for customization.
|
|
1447
|
+
*
|
|
1448
|
+
* @see https://posthog.com/docs/libraries/react-native#error-tracking
|
|
1449
|
+
*/
|
|
1450
|
+
const triggerTestError = () => {
|
|
1451
|
+
try {
|
|
1452
|
+
throw new Error('Test error for PostHog error tracking')
|
|
1453
|
+
} catch (err) {
|
|
1454
|
+
const error = err as Error
|
|
1455
|
+
|
|
1456
|
+
posthog.captureException(error, {
|
|
1457
|
+
username: user.username,
|
|
1458
|
+
screen: 'Profile',
|
|
1459
|
+
})
|
|
1460
|
+
|
|
1461
|
+
console.error('Captured error:', error)
|
|
1462
|
+
Alert.alert(
|
|
1463
|
+
'Error Captured',
|
|
1464
|
+
'The test error has been sent to PostHog!',
|
|
1465
|
+
[{ text: 'OK' }],
|
|
1466
|
+
)
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
const getJourneyMessage = () => {
|
|
1471
|
+
const count = user.burritoConsiderations
|
|
1472
|
+
if (count === 0) {
|
|
1473
|
+
return "You haven't considered any burritos yet. Visit the Burrito Consideration page to start!"
|
|
1474
|
+
} else if (count === 1) {
|
|
1475
|
+
return "You've considered the burrito potential once. Keep going!"
|
|
1476
|
+
} else if (count < 5) {
|
|
1477
|
+
return "You're getting the hang of burrito consideration!"
|
|
1478
|
+
} else if (count < 10) {
|
|
1479
|
+
return "You're becoming a burrito consideration expert!"
|
|
1480
|
+
} else {
|
|
1481
|
+
return 'You are a true burrito consideration master!'
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
return (
|
|
1486
|
+
<View style={styles.container}>
|
|
1487
|
+
<View style={styles.card}>
|
|
1488
|
+
<Text style={styles.title}>User Profile</Text>
|
|
1489
|
+
|
|
1490
|
+
<View style={styles.stats}>
|
|
1491
|
+
<Text style={styles.statsTitle}>Your Information</Text>
|
|
1492
|
+
<View style={styles.infoRow}>
|
|
1493
|
+
<Text style={styles.infoLabel}>Username:</Text>
|
|
1494
|
+
<Text style={styles.infoValue}>{user.username}</Text>
|
|
1495
|
+
</View>
|
|
1496
|
+
<View style={styles.infoRow}>
|
|
1497
|
+
<Text style={styles.infoLabel}>Burrito Considerations:</Text>
|
|
1498
|
+
<Text style={styles.infoValue}>{user.burritoConsiderations}</Text>
|
|
1499
|
+
</View>
|
|
1500
|
+
</View>
|
|
1501
|
+
|
|
1502
|
+
{/*
|
|
1503
|
+
testID is captured by PostHog autocapture for touch events
|
|
1504
|
+
@see https://posthog.com/docs/libraries/react-native#autocapture
|
|
1505
|
+
*/}
|
|
1506
|
+
<TouchableOpacity
|
|
1507
|
+
style={styles.errorButton}
|
|
1508
|
+
onPress={triggerTestError}
|
|
1509
|
+
activeOpacity={0.8}
|
|
1510
|
+
testID="trigger-error-button"
|
|
1511
|
+
>
|
|
1512
|
+
<Text style={styles.buttonText}>Trigger Test Error (for PostHog)</Text>
|
|
1513
|
+
</TouchableOpacity>
|
|
1514
|
+
|
|
1515
|
+
<View style={styles.journey}>
|
|
1516
|
+
<Text style={styles.journeyTitle}>Your Burrito Journey</Text>
|
|
1517
|
+
<Text style={styles.journeyText}>{getJourneyMessage()}</Text>
|
|
1518
|
+
</View>
|
|
1519
|
+
</View>
|
|
1520
|
+
</View>
|
|
1521
|
+
)
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
const styles = StyleSheet.create({
|
|
1525
|
+
container: {
|
|
1526
|
+
flex: 1,
|
|
1527
|
+
backgroundColor: colors.background,
|
|
1528
|
+
padding: spacing.md,
|
|
1529
|
+
},
|
|
1530
|
+
card: {
|
|
1531
|
+
backgroundColor: colors.cardBackground,
|
|
1532
|
+
borderRadius: borderRadius.md,
|
|
1533
|
+
padding: spacing.lg,
|
|
1534
|
+
...shadows.md,
|
|
1535
|
+
},
|
|
1536
|
+
title: {
|
|
1537
|
+
fontSize: typography.sizes.xl,
|
|
1538
|
+
fontWeight: typography.weights.bold,
|
|
1539
|
+
color: colors.text,
|
|
1540
|
+
marginBottom: spacing.md,
|
|
1541
|
+
},
|
|
1542
|
+
stats: {
|
|
1543
|
+
backgroundColor: colors.statsBackground,
|
|
1544
|
+
padding: spacing.md,
|
|
1545
|
+
borderRadius: borderRadius.sm,
|
|
1546
|
+
},
|
|
1547
|
+
statsTitle: {
|
|
1548
|
+
fontSize: typography.sizes.lg,
|
|
1549
|
+
fontWeight: typography.weights.semibold,
|
|
1550
|
+
color: colors.text,
|
|
1551
|
+
marginBottom: spacing.sm,
|
|
1552
|
+
},
|
|
1553
|
+
infoRow: {
|
|
1554
|
+
flexDirection: 'row',
|
|
1555
|
+
marginBottom: spacing.xs,
|
|
1556
|
+
},
|
|
1557
|
+
infoLabel: {
|
|
1558
|
+
fontSize: typography.sizes.md,
|
|
1559
|
+
fontWeight: typography.weights.bold,
|
|
1560
|
+
color: colors.text,
|
|
1561
|
+
marginRight: spacing.xs,
|
|
1562
|
+
},
|
|
1563
|
+
infoValue: {
|
|
1564
|
+
fontSize: typography.sizes.md,
|
|
1565
|
+
color: colors.text,
|
|
1566
|
+
},
|
|
1567
|
+
errorButton: {
|
|
1568
|
+
backgroundColor: colors.danger,
|
|
1569
|
+
borderRadius: borderRadius.sm,
|
|
1570
|
+
padding: spacing.md,
|
|
1571
|
+
alignItems: 'center',
|
|
1572
|
+
marginTop: spacing.lg,
|
|
1573
|
+
},
|
|
1574
|
+
buttonText: {
|
|
1575
|
+
color: colors.white,
|
|
1576
|
+
fontSize: typography.sizes.md,
|
|
1577
|
+
fontWeight: typography.weights.semibold,
|
|
1578
|
+
},
|
|
1579
|
+
journey: {
|
|
1580
|
+
marginTop: spacing.lg,
|
|
1581
|
+
},
|
|
1582
|
+
journeyTitle: {
|
|
1583
|
+
fontSize: typography.sizes.lg,
|
|
1584
|
+
fontWeight: typography.weights.semibold,
|
|
1585
|
+
color: colors.text,
|
|
1586
|
+
marginBottom: spacing.sm,
|
|
1587
|
+
},
|
|
1588
|
+
journeyText: {
|
|
1589
|
+
fontSize: typography.sizes.md,
|
|
1590
|
+
color: colors.text,
|
|
1591
|
+
lineHeight: 24,
|
|
1592
|
+
},
|
|
1593
|
+
})
|
|
1594
|
+
|
|
1595
|
+
```
|
|
1596
|
+
|
|
1597
|
+
---
|
|
1598
|
+
|
|
1599
|
+
## src/services/storage.ts
|
|
1600
|
+
|
|
1601
|
+
```ts
|
|
1602
|
+
import AsyncStorage from '@react-native-async-storage/async-storage'
|
|
1603
|
+
|
|
1604
|
+
const CURRENT_USER_KEY = 'currentUser'
|
|
1605
|
+
const USERS_KEY = 'users'
|
|
1606
|
+
|
|
1607
|
+
export interface User {
|
|
1608
|
+
username: string
|
|
1609
|
+
burritoConsiderations: number
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
/**
|
|
1613
|
+
* Storage service for persisting user data
|
|
1614
|
+
* Uses AsyncStorage (React Native's async key-value storage)
|
|
1615
|
+
*/
|
|
1616
|
+
export const storage = {
|
|
1617
|
+
/**
|
|
1618
|
+
* Get the currently logged in user's username
|
|
1619
|
+
*/
|
|
1620
|
+
getCurrentUser: async (): Promise<string | null> => {
|
|
1621
|
+
try {
|
|
1622
|
+
return await AsyncStorage.getItem(CURRENT_USER_KEY)
|
|
1623
|
+
} catch (error) {
|
|
1624
|
+
console.error('Error getting current user:', error)
|
|
1625
|
+
return null
|
|
1626
|
+
}
|
|
1627
|
+
},
|
|
1628
|
+
|
|
1629
|
+
/**
|
|
1630
|
+
* Set the currently logged in user's username
|
|
1631
|
+
*/
|
|
1632
|
+
setCurrentUser: async (username: string): Promise<void> => {
|
|
1633
|
+
try {
|
|
1634
|
+
await AsyncStorage.setItem(CURRENT_USER_KEY, username)
|
|
1635
|
+
} catch (error) {
|
|
1636
|
+
console.error('Error setting current user:', error)
|
|
1637
|
+
}
|
|
1638
|
+
},
|
|
1639
|
+
|
|
1640
|
+
/**
|
|
1641
|
+
* Remove the current user (logout)
|
|
1642
|
+
*/
|
|
1643
|
+
removeCurrentUser: async (): Promise<void> => {
|
|
1644
|
+
try {
|
|
1645
|
+
await AsyncStorage.removeItem(CURRENT_USER_KEY)
|
|
1646
|
+
} catch (error) {
|
|
1647
|
+
console.error('Error removing current user:', error)
|
|
1648
|
+
}
|
|
1649
|
+
},
|
|
1650
|
+
|
|
1651
|
+
/**
|
|
1652
|
+
* Get all stored users
|
|
1653
|
+
*/
|
|
1654
|
+
getUsers: async (): Promise<Record<string, User>> => {
|
|
1655
|
+
try {
|
|
1656
|
+
const data = await AsyncStorage.getItem(USERS_KEY)
|
|
1657
|
+
return data ? JSON.parse(data) : {}
|
|
1658
|
+
} catch (error) {
|
|
1659
|
+
console.error('Error getting users:', error)
|
|
1660
|
+
return {}
|
|
1661
|
+
}
|
|
1662
|
+
},
|
|
1663
|
+
|
|
1664
|
+
/**
|
|
1665
|
+
* Get a specific user by username
|
|
1666
|
+
*/
|
|
1667
|
+
getUser: async (username: string): Promise<User | null> => {
|
|
1668
|
+
try {
|
|
1669
|
+
const users = await storage.getUsers()
|
|
1670
|
+
return users[username] || null
|
|
1671
|
+
} catch (error) {
|
|
1672
|
+
console.error('Error getting user:', error)
|
|
1673
|
+
return null
|
|
1674
|
+
}
|
|
1675
|
+
},
|
|
1676
|
+
|
|
1677
|
+
/**
|
|
1678
|
+
* Save a user to storage
|
|
1679
|
+
*/
|
|
1680
|
+
saveUser: async (user: User): Promise<void> => {
|
|
1681
|
+
try {
|
|
1682
|
+
const users = await storage.getUsers()
|
|
1683
|
+
users[user.username] = user
|
|
1684
|
+
await AsyncStorage.setItem(USERS_KEY, JSON.stringify(users))
|
|
1685
|
+
} catch (error) {
|
|
1686
|
+
console.error('Error saving user:', error)
|
|
1687
|
+
}
|
|
1688
|
+
},
|
|
1689
|
+
|
|
1690
|
+
/**
|
|
1691
|
+
* Clear all stored data (for testing/debugging)
|
|
1692
|
+
*/
|
|
1693
|
+
clearAll: async (): Promise<void> => {
|
|
1694
|
+
try {
|
|
1695
|
+
await AsyncStorage.multiRemove([CURRENT_USER_KEY, USERS_KEY])
|
|
1696
|
+
} catch (error) {
|
|
1697
|
+
console.error('Error clearing storage:', error)
|
|
1698
|
+
}
|
|
1699
|
+
},
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
```
|
|
1703
|
+
|
|
1704
|
+
---
|
|
1705
|
+
|
|
1706
|
+
## src/styles/theme.ts
|
|
1707
|
+
|
|
1708
|
+
```ts
|
|
1709
|
+
/**
|
|
1710
|
+
* Theme constants for consistent styling across the app
|
|
1711
|
+
* Matches the color scheme from the TanStack Start web version
|
|
1712
|
+
*/
|
|
1713
|
+
|
|
1714
|
+
export const colors = {
|
|
1715
|
+
// Primary colors
|
|
1716
|
+
primary: '#0070f3',
|
|
1717
|
+
primaryDark: '#0051cc',
|
|
1718
|
+
|
|
1719
|
+
// Status colors
|
|
1720
|
+
success: '#28a745',
|
|
1721
|
+
successDark: '#218838',
|
|
1722
|
+
danger: '#dc3545',
|
|
1723
|
+
dangerDark: '#c82333',
|
|
1724
|
+
|
|
1725
|
+
// Feature colors
|
|
1726
|
+
burrito: '#e07c24',
|
|
1727
|
+
burritoDark: '#c96a1a',
|
|
1728
|
+
|
|
1729
|
+
// Neutral colors
|
|
1730
|
+
background: '#f5f5f5',
|
|
1731
|
+
white: '#ffffff',
|
|
1732
|
+
text: '#333333',
|
|
1733
|
+
textSecondary: '#666666',
|
|
1734
|
+
textLight: '#999999',
|
|
1735
|
+
border: '#dddddd',
|
|
1736
|
+
borderLight: '#eeeeee',
|
|
1737
|
+
|
|
1738
|
+
// Component-specific
|
|
1739
|
+
statsBackground: '#f8f9fa',
|
|
1740
|
+
headerBackground: '#333333',
|
|
1741
|
+
headerText: '#ffffff',
|
|
1742
|
+
inputBackground: '#ffffff',
|
|
1743
|
+
cardBackground: '#ffffff',
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
export const spacing = {
|
|
1747
|
+
xs: 4,
|
|
1748
|
+
sm: 8,
|
|
1749
|
+
md: 16,
|
|
1750
|
+
lg: 24,
|
|
1751
|
+
xl: 32,
|
|
1752
|
+
xxl: 48,
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
export const typography = {
|
|
1756
|
+
sizes: {
|
|
1757
|
+
xs: 12,
|
|
1758
|
+
sm: 14,
|
|
1759
|
+
md: 16,
|
|
1760
|
+
lg: 18,
|
|
1761
|
+
xl: 24,
|
|
1762
|
+
xxl: 32,
|
|
1763
|
+
},
|
|
1764
|
+
weights: {
|
|
1765
|
+
normal: '400' as const,
|
|
1766
|
+
medium: '500' as const,
|
|
1767
|
+
semibold: '600' as const,
|
|
1768
|
+
bold: '700' as const,
|
|
1769
|
+
},
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
export const borderRadius = {
|
|
1773
|
+
sm: 4,
|
|
1774
|
+
md: 8,
|
|
1775
|
+
lg: 12,
|
|
1776
|
+
full: 9999,
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
export const shadows = {
|
|
1780
|
+
sm: {
|
|
1781
|
+
shadowColor: '#000',
|
|
1782
|
+
shadowOffset: { width: 0, height: 1 },
|
|
1783
|
+
shadowOpacity: 0.05,
|
|
1784
|
+
shadowRadius: 2,
|
|
1785
|
+
elevation: 1,
|
|
1786
|
+
},
|
|
1787
|
+
md: {
|
|
1788
|
+
shadowColor: '#000',
|
|
1789
|
+
shadowOffset: { width: 0, height: 2 },
|
|
1790
|
+
shadowOpacity: 0.1,
|
|
1791
|
+
shadowRadius: 4,
|
|
1792
|
+
elevation: 3,
|
|
1793
|
+
},
|
|
1794
|
+
lg: {
|
|
1795
|
+
shadowColor: '#000',
|
|
1796
|
+
shadowOffset: { width: 0, height: 4 },
|
|
1797
|
+
shadowOpacity: 0.15,
|
|
1798
|
+
shadowRadius: 8,
|
|
1799
|
+
elevation: 5,
|
|
1800
|
+
},
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
```
|
|
1804
|
+
|
|
1805
|
+
---
|
|
1806
|
+
|
|
1807
|
+
## src/types/env.d.ts
|
|
1808
|
+
|
|
1809
|
+
```ts
|
|
1810
|
+
declare module 'react-native-config' {
|
|
1811
|
+
export interface NativeConfig {
|
|
1812
|
+
POSTHOG_PROJECT_TOKEN?: string
|
|
1813
|
+
POSTHOG_HOST?: string
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
export const Config: NativeConfig
|
|
1817
|
+
export default Config
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
```
|
|
1821
|
+
|
|
1822
|
+
---
|
|
1823
|
+
|