Skip to content

A gift management application. Built with React Native, Expo, and Firebase.

Notifications You must be signed in to change notification settings

brroMonta/gifting

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

13 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🎁 Luxury Gifting App

A gift management application designed to help you track, organize, and remember gifts for the special people in your life. Built with React Native and Expo.

License React Native Expo TypeScript

✨ Features

πŸ‘₯ People Management

  • Add and organize people you buy gifts for
  • Track birthdays (with optional year for age calculation)
  • Store relationship types and personal notes
  • Group people into categories (Family, Friends, Colleagues)

🎁 Gift Maps

  • Create private gift idea lists for each person
  • Add gift ideas with names, URLs, and notes
  • Share gift maps with family and friends via unique links
  • Others can reserve items anonymously (perfect for coordinated gifting)
  • Real-time updates when items are reserved

πŸ“… Event Tracking

  • Track birthdays, holidays, anniversaries, and custom events
  • Four-stage gift status workflow: Idea β†’ Shopping β†’ Bought β†’ Delivered
  • View upcoming events at a glance
  • Automatic countdown to important dates

πŸ”” Smart Notifications

  • Local push notifications for upcoming events
  • Automated reminders at 30 days, 7 days, and 1 day before events
  • Configurable per-event notification preferences

πŸ“– Gift History

  • Record past gifts (both given and received)
  • Attach photos and receipts
  • Add product links and notes
  • Search and filter by person or direction (gave/received)
  • Avoid duplicate gifts by referencing history

πŸ—‚οΈ Groups

  • Organize people into custom groups
  • Bidirectional relationship management
  • Quick access to all group members

πŸ“€ Calendar Export

  • One-way export to device calendar
  • Keep gift events synchronized with your schedule

🎨 Design System

Luxury Aesthetic

