TaloFix is a mobile application for housing companies, residents, maintenance teams, and service partners. It centralizes fault reporting, announcements, invite-based onboarding, and role-specific operations in one Expo React Native app backed by Firebase.
The project uses a strict MVVM architecture: UI screens call ViewModels, ViewModels call repositories, and repositories are the only layer allowed to access Firebase SDK or Cloud Functions.
- Introduction
- Features
- Technology Stack
- Installation \& Setup
- How to Run (Dev)
- Environment Variables
- Directory Structure
- Data \& Security Architecture
- Cloud Functions (Callable Operations)
- Role-based app flows: Admin, Housing Company, Resident, Maintenance / Property Manager, and Service Company.
- Invite-code onboarding: separate invite and registration flows for housing companies, residents, management, and service companies.
- Fault reports: create, edit (allowed fields), status updates, work logs, and image attachments.
- Announcements: role-gated create/update/delete, active/expired listing, pinned posts, and file attachments (images/PDF).
- Partner management: housing company can manage management/service company partner users.
- Localization: Finnish and English with i18next.
- Theming and settings: light/dark theme and language persisted via AsyncStorage.
- Firebase hybrid security: direct Firestore for simple reads/creates + Cloud Functions for privileged operations.
Mobile App: Expo SDK 54, React Native 0.81, React 19, TypeScript (strict)
UI: react-native-paper (Material Design 3), react-native-safe-area-context
Navigation: @react-navigation/native (native stack + bottom tabs)
State \& Forms: zustand, react-hook-form, zod
Localization: i18next, react-i18next, expo-localization
Backend: Firebase Auth, Firestore, Storage, Cloud Functions (Node 20, TypeScript)
Tooling: firebase-tools
- Node.js 18+ (Cloud Functions target Node 20)
- npm
- Firebase project (Auth, Firestore, Storage enabled)
- Expo Go or iOS/Android simulator
npm installCopy .env.example to .env at project root and fill your Firebase values:
EXPO_PUBLIC_FIREBASE_API_KEY=your_api_key_here
EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN=your_project_id.firebaseapp.com
EXPO_PUBLIC_FIREBASE_PROJECT_ID=your_project_id
EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET=your_project_id.appspot.com
EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_id
EXPO_PUBLIC_FIREBASE_APP_ID=your_app_idcd functions
npm installFrom project root:
# Firestore rules + indexes
firebase deploy --only firestore:rules,firestore:indexes
# Storage rules
firebase deploy --only storage
# Cloud Functions
firebase deploy --only functionsnpm run start:cleanUse Expo CLI shortcuts:
i= iOS simulatora= Android emulatorw= web
# Terminal 1 - app
npm install
npm run start
# Terminal 2 - functions (optional local emulator)
cd functions
npm install
npm run serve- App (Expo): URL shown by Expo CLI (LAN/tunnel/local)
- Functions emulator (optional): started by Firebase Emulator Suite
.env(project root, consumed by Expo app)EXPO_PUBLIC_FIREBASE_API_KEYEXPO_PUBLIC_FIREBASE_AUTH_DOMAINEXPO_PUBLIC_FIREBASE_PROJECT_IDEXPO_PUBLIC_FIREBASE_STORAGE_BUCKETEXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_IDEXPO_PUBLIC_FIREBASE_APP_ID
No additional runtime environment variables are required by the checked-in Cloud Functions source.
talofix-react-native/
├─ App.tsx
├─ app.json
├─ firebase.json
├─ firestore.indexes.json
├─ firestore.rules
├─ index.ts
├─ package.json
├─ storage.rules
├─ tsconfig.json
├─ assets/
├─ scripts/
├─ functions/
│ ├─ package.json
│ ├─ tsconfig.json
│ ├─ src/
│ │ ├─ index.ts
│ │ ├─ announcements.ts
│ │ ├─ faultReports.ts
│ │ ├─ housingCompanies.ts
│ │ ├─ managementInvites.ts
│ │ ├─ partnerManagement.ts
│ │ ├─ residentInvites.ts
│ │ ├─ serviceCompanyInvites.ts
│ │ ├─ userProfile.ts
│ │ └─ utils.ts
│ └─ lib/
└─ src/
├─ @types/
├─ app/
│ ├─ i18n/
│ │ └─ locales/
│ ├─ navigation/
│ ├─ providers/
│ └─ theme/
├─ data/
│ ├─ firebase/
│ ├─ models/
│ └─ repositories/
├─ features/
│ ├─ admin/
│ ├─ auth/
│ ├─ housingCompany/
│ ├─ maintenance/
│ ├─ resident/
│ ├─ serviceCompany/
│ └─ settings/
│
└─ shared/
├─ components/
├─ config/
├─ hooks/
├─ navigation/
├─ styles/
├─ types/
├─ utils/
└─ viewmodels/
- View (UI): feature screens under
src/features/**/views - ViewModel: Zustand-based state/actions under
src/features/**/viewmodels - Repository: only layer allowed to call Firebase SDK/Functions (
src/data/repositories)
Flow:
UI Screen -> ViewModel -> Repository -> Firestore / Storage / Cloud Functions
- Admin: creates and manages housing companies.
- Housing Company: manages residents/partners, announcements, and housing-company-level operations.
- Resident: creates and tracks own fault reports, reads announcements.
- Maintenance / Property Manager: manages fault lifecycle and announcements.
- Service Company: handles assigned maintenance workflows and work logs.
- Firestore rules scope access by
housingCompanyId. - Residents can create reports and update only allowed own-report fields while open.
- Announcement writes are blocked from clients and routed through Functions.
- Most privileged operations (status changes, partner/user lifecycle, invite operations, announcement mutations) run through callable Functions.
- Storage writes for report/announcement attachments are blocked from clients and handled by Functions.
All callable functions are deployed to region europe-west1.
-
Fault reports
uploadFaultReportImageaddWorkLogdeleteWorkLogupdateFaultReportStatusupdateFaultReportDetails
-
Announcements
publishAnnouncementupdateAnnouncementdeleteAnnouncementuploadAnnouncementAttachmentupdateAnnouncementWithAttachmentsdeleteAnnouncementAttachmentFile
-
Housing companies
createHousingCompanygenerateInviteCodedeleteHousingCompanyvalidateInviteCoderegisterWithInviteCodejoinWithInviteCode
-
Resident invites
generateResidentInviteCodevalidateResidentInviteCodegetResidentInviteCodesjoinWithResidentInviteCodedeleteResidentInvite
-
Management invites
generateManagementInviteCodevalidateManagementInviteCodejoinWithManagementInviteCode
-
Service company invites
generateServiceCompanyInviteCodevalidateServiceCompanyInviteCodejoinWithServiceCompanyInviteCode
-
Partner management
getManagementUsergetServiceCompanyUserremoveManagementUserremoveServiceCompanyUser
-
User profile
deleteResidentAccount
