Loading portfolio...
Loading portfolio...
A "Tinder for massage bookings" marketplace targeting Thailand. Users swipe through studio cards, book instantly via real-time Socket.IO events, and chat with owners in auto-translated EN/TH conversations — all powered by Firebase & Google Cloud Run.
How Luci connects massage seekers with studio owners in Thailand
What Luci delivers to the Thai massage marketplace
End-to-end data flow of the Luci platform
Two separate singleton connections — one for chat, one for bookings
Complete user journey from first launch to active marketplace user
Multi-step progressive profiling with smart route resumption
The main marketplace experience
Swipe deck with PanResponder. Studio cards show name, services, distance, rating. 5 fallback sample studios for empty state.
Real-time 1:1 messaging via Socket.IO. Messages auto-translated between EN/TH with cache-first strategy. Typing indicators.
Conversation list showing all active chat rooms. Unread badges, last message preview, timestamps with relative formatting.
Push notification center for booking confirmations, new messages, and system alerts. Grouped by date with read/unread states.
User preferences, language toggle (EN/TH), account management, and logout. Tablet-aware responsive layout with breakpoints.
User profile with photo upload to Firebase Storage, display name, contact info, and booking history. Progressive profiling data.
4 custom scaling functions for pixel-perfect adaptive UI
scale(size)Horizontal scaling relative to a base width. Keeps element proportions across screen widths.
verticalScale(size)Vertical scaling relative to base height. Adapts spacing & heights for tall/short screens.
moderateScale(size, factor)Blended horizontal scale with an adjustable dampening factor. Prevents extreme scaling.
scaleFont(size)Font-specific scaling using moderateScale. Ensures readable text on all device sizes.
From swipe to confirmed booking — every step in the real-time flow
PanResponder detects right-swipe gesture on studio card. Triggers booking request event on the Booking Socket.
Booking Socket singleton sends 'booking:request' event to the Node.js backend on Cloud Run.
Backend creates a pending booking document in Firestore with user/studio IDs, timestamp, and status.
Studio owner receives real-time push notification via Socket.IO with booking details and accept/decline options.
Owner taps Accept — Booking Socket broadcasts 'booking:confirmed' event. Firestore document status updated.
Backend auto-creates a new Firestore chat room document linking the user and studio owner for direct messaging.
Both parties now have a real-time chat channel via the Chat Socket. Messages auto-translated between EN/TH.
User arrives for massage session. Booking status marked as completed in Firestore. Rating prompt shown.
PanResponder-based card stack with graceful degradation
Bilingual messaging with intelligent caching to minimize API calls
Named design patterns applied throughout the Luci codebase
Both Socket.IO connections (chat & booking) use the Singleton pattern — ensuring exactly one connection instance per session, preventing duplicate event listeners.
Data access abstracted behind repository classes. Firestore reads/writes are encapsulated, allowing easy swap of data sources without touching UI code.
React Context wraps auth state, user profile, and socket instances. Child components consume context without prop-drilling across the navigation tree.
Runtime toggleable features (e.g., translation, fallback studios). Allows gradual rollout and A/B testing without redeployment.
5 fallback sample studios when API is unavailable. App never shows an empty state — always functional, even offline-first for cached data.
Booking confirmations and message sends update the UI immediately before server acknowledgment. Rollback on failure with user-friendly error toast.
Translated strings stored in local cache. MyMemory API called only on cache miss, reducing API calls by ~70% for repeated phrases.
Onboarding collects minimal data first (email + OTP), then progressively asks for profile photo, name, and location across separate screens.
Persisted onboarding state in AsyncStorage. If a user drops off mid-funnel, the app resumes exactly where they left off on next launch.
Socket.IO event listeners follow the Observer pattern. Components subscribe to specific events and auto-unsubscribe on unmount to prevent memory leaks.
Technologies powering the Luci marketplace
Hard problems tackled during Luci's development
Chat and booking events needed separate connection lifecycles. Solved with two independent Singleton Socket.IO instances, each with its own reconnection strategy.
PanResponder on Android had inconsistent fling detection. Implemented velocity thresholds + distance fallbacks ensuring consistent swipe recognition.
MyMemory free tier has request caps. Built a cache-first strategy with AsyncStorage persistence, reducing API calls by ~70% for common phrases.
Users abandon mid-onboarding frequently. Persisted exact funnel position in AsyncStorage with smart route resumption on next app launch.
Keeping Firestore documents in sync with real-time Socket.IO events without duplication or race conditions. Implemented idempotent event handlers.
From iPhone SE to iPad Pro — built 4 custom scaling functions (scale, verticalScale, moderateScale, scaleFont) for pixel-perfect adaptive layouts.
Protecting user data and platform integrity
I build real-time marketplace apps with the same architectural rigor — Socket.IO, Firebase, responsive design systems, and production-grade deployment. Let's discuss your project.