fix: Build crash due to room migration,
chore: CONTRIBUTING.md
This commit is contained in:
+222
@@ -0,0 +1,222 @@
|
||||
# Contributing to Flux
|
||||
|
||||
Thank you for your interest in contributing to Flux! Your support means a lot.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Getting Started](#getting-started)
|
||||
- [How to Contribute](#how-to-contribute)
|
||||
- [Branch Naming](#branch-naming)
|
||||
- [Commit Messages](#commit-messages)
|
||||
- [Pull Request Guidelines](#pull-request-guidelines)
|
||||
- [Architecture Overview](#architecture-overview)
|
||||
- [Module Structure](#module-structure)
|
||||
- [Code Style & Conventions](#code-style--conventions)
|
||||
- [Tech Stack](#tech-stack)
|
||||
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Install the latest stable version of [Android Studio](https://developer.android.com/studio).
|
||||
2. Clone the repository:
|
||||
```bash
|
||||
git clone git@github.com:chindaronit/Flux.git
|
||||
```
|
||||
3. Open the project in Android Studio and let Gradle sync complete.
|
||||
4. Select `Run > Run 'app'` to build and launch the app on a device or emulator.
|
||||
|
||||
**Requirements:**
|
||||
- Android SDK target: **36**, minimum: **29** (Android 10+)
|
||||
- Kotlin: **2.2.10**
|
||||
- JVM Target: **11**
|
||||
- Build tool: **Gradle with Kotlin DSL**
|
||||
|
||||
---
|
||||
|
||||
## How to Contribute
|
||||
|
||||
1. **Fork the Repository** — Fork to your own GitHub account.
|
||||
2. **Create a Branch** — Branch off from `main` using the naming conventions below.
|
||||
3. **Make Your Changes** — Follow the architecture and code style described in this guide.
|
||||
4. **Open an Issue First** — For non-trivial changes, create an issue before opening a PR so the approach can be discussed.
|
||||
5. **Submit a Pull Request** — Target the `dev` branch. Reference the related issue in the PR description.
|
||||
|
||||
---
|
||||
|
||||
## Branch Naming
|
||||
|
||||
Use descriptive, hyphenated branch names prefixed by type:
|
||||
|
||||
| Type | Pattern | Example |
|
||||
|------|---------|---------|
|
||||
| Feature | `feat/<short-description>` | `feat/add-calendar-widget` |
|
||||
| Bug fix | `fix/<issue-or-description>` | `fix/resolve-issue-123` |
|
||||
| Refactor | `refactor/<description>` | `refactor/viewmodel-cleanup` |
|
||||
| Translation | `i18n/<language>` | `i18n/add-japanese` |
|
||||
| Documentation | `docs/<description>` | `docs/update-contributing` |
|
||||
|
||||
---
|
||||
|
||||
## Commit Messages
|
||||
|
||||
Write concise, present-tense commit messages that describe *what* the change does:
|
||||
|
||||
```
|
||||
feat: add biometric lock toggle in settings
|
||||
fix: resolve crash when deleting last workspace
|
||||
refactor: extract reminder logic into ReminderReceiver
|
||||
i18n: add Spanish translation strings
|
||||
```
|
||||
|
||||
Avoid vague messages like `fix stuff`, `WIP`, or `update`.
|
||||
|
||||
---
|
||||
|
||||
## Pull Request Guidelines
|
||||
|
||||
- Target the **`dev`** branch, not `master`/`main`.
|
||||
- Reference the issue your PR addresses (e.g., `Closes #42`).
|
||||
- Keep PRs focused — one feature or fix per PR.
|
||||
- Ensure the app builds and runs without errors before submitting.
|
||||
- Add or update string resources in `res/values/strings.xml` for any user-facing text.
|
||||
- If adding a new screen, register it in the navigation graph (`navigation/`).
|
||||
- If adding a new data entity, update the relevant DAO, model, repository, and DI module.
|
||||
- For new languages, add a corresponding `res/values-<lang>/strings.xml` file.
|
||||
|
||||
I'll review your pull request as soon as possible. Thank you for your contribution!
|
||||
|
||||
---
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
Flux follows **MVI (Model–View–Intent)** architecture with **Jetpack Compose** for the UI layer. The data flow is strictly unidirectional:
|
||||
|
||||
```
|
||||
User Interaction (Intent / Event)
|
||||
↓
|
||||
ViewModel
|
||||
↓
|
||||
State Update
|
||||
↓
|
||||
Composable UI (re-renders from State)
|
||||
```
|
||||
|
||||
- **Model** — Data layer: Room entities, DAOs, repositories.
|
||||
- **View** — Composable screens that observe `State` objects and emit `Events`.
|
||||
- **Intent/ViewModel** — Processes events, calls repository methods, and emits new `State`.
|
||||
|
||||
Side effects (navigation, toasts, etc.) are handled via `Effects` (one-shot flows) separate from persistent UI state.
|
||||
|
||||
**Dependency injection** is provided by **Hilt**, keeping ViewModels and repositories decoupled and testable.
|
||||
|
||||
---
|
||||
|
||||
## Module Structure
|
||||
|
||||
All source code lives under `app/src/main/java/com/flux/`.
|
||||
|
||||
```
|
||||
app/src/main/java/com/flux/
|
||||
│
|
||||
├── data/ # Data layer
|
||||
│ ├── dao/ # Room DAO interfaces (one per entity)
|
||||
│ ├── models/ # Room @Entity data classes and related data models
|
||||
│ ├── repository/ # Repository implementations (single source of truth)
|
||||
│ └── database/ # Room database definition and migrations
|
||||
│
|
||||
├── di/ # Hilt dependency injection modules
|
||||
│ ├── DataModules.kt # Provides database and DAO instances
|
||||
│ ├── RepositoryModules.kt # Binds repository interfaces to implementations
|
||||
│ └── Flux.kt # @HiltAndroidApp application class
|
||||
│
|
||||
├── navigation/ # Compose Navigation graph and route definitions
|
||||
│
|
||||
├── other/ # Utility and system-integration classes
|
||||
│ ├── utils/ # General-purpose helper functions and extensions
|
||||
│ ├── BackupManager.kt # Handles data backup and restore logic
|
||||
│ ├── ReminderReceiver.kt # BroadcastReceiver for scheduled reminders
|
||||
│ ├── BootReceiver.kt # Reschedules reminders after device reboot
|
||||
│ ├── workspaceIcons/ # Icon assets and mapping utilities for workspaces
|
||||
│ ├── BiometricAuthentication.kt # Biometric lock integration
|
||||
│ └── [other app-wide utilities]
|
||||
│
|
||||
├── ui/ # UI layer (Jetpack Compose)
|
||||
│ ├── common/ # Shared, reusable composables (buttons, dialogs, cards, etc.)
|
||||
│ ├── events/ # Sealed classes / data classes representing user intents per screen
|
||||
│ ├── states/ # Data classes holding observable UI state per screen
|
||||
│ ├── effects/ # One-shot side-effect flows (navigation, snackbars, etc.)
|
||||
│ ├── screens/ # Full-screen composables, one file per screen
|
||||
│ ├── viewModels/ # Hilt-injected ViewModels; process events, expose state
|
||||
│ └── theme/ # Material 3 color scheme, typography, and shape definitions
|
||||
│
|
||||
└── MainActivity.kt # Single-activity entry point; hosts NavHost
|
||||
```
|
||||
|
||||
```
|
||||
app/src/main/res/
|
||||
│
|
||||
├── drawable/ # Vector drawables and icons
|
||||
└── values/ # Resource files
|
||||
├── strings.xml # Default (English) strings
|
||||
└── values-<lang>/ # Translations (hi, fr, pt-BR, ru, de, es, nl, zh-rCN, …)
|
||||
```
|
||||
|
||||
### Adding a New Feature — Checklist
|
||||
|
||||
When building a new screen or feature, create/update files in this order:
|
||||
|
||||
1. **`data/models/`** — Define the Room entity or data class.
|
||||
2. **`data/dao/`** — Write the DAO interface with required queries.
|
||||
3. **`data/database/`** — Add the entity to the database and increment the version with a migration.
|
||||
4. **`data/repository/`** — Implement the repository exposing `Flow`s and suspend functions.
|
||||
5. **`di/`** — Bind the new DAO/repository in the appropriate Hilt module.
|
||||
6. **`ui/events/`** — Define a sealed class for all user actions on the new screen.
|
||||
7. **`ui/states/`** — Define the data class representing the full UI state for the screen.
|
||||
8. **`ui/viewModels/`** — Implement the ViewModel; inject the repository via Hilt.
|
||||
9. **`ui/screens/`** — Build the Composable screen; collect state, dispatch events.
|
||||
10. **`navigation/`** — Register the new route and composable in the nav graph.
|
||||
11. **`res/values/strings.xml`** — Add all user-facing strings; keep zero hardcoded text in composables.
|
||||
|
||||
---
|
||||
|
||||
## Code Style & Conventions
|
||||
|
||||
- **Language**: Kotlin only. No Java files.
|
||||
- **Formatting**: Follow standard Kotlin style (4-space indent, no wildcard imports).
|
||||
- **Naming**:
|
||||
- ViewModels: `<Feature>ViewModel` (e.g., `NoteViewModel`)
|
||||
- States: `<Feature>State` (e.g., `NoteState`)
|
||||
- Events: `<Feature>Event` (e.g., `NoteEvent`)
|
||||
- Effects: `<Feature>Effect` (e.g., `NoteEffect`)
|
||||
- Screens: `<Feature>Screen` (e.g., `NoteScreen`)
|
||||
- DAOs: `<Entity>Dao` (e.g., `NoteDao`)
|
||||
- **State hoisting**: Keep composables stateless where possible; hoist state to the ViewModel.
|
||||
- **No business logic in composables**: Composables should only render state and forward events.
|
||||
- **Strings**: All user-visible text must live in `strings.xml`. Never hardcode strings in composables.
|
||||
- **Markdown content**: Flux uses CommonMark + GitHub Flavored Markdown (GFM) with LaTeX math support. Refer to `Guide.md` for supported syntax.
|
||||
|
||||
---
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Layer | Technology |
|
||||
|-------|-----------|
|
||||
| Language | Kotlin 2.2.10 |
|
||||
| UI | Jetpack Compose + Material 3 (Material You) |
|
||||
| Architecture | MVI + ViewModel |
|
||||
| DI | Hilt |
|
||||
| Database | Room (SQLite abstraction) |
|
||||
| Navigation | Compose Navigation |
|
||||
| Markdown | CommonMark + Flexmark (HTML→MD) |
|
||||
| Math | LaTeX via inline renderer |
|
||||
| Async | Kotlin Coroutines + Flow |
|
||||
| Build | Gradle Kotlin DSL |
|
||||
|
||||
---
|
||||
|
||||
## Questions?
|
||||
|
||||
Open a [Discussion](https://github.com/chindaronit/Flux/discussions) or file an [Issue](https://github.com/chindaronit/Flux/issues) — happy to help you get oriented.
|
||||
@@ -354,16 +354,40 @@ val MIGRATION_7_8 = object : Migration(7, 8) {
|
||||
|
||||
val MIGRATION_8_9 = object : Migration(8, 9) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
if (!db.columnExists("TodoModel", "recurrence"))
|
||||
db.safeExec("ALTER TABLE TodoModel ADD COLUMN recurrence TEXT NOT NULL DEFAULT '{\"type\":\"NONE\"}'")
|
||||
|
||||
if(!db.columnExists("TodoModel", "startDateTime"))
|
||||
db.safeExec("ALTER TABLE TodoModel ADD COLUMN startDateTime INTEGER DEFAULT 0")
|
||||
// 1. Create corrected TodoModel table with proper constraints
|
||||
db.safeExec("""
|
||||
CREATE TABLE TodoModel_new (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
workspaceId TEXT NOT NULL DEFAULT '',
|
||||
title TEXT NOT NULL DEFAULT '',
|
||||
items TEXT NOT NULL DEFAULT '[]',
|
||||
startDateTime INTEGER NOT NULL DEFAULT ${System.currentTimeMillis()},
|
||||
recurrence TEXT NOT NULL DEFAULT '{"type":"NONE"}'
|
||||
)
|
||||
""".trimIndent())
|
||||
|
||||
// 2. Copy existing data, coercing any nulls
|
||||
db.safeExec("""
|
||||
INSERT INTO TodoModel_new
|
||||
SELECT
|
||||
id,
|
||||
COALESCE(workspaceId, ''),
|
||||
COALESCE(title, ''),
|
||||
COALESCE(items, '[]'),
|
||||
COALESCE(startDateTime, ${System.currentTimeMillis()}),
|
||||
COALESCE(recurrence, '{"type":"NONE"}')
|
||||
FROM TodoModel
|
||||
""".trimIndent())
|
||||
|
||||
// 3. Swap tables
|
||||
db.safeExec("DROP TABLE TodoModel")
|
||||
db.safeExec("ALTER TABLE TodoModel_new RENAME TO TodoModel")
|
||||
|
||||
// 4. Create TodoInstance table
|
||||
if (!db.tableExists("TodoInstance")) {
|
||||
db.safeExec(
|
||||
"""
|
||||
CREATE TABLE TodoInstance (
|
||||
db.safeExec("""
|
||||
CREATE TABLE TodoInstance (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
todoId TEXT NOT NULL,
|
||||
instanceDate INTEGER NOT NULL,
|
||||
@@ -371,16 +395,14 @@ val MIGRATION_8_9 = object : Migration(8, 9) {
|
||||
items TEXT NOT NULL DEFAULT '[]',
|
||||
FOREIGN KEY (todoId) REFERENCES TodoModel(id) ON DELETE CASCADE
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
db.safeExec(
|
||||
"""
|
||||
// 5. Create unique index on TodoInstance
|
||||
db.safeExec("""
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS
|
||||
index_TodoInstance_todoId_instanceDate
|
||||
ON TodoInstance(todoId, instanceDate)
|
||||
""".trimIndent()
|
||||
)
|
||||
""".trimIndent())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user