Inspired by high-end jewelry brands (Cartier, Rolex, Tiffany's):

Color Palette:

  • Primary: Deep charcoal navy (#1a1f2e)
  • Accent: Champagne gold (#c9a961)
  • Rose Gold: Available for special highlights
  • Background: Warm cream/ivory (#fafaf9)
  • Surface: Pure white with elevated cream variations

Typography:

  • Light weights (300-400) for elegance
  • Generous letter spacing for refinement
  • Uppercase buttons with extended tracking
  • Small caps labels

Details:

  • Minimal border radius (4-12px)
  • Subtle shadows (0.04-0.08 opacity)
  • Gold accent borders and dividers
  • Sophisticated spacing scale

πŸ› οΈ Tech Stack

Frontend

  • React Native - Cross-platform mobile framework
  • Expo - Development platform and toolchain
  • TypeScript - Type safety and better DX
  • Expo Router - File-based navigation

State Management

  • Zustand - Lightweight state management
  • Custom hooks for feature-specific logic

Backend & Services

  • Firebase Authentication - User auth with email/password
  • Cloud Firestore - Real-time NoSQL database
  • Firebase Storage - Photo and file storage
  • Expo Notifications - Local push notifications
  • Expo Calendar - Device calendar integration

Development Tools

  • React Hook Form - Form validation and management
  • date-fns - Date manipulation and formatting

πŸ“ Project Structure

gifting/
β”œβ”€β”€ app/                          # Expo Router screens
β”‚   β”œβ”€β”€ (auth)/                   # Authentication screens
β”‚   β”‚   β”œβ”€β”€ login.tsx
β”‚   β”‚   └── register.tsx
β”‚   β”œβ”€β”€ (tabs)/                   # Main app tabs
β”‚   β”‚   β”œβ”€β”€ index.tsx             # Home/Dashboard
β”‚   β”‚   β”œβ”€β”€ people/
β”‚   β”‚   β”‚   β”œβ”€β”€ index.tsx         # People list
β”‚   β”‚   β”‚   β”œβ”€β”€ [id].tsx          # Person detail
β”‚   β”‚   β”‚   └── add.tsx           # Add person
β”‚   β”‚   β”œβ”€β”€ events.tsx            # Events list
β”‚   β”‚   └── history.tsx           # History list
β”‚   β”œβ”€β”€ gift-map/
β”‚   β”‚   └── [personId].tsx        # Gift map (owner view)
β”‚   β”œβ”€β”€ shared/
β”‚   β”‚   └── [shareToken].tsx      # Shared gift map (public)
β”‚   └── groups/
β”‚       β”œβ”€β”€ index.tsx             # Groups list
β”‚       └── [id].tsx              # Group detail
β”‚
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ components/               # Reusable components
β”‚   β”‚   β”œβ”€β”€ ui/                   # Base UI components
β”‚   β”‚   β”‚   β”œβ”€β”€ Button.tsx
β”‚   β”‚   β”‚   β”œβ”€β”€ Input.tsx
β”‚   β”‚   β”‚   β”œβ”€β”€ Loading.tsx
β”‚   β”‚   β”‚   └── EmptyState.tsx
β”‚   β”‚   β”œβ”€β”€ people/
β”‚   β”‚   β”‚   β”œβ”€β”€ PersonCard.tsx
β”‚   β”‚   β”‚   └── PersonForm.tsx
β”‚   β”‚   β”œβ”€β”€ gift-maps/
β”‚   β”‚   β”‚   β”œβ”€β”€ GiftItemCard.tsx
β”‚   β”‚   β”‚   β”œβ”€β”€ GiftItemForm.tsx
β”‚   β”‚   β”‚   └── ShareLinkModal.tsx
β”‚   β”‚   β”œβ”€β”€ events/
β”‚   β”‚   β”‚   β”œβ”€β”€ EventCard.tsx
β”‚   β”‚   β”‚   └── EventForm.tsx
β”‚   β”‚   β”œβ”€β”€ history/
β”‚   β”‚   β”‚   β”œβ”€β”€ HistoryCard.tsx
β”‚   β”‚   β”‚   └── HistoryForm.tsx
β”‚   β”‚   └── groups/
β”‚   β”‚       β”œβ”€β”€ GroupCard.tsx
β”‚   β”‚       └── GroupForm.tsx
β”‚   β”‚
β”‚   β”œβ”€β”€ store/                    # Zustand stores
β”‚   β”‚   β”œβ”€β”€ authStore.ts
β”‚   β”‚   β”œβ”€β”€ peopleStore.ts
β”‚   β”‚   β”œβ”€β”€ giftMapsStore.ts
β”‚   β”‚   β”œβ”€β”€ eventsStore.ts
β”‚   β”‚   β”œβ”€β”€ historyStore.ts
β”‚   β”‚   └── groupsStore.ts
β”‚   β”‚
β”‚   β”œβ”€β”€ repositories/             # Data access layer
β”‚   β”‚   β”œβ”€β”€ person.repository.ts
β”‚   β”‚   β”œβ”€β”€ giftMap.repository.ts
β”‚   β”‚   β”œβ”€β”€ event.repository.ts
β”‚   β”‚   β”œβ”€β”€ history.repository.ts
β”‚   β”‚   └── group.repository.ts
β”‚   β”‚
β”‚   β”œβ”€β”€ services/                 # External services
β”‚   β”‚   β”œβ”€β”€ firebase.ts
β”‚   β”‚   β”œβ”€β”€ firestore.service.ts
β”‚   β”‚   β”œβ”€β”€ storage.service.ts
β”‚   β”‚   β”œβ”€β”€ notifications.service.ts
β”‚   β”‚   └── calendar.service.ts
β”‚   β”‚
β”‚   β”œβ”€β”€ hooks/                    # Custom React hooks
β”‚   β”‚   β”œβ”€β”€ useAuth.ts
β”‚   β”‚   β”œβ”€β”€ usePeople.ts
β”‚   β”‚   β”œβ”€β”€ useGiftMaps.ts
β”‚   β”‚   β”œβ”€β”€ useEvents.ts
β”‚   β”‚   β”œβ”€β”€ useHistory.ts
β”‚   β”‚   └── useGroups.ts
β”‚   β”‚
β”‚   β”œβ”€β”€ types/                    # TypeScript types
β”‚   β”‚   β”œβ”€β”€ person.ts
β”‚   β”‚   β”œβ”€β”€ giftMap.ts
β”‚   β”‚   β”œβ”€β”€ giftEvent.ts
β”‚   β”‚   β”œβ”€β”€ giftHistory.ts
β”‚   β”‚   └── group.ts
β”‚   β”‚
β”‚   β”œβ”€β”€ utils/                    # Utility functions
β”‚   β”‚   β”œβ”€β”€ date.ts
β”‚   β”‚   β”œβ”€β”€ validation.ts
β”‚   β”‚   └── constants.ts
β”‚   β”‚
β”‚   └── styles/                   # Design system
β”‚       └── theme.ts              # Luxury theme
β”‚
β”œβ”€β”€ assets/                       # Static assets
β”œβ”€β”€ package.json
β”œβ”€β”€ tsconfig.json
β”œβ”€β”€ app.json                      # Expo configuration
└── README.md

πŸ—„οΈ Data Model

Firestore Structure

users/{userId}/
β”œβ”€β”€ people/{personId}
β”‚   β”œβ”€β”€ name: string
β”‚   β”œβ”€β”€ relationship: string
β”‚   β”œβ”€β”€ birthdayMonth: number (1-12)
β”‚   β”œβ”€β”€ birthdayDay: number (1-31)
β”‚   β”œβ”€β”€ birthdayYear: number | null
β”‚   β”œβ”€β”€ notes: string
β”‚   β”œβ”€β”€ groupIds: string[]
β”‚   β”œβ”€β”€ createdAt: timestamp
β”‚   └── updatedAt: timestamp
β”‚
β”œβ”€β”€ gift_maps/{giftMapId}
β”‚   β”œβ”€β”€ personId: string
β”‚   β”œβ”€β”€ personName: string (denormalized)
β”‚   β”œβ”€β”€ shareToken: string | null
β”‚   β”œβ”€β”€ isShared: boolean
β”‚   β”œβ”€β”€ items: Array<{
β”‚   β”‚     id: string
β”‚   β”‚     name: string
β”‚   β”‚     url: string | null
β”‚   β”‚     notes: string | null
β”‚   β”‚     isReserved: boolean
β”‚   β”‚     reservedAt: timestamp | null
β”‚   β”‚     order: number
β”‚   β”‚   }>
β”‚   β”œβ”€β”€ createdAt: timestamp
β”‚   └── updatedAt: timestamp
β”‚
β”œβ”€β”€ gift_events/{eventId}
β”‚   β”œβ”€β”€ personId: string
β”‚   β”œβ”€β”€ personName: string (denormalized)
β”‚   β”œβ”€β”€ eventType: 'birthday' | 'holiday' | 'custom'
β”‚   β”œβ”€β”€ eventDate: timestamp
β”‚   β”œβ”€β”€ status: 'idea' | 'shopping' | 'bought' | 'delivered'
β”‚   β”œβ”€β”€ remindersEnabled: boolean
β”‚   β”œβ”€β”€ notes: string | null
β”‚   β”œβ”€β”€ createdAt: timestamp
β”‚   └── updatedAt: timestamp
β”‚
β”œβ”€β”€ gift_history/{historyId}
β”‚   β”œβ”€β”€ personId: string
β”‚   β”œβ”€β”€ personName: string (denormalized)
β”‚   β”œβ”€β”€ giftName: string
β”‚   β”œβ”€β”€ direction: 'gave' | 'received'
β”‚   β”œβ”€β”€ date: timestamp
β”‚   β”œβ”€β”€ notes: string | null
β”‚   β”œβ”€β”€ link: string | null
β”‚   β”œβ”€β”€ photoUrl: string | null
β”‚   β”œβ”€β”€ createdAt: timestamp
β”‚   └── updatedAt: timestamp
β”‚
└── groups/{groupId}
    β”œβ”€β”€ name: string
    β”œβ”€β”€ memberIds: string[] (person IDs)
    β”œβ”€β”€ createdAt: timestamp
    └── updatedAt: timestamp

shared_gift_maps/{shareToken}/    # Public collection
β”œβ”€β”€ userId: string
β”œβ”€β”€ giftMapId: string
β”œβ”€β”€ personName: string
β”œβ”€β”€ items: Array<{...}>
└── updatedAt: timestamp

Key Design Decisions

Gift Map Items as Array: Embedded array for better performance and offline support

Denormalization: Person names stored in events/history/maps to avoid complex queries

Shared Maps Duplication: Separate collection for public access without authentication

Bidirectional Relationships: Groups maintain memberIds, persons maintain groupIds

πŸš€ Getting Started

Prerequisites

  • Node.js (v16 or higher)
  • npm or yarn
  • Expo CLI: npm install -g expo-cli
  • iOS Simulator (macOS) or Android Emulator
  • Expo Go app (for testing on physical devices)

Firebase Setup

  1. Create a Firebase project at console.firebase.google.com
  2. Enable Authentication with Email/Password provider
  3. Create a Firestore Database in production mode
  4. Enable Firebase Storage for photo uploads
  5. Copy your Firebase config

Installation

  1. Clone the repository:

    git clone https://github.com/yourusername/gifting-app.git
    cd gifting-app
  2. Install dependencies:

    npm install
  3. Configure Firebase:

    Create src/config/firebase.config.ts:

    export const firebaseConfig = {
      apiKey: "YOUR_API_KEY",
      authDomain: "YOUR_AUTH_DOMAIN",
      projectId: "YOUR_PROJECT_ID",
      storageBucket: "YOUR_STORAGE_BUCKET",
      messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
      appId: "YOUR_APP_ID"
    };
  4. Deploy Firestore Security Rules:

    In Firebase Console β†’ Firestore β†’ Rules:

    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        // Users can only access their own data
        match /users/{userId} {
          allow read, write: if request.auth != null && request.auth.uid == userId;
    
          match /{document=**} {
            allow read, write: if request.auth != null && request.auth.uid == userId;
          }
        }
    
        // Shared gift maps are publicly readable
        match /shared_gift_maps/{shareToken} {
          allow read: if true;
          allow write: if request.auth != null && request.auth.uid == resource.data.userId;
        }
      }
    }
  5. Deploy Storage Security Rules:

    In Firebase Console β†’ Storage β†’ Rules:

    rules_version = '2';
    service firebase.storage {
      match /b/{bucket}/o {
        match /users/{userId}/history_photos/{photoId} {
          allow read, write: if request.auth != null && request.auth.uid == userId;
        }
      }
    }
  6. Start the development server:

    npx expo start
  7. Run on a device:

    • Scan QR code with Expo Go app (iOS/Android)
    • Press i for iOS Simulator
    • Press a for Android Emulator

πŸ“± Usage

Creating Your First Person

  1. Tap the People tab
  2. Tap + Add button
  3. Fill in name and relationship
  4. Optionally add birthday (month, day, year)
  5. Add notes about preferences, sizes, etc.
  6. Tap Add Person

Creating a Gift Map

  1. Open a person's detail page
  2. Tap Gift Map
  3. Tap + Add Gift Item
  4. Add gift name, URL, and notes
  5. Save the item

Sharing a Gift Map

  1. Open a gift map
  2. Tap Share Gift Map
  3. Copy the share link
  4. Send to family/friends via text or email
  5. Recipients can view and reserve items anonymously

Setting Up Event Reminders

  1. Go to Events tab
  2. Tap + Add
  3. Select person and event type
  4. Choose date
  5. Enable Reminders
  6. You'll receive notifications at 30d, 7d, and 1d before

Recording Gift History

  1. Go to History tab
  2. Tap + Add
  3. Select person
  4. Enter gift name and direction (gave/received)
  5. Add photo, link, and notes
  6. Save

πŸ”§ Development

Available Scripts

# Start development server
npm start

# Start with cleared cache
npm start -- --clear

# Run on iOS
npm run ios

# Run on Android
npm run android

# Type checking
npm run type-check

# Lint code
npm run lint

Code Style

  • TypeScript for type safety
  • Functional components with hooks
  • Zustand for state management
  • Repository pattern for data access
  • Custom hooks for business logic

Adding a New Feature

  1. Define types in src/types/
  2. Create repository in src/repositories/
  3. Create Zustand store in src/store/
  4. Create custom hook in src/hooks/
  5. Build UI components in src/components/
  6. Create screen in app/

πŸ§ͺ Testing

Manual Testing Checklist

  • Sign up with email/password
  • Log in and log out
  • Create, edit, delete people
  • Add birthday with and without year
  • Create gift maps with items
  • Generate and test share links
  • Reserve items on shared maps
  • Create events with different types
  • Verify notifications trigger
  • Add history with photos
  • Create and manage groups
  • Add/remove members from groups
  • Export events to calendar
  • Test offline functionality

Testing Notifications

  1. Create an event for tomorrow
  2. Enable reminders
  3. Set device time forward 23 hours
  4. Verify notification appears

πŸ“¦ Building for Production

iOS

# Install EAS CLI
npm install -g eas-cli

# Configure EAS
eas build:configure

# Build for iOS
eas build --platform ios

# Submit to App Store
eas submit --platform ios

Android

# Build for Android
eas build --platform android

# Submit to Play Store
eas submit --platform android

πŸ” Security Considerations

Authentication

  • Email/password authentication via Firebase
  • No user data stored in AsyncStorage
  • Secure token management by Firebase SDK

Data Privacy

  • User data isolated per Firebase Auth UID
  • Firestore security rules enforce user boundaries
  • Shared gift maps use UUID tokens (not user IDs)

Best Practices

  • Never commit firebase.config.ts to version control
  • Use environment variables for sensitive data
  • Validate all user inputs
  • Sanitize URLs before opening

🀝 Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Commit Message Convention

feat: Add new feature
fix: Fix bug
docs: Update documentation
style: Format code
refactor: Refactor code
test: Add tests
chore: Update dependencies

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments

  • Design inspired by Cartier, Rolex, and Tiffany & Co.
  • Built with React Native and Expo
  • Backend powered by Firebase
  • Icons from Expo Icons

πŸ“ž Support

If you have any questions or need help:

πŸ—ΊοΈ Roadmap

  • Add web version using React Native Web
  • Implement push notifications (FCM)
  • Add social authentication (Google, Apple)
  • Create gift recommendations AI
  • Add collaborative shopping lists
  • Integrate with Amazon/shopping APIs
  • Add budget tracking
  • Create gift wrapping tracker
  • Add multi-language support
  • Dark mode

Made with ❀️ and attention to detail

About

A gift management application. Built with React Native, Expo, and Firebase.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •