A cross-platform note-taking app with cloud sync, markdown support, and productivity features.
- 📝 Rich Text Editing - Markdown-like text formatting with headers, lists, quotes, and code blocks
- 📁 Folder Organization - Organize notes in a hierarchical folder structure
- 🏷️ Tags - Add tags to notes for easy categorization and filtering
- 🔍 Search - Full-text search across all notes
- 📊 Statistics - Line and character count displayed in real-time
- ☁️ Cloud Sync - Real-time synchronization via Firebase
- 📴 Offline First - Works offline with background sync when connected
- 💾 Storage Tiers:
- Free: unlimited local notes with a small 5 MB cloud trial
- TypeSync Lite: 1 GB (€2.99/month)
- 📅 Calendar - Test reminders and event scheduling
- 📋 Timetable - Weekly class schedule management
- ✅ Homework Todo - Task list with due dates and priorities
- 🌙 Dark Mode - Toggle dark mode or sync with system
- 🎨 Theme Colors - Customizable accent colors
- 🖼️ Background Colors - Per-file/folder background colors
- 📄 PDF Insert - Insert and view PDFs in notes
- 👤 User Profiles - Login/register with email
⚠️ Smart Errors - Expandable error messages (simple by default)
- Nix (recommended for development)
- Flutter SDK (3.22.0 or later)
- Firebase CLI (for deployment)
-
Install Nix if you haven't already:
curl -L https://nixos.org/nix/install | sh -
Enable Flakes (if not already enabled):
echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf
-
Enter development environment:
cd TypeSync nix developThis automatically sets up:
- Flutter SDK
- Dart SDK
- Android SDK with required components
- Linux desktop build dependencies
- Node.js (for firebase-tools)
-
Install dependencies:
flutter pub get
-
Configure Firebase (see Firebase Setup section)
-
Run the app:
# Linux desktop flutter run -d linux # Android (connect device or start emulator first) flutter run -d android
-
Install Flutter: Follow the official guide
-
Install Android Studio (for Android development)
-
Install Linux dependencies (for Linux desktop):
sudo apt-get install clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev
-
Clone and setup:
git clone https://github.com/YOUR_USERNAME/TypeSync.git cd TypeSync flutter pub get
-
Install Firebase CLI (if not already installed):
npm install -g firebase-tools
-
Create a Firebase project:
- Go to Firebase Console
- Create a new project
- Enable Authentication (Email/Password)
- Enable Cloud Firestore
- Enable Cloud Storage
-
Configure Firebase (recommended method):
# Install FlutterFire CLI dart pub global activate flutterfire_cli # Configure Firebase flutterfire configure
This will automatically update
lib/firebase_options.dartwith your project's configuration. -
Alternative manual setup:
- Download configuration files from Firebase Console
- For Android: Place
google-services.jsoninandroid/app/ - Update
lib/firebase_options.dartwith your values
-
Set up Firestore Security Rules:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /users/{userId} { allow read, write: if request.auth != null && request.auth.uid == userId; } match /notes/{noteId} { allow read, write: if request.auth != null && request.auth.uid == resource.data.userId; } match /folders/{folderId} { allow read, write: if request.auth != null && request.auth.uid == resource.data.userId; } } }
-
Set up Storage Security Rules:
rules_version = '2'; service firebase.storage { match /b/{bucket}/o { match /users/{userId}/{allPaths=**} { allow read, write: if request.auth != null && request.auth.uid == userId; } } }
-
Configure transactional email with Resend:
- Verify and enable
typesync@khonager.deas a valid sender in Resend - Store the Resend API key in Firebase Functions secrets before deploying:
firebase functions:secrets:set RESEND_API_KEY
- Deploy Functions after configuration:
firebase deploy --only functions
- Verify and enable
-
Configure subscriptions with RevenueCat:
- Create the three monthly products/packages:
monthly,typesync_plus_monthly, andtypesync_pro_monthly. - Create matching entitlements:
TypeSync Lite,plus, andpro. - Use the
defaultoffering - The default web SDK key is scaffolded for testing, or override it with:
flutter build web --dart-define=REVENUECAT_WEB_API_KEY=public_web_key
- Native builds never fall back to the RevenueCat test key. Pass the platform production SDK key:
flutter build apk --dart-define=REVENUECAT_ANDROID_API_KEY=public_android_key flutter build ios --dart-define=REVENUECAT_APPLE_API_KEY=public_apple_key
- Add
REVENUECAT_ANDROID_API_KEYas a GitHub Actions secret andREVENUECAT_APPLE_API_KEYas a Codemagic environment variable. Mobile release jobs fail before building if either key is missing or starts withtest_, preventing another store build that RevenueCat will close. - Store the RevenueCat webhook token before deploying Functions:
firebase functions:secrets:set REVENUECAT_WEBHOOK_AUTH_TOKEN
- Configure the RevenueCat webhook to POST to
revenuecat_webhookwith the same token. The webhook, not the app, writes the effective plan and cloud quota. Do not grant billing fields from a client. - For web and Linux checkout, set each public RevenueCat paywall URL in both
web/billing_config.jsonandassets/billing_config.json. Leave a plan's URL empty until its checkout is live; the app will keep that plan unavailable instead of showing a broken purchase button.
- Create the three monthly products/packages:
-
Configure complimentary-access administrators:
- Never add personal email addresses to Dart, Python, or JSON files.
- Set the server-only allow-list interactively (comma-separated) and deploy:
firebase functions:secrets:set ADMIN_EMAILS firebase deploy --only functions,firestore:rules
- Alternatively give an account the Firebase Auth custom claim
admin: trueusing a trusted server/Admin SDK. Both mechanisms are verified only by Cloud Functions. - An administrator will see an Admin: complimentary access card on the Storage Plans screen. It can grant TypeSync Lite, Plus, or Pro temporarily or until revoked. These grants are separate from RevenueCat purchases, so a webhook cannot erase them and a revoke cannot remove a paid plan.
- The previous Gumroad and Patreon activation endpoints are deliberately disabled. Keep all self-service access on RevenueCat so there is one server-verified source of payment truth.
flutter build apk --releaseOutput: build/app/outputs/flutter-apk/app-release.apk
flutter build appbundle --releaseOutput: build/app/outputs/bundle/release/app-release.aab
flutter build linux --releaseOutput: build/linux/x64/release/bundle/
lib/
├── main.dart # App entry point
├── app/
│ └── app.dart # MaterialApp configuration
├── core/
│ ├── models/ # Data models (Note, Folder, User, etc.)
│ ├── providers/ # State management (Provider)
│ ├── services/ # Business logic (Auth, Sync, Storage)
│ ├── theme/ # App theming
│ └── routes/ # Navigation routes
└── features/
├── home/ # Home screen with folder/file browser
├── editor/ # Note editor with rich text
├── auth/ # Login/Register screens
├── settings/ # App settings
├── calendar/ # Calendar & reminders
├── timetable/ # Weekly schedule
├── homework/ # Todo list
├── profile/ # User profile
└── subscription/ # Storage plans
The project includes automated CI/CD with GitHub Actions:
main: Development branchstable: Stable channel branchunstable: Dev builds - updates thedevtag
-
CI/CD: Runs analysis/tests on pushes and pull requests, plus unstable/dev build work.
-
Stable release workflow: After
CI/CDsucceeds onstable, a separateRelease Stableworkflow:- Validates that
pubspec.yamland the latest changelog entry match - Creates a new tagged release (
v1.0.0,v1.0.1, etc.) only when the changelog introduces a version newer than the latest version tag - Otherwise refreshes the rolling
stable-latestrelease/tag on the neweststablecommit - Builds Android APK, AAB, Linux bundle, and Web bundle
- Validates that
-
Stable deploy workflow: After
CI/CDsucceeds onstable, a separateDeploy Web Stableworkflow builds and deploys the web app -
Dev releases: When pushed to
unstable:- Publishes a new, versioned pre-release (for example,
v1.1.18-dev.541) - Deletes the preceding dev release and tag, so only the current dev build is listed
- Builds and uploads the Android and Linux development artifacts
- The newest dev build appears first in GitHub Releases and has a monotonically increasing version for update clients such as Obtainium
- In Obtainium, enable Include prereleases for the TypeSync dev installation
- Publishes a new, versioned pre-release (for example,
# Run tests
flutter test
# Check formatting
dart format lib test integration_test
# Analyze code
flutter analyze
# After building Android, verify the packaged native Firebase Auth version
flutter build apk --release
bash scripts/verify_android_firebase_auth_version.sh releaseThe repository includes a dedicated probe that signs in against the local Firebase Auth emulator, force-stops the Android process, relaunches it, and fails unless Firebase restores the same user:
bash scripts/test_android_auth_persistence.shRun it with one Android Studio emulator connected and the Firebase CLI installed. For safety, the script refuses physical devices because it clears the TypeSync app data before the probe.
- Canonical source:
changelog/changelog.yaml - Release entry sections:
important(highlighted at top in-app)new_featuresfixes_improvementsnotes(muted at bottom in-app)
- Generate app and release artifacts:
python3 scripts/generate_changelog.py-
Generated per-version release bodies for GitHub are written to:
changelog/generated/releases/<version>.mdchangelog/generated/releases/<version>.txt
-
On
stable, GitHub Actions compares the latest changelog version to the latestv*tag:- If the changelog version is newer, CI creates that immutable versioned release
- If not, CI updates the rolling
stable-latestrelease instead
-
Validate generated output is up to date (CI-friendly):
python3 scripts/generate_changelog.py --check| File | Purpose |
|---|---|
flake.nix |
Nix development environment |
pubspec.yaml |
Flutter dependencies |
analysis_options.yaml |
Linter rules |
lib/firebase_options.dart |
Firebase configuration |
.github/workflows/ci.yml |
CI/CD pipeline |
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Make your changes
- Run tests:
flutter test - Format code:
dart format lib test - Commit:
git commit -m 'Add my feature' - Push:
git push origin feature/my-feature - Create a Pull Request
- Follow Effective Dart
- Use trailing commas for better diffs
- Document public APIs with
///comments - Keep functions focused and under 50 lines when possible
This project is licensed under the MIT License - see the LICENSE file for details.
- Flutter - UI framework
- Firebase - Backend services
- Provider - State management
- Flutter Quill - Rich text editor
