Skip to content

Commit 64152df

Browse files
Merge pull request #13 from adityabhalsod/beta
Enhance wallet features with income tracking, transfers, and UI improvements
2 parents ded7918 + e431f83 commit 64152df

74 files changed

Lines changed: 11268 additions & 1885 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/android-release.yml‎

Lines changed: 148 additions & 92 deletions
Large diffs are not rendered by default.

‎.github/workflows/ci.yml‎

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# ============================================================================
2+
# CI — Code Quality Checks (Lint + TypeScript)
3+
# ============================================================================
4+
# Runs on every pull request targeting main, and on pushes to main.
5+
# Both ESLint and TypeScript type-checking must pass before merging.
6+
#
7+
# To enforce this as a merge gate, enable branch protection on `main`:
8+
# Settings → Branches → Add rule for `main`
9+
# ✅ Require status checks to pass → select "lint" and "typecheck"
10+
# ✅ Require branches to be up to date before merging
11+
# ============================================================================
12+
13+
name: CI
14+
15+
on:
16+
# Run on every PR targeting the main branch
17+
pull_request:
18+
branches:
19+
- main
20+
# Also run on direct pushes to main (safety net)
21+
push:
22+
branches:
23+
- main
24+
25+
# Cancel in-progress runs for the same PR/branch to save resources
26+
concurrency:
27+
group: ci-${{ github.ref }}
28+
cancel-in-progress: true
29+
30+
jobs:
31+
# ── ESLint ────────────────────────────────────────────────────────────
32+
lint:
33+
name: ESLint
34+
runs-on: ubuntu-latest
35+
timeout-minutes: 10
36+
37+
steps:
38+
# Check out the repository code
39+
- name: Checkout code
40+
uses: actions/checkout@v4
41+
42+
# Set up Node.js with dependency caching for faster installs
43+
- name: Setup Node.js
44+
uses: actions/setup-node@v4
45+
with:
46+
node-version: 20
47+
cache: npm
48+
49+
# Install all project dependencies (frozen lockfile for reproducibility)
50+
- name: Install dependencies
51+
run: npm ci
52+
53+
# Run ESLint on all TypeScript files — must exit with 0 warnings
54+
- name: Run ESLint
55+
run: npm run lint
56+
57+
# ── TypeScript ────────────────────────────────────────────────────────
58+
typecheck:
59+
name: TypeScript
60+
runs-on: ubuntu-latest
61+
timeout-minutes: 10
62+
63+
steps:
64+
# Check out the repository code
65+
- name: Checkout code
66+
uses: actions/checkout@v4
67+
68+
# Set up Node.js with dependency caching
69+
- name: Setup Node.js
70+
uses: actions/setup-node@v4
71+
with:
72+
node-version: 20
73+
cache: npm
74+
75+
# Install all project dependencies
76+
- name: Install dependencies
77+
run: npm ci
78+
79+
# Run the TypeScript compiler in check-only mode — must exit with 0 errors
80+
- name: Run TypeScript type check
81+
run: npm run typecheck

‎.prettierrc‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"semi": true,
3+
"singleQuote": true,
4+
"trailingComma": "all",
5+
"printWidth": 120,
6+
"tabWidth": 2,
7+
"bracketSpacing": true,
8+
"arrowParens": "always"
9+
}

‎App.tsx‎

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,16 @@ import { LanguageProvider, useLanguage } from './src/i18n';
1010
import AppNavigator from './src/navigation';
1111
import { useAppStore, selectIsInitialized, selectSettings } from './src/store';
1212
import { processRecurringExpenses } from './src/services/recurringExpenses';
13-
import { requestNotificationPermissions, checkBudgetNotifications } from './src/services/notifications';
13+
import {
14+
requestNotificationPermissions,
15+
checkBudgetNotifications,
16+
scheduleWeeklyDigest,
17+
} from './src/services/notifications';
18+
import { checkForUpdate } from './src/services/updateChecker';
1419
import PinLockScreen from './src/components/PinLockScreen';
20+
import AsyncStorage from '@react-native-async-storage/async-storage';
21+
22+
const ONBOARDING_KEY = '@onboarding_completed';
1523

