- Project Overview
- Tech Stack
- Features
- Project Structure
- Environment Variables
- Installation & Setup
- Running the Project
- Navigation Structure
- Key Components
- Context & State Management
- Real-Time Features
- API Integration
- Styling & Theme
- Scripts
- Building for Distribution
- Troubleshooting
Soothe is a comprehensive mobile music streaming and social platform frontend built with React Native and Expo. It enables users to discover music based on their mood, manage playlists, interact with friends, and enjoy real-time chat functionality. The application provides a seamless, intuitive interface for mood-based music consumption with end-to-end encrypted messaging.
The frontend supports both iOS and Android platforms through a single codebase, ensuring consistent user experience across devices.
- React Native (v0.81.5) - Cross-platform mobile framework
- Expo (~54.0.33) - Managed React Native framework
- Expo Router (~6.0.23) - File-based routing (like Next.js)
- TypeScript (~5.9.2) - Type-safe development
- @react-navigation/native (^7.1.8) - Core navigation
- @react-navigation/bottom-tabs (^7.4.0) - Tab navigation
- @react-navigation/elements (^2.6.3) - Navigation components
- React Context API - Theme and app-wide state
- @react-native-async-storage/async-storage (2.2.0) - Local data persistence
- expo-av (^16.0.8) - Audio/video playback
- expo-audio (~1.1.1) - Audio recording and playback
- expo-image (~3.0.11) - Optimized image rendering
- expo-image-picker (~17.0.10) - Image selection from device
- expo-linear-gradient (~15.0.8) - Gradient backgrounds
- react-native-linear-gradient (^2.8.3) - Additional gradient support
- react-native-svg (15.12.1) - SVG support
- react-native-gifted-charts (^1.4.76) - Chart/graph rendering
- @expo/vector-icons (^15.0.3) - Icon library
- axios (^1.14.0) - HTTP client for API requests
- socket.io-client (^4.8.3) - Real-time WebSocket communication
- jwt-decode (^4.0.0) - JWT token parsing
- crypto-js (^4.2.0) - End-to-end message encryption
- react-native-get-random-values (~1.11.0) - Cryptographic randomness
- expo-constants (~18.0.13) - App constants and configuration
- expo-haptics (~15.0.8) - Haptic feedback
- react-native-gesture-handler (~2.28.0) - Gesture recognition
- react-native-reanimated (~4.1.1) - Smooth animations
- react-native-screens (~4.16.0) - Native screen optimization
- ESLint (^9.25.0) - Code linting (Expo config)
-
User Authentication
- Email/password registration with OTP verification
- JWT-based session management
- Automatic token refresh
- Secure logout
-
Mood-Based Music Discovery
- 6 distinct mood categories (Upbeat, Relaxed, Focus, Energetic, Romantic, Melancholic)
- AI-powered song recommendations based on selected mood
- Mood-specific color theming throughout the app
- Dynamic playlist suggestions
-
Music Playback
- Stream songs with adaptive quality
- Play/pause/skip controls
- Playlist queueing
- Progress seeking
- Volume control with haptic feedback
-
Playlist Management
- Create and manage personal playlists
- Add/remove songs from playlists
- Rename playlists
- Browse public community playlists
- Share playlists with friends
-
User Profiles
- Profile information management
- Profile picture upload from device gallery
- View listening history
- Track top songs statistics
- Edit profile settings
-
Social Features
- Discover and search for other users
- Send and accept friend requests
- View friends list
- Personal friends network
- User discovery and recommendations
-
Real-Time Chat
- End-to-end encrypted messaging with friends
- Real-time message delivery via WebSocket
- Chat history persistence
- Typing indicators
- Message sharing functionality
- Multiple active chats
-
Favorites System
- Add/remove favorite songs
- Quick access to favorite songs
- Persistent favorite collection
soothe/
├── app/ # Expo Router app directory (file-based routing)
│ ├── _layout.tsx # Root layout with providers
│ ├── welcome.tsx # Onboarding/splash screen
│ ├── login.tsx # Login screen
│ ├── signup.tsx # Registration screen
│ ├── edit-profile.tsx # Profile editor
│ ├── friends.tsx # Friend discovery and requests
│ ├── history.tsx # Listening history view
│ ├── mood-results.tsx # Mood-based recommendations
│ ├── modal.tsx # Modal presentation
│ │
│ ├── (tabs)/ # Tab-based navigation group
│ │ ├── _layout.tsx # Tab navigator setup
│ │ ├── index.tsx # Home/Now Playing tab
│ │ ├── explore.tsx # Music discovery tab
│ │ ├── playlist.tsx # User playlists tab
│ │ ├── playlist-details.tsx # Playlist content view
│ │ ├── chats.tsx # Chat list tab
│ │ ├── mic.tsx # Mood selection/input tab
│ │ ├── search.tsx # Search functionality
│ │ └── profile.tsx # User profile tab
│ │
│ └── messages/
│ ├── _layout.tsx # Messages stack layout
│ └── [id].tsx # Individual chat screen
│
├── components/ # Reusable UI components
│ ├── external-link.tsx # External link component
│ ├── haptic-tab.tsx # Haptic feedback tab button
│ ├── hello-wave.tsx # Welcome animation
│ ├── parallax-scroll-view.tsx # Parallax scrolling component
│ ├── themed-text.tsx # Theme-aware text
│ ├── themed-view.tsx # Theme-aware container
│ ├── FloatingTabBar.tsx # Custom tab bar UI
│ │
│ ├── auth/
│ │ └── SootheLogo.tsx # App logo component
│ │
│ ├── chats/
│ │ ├── ChatListItem.tsx # Chat list item UI
│ │ ├── StoriesRow.tsx # Stories/recent contacts
│ │ └── StoryAvatar.tsx # Story avatar display
│ │
│ ├── context/
│ │ └── ThemeContext.tsx # Mood/theme context provider
│ │
│ ├── explore/
│ │ ├── ArtistCard.tsx # Artist preview card
│ │ ├── PlaylistCard.tsx # Playlist preview card
│ │ ├── RecentSongCard.tsx # Recent song mini card
│ │ └── SectionHeader.tsx # Explore section header
│ │
│ ├── index/
│ │ ├── ActiveMoodCard.tsx # Current mood display
│ │ ├── MoodPlayerScreen.tsx # Player with mood controls
│ │ ├── ShareWithFriendMenu.tsx # Song sharing menu
│ │ ├── SongActionsMenu.tsx # Song options menu
│ │ ├── SongPlayerContext.tsx # Player state context
│ │ ├── SongPlayerScreen.tsx # Main player UI
│ │ └── SpinWheel.tsx # Mood selection wheel
│ │
│ ├── profile/
│ │ └── piechart.tsx # Statistics pie chart
│ │
│ └── ui/
│ ├── collapsible.tsx # Collapsible component
│ ├── icon-symbol.tsx # Icon symbols
│ └── icon-symbol.ios.tsx # iOS-specific icons
│
├── constants/ # App constants and configuration
│ ├── moods.tsx # Mood definitions and colors
│ ├── theme.ts # App-wide theme configuration
│ │
│ └── explore/
│ ├── exploreMockData.ts # Mock data for explore tab
│ └── ExploreTypes.ts # Type definitions for explore
│
├── hooks/ # Custom React hooks
│ ├── use-color-scheme.ts # System color scheme detection
│ ├── use-color-scheme.web.ts # Web color scheme detection
│ └── use-theme-color.ts # Theme color management
│
├── utils/
│ └── PlayEventEmitter.ts # Audio event emitter
│
├── assets/
│ ├── fonts/ # Custom font files
│ └── images/
│ └── index_page/ # Home screen images
│
├── app.json # Expo app configuration
├── babel.config.js # Babel configuration
├── metro.config.js # Metro bundler config
├── tsconfig.json # TypeScript configuration
├── eslint.config.js # ESLint rules
├── eas.json # Expo Application Services config
├── expo-env.d.ts # Expo environment types
├── svg.d.ts # SVG type declarations
├── package.json # Dependencies and scripts
├── withGradleFix.js # Android build fix plugin
└── README.md # This file
Create a .env file at the root of the project to configure API endpoints and services:
# API Configuration
EXPO_PUBLIC_API_BASE_URL=http://your-backend-url:3000/api
EXPO_PUBLIC_SOCKET_URL=http://your-backend-url:3000
# Feature Flags (optional)
EXPO_PUBLIC_ENABLE_ANALYTICS=trueImportant: In Expo, environment variables must be prefixed with EXPO_PUBLIC_ to be accessible in the client.
Example for development:
EXPO_PUBLIC_API_BASE_URL=http://192.168.x.x:3000/api
EXPO_PUBLIC_SOCKET_URL=http://192.168.x.x:3000Example for production:
EXPO_PUBLIC_API_BASE_URL=https://api.soothe.com/api
EXPO_PUBLIC_SOCKET_URL=https://api.soothe.com- Node.js (v16 or higher)
- npm or yarn package manager
- Expo CLI (
npm install -g expo-cli) - Git for version control
- For iOS: Xcode (on macOS) or Expo Go app
- For Android: Android Studio or Expo Go app
git clone https://github.com/raj-jaiswal/soothe.git
cd soothenpm installor with yarn:
yarn installcp .env.example .envEdit the .env file with your backend server URL:
EXPO_PUBLIC_API_BASE_URL=http://localhost:3000/api
EXPO_PUBLIC_SOCKET_URL=http://localhost:3000npm startThis launches the Expo CLI with a QR code for connecting mobile clients.
- iOS: Open Expo Go app → Scan QR code from terminal
- Android: Open Expo Go app → Scan QR code from terminal
npm run webOpens the app in your browser at http://localhost:19006
npm run androidRequires Android Studio and configured emulator or connected device.
npm run iosRequires Xcode on macOS.
Check code quality with ESLint:
npm run lintThis follows Expo's recommended ESLint configuration.
The app uses Expo Router for file-based routing, similar to Next.js:
/ (Root)
├── welcome.tsx # Splash/onboarding
├── login.tsx # Login screen
├── signup.tsx # Registration screen
├── (tabs) # Authenticated user tabs
│ ├── index.tsx # Now Playing (home)
│ ├── explore.tsx # Music Discovery
│ ├── playlist.tsx # My Playlists
│ ├── chats.tsx # Chat List
│ ├── mic.tsx # Mood Selection
│ ├── search.tsx # Search Songs/Artists
│ └── profile.tsx # User Profile
├── messages
│ └── [id].tsx # Individual Chat
├── edit-profile.tsx # Profile Editor
├── friends.tsx # Friend Management
├── history.tsx # Listening History
└── mood-results.tsx # AI Recommendations
- Authentication Flow: welcome → login/signup → (tabs)
- From Tabs: Any tab can navigate to messages, edit-profile, friends, history, or mood-results
- Deep Linking: The app supports deep links defined in
app.json
Main music playback UI with:
- Song artwork display
- Play/pause/skip buttons
- Progress bar with seeking
- Volume control
- Current mood indicator
- Favorite song toggle
- Share options
Interactive mood selector component with:
- 6 mood options with distinct colors
- Smooth rotation animation
- Visual feedback on selection
- Auto-triggers mood-based recommendations
- ChatListItem: Shows last message, unread count, timestamp
- StoriesRow: Recent contacts for quick messaging
- StoryAvatar: User avatar with online/offline status
- PlaylistCard: Preview of public playlists with song count
- ArtistCard: Artist profile with follower count
- RecentSongCard: Compact song preview
- Piechart: Visualize mood distribution of top songs
- Shows top genres, artists, and listening stats
Manages mood-based theming across the entire app.
File: components/context/ThemeContext.tsx
Usage:
import { ThemeContext } from '@/components/context/ThemeContext';
const { currentMood, setAppMood } = useContext(ThemeContext);Key Functions:
setAppMood(mood)- Change the active mood and color schemecurrentMood- Get current mood object with color and ID
Manages music playback state.
File: components/index/SongPlayerContext.tsx
Includes:
- Current playing song
- Player status (playing, paused, stopped)
- Play queue management
- Repeat/shuffle modes
Persists user data on device:
- JWT token for authentication
- User preferences
- Recently played songs
- Offline data cache
Example:
import AsyncStorage from '@react-native-async-storage/async-storage';
await AsyncStorage.setItem('userToken', token);
const token = await AsyncStorage.getItem('userToken');Real-time messaging with end-to-end encryption.
Connection Setup:
import io from 'socket.io-client';
import { jwtDecode } from 'jwt-decode';
const token = await AsyncStorage.getItem('userToken');
const socket = io(process.env.EXPO_PUBLIC_SOCKET_URL, {
auth: { token }
});Socket Events:
// Join a chat room
socket.emit('join', { chatId: 'chat_room_id' });
// Send encrypted message
socket.emit('sendMessage', {
chatId: 'chat_room_id',
recipientUsername: 'friend_username',
ciphertext: CryptoJS.AES.encrypt(message, encryptionKey).toString(),
iv: initializationVector,
messageType: 'text'
});
// Listen for incoming messages
socket.on('message', (data) => {
const decrypted = CryptoJS.AES.decrypt(data.ciphertext, encryptionKey).toString();
console.log('New message:', decrypted);
});
// Handle disconnect
socket.on('disconnect', () => {
console.log('Disconnected from chat server');
});End-to-End Encryption:
Uses crypto-js library for AES encryption:
import CryptoJS from 'crypto-js';
const message = 'Hello, World!';
const key = 'shared-secret-key';
const encrypted = CryptoJS.AES.encrypt(message, key).toString();
const decrypted = CryptoJS.AES.decrypt(encrypted, key).toString(CryptoJS.enc.Utf8);The app uses Axios for all HTTP requests to the backend API.
Base Configuration:
import axios from 'axios';
import AsyncStorage from '@react-native-async-storage/async-storage';
const apiClient = axios.create({
baseURL: process.env.EXPO_PUBLIC_API_BASE_URL,
timeout: 10000,
});
// Add JWT token to all requests
apiClient.interceptors.request.use(async (config) => {
const token = await AsyncStorage.getItem('userToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// Handle token refresh on 401
apiClient.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 401) {
// Handle token refresh or logout
await AsyncStorage.removeItem('userToken');
navigation.reset({ index: 0, routes: [{ name: 'login' }] });
}
return Promise.reject(error);
}
);The frontend communicates with the Soothe backend API:
| Feature | Method | Endpoint |
|---|---|---|
| Auth | POST | /api/auth/signup |
| POST | /api/auth/verify-otp |
|
| POST | /api/auth/login |
|
| Songs | GET | /api/songs |
| GET | /api/songs/:id/stream |
|
| POST | /api/songs/suggest |
|
| User | GET | /api/user/me |
| GET | /api/user/me/history |
|
| GET | /api/user/me/top-songs |
|
| PUT | /api/user/me |
|
| POST | /api/user/me/profile-pic |
|
| Playlists | POST | /api/personal-playlists |
| GET | /api/personal-playlists |
|
| PATCH | /api/personal-playlists/:id |
|
| POST | /api/personal-playlists/:id/songs |
|
| DELETE | /api/personal-playlists/:id/songs/:songId |
|
| Favorites | GET | /api/favourites |
| POST | /api/favourites/:songId |
|
| DELETE | /api/favourites/:songId |
|
| Friends | GET | /api/friends/search |
| GET | /api/friends |
|
| POST | /api/friends/request |
|
| POST | /api/friends/accept |
|
| POST | /api/friends/reject |
|
| Chat | GET | /api/chats |
| GET | /api/chats/:chatId/messages |
See the backend README for detailed endpoint documentation.
The app dynamically changes colors based on the selected mood.
Mood Configuration: constants/moods.tsx
export interface MoodItem {
id: string;
label: string;
color: string;
accentColor: string;
icon: string;
description: string;
}Available Moods:
- Upbeat
- Euphoric
- Love
- Angry
- Grief
- Anxious
- Calm
File: constants/theme.ts
Defines:
- Font sizing
- Spacing scale
- Border radius
- Opacity values
- Animation durations
Use themed components for consistency:
import { ThemedView } from '@/components/themed-view';
import { ThemedText } from '@/components/themed-text';
export function MyComponent() {
return (
<ThemedView>
<ThemedText>This text adapts to the current mood!</ThemedText>
</ThemedView>
);
}Available npm scripts defined in package.json:
npm start # Start development server
npm run ios # Run on iOS simulator/device
npm run android # Run on Android emulator/device
npm run web # Run in web browsernpm run lint # Check code quality (ESLint)
npm run reset-project # Reset project to initial state
npm run clean # Clear Android build cacheThe project is configured for EAS Build for seamless app store deployment.
Configuration: eas.json
Build and upload to Apple App Store:
eas build --platform iosBuild and upload to Google Play Store:
eas build --platform android- Expo account (eas.expo.dev)
- Apple Developer account (iOS)
- Google Play Developer account (Android)
- Proper signing certificates and provisioning profiles
Check build progress in the Expo dashboard or terminal.
- Issue: Network timeout or connection refused
- Solution:
- Ensure backend is running on the correct port
- Check
EXPO_PUBLIC_API_BASE_URLin.env - For emulator, use
10.0.2.2:3000(Android) orlocalhost:3000(iOS) - For physical device, use your machine's IP address
- Issue: Chat functionality not working
- Solution:
- Verify backend supports Socket.io
- Check
EXPO_PUBLIC_SOCKET_URLmatches backend URL - Ensure JWT token is valid before connecting
- Issue: Songs don't play or pause
- Solution:
- Check audio permissions in
app.json - Verify stream URL is accessible
- For iOS: Check Background Modes in Xcode
- For Android: Check microphone permissions
- Check audio permissions in
- Issue: User data/token disappears on app restart
- Solution:
- AsyncStorage requires app closure to persist
- For debugging, use Expo DevTools
- Clear app cache:
npm run clean
- Issue: EAS build succeeds but app crashes
- Solution:
- Check
app.jsonfor configuration errors - Verify all native modules are compatible with Expo SDK version
- Review EAS build logs in dashboard
- Check
- Issue: Images from API don't load
- Solution:
- Use
expo-imagecomponent for optimized loading - Ensure image URLs are accessible from the network
- Check CORS headers on backend
- Use
Enable debug logs:
import { setLoggingEnabled } from 'react-native';
setLoggingEnabled(true);Use Expo DevTools:
npm start -- --dev-client- Screens (app/) - Full page/route components
- Components - Reusable UI elements
- Contexts - State management
- Utils - Helper functions
- Constants - App-wide constants
- React components:
PascalCase(e.g.,SongPlayer.tsx) - Files:
kebab-caseorPascalCasematching component name - Functions:
camelCase - Constants:
UPPER_SNAKE_CASE
- All components and functions should have proper typing
- Avoid
anytype; use specific types or generics - Define interfaces in separate files when needed
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes
- Test thoroughly on both iOS and Android
- Run linting:
npm run lint - Commit:
git commit -m 'Add amazing feature' - Push:
git push origin feature/amazing-feature - Open a Pull Request
MIT License
- Divya Swaroop Jaiswal - https://github.com/raj-jaiswal
- Darla Sravan Kumar - https://github.com/DSK-champ
- Vivekananda Katakam - https://github.com/VivekanandaK123
- Parth Agarwal - https://github.com/parthagarwal8910
- Vennela Jangiti - https://github.com/vennelajangiti17
- Shivam Prakash - https://github.com/Phoenix1729398