A mobile-first community platform for the Sarajevo expat community
CS308 Software Engineering — International University of Sarajevo, Spring 2025–2026
Sarajevo Expats replaces fragmented WhatsApp coordination and manual Excel RSVP tracking with a structured, mobile-first platform. The app gives the Sarajevo expat community a single place for daily news, event discovery, RSVP management, real-time chat, explore listings, and personal profile tracking.
| Problem | Solution |
|---|---|
| WhatsApp group limits & noise | Structured news feed + dedicated chat rooms |
| Events buried in chat streams | Calendar with filtering, capacity visibility & countdowns |
| Manual RSVP via spreadsheets | In-app RSVP with automatic waitlist handling |
| No central place directory | Explore screen with places, real estate, services & trips |
- Daily News Hub — curated community news with live search filtering
- Event Discovery — browse, filter by date range or category, see capacity & countdowns
- RSVP & Waitlist — one-tap join with automatic waitlist placement when full
- Real-Time Chat — global community chat and per-event chat rooms via Socket.IO
- Explore — places, real estate, services, and trip listings with type-chip filtering
- User Profile — interests, attended events, edit personal details
- GM Admin Panel — create and manage events, inspect participant lists
- Submit Place / Real Estate — add community-relevant listings directly from the app
- Business Partnership — sponsorship and partnership enquiry flow
- Dark & Light theme — system-aware with manual toggle in Settings
- Dynamic Island / notch safe — all headers respect
useSafeAreaInsetson iOS 26+ - Offline-friendly — stale-while-revalidate cache returns data instantly even without network
- JWT auth — token stored in AsyncStorage, auto-attached to every request via Axios interceptor
| Technology | Version | Purpose |
|---|---|---|
| React Native | 0.81.5 | Core mobile framework |
| Expo | 54.0 | Build toolchain & native modules |
| TypeScript | 5.9 | Type safety across the entire codebase |
| React Navigation | 7.x | Stack + bottom-tab navigation |
| Axios | 1.13 | HTTP client with JWT interceptor |
| Socket.IO Client | 4.8 | Real-time bidirectional chat |
| AsyncStorage | 2.2 | Token persistence & TTL cache |
| TanStack Query | 5.0 | Server state management |
| date-fns | 3.6 | Date formatting & calendar logic |
| Expo Image Picker | 17.0 | Photo attachment for listings |
| react-native-maps | 1.27 | Map views in event & place screens |
| Technology | Purpose |
|---|---|
| Node.js + Express | REST API server |
| TypeScript | Typed route handlers and services |
| Socket.IO | Real-time chat rooms |
| Mongoose + MongoDB | Data models and persistence |
JWT (jsonwebtoken) |
Token signing and middleware |
| bcrypt | Password hashing |
| Helmet | Secure HTTP headers |
| CORS | Cross-origin policy |
| express-rate-limit | Brute-force protection |
| morgan | Request logging |
The
backend/folder is a local development server. The production backend is maintained separately by the backend team and reached viaEXPO_PUBLIC_API_URL.
Sarajevo Expats uses a Layered Client-Server Architecture with an event-driven real-time subsystem.
┌─────────────────────────────────────────────────────────┐
│ Presentation Layer │
│ React Native / Expo Mobile Client │
│ Screens · Components · Navigation · Theme Context │
└──────────────────┬──────────────────────────────────────┘
│ HTTPS + JSON / WSS + Socket.IO
┌──────────────────▼──────────────────────────────────────┐
│ Communication Layer │
│ Axios (REST) + Socket.IO Client │
│ JWT interceptor · TTL cache · AsyncStorage │
└──────────────────┬──────────────────────────────────────┘
│
┌──────────────────▼──────────────────────────────────────┐
│ Backend Application Layer │
│ Express API · Middleware · Controllers · Services │
│ Socket.IO Chat Handler │
└──────────────────┬──────────────────────────────────────┘
│
┌──────────────────▼──────────────────────────────────────┐
│ Data Access Layer │
│ EventRepository · ChatRepository · Mongoose │
│ MongoDB Atlas │
└─────────────────────────────────────────────────────────┘
| Pattern | Where | Purpose |
|---|---|---|
| Repository | backend/src/repositories/ |
Decouples data access from business logic |
| Service Layer | src/services/ · backend/src/services/ |
Centralises business rules, keeps controllers thin |
| Observer / Pub-Sub | backend/src/socket/chatHandler.ts |
Socket.IO room broadcast without polling |
| Singleton-Like Instance | src/services/api.ts · backend/src/index.ts |
Single Axios instance + single DB/socket connection |
| Cache-Aside / Stale-While-Revalidate | src/services/storageService.ts |
Show cached data instantly, refresh in background |
| DTO / Type Definitions | src/types/ |
Typed contracts at every layer boundary |
| Strategy (RSVP) | backend/src/services/eventService.ts |
Direct join vs. waitlist via same processRsvp() entry point |
Advanced-Messaging-App/
├── src/
│ ├── screens/ # All user-facing screens
│ ├── components/ # Shared UI (AppHeader, ScreenHeader, BottomNav…)
│ ├── services/ # API + business logic layer
│ │ ├── api.ts # Axios instance with JWT interceptor
│ │ ├── authService.ts # Login, register, getMe, updateMe
│ │ ├── eventService.ts # Event CRUD + RSVP
│ │ ├── chatService.ts # Chat room operations
│ │ ├── socketService.ts # Socket.IO connection with JWT handshake
│ │ ├── storageService.ts # TTL cache + useCachedFetch hook
│ │ ├── placesService.ts
│ │ ├── newsService.ts
│ │ ├── realEstateService.ts
│ │ ├── tripsService.ts
│ │ └── … # sponsors, qaas, roles, uploads…
│ ├── types/ # TypeScript interfaces (User, Event, News…)
│ ├── contexts/ # ThemeContext (dark/light)
│ ├── navigation/ # AppNavigator, MainTabNavigator, stacks
│ └── utils/ # apiUnwrap, eventDate helpers
├── backend/
│ └── src/
│ ├── routes/ # auth, events, places, realEstate, services, trips…
│ ├── controllers/ # Thin route handlers
│ ├── services/ # Business logic
│ ├── repositories/ # eventRepository, chatRepository
│ ├── socket/ # chatHandler.ts — Socket.IO pub-sub
│ ├── models/ # Mongoose User, Event, Message
│ └── middleware/ # authMiddleware (JWT), requireRole
├── assets/ # Icons, splash, adaptive icon
├── app.json # Expo config (bundle ID, scheme, plugins)
└── package.json
| Screen | Description |
|---|---|
AuthScreen |
Login / register with JWT |
SplashScreen |
Animated orange branded splash |
HomeScreen |
News carousel + searchable feed + recommended events |
CalendarScreen |
Events with date-range presets & chip filters |
ExploreScreen |
Places, real estate, services, trips + type-chip filtering |
GlobalChatScreen |
Community-wide Socket.IO chat room |
ChatsScreen |
All available chat rooms list |
ChatDetailScreen |
Per-event or direct-message chat room |
EventDetailScreen |
Full event info, countdown timer, RSVP / waitlist |
PlaceDetailScreen |
Place info + map |
RealEstateDetailScreen |
Listing details |
ServiceDetailScreen |
Community service detail |
TripDetailScreen |
Trip info |
NewsDetailScreen |
Full article view |
SponsorDetailScreen |
Sponsor profile |
ProfileScreen |
User info, interests, stats |
UserProfileScreen |
View another user's profile |
MyEventsScreen |
User's RSVPed events |
SettingsScreen |
Theme toggle, account options |
GmAdminScreen |
Create & manage events (GM role) |
SubmitPlaceScreen |
Add community place (GM role) |
SubmitRealEstateScreen |
Add listing (GM role) |
BusinessPartnershipScreen |
Sponsorship enquiry |
QaasScreen |
Community Q&A / FAQ |
- Node.js 18+
- npm or yarn
- Expo CLI —
npm install -g expo-cli - iOS Simulator / Android Emulator or Expo Go on a physical device
# Clone the repo
git clone https://github.com/kayraobi/Advanced-Messaging-App.git
cd Advanced-Messaging-App
# Install dependencies
npm install
# Start the Expo dev server
npx expo startPress i for iOS simulator, a for Android emulator, or scan the QR code with Expo Go.
npx expo start --ios # iOS only
npx expo start --android # Android only
npx expo start --web # Web (Metro bundler)cd backend
npm install
npm run dev # starts on http://localhost:3030Create a .env file in the project root:
EXPO_PUBLIC_API_URL=http://localhost:3030
EXPO_PUBLIC_SOCKET_URL=http://localhost:3030
EXPO_PUBLIC_USE_MOCK=falseSet EXPO_PUBLIC_USE_MOCK=true to run the app entirely on local mock data without a running backend.
- User submits credentials on
AuthScreen authService.login()posts toPOST /api/users/login- Backend validates credentials with bcrypt and returns a signed JWT
- Token is stored in AsyncStorage; user object cached locally
api.tsinterceptor attachesAuthorization: Bearer <token>to every subsequent requestsocketService.tspasses the token in the Socket.IO handshakeauthfield- On app start,
getStoredUser()decodes the JWT to restore the session — clears storage if expired
storageService.ts implements a Cache-Aside / Stale-While-Revalidate pattern using AsyncStorage:
| Data type | TTL |
|---|---|
| Events | 5 minutes |
| Places / News / Real Estate / Trips | 10 minutes |
| Chat messages | 2 minutes |
Behaviour on screen open:
- Return cached data immediately (even if stale) → zero perceived loading time
- If stale, fetch fresh data in the background and silently update the cache
- If network fails entirely, keep showing the last known data as an offline fallback
Use useCachedFetch to adopt this in any screen:
const { data, loading, refresh } = useCachedFetch('events', eventService.getAll);- JWT authentication — signed tokens, verified by
authMiddleware.tsbefore every protected route - Role-based access control — Guest / Member / GM / Admin roles enforced via
requireRole()middleware - Helmet — sets secure HTTP response headers automatically
- CORS — configurable allowed origins
- Rate limiting — 100 requests / 15 minutes / IP via
express-rate-limit - bcrypt — passwords hashed in production auth routes
- Socket authentication — JWT token passed in Socket.IO
authobject during handshake
- Automated waitlist promotion on cancellation
- Push notifications via Expo Notifications
- AI chat moderation (
hiddenflag already in the Message model) - Recommendation engine based on user interests
- Weather-aware event alerts
- Full MongoDB Atlas production deployment
- Admin moderation dashboard
| Name | Student ID | Role |
|---|---|---|
| Fatih Bahadır Karakuş | 220302370 | Frontend Developer & Backend Developer |
| Ömer Faruk Yaşar | 220302323 | Backend Developer & Frontend Developer |
| Taylan Taşkın | 220302443 | Real-Time Systems & Backend Developer |
| Kayra Yılmaz | 220302421 | Lead Developer |
| Ata Arda Kara | 230302007 | Database Engineer |
Instructor: Mirza Selimović | Lab Assistant: Adna Dedić | Course: CS308 Software Engineering — IUS
This is an academic project. Team members follow a feature-branch workflow:
git checkout -b feature/your-feature-name
git add .
git commit -m "feat: describe your change"
git push --set-upstream origin feature/your-feature-nameActive development branch: chat-and-cache-feature
Made with ☕ in Sarajevo · Spring 2026