1624
// Loading screen displayed while the app initializes data from SQLite
1725
const LoadingScreen = () => {
@@ -20,29 +28,36 @@ const LoadingScreen = () => {
2028
return (
2129
<View style={[styles.loadingContainer, { backgroundColor: theme.colors.background }]}>
2230
<ActivityIndicator size="large" color={theme.colors.primary} />
23-
<Text style={[styles.loadingText, { color: theme.colors.textSecondary }]}>
24-
{t.common.loading}
25-
</Text>
31+
<Text style={[styles.loadingText, { color: theme.colors.textSecondary }]}>{t.common.loading}</Text>
2632
</View>
2733
);
2834
};
2935

3036
// Inner app component that handles store initialization and security gate
3137
const AppContent = () => {
3238
const { theme, isDark } = useTheme();
39+
const { t } = useLanguage(); // Access translations for update checker alert
3340
const isInitialized = useAppStore(selectIsInitialized);
3441
const settings = useAppStore(selectSettings);
3542
const initialize = useAppStore((s) => s.initialize);
3643
const [isAuthenticated, setIsAuthenticated] = useState(false); // Security gate state
44+
const [showOnboarding, setShowOnboarding] = useState(false); // First-launch onboarding
3745

3846
// Determine if security gate should show
3947
// PIN requires both the toggle AND a stored PIN hash; biometric just needs the toggle
4048
const needsAuth = ((settings.enablePin && !!settings.pinHash) || settings.enableBiometric) && !isAuthenticated;
4149

42-
// Initialize the database, process recurring expenses, and check budgets
50+
// Initialize the database, process recurring expenses, check budgets, and set up onboarding
4351
useEffect(() => {
4452
const boot = async () => {
4553
await initialize();
54+
55+
// Check if this is the first launch (show onboarding)
56+
const onboardingDone = await AsyncStorage.getItem(ONBOARDING_KEY);
57+
if (!onboardingDone) {
58+
setShowOnboarding(true);
59+
}
60+
4661
// Process any due recurring expenses after data is loaded
4762
try {
4863
await processRecurringExpenses();
@@ -54,27 +69,30 @@ const AppContent = () => {
5469
const hasPermission = await requestNotificationPermissions();
5570
if (hasPermission) {
5671
await checkBudgetNotifications();
72+
// Schedule weekly digest notification (every Sunday 9 AM)
73+
await scheduleWeeklyDigest();
5774
}
5875
} catch (e) {
5976
console.warn('Notification setup skipped:', e);
6077
}
78+
79+
// Check GitHub releases for a newer app version (non-blocking)
80+
checkForUpdate(t.updateChecker);
6181
};
6282
boot();
83+
// eslint-disable-next-line react-hooks/exhaustive-deps
6384
}, []);
6485

6586
return (
6687
<>
67-
<StatusBar
68-
barStyle={isDark ? 'light-content' : 'dark-content'}
69-
backgroundColor={theme.colors.background}
70-
/>
88+
<StatusBar barStyle={isDark ? 'light-content' : 'dark-content'} backgroundColor={theme.colors.background} />
7189
{!isInitialized ? (
7290
<LoadingScreen />
7391
) : needsAuth ? (
7492
<PinLockScreen onAuthenticated={() => setIsAuthenticated(true)} />
7593
) : (
7694
<>
77-
<AppNavigator />
95+
<AppNavigator initialRoute={showOnboarding ? 'Onboarding' : 'MainTabs'} />
7896
</>
7997
)}
8098
</>

‎README.md‎

Lines changed: 85 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<img src="https://img.shields.io/badge/TypeScript-5.9-3178C6?logo=typescript&logoColor=white" alt="TypeScript" />
1515
<img src="https://img.shields.io/badge/Platform-Android_|_iOS-green" alt="Platform" />
1616
<img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License" />
17+
<a href="https://github.com/adityabhalsod/expense-tracker/actions/workflows/ci.yml"><img src="https://github.com/adityabhalsod/expense-tracker/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
1718
<a href="https://github.com/adityabhalsod/expense-tracker/actions/workflows/android-release.yml"><img src="https://github.com/adityabhalsod/expense-tracker/actions/workflows/android-release.yml/badge.svg" alt="Android Release" /></a>
1819
<a href="https://github.com/adityabhalsod/expense-tracker/releases/latest"><img src="https://img.shields.io/github/v/release/adityabhalsod/expense-tracker?include_prereleases&label=Latest%20Release" alt="Latest Release" /></a>
1920
</p>
@@ -22,32 +23,50 @@
2223

2324
## What is Expense Tracker?
2425

25-
Expense Tracker helps you take control of your money. Track every rupee, dollar, or euro you spend — all from your phone, no internet required.
26+
Expense Tracker helps you take control of your money. Track every rupee, dollar, or euro you spend and earn — all from your phone, no internet required.
2627

2728
- **No account needed** — your data stays on your device
2829
- **Works offline** — powered by a local SQLite database
2930
- **Fast & lightweight** — optimized for smooth performance on any device
31+
- **Track income & expenses** — see your net savings at a glance
3032

3133
---
3234

3335
## Features
3436

37+
### Core
38+
3539
| | Feature | Description |
3640
|---|---|---|
3741
| 💰 | **Expense Tracking** | Add, edit, and delete expenses with categories, payment methods, notes, and tags |
38-
| 👛 | **Monthly Wallets** | Set a starting balance each month — expenses auto-deduct in real time |
39-
| 📊 | **Analytics Dashboard** | Pie charts, bar charts, and line charts to visualize spending patterns |
42+
| 💵 | **Income Tracking** | Record salary, freelance, business, and other income sources with wallet integration |
43+
| 🔄 | **Wallet Transfers** | Move money between wallets — ATM withdrawals, UPI transfers, bank-to-cash, etc. |
44+
| 📈 | **Net Savings** | Monthly income vs expense comparison with real-time savings calculation |
45+
| 👛 | **Multi-Wallet** | Manage cash, bank accounts, digital wallets, and credit cards with real-time balance tracking |
46+
| 📊 | **Analytics Dashboard** | Pie charts, bar charts, line charts, stacked bar charts, and spending flow diagrams |
4047
| 🏷️ | **15+ Categories** | Pre-loaded categories + create your own with custom icons and colors |
4148
| 💱 | **Multi-Currency** | Supports 10 currencies — INR, USD, EUR, GBP, JPY, CAD, AUD, CNY, SGD, AED |
4249
| 🔄 | **Recurring Expenses** | Auto-generate daily, weekly, biweekly, monthly, quarterly, or yearly entries |
43-
| 🎯 | **Budgets** | Set per-category monthly limits with visual progress bars |
50+
| 🎯 | **Budgets** | Set per-category budgets (daily/weekly/monthly/quarterly/yearly) with visual progress bars |
4451
| 🔔 | **Budget Alerts** | Get notified at 80% and 100% of your budget limits |
45-
| 🔍 | **Search** | Full-text search across notes, categories, and tags |
52+
| 🔍 | **Search** | Full-text search across notes, categories, and tags with category filters |
4653
| 📤 | **Export Reports** | Export to JSON, CSV, Excel (XML), or HTML/PDF and share instantly |
54+
| ⚡ | **Quick Add** | Bottom-sheet quick-entry with preset amounts, mode switcher (expense/income), and auto-close |
55+
| 🎯 | **Savings Goals** | Set financial targets with progress bars, contribute funds, and track completion |
56+
| ⚡ | **Expense Templates** | Save frequently-used expense patterns for one-tap creation with usage tracking |
57+
| 📅 | **Calendar Heatmap** | Visualize daily spending intensity across a month with color-coded cells |
58+
| 🔥 | **Streaks & Gamification** | Track daily logging consistency, earn badges at 3/7/14/30/60/100/365 day milestones |
59+
| 💡 | **Monthly Insights** | Smart spending analysis — trend detection, savings rate, category spikes, and top categories |
60+
| 🎓 | **Onboarding Walkthrough** | Guided first-time setup with feature highlights and horizontal pager |
61+
| 📬 | **Weekly Digest** | Push notification summaries of weekly spending patterns |
62+
| 📸 | **Receipt Attachments** | Attach photos to expenses, view thumbnails on detail and edit screens |
63+
| 📊 | **Advanced Data Viz** | Stacked bar charts for weekly category breakdown + custom SVG spending flow diagrams |
4764
| 🔒 | **PIN & Biometric Lock** | Protect your data with a 4–6 digit PIN or fingerprint/Face ID |
65+
| 🔐 | **End-to-End Encryption** | AES-256-GCM encryption with PBKDF2 key derivation, hardware-backed key storage via Secure Store |
4866
| 🌙 | **Dark Mode** | Automatic (follows system) or manual toggle |
4967
| 🌐 | **Multi-Language** | English, हिन्दी (Hindi), ગુજરાતી (Gujarati) |
50-
| 🏦 | **Payment Sources** | Manage bank accounts, digital wallets, and credit cards — sensitive data encrypted at rest |
68+
| ☁️ | **Cloud Backup** | Export and back up all financial data with one tap |
69+
— sensitive data encrypted at rest |
5170
| 🔐 | **End-to-End Encryption** | AES-256-GCM encryption with PBKDF2 key derivation, hardware-backed key storage via Secure Store |
5271

5372
---
@@ -191,9 +210,10 @@ Pushing to specific branches triggers a GitHub Actions pipeline that builds a cl
191210
| **State** | Zustand 5 | Lightweight reactive state management |
192211
| **Navigation** | React Navigation 7 | Bottom tabs + native stack transitions |
193212
| **UI** | react-native-paper, MaterialCommunityIcons | Material Design components |
194-
| **Charts** | react-native-chart-kit, react-native-svg | Data visualization |
213+
| **Charts** | react-native-chart-kit, react-native-svg | Data visualization (pie, bar, line, stacked bar, custom SVG) |
195214
| **Dates** | date-fns 4 | Date formatting and range calculations |
196215
| **Export** | expo-file-system, expo-sharing | File generation and sharing |
216+
| **Camera** | expo-image-picker | Receipt photo capture and attachment |
197217
| **Security** | expo-local-authentication, expo-secure-store | Biometrics and encrypted storage |
198218
| **Encryption** | SubtleCrypto (Web Crypto API), expo-crypto | AES-256-GCM + PBKDF2 key derivation |
199219
| **Notifications** | expo-notifications | Budget alert push notifications |
@@ -209,24 +229,41 @@ expense-tracker/
209229
│ ├── components/ # Reusable UI components
210230
│ │ ├── common/ # Card, Button, EmptyState
211231
│ │ └── PinLockScreen # Security lock gate
212-
│ ├── constants/ # App constants, default categories, currencies
213-
│ ├── database/ # SQLite service (all CRUD operations)
232+
│ ├── constants/ # App constants, default categories, currencies, income sources
233+
│ ├── database/ # SQLite service (all CRUD operations, 11 tables)
214234
│ ├── i18n/ # Translations (en, hi, gu) + LanguageProvider
215235
│ ├── navigation/ # Tab navigator + stack screens
216-
│ ├── screens/ # 17 app screens
217-
│ │ ├── HomeScreen # Dashboard with wallet summary
218-
│ │ ├── ExpensesScreen # Filtered expense list
219-
│ │ ├── AnalyticsScreen # Charts and insights
220-
│ │ ├── WalletScreen # Balance and history
221-
│ │ ├── SettingsScreen # Theme, language, security
222-
│ │ ├── PaymentSources # Bank accounts & payment methods
223-
│ │ └── ... # Add, Detail, Search, Export, Budget, etc.
224-
│ ├── hooks/ # Custom hooks
225-
│ ├── services/ # Recurring expenses, notifications
226-
│ ├── store/ # Zustand global state
227-
│ ├── theme/ # Light & dark theme definitions
236+
│ ├── screens/ # 25 app screens
237+
│ │ ├── HomeScreen # Dashboard with wallet summary, net savings, quick actions
238+
│ │ ├── ExpensesScreen # Filtered expense list (All/Today/Week/Month)
239+
│ │ ├── AnalyticsScreen # Charts, stacked bars, spending flow diagrams
240+
│ │ ├── WalletScreen # Balance overview and wallet management
241+
│ │ ├── SettingsScreen # Theme, language, security
242+
│ │ ├── AddExpenseScreen # Add/edit expense with receipt attachments
243+
│ │ ├── AddIncomeScreen # Add/edit income with source selection
244+
│ │ ├── IncomeListScreen # Income history with sorting
245+
│ │ ├── TransferScreen # Wallet-to-wallet transfers
246+
│ │ ├── QuickAddScreen # Bottom-sheet quick-entry (expense/income)
247+
│ │ ├── SavingsGoalsScreen # Financial targets with progress tracking
248+
│ │ ├── ExpenseTemplatesScreen # Saved expense patterns for quick re-use
249+
│ │ ├── CalendarHeatmapScreen # Monthly spending heatmap grid
250+
│ │ ├── StreaksScreen # Daily logging streaks and badges
251+
│ │ ├── MonthlyInsightsScreen # Smart spending analysis and trends
252+
│ │ ├── OnboardingScreen # First-time walkthrough pager
253+
│ │ ├── ExpenseDetailScreen # Expense details with receipt thumbnails
254+
│ │ ├── AllExpensesScreen # Full expense list with advanced sorting
255+
│ │ ├── SearchScreen # Full-text search with category filters
256+
│ │ ├── BudgetSetupScreen # Per-category budget management
257+
│ │ ├── CategoryManagementScreen # Custom categories with batch operations
258+
│ │ ├── ExportReportScreen # Multi-format export (JSON/CSV/Excel/PDF)
259+
│ │ ├── SecurityScreen # PIN and biometric settings
260+
│ │ ├── CloudBackupScreen # Data backup and restore
261+
│ │ └── WalletSetupScreen # Create/edit wallet (payment source)
262+
│ ├── services/ # Recurring expenses, notifications, weekly digest
263+
│ ├── store/ # Zustand global state (granular selectors)
264+
│ ├── theme/ # Light and dark theme definitions
228265
│ ├── types/ # TypeScript interfaces
229-
│ └── utils/ # Formatters, helpers, export service
266+
│ └── utils/ # Formatters, helpers, export service, encryption
230267
├── android/ # Native Android project
231268
└── assets/ # App icons and images
232269
```
@@ -255,6 +292,32 @@ Translation files are in `src/i18n/`. To add a new language, create a new transl
255292
4. Push to the branch (`git push origin feature/amazing-feature`)
256293
5. Open a Pull Request
257294

295+
### Code Quality Requirements
296+
297+
Every pull request targeting `main` must pass these automated checks before it can be merged:
298+
299+
| Check | Command | What it verifies |
300+
|-------|---------|------------------|
301+
| **ESLint** | `npm run lint` | Zero warnings across all `.ts` / `.tsx` files |
302+
| **TypeScript** | `npm run typecheck` | Zero type errors (`tsc --noEmit`) |
303+
304+
Both checks run automatically via the [CI workflow](.github/workflows/ci.yml) on every PR.
305+
306+
**Run checks locally before pushing:**
307+
```bash
308+
# Run both lint and typecheck in one command
309+
npm run check
310+
```
311+
312+
### Branch Protection
313+
314+
The `main` branch is protected with these rules:
315+
- **Require status checks to pass** — `lint` and `typecheck` jobs must be green
316+
- **Require branches to be up to date** — PR must be rebased on latest `main`
317+
- **No direct pushes** — all changes go through pull requests
318+
319+
To configure branch protection: **Settings → Branches → Add rule** for `main`, then select the `lint` and `typecheck` status checks as required.
320+
258321
---
259322

260323
## License

‎android/app/build.gradle‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ android {
9292
applicationId 'com.adityabhalsod.expensetracker'
9393
minSdkVersion rootProject.ext.minSdkVersion
9494
targetSdkVersion rootProject.ext.targetSdkVersion
95-
versionCode 1
96-
versionName "1.2.0"
95+
versionCode 4
96+
versionName "1.3.0"
9797

9898
buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\""
9999
}

0 commit comments

Comments
 (0)