A trusted van-life community app that combines nomadic dating, activity-based friendships, and a premium builder-help platform with verified access to keep it safe and intentional.
Built for Shipyard: Creator Contest 2026 by RevenueCat
Quin Gable's Brief
This app was built as part of Shipyard: Creator Contest 2026 — a 4-week app-building competition organized by RevenueCat where developers collaborate with content creators to build, launch, and monetize MVP apps.
Quin Gable is a TikTok creator with 1.2M+ followers and 30M+ likes who documents solo van life adventures with her cats Atlas and Otto, featuring hiking and travel content.
"Dating and making friends on the road is hard when you're always moving—and van life is a tight, protective community."
Quin's Vision: A van-life app with nomadic dating, activity-based friend finding, and a paid 'builder help' section for van projects, with invite-only or verified access to keep it safe and intentional.
| Detail | Value |
|---|---|
| Prize Pool | $140,000 |
| Duration | 4 weeks (Jan 15 - Feb 12, 2026) |
| Requirements | Mobile app (iOS/Android), RevenueCat integration |
| Distribution | TestFlight (iOS) / Play Internal Testing (Android) |
- Overview
- Features
- Tech Stack
- Getting Started
- Project Structure
- App Architecture
- Database Schema
- Authentication
- Key Components
- State Management
- Theming
- Haptic Feedback
- Premium Features
- Environment Variables
- Scripts
- Contributing
- License
Vancore is a social networking mobile application designed specifically for the van-life community, built to address the unique challenges of making connections while living life on the road.
"Dating and making friends on the road is hard when you're always moving—and van life is a tight, protective community." — Quin Gable
The app serves as a trusted platform connecting nomads, travelers, and van enthusiasts through:
- Swipe-Based Matching: Find romantic connections or platonic friendships with other van-lifers
- Activity Coordination: Create and join micro-events/activities with nearby community members
- Builder Help Q&A: A premium community knowledge base for van build assistance
- Verified Access: Identity verification to keep the community safe and intentional
Van life is an incredible lifestyle, but it comes with unique social challenges:
- Constant Movement: Traditional dating apps and social networks don't account for nomadic lifestyles
- Trust & Safety: The van life community is tight-knit and protective — strangers need to be vetted
- Finding Your Tribe: Meeting like-minded people who understand the lifestyle is difficult
- Builder Knowledge: Van builds are complex, and finding reliable advice is scattered across forums
Vancore brings the van life community together in one trusted space.
- People living the van-life or nomadic lifestyle
- Digital nomads and remote workers
- Van builders (planning, building, or upgrading their rigs)
- Travelers seeking community connections on the road
- Quin Gable's 1.2M+ TikTok community
| Property | Value |
|---|---|
| App Name | Vancore |
| Bundle ID | com.vanlife.community |
| URL Scheme | vanlife:// |
| Primary Color | Green (#16a34a) |
| Built For | Shipyard 2026 |
| Creator | Quin Gable |
The heart of the app - a swipe-based discovery system for finding friends and romantic connections.
- Swipe Cards: Tinder-style card interface with smooth gesture animations
- Two Modes: Switch between "Friends" and "Dating" discovery modes
- Actions: Like, Pass, and Super Like other profiles
- Mutual Matching: Get notified when both users like each other
- Location-Based: Filter profiles by distance from your current location
- Second Chance: Revisit profiles you previously passed on
- Interest Filtering: Find people with similar interests
- Prefetch Optimization: Images preloaded for smooth scrolling
Coordinate meetups and activities with the van-life community.
- Create Activities: Host hiking trips, coworking sessions, campfire hangouts, and more
- Categories:
- 🥾 Hiking
- 🧗 Climbing
- 🏄 Surfing
- ⛺ Camping
- 💻 Coworking
- 🎉 Social
- 📸 Photography
- 🎵 Music
- 🍕 Food
- 💪 Fitness
- Join/Leave: RSVP to activities with attendee limits
- Status Tracking: See upcoming, in-progress, and ended activities
- Comments: Discuss activities with other attendees
- Edit & Delete: Manage your own activities
- Distance Sorting: Find activities near you
Connect with your matches through real-time messaging.
- Real-Time Chat: Powered by Supabase Realtime subscriptions
- Typing Indicators: See when the other person is typing
- Message Types: Text, images, voice messages, and location sharing
- Unread Counts: Badge indicators for unread messages
- Block & Report: Safety features for unwanted contacts
- Archive & Mute: Organize your conversations
- Message Requests: Control who can message you
A premium community-driven knowledge base for van build assistance.
- Topics:
- ⚡ Electrical Systems
- ☀️ Solar Setup
- 🚿 Plumbing
- 🌡️ HVAC & Ventilation
- 🧱 Insulation
- 🛠️ Carpentry & Framing
- 🚐 Mechanical & Maintenance
- 📡 Off-Grid Tech
- 🎨 Interior Design
- 💰 Budgeting
- Ask Questions: Premium feature to post detailed questions
- Answer & Reply: Help others with your expertise
- Voting System: Upvote/downvote answers
- Accepted Answers: Mark the best answer
- View Counts: Track question engagement
- Image Attachments: Share photos of your build
Showcase your van-life journey and personality.
- Photo Gallery: Multiple photos organized in albums
- Photo Likes: See who liked your photos
- Primary Photo: Set your main profile picture
- Vanlife Details:
- Rig Type (Van, Bus, RV, Truck Camper, etc.)
- Travel Pace (Full-time, Part-time, Weekender)
- Current Location
- Interests: Display your hobbies and passions
- Bio: Tell your story
- Verification Badge: Show you're a verified member
Comprehensive controls for privacy and preferences.
- Privacy Settings:
- Show/hide age, location, online status
- Control profile visibility
- Manage who can message you
- Notification Preferences: Customize push and in-app notifications
- Blocked Users: Manage your block list
- Identity Verification: Request verification for trust
- Help & Feedback: Contact support
- Account Management: Delete account option
Secure and flexible sign-in options.
- Email/Password: Traditional authentication with email verification
- Apple Sign-In: Native iOS authentication
- Google Sign-In: OAuth integration
- Password Reset: Self-service password recovery
Guided profile setup for new users.
- Basics: Name and age
- Vanlife Details: Rig type and travel pace
- Interests: Select your hobbies (multi-select)
- Preferences: Choose discovery mode (Friends/Dating/Both)
- Photo: Upload your profile picture
| Technology | Version | Purpose |
|---|---|---|
| React Native | 0.83.1 | Cross-platform mobile framework |
| Expo | SDK 55 | Development platform and native APIs |
| React | 19.2.0 | UI library |
| TypeScript | 5.3.3 | Type safety |
| Technology | Purpose |
|---|---|
| Expo Router 55 | File-based routing with typed routes |
| NativeTabs | Native tab bar implementation |
| Service | Purpose |
|---|---|
| Supabase | Backend-as-a-Service |
| - Auth | Authentication (Email, Apple, Google) |
| - Database | PostgreSQL with Row Level Security |
| - Realtime | WebSocket subscriptions |
| - Storage | File uploads (avatars, photos, attachments) |
| - Edge Functions | Serverless functions |
| Technology | Purpose |
|---|---|
| Zustand 5.0.2 | Lightweight state management |
| Technology | Purpose |
|---|---|
| RevenueCat | Subscription management |
| react-native-purchases | RevenueCat SDK |
| Library | Purpose |
|---|---|
| React Native Reanimated 4.2.1 | High-performance animations |
| React Native Gesture Handler 2.30.0 | Touch gestures |
| Expo Linear Gradient | Gradient backgrounds |
| Expo Glass Effect | iOS blur effects |
| Expo Symbols | SF Symbols icons |
| @shopify/flash-list | Performant lists |
| Zeego | Native context menus |
| Library | Purpose |
|---|---|
| Expo Image | Optimized image component |
| Expo Image Picker | Photo/video selection |
| Expo Video | Video playback |
| Expo Audio | Audio recording/playback |
| Library | Purpose |
|---|---|
| date-fns | Date formatting |
| @renegades/react-native-tickle | Haptic feedback |
| react-native-keyboard-controller | Keyboard handling |
| expo-secure-store | Secure credential storage |
| expo-notifications | Push notifications |
| expo-location | Location services |
| expo-maps | Map components |
- Node.js 18+
- npm or yarn
- Expo CLI (
npm install -g expo-cli) - Supabase account
- iOS Simulator / Android Emulator or physical device
- Xcode (for iOS development)
- Android Studio (for Android development)
git clone <repository-url>
cd vanlife-appnpm install- Create a new project at supabase.com
- Run the database migrations from
supabase/migrations/in order - Enable Auth providers:
- Email (with email verification)
- Apple (configure with your Apple Developer credentials)
- Google (configure with your Google Cloud credentials)
- Create storage buckets:
avatars(public)photos(public)attachments(public)
- Create a RevenueCat account at revenuecat.com
- Set up your app and products
- Create entitlements (e.g., "Builder Help Plus")
- Add your API keys to environment variables
Copy the example environment file:
cp .env.example .envFill in your values (see Environment Variables section).
# Start Expo development server
npx expo start
# Run on iOS Simulator
npx expo run:ios
# Run on Android Emulator
npx expo run:android
# Run with development build
npx expo start --dev-clientvanlife-app/
├── app/ # Expo Router screens (file-based routing)
│ ├── _layout.tsx # Root layout with providers
│ ├── +not-found.tsx # 404 screen
│ │
│ ├── (auth)/ # Auth group (unauthenticated users)
│ │ ├── _layout.tsx # Auth layout
│ │ ├── welcome.tsx # Welcome/landing screen
│ │ ├── sign-in.tsx # Sign in screen
│ │ ├── sign-up.tsx # Sign up screen
│ │ ├── forgot-password.tsx # Password reset
│ │ └── onboarding.tsx # Profile setup wizard
│ │
│ ├── (tabs)/ # Main tab navigator
│ │ ├── _layout.tsx # Tab bar configuration
│ │ ├── match/ # Discovery/matching
│ │ │ └── index.tsx
│ │ ├── activities/ # Activities list
│ │ │ └── index.tsx
│ │ ├── messages/ # Conversations list
│ │ │ └── index.tsx
│ │ └── builder/ # Q&A forum
│ │ └── index.tsx
│ │
│ ├── profile/ # Profile screens
│ │ ├── [id].tsx # View profile
│ │ ├── edit.tsx # Edit profile
│ │ └── photos.tsx # Manage photos
│ │
│ ├── activities/ # Activity detail screens
│ │ ├── [id].tsx # Activity details
│ │ ├── create.tsx # Create activity
│ │ └── edit.tsx # Edit activity
│ │
│ ├── messages/ # Chat screens
│ │ └── [id].tsx # Chat conversation
│ │
│ ├── builder/ # Builder help screens
│ │ ├── [id].tsx # Question details
│ │ └── ask.tsx # Ask question
│ │
│ ├── settings/ # Settings screens
│ │ ├── index.tsx # Settings menu
│ │ ├── privacy.tsx # Privacy settings
│ │ ├── notifications.tsx # Notification preferences
│ │ ├── blocked.tsx # Blocked users
│ │ ├── verification.tsx # Identity verification
│ │ └── help.tsx # Help & feedback
│ │
│ └── subscription.tsx # Premium subscription paywall
│
├── components/ # Reusable components
│ ├── ui/ # Base UI components
│ │ ├── Button.tsx
│ │ ├── Input.tsx
│ │ ├── Card.tsx
│ │ ├── Avatar.tsx
│ │ ├── Badge.tsx
│ │ ├── Chip.tsx
│ │ ├── EmptyState.tsx
│ │ ├── LoadingSpinner.tsx
│ │ ├── BottomSheet.tsx
│ │ ├── CircularCarousel.tsx
│ │ ├── ScaleCarousel.tsx
│ │ ├── ParallaxCarousel.tsx
│ │ ├── GlassProgressBar.tsx
│ │ ├── SafeGlassView.tsx
│ │ └── ...
│ │
│ ├── match/ # Match/discovery components
│ │ ├── CardStack.tsx # Swipeable card stack
│ │ ├── SwipeableCard.tsx # Individual swipe card
│ │ ├── ProfileCard.tsx # Profile display
│ │ ├── ActionButton.tsx # Like/Pass buttons
│ │ ├── MatchModal.tsx # Match celebration
│ │ ├── ModeToggle.tsx # Friends/Dating toggle
│ │ └── EndOfStack.tsx # Empty state
│ │
│ ├── builder/ # Builder help components
│ │ ├── AnswerCard.tsx # Answer display
│ │ ├── ReplyItem.tsx # Nested replies
│ │ └── AnswerActions.tsx # Vote/accept actions
│ │
│ ├── messages/ # Messaging components
│ │ ├── MessageBubble.tsx
│ │ ├── ChatInput.tsx
│ │ └── ConversationItem.tsx
│ │
│ ├── onboarding/ # Onboarding step components
│ │ ├── BasicsStepNew.tsx
│ │ ├── VanlifeStepNew.tsx
│ │ ├── InterestsStepNew.tsx
│ │ ├── PreferencesStepNew.tsx
│ │ └── PhotoStepNew.tsx
│ │
│ ├── notifications/ # Notification components
│ │ └── NotificationToast.tsx
│ │
│ └── molecules/ # Compound components
│ └── ...
│
├── store/ # Zustand state stores
│ ├── authStore.ts # Authentication state
│ ├── discoveryStore.ts # Match/discovery state
│ ├── discoveryLocationStore.ts # Discovery location state
│ ├── messagesStore.ts # Chat/messaging state
│ ├── activitiesStore.ts # Activities state
│ ├── builderStore.ts # Builder Q&A state
│ ├── profileStore.ts # Profile state
│ ├── settingsStore.ts # Settings state
│ ├── locationStore.ts # User location state
│ ├── notificationsStore.ts # In-app notifications
│ ├── purchasesStore.ts # RevenueCat subscriptions
│ └── pushNotificationsStore.ts # Push notification tokens
│
├── types/ # TypeScript type definitions
│ └── database.ts # Supabase database types
│
├── constants/ # App constants
│ ├── theme.ts # Colors, spacing, typography
│ └── topics.ts # Builder help topics
│
├── lib/ # Library utilities
│ ├── supabase/
│ │ └── client.ts # Supabase client initialization
│ └── haptics/
│ └── index.ts # Haptic feedback system
│
├── hooks/ # Custom React hooks
│ ├── useTheme.tsx # Theme context and hook
│ └── useLocationTracking.ts # Location permissions
│
├── supabase/ # Supabase configuration
│ ├── functions/ # Edge functions
│ └── migrations/ # Database migrations
│
├── scripts/ # Utility scripts
│ └── ...
│
├── assets/ # Static assets
│ ├── images/ # App icons, splash screens
│ └── fonts/ # Custom fonts
│
├── app.json # Expo app configuration
├── eas.json # EAS Build configuration
├── package.json # Dependencies
├── tsconfig.json # TypeScript configuration
└── .env.example # Environment variables template
┌─────────────────────────────────────────────────────────────┐
│ Root Layout │
│ (_layout.tsx) │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Auth Check │ │
│ │ │ │
│ │ Not Authenticated? ──────► (auth) Group │ │
│ │ ├── welcome │ │
│ │ ├── sign-in │ │
│ │ ├── sign-up │ │
│ │ ├── forgot-password │ │
│ │ └── onboarding │ │
│ │ │ │
│ │ Authenticated? ──────────► (tabs) Group │ │
│ │ ├── match │ │
│ │ ├── activities │ │
│ │ ├── messages │ │
│ │ └── builder │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Modal Screens (accessible from anywhere): │
│ ├── profile/[id] │
│ ├── profile/edit │
│ ├── activities/[id] │
│ ├── activities/create │
│ ├── messages/[id] │
│ ├── builder/[id] │
│ ├── builder/ask │
│ ├── settings/* │
│ └── subscription (modal presentation) │
└─────────────────────────────────────────────────────────────┘
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Component │────►│ Zustand Store│────►│ Supabase │
│ │◄────│ │◄────│ │
└──────────────┘ └──────────────┘ └──────────────┘
│
▼
┌──────────────┐
│ Realtime │
│ Subscriptions│
└──────────────┘
User profile information including vanlife-specific details.
| Column | Type | Description |
|---|---|---|
| id | uuid | Primary key (references auth.users) |
| full_name | text | Display name |
| bio | text | Profile bio |
| avatar_url | text | Profile picture URL |
| date_of_birth | date | Birthday |
| gender | text | Gender identity |
| rig_type | text | Vehicle type (van, bus, RV, etc.) |
| travel_pace | text | Full-time, part-time, weekender |
| latitude | float | Current latitude |
| longitude | float | Current longitude |
| location_name | text | Location display name |
| discovery_mode | text | friends, dating, or both |
| is_verified | boolean | Verification status |
| is_online | boolean | Online status |
| last_online | timestamp | Last activity |
| created_at | timestamp | Account creation |
User photo gallery.
| Column | Type | Description |
|---|---|---|
| id | uuid | Primary key |
| user_id | uuid | Owner |
| url | text | Image URL |
| album_id | uuid | Album reference |
| is_primary | boolean | Main profile photo |
| sort_order | int | Display order |
| created_at | timestamp | Upload date |
Discovery likes and passes.
| Column | Type | Description |
|---|---|---|
| id | uuid | Primary key |
| swiper_id | uuid | User who swiped |
| swiped_id | uuid | User who was swiped on |
| direction | text | like, pass, super_like |
| mode | text | friends or dating |
| created_at | timestamp | Swipe time |
Mutual matches between users.
| Column | Type | Description |
|---|---|---|
| id | uuid | Primary key |
| user1_id | uuid | First user |
| user2_id | uuid | Second user |
| mode | text | friends or dating |
| created_at | timestamp | Match time |
Conversation metadata.
| Column | Type | Description |
|---|---|---|
| id | uuid | Primary key |
| match_id | uuid | Associated match |
| last_message_at | timestamp | Last activity |
| created_at | timestamp | Chat creation |
Chat messages.
| Column | Type | Description |
|---|---|---|
| id | uuid | Primary key |
| chat_id | uuid | Conversation |
| sender_id | uuid | Message author |
| content | text | Message text |
| message_type | text | text, image, voice, location, system |
| attachment_url | text | Media URL |
| is_read | boolean | Read status |
| created_at | timestamp | Send time |
Events and meetups.
| Column | Type | Description |
|---|---|---|
| id | uuid | Primary key |
| creator_id | uuid | Event host |
| title | text | Activity name |
| description | text | Details |
| category | text | Activity type |
| location_name | text | Venue |
| latitude | float | Location lat |
| longitude | float | Location lng |
| start_time | timestamp | Event start |
| end_time | timestamp | Event end |
| max_attendees | int | Capacity |
| is_public | boolean | Visibility |
| created_at | timestamp | Creation date |
Q&A questions.
| Column | Type | Description |
|---|---|---|
| id | uuid | Primary key |
| author_id | uuid | Question author |
| topic_id | uuid | Category |
| title | text | Question title |
| body | text | Question details |
| view_count | int | Views |
| is_answered | boolean | Has accepted answer |
| created_at | timestamp | Post date |
Q&A answers and replies.
| Column | Type | Description |
|---|---|---|
| id | uuid | Primary key |
| question_id | uuid | Parent question |
| parent_id | uuid | Parent answer (for replies) |
| author_id | uuid | Answer author |
| body | text | Answer content |
| is_accepted | boolean | Accepted answer |
| vote_count | int | Net votes |
| created_at | timestamp | Post date |
| Function | Purpose |
|---|---|
get_discovery_profiles |
Complex profile discovery with filtering |
get_second_chance_profiles |
Retrieve previously passed profiles |
increment_question_views |
Track question views |
update_user_online_status |
Presence management |
use_invite_code |
Referral system |
// Sign up
await supabase.auth.signUp({
email: 'user@example.com',
password: 'securepassword',
});
// Sign in
await supabase.auth.signInWithPassword({
email: 'user@example.com',
password: 'securepassword',
});const credential = await AppleAuthentication.signInAsync({
requestedScopes: [
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
AppleAuthentication.AppleAuthenticationScope.EMAIL,
],
});
await supabase.auth.signInWithIdToken({
provider: 'apple',
token: credential.identityToken,
});await GoogleSignin.hasPlayServices();
const userInfo = await GoogleSignin.signIn();
await supabase.auth.signInWithIdToken({
provider: 'google',
token: userInfo.idToken,
});- Unauthenticated: User sees Welcome screen
- Sign In/Up: User authenticates via chosen method
- Profile Check: If
full_nameis missing, redirect to Onboarding - Onboarding: 5-step profile creation wizard
- Authenticated: User accesses main tab navigator
| Component | Description |
|---|---|
Button |
Primary/secondary/ghost buttons with loading states |
Input |
Text inputs with validation and error states |
Card |
Elevated/flat card containers |
Avatar |
User avatars with verification badges |
Badge |
Category and status badges |
Chip |
Selectable tag chips for filters |
EmptyState |
Illustrated empty state placeholders |
LoadingSpinner |
Activity indicators |
BottomSheet |
Modal bottom sheets |
CircularCarousel |
Circular image carousel |
ParallaxCarousel |
Parallax scrolling carousel |
GlassProgressBar |
iOS glass-effect progress indicator |
SafeGlassView |
Cross-platform blur effect wrapper |
| Component | Description |
|---|---|
CardStack |
Manages stack of swipeable profile cards |
SwipeableCard |
Individual card with pan gesture handling |
ProfileCard |
Profile information display |
ActionButton |
Like/Pass/Super Like action buttons |
MatchModal |
Celebration modal for mutual matches |
ModeToggle |
Friends/Dating mode switcher |
EndOfStack |
Empty state when no profiles available |
CardSkeleton |
Loading skeleton for cards |
The app uses Zustand for state management with domain-specific stores:
| Store | Purpose | Key State |
|---|---|---|
authStore |
Authentication | user, session, profile, isLoading |
discoveryStore |
Matching | profiles, currentIndex, matches, mode |
discoveryLocationStore |
Discovery location | coordinates, locationName, radius |
messagesStore |
Messaging | conversations, messages, typing |
activitiesStore |
Activities | activities, selectedActivity, filters |
builderStore |
Q&A | questions, answers, topics, filters |
profileStore |
Profile viewing | profile, photos, isLoading |
settingsStore |
Settings | privacy, notifications, blocked |
locationStore |
User location | coordinates, permissions |
purchasesStore |
Subscriptions | offerings, entitlements, isPremium |
notificationsStore |
In-app notifications | notifications, unreadCount |
pushNotificationsStore |
Push tokens | expoPushToken, permission |
// Using a store in a component
import { useDiscoveryStore } from '@/store/discoveryStore';
function MatchScreen() {
const { profiles, currentIndex, like, pass, loadProfiles } = useDiscoveryStore();
useEffect(() => {
loadProfiles();
}, []);
const handleLike = () => {
like(profiles[currentIndex].id);
};
// ...
}The app uses a centralized theme system defined in constants/theme.ts:
export const Colors = {
light: {
primary: '#16a34a', // Green
background: '#ffffff',
surface: '#f5f5f5',
text: '#1a1a1a',
textSecondary: '#666666',
border: '#e5e5e5',
error: '#ef4444',
success: '#22c55e',
warning: '#f59e0b',
},
dark: {
primary: '#22c55e',
background: '#0a0a0a',
surface: '#1a1a1a',
text: '#ffffff',
textSecondary: '#a3a3a3',
border: '#333333',
error: '#f87171',
success: '#4ade80',
warning: '#fbbf24',
},
};
export const Spacing = {
xs: 4,
sm: 8,
md: 16,
lg: 24,
xl: 32,
xxl: 48,
};
export const FontSizes = {
xs: 12,
sm: 14,
md: 16,
lg: 18,
xl: 24,
xxl: 32,
};
export const BorderRadius = {
sm: 4,
md: 8,
lg: 12,
xl: 16,
full: 9999,
};import { useTheme } from '@/hooks/useTheme';
function MyComponent() {
const { colors, isDark } = useTheme();
return (
<View style={{ backgroundColor: colors.background }}>
<Text style={{ color: colors.text }}>Hello</Text>
</View>
);
}- Light Mode: Default light theme
- Dark Mode: Dark theme for low-light conditions
- System: Follows device system preference
The app uses @renegades/react-native-tickle for comprehensive haptic feedback:
| Pattern | Usage |
|---|---|
light |
Subtle taps, toggles |
medium |
Button presses |
heavy |
Important actions |
success |
Successful operations |
warning |
Warnings |
error |
Errors |
selection |
Selection changes |
import { useHaptics } from '@/lib/haptics';
function MyComponent() {
const { trigger } = useHaptics();
const handlePress = () => {
trigger('medium');
// ... action
};
return <Button onPress={handlePress}>Press Me</Button>;
}Managed through RevenueCat:
| Plan | Duration | Features |
|---|---|---|
| Weekly | 7 days | Builder Help Plus |
| Monthly | 30 days | Builder Help Plus |
| Quarterly | 90 days | Builder Help Plus |
| Yearly | 365 days | Builder Help Plus (best value) |
- Ask Questions: Post detailed questions to the community
- Priority Support: Featured question placement
- No Ads: Ad-free experience (future feature)
import { usePurchasesStore } from '@/store/purchasesStore';
function PremiumFeature() {
const { isPremium } = usePurchasesStore();
if (!isPremium) {
return <UpgradePrompt />;
}
return <PremiumContent />;
}Create a .env file with the following variables:
# Supabase Configuration (Required)
EXPO_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
EXPO_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
# RevenueCat (Required for premium features)
EXPO_PUBLIC_REVENUECAT_API_KEY_IOS=your-ios-key
EXPO_PUBLIC_REVENUECAT_API_KEY_ANDROID=your-android-key
# Google Sign-In (Required for Google auth)
EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID=your-web-client-id
EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID=your-ios-client-id
# Apple Sign-In (configured in app.json)
# Uses native Expo Apple Authentication
# Optional
EXPO_PUBLIC_APP_ENV=development # development | staging | production# Start development server
npm start
# Start with cache cleared
npm start -- --clear
# Run on iOS
npm run ios
# Run on Android
npm run android
# Run type checking
npm run typecheck
# Run linting
npm run lint# Build for iOS (EAS)
eas build --platform ios
# Build for Android (EAS)
eas build --platform android
# Build for both platforms
eas build --platform all# Generate TypeScript types from Supabase
supabase gen types typescript --project-id your-project-id > types/database.ts
# Run migrations locally
supabase db push
# Reset local database
supabase db resetWe welcome contributions! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Use TypeScript for all new code
- Follow existing patterns and conventions
- Add types for all props and state
- Use functional components with hooks
- Keep components small and focused
Follow conventional commits:
feat:New featuresfix:Bug fixesdocs:Documentation changesstyle:Code style changesrefactor:Code refactoringtest:Test additions/changeschore:Maintenance tasks
MIT License - see LICENSE file for details.
- Quin Gable — For the inspiring brief and vision for this app
- RevenueCat — For organizing Shipyard and providing subscription infrastructure
- Shipyard 2026 — For the opportunity to build something meaningful
- Expo — For the amazing React Native development platform
- Supabase — For the backend infrastructure
Built with ❤️ for the van-life community