Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
5dc4f5a
refactor: reorg env and rename props
ovitrif Jan 5, 2026
e042680
chore: update README
ovitrif Jan 5, 2026
a66feef
chore: cleanup
ovitrif Jan 5, 2026
fefa5a3
chore: fix warnings
ovitrif Jan 5, 2026
08dc1af
chore: add compose-stability-analyzer dependency
ovitrif Jan 5, 2026
9e8d5ea
chore: update agp
ovitrif Jan 5, 2026
6f9fa8f
chore: update env
ovitrif Jan 5, 2026
464a2f4
chore: update ai rules
ovitrif Jan 6, 2026
64be2d3
fix: replace WalletRepo uiState with repo states
ovitrif Jan 6, 2026
0d3f3a5
feat: localize remaining hardcoded strings
ovitrif Jan 6, 2026
5467100
refactor: turn NotificationUtils fns to ext
ovitrif Jan 6, 2026
2b94b6f
feat: localize NodeLifecycleState strings
ovitrif Jan 6, 2026
962a030
chore: update gitignore
ovitrif Jan 6, 2026
40654c1
chore: update readme
ovitrif Jan 6, 2026
8c59952
feat: dynamic boost fee time estimates
ovitrif Jan 6, 2026
c23f3f6
feat: weather widget API calls caching
ovitrif Jan 7, 2026
d3a5abe
chore: cleanup todos
ovitrif Jan 7, 2026
79bb268
chore: remove deprecated LdkMigrationTest.kt
ovitrif Jan 7, 2026
58c15de
Merge branch 'master' into fix/all-warnings
ovitrif Jan 9, 2026
b684ca2
refactor: use class for all errors to fix warnings
ovitrif Jan 12, 2026
f359342
Merge branch 'master' into fix/all-warnings
ovitrif Jan 12, 2026
147d762
fix: complete error type refactoring
ovitrif Jan 12, 2026
5cb7d83
chore:remove unused rnBackupServerPubKey
ovitrif Jan 12, 2026
8bab20d
Merge branch 'master' into fix/all-warnings
ovitrif Jan 13, 2026
1fab9e6
chore: fix detekt issues in Env
ovitrif Jan 13, 2026
5f6fc75
chore: extract remaining non-dev strings
ovitrif Jan 13, 2026
6c48ea7
Merge branch 'master' into fix/all-warnings
ovitrif Jan 13, 2026
866a040
Merge branch 'master' into fix/all-warnings
ovitrif Jan 13, 2026
102bcac
chore: add power of 2 to MagicNumber ignoreNumbers
ovitrif Jan 13, 2026
d9f3fa9
refactor: solve or suppress all lint issue
ovitrif Jan 13, 2026
b8ea398
chore: update detekt.yml
ovitrif Jan 13, 2026
f4125ff
chore: update ai rules
ovitrif Jan 13, 2026
b955a4e
chore: cleanup LN repo and service
ovitrif Jan 14, 2026
9f7a62f
chore: review
ovitrif Jan 14, 2026
fa9a9de
chore: cleanup LN repo pass 2
ovitrif Jan 14, 2026
9d05e20
chore: fix instrumentation build
ovitrif Jan 14, 2026
72375a1
refactor: run registerForNotifications in bg
ovitrif Jan 14, 2026
f2a2625
chore: cleanup LN repo pass 3
ovitrif Jan 14, 2026
eabd268
chore: update ai rules
ovitrif Jan 14, 2026
b3502be
chore: cleanup and reorder translation keys
ovitrif Jan 14, 2026
21f8c24
refactor: localize article timestamps
ovitrif Jan 14, 2026
f8c5ad5
refactor: use runCatching and add listenerJob
ovitrif Jan 14, 2026
0c7fcba
chore: update ai rules
ovitrif Jan 14, 2026
07b1f7f
fix: toast blur on wallet restore error screen
ovitrif Jan 14, 2026
7e47e6e
fix: restart node to retry restore + lift state
ovitrif Jan 14, 2026
c7dcc50
refactor: remove repeated error logs
ovitrif Jan 14, 2026
b3ae57b
chore: consolidate logs
ovitrif Jan 14, 2026
5a13df0
refactor: dont reset logs on wipe in release app
ovitrif Jan 14, 2026
094518f
feat: add wipe logs button to dev settings
ovitrif Jan 14, 2026
aab6297
chore: cleanup dev settings vm
ovitrif Jan 14, 2026
8519098
fix: activity list update
ovitrif Jan 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .github/img/detekt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 12 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,12 @@ suspend fun getData(): Result<Data> = withContext(Dispatchers.IO) {
- ALWAYS run `./gradlew detekt` after code changes to check for new lint issues and fix accordingly
- ALWAYS ask clarifying questions to ensure an optimal plan when encountering functional or technical uncertainties in requests
- ALWAYS when fixing lint or test failures prefer to do the minimal amount of changes to fix the issues
- USE single-line commit messages under 50 chars; use template format: `feat: add something new`
- USE single-line commit messages under 50 chars; use conventional commit messages template format: `feat: add something new`
- USE `git diff HEAD sourceFilePath` to diff an uncommitted file against the last commit
- ALWAYS run `git status` to check ALL uncommitted changes after completing any code edits, then provide exactly 3 commit message suggestions covering the ENTIRE uncommitted diff
- ALWAYS check existing code patterns before implementing new features
- USE existing extensions and utilities rather than creating new ones
- ALWAYS consider applying YAGNI (You Aren't Gonna Need It) principle for new code
- ALWAYS consider applying YAGNI (You Ain't Gonna Need It) principle for new code
- ALWAYS reuse existing constants
- ALWAYS ensure a method exist before calling it
- ALWAYS remove unused code after refactors
Expand All @@ -175,14 +176,17 @@ suspend fun getData(): Result<Data> = withContext(Dispatchers.IO) {
- ALWAYS acknowledge datastore async operations run synchronously in a suspend context
- NEVER use `runBlocking` in suspend functions
- ALWAYS pass the TAG as context to `Logger` calls, e.g. `Logger.debug("message", context = TAG)`
- ALWAYS log errors at the final handling layer where the error is acted upon, not in intermediate layers that just propagate it
- ALWAYS use the Result API instead of try-catch
- NEVER wrap methods returning `Result<T>` in try-catch
- PREFER to use `it` instead of explicit named parameters in lambdas e.g. `fn().onSuccess { log(it) }.onFailure { log(it) }`
- NEVER inject ViewModels as dependencies - Only android activities and composable functions can use viewmodels
- NEVER hardcode strings and always preserve string resources
- ALWAYS localize in ViewModels using injected `@ApplicationContext`, e.g. `context.getString()`
- ALWAYS use `remember` for expensive Compose computations
- ALWAYS add modifiers to the last place in the argument list when calling `@Composable` functions
- ALWAYS add modifiers to the last place in the argument list when calling composable functions
- NEVER add parameters with default values BEFORE the `modifier` parameter in composable functions - modifier must be the FIRST optional parameter
- ALWAYS prefer `VerticalSpacer`, `HorizontalSpacer`, `FillHeight` and `FillWidth` over `Spacer` when applicable
- PREFER declaring small dependant classes, constants, interfaces or top-level functions in the same file with the core class where these are used
- ALWAYS create data classes for state AFTER viewModel class in same file
- ALWAYS return early where applicable, PREFER guard-like `if` conditions like `if (condition) return`
Expand All @@ -195,22 +199,23 @@ suspend fun getData(): Result<Data> = withContext(Dispatchers.IO) {
- ALWAYS be mindful of thread safety when working with mutable lists & state
- ALWAYS split screen composables into parent accepting viewmodel + inner private child accepting state and callbacks `Content()`
- ALWAYS name lambda parameters in a composable function using present tense, NEVER use past tense
- ALWAYS list 3 suggested commit messages after implementation work for the entire set of uncommitted changes
- NEVER use `wheneverBlocking` in unit test expression body functions wrapped in a `= test {}` lambda
- ALWAYS wrap unit tests `setUp` methods mocking suspending calls with `runBlocking`, e.g `setUp() = runBlocking { }`
- ALWAYS wrap unit tests `setUp` methods mocking suspending calls with `runBlocking`, e.g `setUp() = runBlocking {}`
- ALWAYS add business logic to Repository layer via methods returning `Result<T>` and use it in ViewModels
- ALWAYS use services to wrap RUST code exposed via bindings
- ALWAYS order upstream architectural data flow this way: `UI -> ViewModel -> Repository -> RUST` and vice-versa for downstream
- ALWAYS add new localizable string string resources in alphabetical order in `strings.xml`
- NEVER add string resources for strings used only in dev settings screens and previews and never localize acronyms
- ALWAYS use template in `.github/pull_request_template.md` for PR descriptions
- ALWAYS wrap `ULong` numbers with `USat` in arithmetic operations, to guard against overflows
- PREFER to use one-liners with `run { }` when applicable, e.g. `override fun someCall(value: String) = run { this.value = value }`
- PREFER to use one-liners with `run {}` when applicable, e.g. `override fun someCall(value: String) = run { this.value = value }`
- ALWAYS add imports instead of inline fully-qualified names
- ALWAYS place `@Suppress(...)` annotations at the narrowest possible scope (statement, property, or function level); NEVER use `@file:Suppress(...)` when a more targeted suppression is possible

### Architecture Guidelines

- Use `LightningNodeService` to manage background notifications while the node is running
- Use `LightningService` to wrap node's RUST APIs and manage the inner lifecycle of the node
- Use `LightningRepo` to defining the business logic for the node operations, usually delegating to `LightningService`
- Use `WakeNodeWorker` to manage the handling of remote notifications received via cloud messages
- Use `*Services` to wrap rust library code exposed via bindings
- Use CQRS pattern of Command + Handler like it's done in the `NotifyPaymentReceived` + `NotifyPaymentReceivedHandler` setup
65 changes: 36 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ This repository contains a **new native Android app** which is **not ready for p

#### 1. Firebase Configuration

Download `google-services.json` from the Firebase Console for each build flavor:
- **Dev/Testnet**: Place in `app/` (default location)
- **Mainnet**: Place in `app/src/mainnet/google-services.json`
Download `google-services.json` from the Firebase Console for each of the following build flavor groups,:
- dev/tnet/mainnetDebug: Place in `app/google-services.json`
- mainnetRelease: Place in `app/src/mainnetRelease/google-services.json`

> **Note**: Each flavor requires its own Firebase project configuration. The mainnet flavor will fail to build without its dedicated `google-services.json` file.

Expand Down Expand Up @@ -57,9 +57,16 @@ See also:

This project uses detekt with default ktlint and compose-rules for android code linting.

Recommended Android Studio plugins:
- EditorConfig
- Detekt
### IDE Plugins
The following IDE plugins are recommended for development with Android Studio or IntelliJ IDEA:
- [Compose Color Preview](https://plugins.jetbrains.com/plugin/21298-compose-color-preview)
- [Compose Stability Analyzer](https://plugins.jetbrains.com/plugin/28767-compose-stability-analyzer)
- [detekt](https://plugins.jetbrains.com/plugin/10761-detekt)
<details>
<summary>See screenshot on how to setup the Detekt plugin after installation.</summary>

![Detekt plugin setup][img_detekt]
</details>

**Commands**
```sh
Expand Down Expand Up @@ -112,16 +119,32 @@ The build config supports building 3 different apps for the 3 bitcoin networks (
- `mainnet` flavour = mainnet
- `tnet` flavour = testnet

### Build for Mainnet
### Build for Internal Testing

To build the mainnet flavor:
**Prerequisites**
Setup the signing config:
- Add the keystore file to root dir (i.e. `internal.keystore`)
- Setup `keystore.properties` file in root dir (`cp keystore.properties.template keystore.properties`)

**Routine**

Increment `versionCode` and `versionName` in `app/build.gradle.kts`, then run:
```sh
./gradlew assembleMainnetDebug # debug build
./gradlew assembleMainnetRelease # release build (requires signing config)
./gradlew assembleDevRelease
#./gradlew assembleMainnetDebug # mainnet debug build
# ./gradlew assembleRelease # for all flavors
```

> **Important**: Ensure `app/src/mainnet/google-services.json` exists before building. See [Firebase Configuration](#1-firebase-configuration).
APK is generated in `app/build/outputs/apk/_flavor_/release`. (`_flavor_` can be any of 'dev', 'mainnet', 'tnet').
Example for dev: `app/build/outputs/apk/dev/release`

### Build for Release

To build the mainnet flavor for release run:

```sh
./gradlew assembleMainnetRelease
```

### Build for E2E Testing

Expand Down Expand Up @@ -152,24 +175,6 @@ By default, geoblocking checks via API are enabled. To disable at build time, us
GEO=false E2E=true ./gradlew assembleDevRelease
```

### Build for Release

**Prerequisites**
Setup the signing config:
- Add the keystore file to root dir (i.e. `release.keystore`)
- Setup `keystore.properties` file in root dir (`cp keystore.properties.template keystore.properties`)

**Routine**

Increment `versionCode` and `versionName` in `app/build.gradle.kts`, then run:
```sh
./gradlew assembleDevRelease
# ./gradlew assembleRelease # for all flavors
```

APK is generated in `app/build/outputs/apk/_flavor_/release`. (`_flavor_` can be any of 'dev', 'mainnet', 'tnet').
Example for dev: `app/build/outputs/apk/dev/release`

## Contributing

### AI Code Review with Claude
Expand Down Expand Up @@ -223,3 +228,5 @@ Destructive operations like `rm -rf`, `git commit`, and `git push` still require

This project is licensed under the MIT License.
See the [LICENSE](./LICENSE) file for more details.

[img_detekt]: .github/img/detekt.png
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import java.util.Properties
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.compose.stability.analyzer)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.ksp)
Expand Down Expand Up @@ -218,6 +219,7 @@ dependencies {
androidTestImplementation(platform(libs.compose.bom))
implementation(libs.compose.material3)
implementation(libs.compose.material.icons.extended)
implementation(libs.compose.runtime.tracing)
implementation(libs.compose.ui)
implementation(libs.compose.ui.graphics)
implementation(libs.compose.ui.tooling.preview)
Expand Down
Loading
Loading