Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ba89b56
Phase 2A-1: Update ItemTag schema to Rails API v2 (#29)
dadachi Apr 24, 2026
74c36e5
Phase 2A-2: Remove NFC / QR / Scan (#31)
dadachi Apr 26, 2026
6d2b86c
Phase 2A-3: Generic CRUD UI for ItemTag (#32)
dadachi Apr 26, 2026
c138771
Comment out Unit Tests step in CI
dadachi Apr 26, 2026
8c3ff14
Reveal swipe actions from trailing (right) edge (#34)
dadachi Apr 26, 2026
337e164
Drop maximum_name_length; remove How-To-Use from Settings (#35)
dadachi Apr 27, 2026
cbb49f5
Add client-side length caps + truncation for Shop name/description (#36)
dadachi Apr 27, 2026
e23c4ba
ShopDetailCardView: 2-column layout with description (#37)
dadachi Apr 28, 2026
630b34f
Drop unused tags-scanned count and howToUse; tighten ItemTag labels (…
dadachi Apr 28, 2026
7c1188c
Slim onboarding to 4 slides and add ImageOrientation enum (#39)
dadachi Apr 29, 2026
2be742c
Add tests for Onboarding model, MainActivityViewModel, AuthIntercepto…
dadachi Apr 29, 2026
97c2814
Ignore .claude/scheduled_tasks.lock
dadachi Apr 29, 2026
ba8248e
Drop NFC-removal leftovers and tighten a few obvious patterns (#41)
dadachi Apr 29, 2026
0d35787
Fix dead password tests and add ItemTag whitespace-name regression te…
dadachi Apr 29, 2026
07b77ad
Remove dead code across utils and designsystem (#43)
dadachi Apr 30, 2026
b532433
Clean up legacy Number Tag/NFC artifacts and tighten utilities (#44)
dadachi May 1, 2026
6f93ba2
Ignore docs-private/
dadachi May 1, 2026
36300a2
Move phase2 prestep number-tag-rename doc out of repo
dadachi May 1, 2026
aee0984
Normalize substrate naming to single NativeAppTemplate stem (#45)
dadachi May 1, 2026
cc0f42e
Add Unreleased changelog entry for substrate naming normalization
dadachi May 2, 2026
b65bf90
Expand 3.2.0 changelog with full upstream entry
dadachi May 2, 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
5 changes: 3 additions & 2 deletions .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ jobs:
- name: Android Lint
run: ./gradlew :app:lintDebug

- name: Unit Tests
run: ./gradlew test --stacktrace
# The test takes a long time to run, so I've commented it out.
# - name: Unit Tests
# run: ./gradlew test --stacktrace
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ local.properties
# Sandbox stuff
_sandbox

# Private docs (local only)
docs-private/

# Android Studio captures folder
captures/

Expand Down Expand Up @@ -82,4 +85,4 @@ replay_pid*.log
.claude/logs/
.claude/tmp/
.claude/*.log

.claude/scheduled_tasks.lock
40 changes: 39 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,49 @@

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [3.2.0] - 2026-05-02

### Added
- Generic CRUD UI for Item Tags (replaces NFC/QR scan flow)
- Client-side length caps and truncation for Shop name and description
- Date + time on `completedAt` timestamps via `cardDateTimeString`
- Tests for Onboarding model, MainActivityViewModel, and AuthInterceptor
- `canCreateItemTags` / `canDeleteItemTags` permission tests on `ItemTagListViewModel`
- Configurable API endpoint via Gradle properties in Debug builds

### Changed
- Renamed "Number Tag" to "Item Tag" throughout the app
- Updated ItemTag schema to Rails API v2
- Collapsed 7-tier role hierarchy to `admin` / `member`
- Slimmed onboarding from 6 to 4 slides and replaced hero imagery
- ItemTag list card now uses a two-column row layout with vertically centered content
- Swipe actions reveal from the trailing (right) edge; renamed `reset` → `idle`
- Card date format changed to `yyyy/MM/dd`
- Renamed `validateEmail` → `isValidEmail`; converted `isNumeric` to a `String?` extension
- Normalized substrate naming to a single `NativeAppTemplate` stem (Kotlin symbols, theme styles, Gradle properties, error-code prefix)

### Removed
- NFC, QR, and Scan features (background tag reading, scan view, camera permission)
- "How To Use" entry from Settings
- Server-driven `maximum_name_length` (moved to client-side `NativeAppTemplateConstants`)
- Success toast for swipe complete/idle actions (reload is the success signal)
- Unused Teal10/Teal30/Teal90 color tokens
- Unused FileProvider declaration and `filepaths` resource
- NATIVEAPPTEMPLATE-3xxx NFC/scan error code range
- Dead code across `utils` and `designsystem`

### Fixed
- ItemTag whitespace-only name now fails validation
- Long shop names no longer overflow the right column in `ShopDetailCardView`
- Shop description field height regressed after switching to two-line `supportingText`

## [3.0.0] - 2026-04-05

### Added
- Implemented pagination for item tags list screen.
- Implemented CodedError system with NATA-XXXX error codes.
- Implemented CodedError system with NATIVEAPPTEMPLATE-XXXX error codes.
- Added unit tests for utils, network, and pre-push hook.
- Added Spotless + ktlint for Kotlin code formatting.
- Added app version and reorganized settings sections.
Expand Down
13 changes: 6 additions & 7 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ MVVM layered architecture following [Android Modern App Architecture](https://de
- **Network Layer** (`network/`): `AuthInterceptor` for token injection, `RequestHelper` for request construction.
- **DI** (`di/modules/`): Hilt modules — `NetModule` (Retrofit/OkHttp), `DataModule` (repository bindings), `DataStoreModule`, `DispatchersModule`, `CoroutineScopesModule`.
- **DataStore** (`datastore/`): Proto DataStore for user preferences. Proto definitions live in `app/src/main/proto/`.
- **Navigation**: `NatNavHost.kt` is the top-level nav graph. Three bottom-nav sections: Shops, Scan, Settings. Each section uses nested navigation graphs via `*BaseRoute`.
- **Navigation**: `NativeAppTemplateNavHost.kt` is the top-level nav graph. Three bottom-nav sections: Shops, Scan, Settings. Each section uses nested navigation graphs via `*BaseRoute`.

## Key Patterns

Expand All @@ -49,16 +49,15 @@ MVVM layered architecture following [Android Modern App Architecture](https://de
- **Proto DataStore**: User preferences and NFC scan state are persisted via Protocol Buffers (lite).

## Error Handling (CodedError System)
All errors should use the `CodedError` interface. Error codes use the `NATA-XXXX` prefix (NativeAppTemplate Android).
All errors should use the `CodedError` interface. Error codes use the `NATIVEAPPTEMPLATE-XXXX` prefix (NativeAppTemplate Android).

| Range | Type | Description |
|-------|------|-------------|
| NATA-1xxx | App/general errors | Unexpected errors, catch-all |
| NATA-2xxx | API/network errors | HTTP request failures, parsing errors |
| NATA-3xxx | NFC/scan errors | NFC tag read/write/scan failures |
| NATIVEAPPTEMPLATE-1xxx | App/general errors | Unexpected errors, catch-all |
| NATIVEAPPTEMPLATE-2xxx | API/network errors | HTTP request failures, parsing errors |

- New error types must implement `CodedError`
- Use `codedDescription` (not `message` or `localizedMessage`) in all user-facing error messages — this prepends `[NATA-XXXX]` for `CodedError` types
- Use `codedDescription` (not `message` or `localizedMessage`) in all user-facing error messages — this prepends `[NATIVEAPPTEMPLATE-XXXX]` for `CodedError` types

## Testing

Expand All @@ -77,4 +76,4 @@ cp scripts/pre-push .git/hooks/pre-push

## Connecting to Local API

The debug `buildConfigField` entries in `app/build.gradle.kts` read `NATEMPLATE_API_DOMAIN`, `NATEMPLATE_API_PORT`, and `NATEMPLATE_API_SCHEME` via `project.findProperty(...)` (not `System.getenv` — Android Studio launched from Finder/Dock does not inherit shell env). Set them in `~/.gradle/gradle.properties` (user-global, per-developer); the same config then works from both the terminal and the IDE. Falls back to `https://api.nativeapptemplate.com` when unset. One-off override: `./gradlew -PNATEMPLATE_API_DOMAIN=... assembleDebug`.
The debug `buildConfigField` entries in `app/build.gradle.kts` read `NATIVEAPPTEMPLATE_API_DOMAIN`, `NATIVEAPPTEMPLATE_API_PORT`, and `NATIVEAPPTEMPLATE_API_SCHEME` via `project.findProperty(...)` (not `System.getenv` — Android Studio launched from Finder/Dock does not inherit shell env). Set them in `~/.gradle/gradle.properties` (user-global, per-developer); the same config then works from both the terminal and the IDE. Falls back to `https://api.nativeapptemplate.com` when unset. One-off override: `./gradlew -PNATIVEAPPTEMPLATE_API_DOMAIN=... assembleDebug`.
12 changes: 5 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,10 @@ NativeAppTemplate-Free-Android uses modern Android development tools and practic
- Email Confirmation
- Forgot Password
- CRUD Operations for Shops (Create/Read/Update/Delete)
- CRUD Operations for Shops' Nested Resource, Number Tags (ItemTags) (Create/Read/Update/Delete)
- CRUD Operations for Shops' Nested Resource, Item Tags (Create/Read/Update/Delete)
- Force App Version Update
- Force Privacy Policy Version Update
- Force Terms of Use Version Update
- Generate QR Code Image for Number Tags (ItemTags) with a Centered Number
- NFC features for Number Tags (ItemTags): Write Application Info to a Tag, Read a Tag, Background Tag Reading
- And more!

## NFC Tag Operations
Expand Down Expand Up @@ -126,12 +124,12 @@ By default the debug build hits the hosted API (`https://api.nativeapptemplate.c
```
# Use your current Wi-Fi IP (macOS: `ipconfig getifaddr en0`), or 10.0.2.2 for emulator → host.
# Never use 127.0.0.1, localhost, or 0.0.0.0 — Rails and this app must agree on one reachable address.
NATEMPLATE_API_DOMAIN=192.168.1.21
NATEMPLATE_API_PORT=3000
NATEMPLATE_API_SCHEME=http
NATIVEAPPTEMPLATE_API_DOMAIN=192.168.1.21
NATIVEAPPTEMPLATE_API_PORT=3000
NATIVEAPPTEMPLATE_API_SCHEME=http
```

Then `./gradlew assembleDebug` — or Build → Rebuild Project from Android Studio. The debug `buildConfigField` entries in `app/build.gradle.kts` read these via `project.findProperty(...)`, so the same config works from both the terminal and the IDE. Remove the three properties to fall back to the hosted default. For a one-off override: `./gradlew -PNATEMPLATE_API_DOMAIN=192.168.1.21 -PNATEMPLATE_API_PORT=3000 -PNATEMPLATE_API_SCHEME=http assembleDebug`.
Then `./gradlew assembleDebug` — or Build → Rebuild Project from Android Studio. The debug `buildConfigField` entries in `app/build.gradle.kts` read these via `project.findProperty(...)`, so the same config works from both the terminal and the IDE. Remove the three properties to fall back to the hosted default. For a one-off override: `./gradlew -PNATIVEAPPTEMPLATE_API_DOMAIN=192.168.1.21 -PNATIVEAPPTEMPLATE_API_PORT=3000 -PNATIVEAPPTEMPLATE_API_SCHEME=http assembleDebug`.

Cleartext HTTP to private IPs is already permitted in debug via `app/src/debug/res/xml/network_security_config.xml`; the release config (in `app/src/main/`) keeps `api.nativeapptemplate.com` HTTPS-only.

Expand Down
9 changes: 3 additions & 6 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ android {
debug {
extra["alwaysUpdateBuildId"] = false
isDebuggable = true
buildConfigField("String", "DOMAIN", "\"${(project.findProperty("NATEMPLATE_API_DOMAIN") as String?)?.trim() ?: "api.nativeapptemplate.com"}\"")
buildConfigField("String", "PORT", "\"${(project.findProperty("NATEMPLATE_API_PORT") as String?)?.trim() ?: ""}\"")
buildConfigField("String", "SCHEME", "\"${(project.findProperty("NATEMPLATE_API_SCHEME") as String?)?.trim() ?: "https"}\"")
buildConfigField("String", "DOMAIN", "\"${(project.findProperty("NATIVEAPPTEMPLATE_API_DOMAIN") as String?)?.trim() ?: "api.nativeapptemplate.com"}\"")
buildConfigField("String", "PORT", "\"${(project.findProperty("NATIVEAPPTEMPLATE_API_PORT") as String?)?.trim() ?: ""}\"")
buildConfigField("String", "SCHEME", "\"${(project.findProperty("NATIVEAPPTEMPLATE_API_SCHEME") as String?)?.trim() ?: "https"}\"")
}

release {
Expand Down Expand Up @@ -96,13 +96,10 @@ dependencies {
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.profileinstaller)
implementation(libs.androidx.tracing.ktx)
implementation(libs.capturable)
implementation(libs.compose.qr.code)
implementation(libs.hilt.android)
implementation(libs.kotlin.stdlib.jdk8)
implementation(libs.kotlinx.coroutines.guava)
implementation(libs.kotlinx.serialization.json)
implementation(libs.lottie.compose)
implementation(libs.okhttp)
implementation(libs.okhttp.logging.interceptor)
implementation(libs.retrofit)
Expand Down
31 changes: 3 additions & 28 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="false" />

<application
android:name=".NativeAppTemplateApplication"
android:allowBackup="false"
Expand All @@ -17,39 +14,17 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Nat.Splash"
android:theme="@style/Theme.NativeAppTemplate.Splash"
android:networkSecurityConfig="@xml/network_security_config"
tools:ignore="GoogleAppIndexingWarning"
tools:targetApi="tiramisu">
<!--
`singleTask` for Background Tag Reading. Avoid running MainActivity onCreate again with Background Tag Reading.
https://qiita.com/takagimeow/items/48b37c55ad8d73d5da88
-->
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTask">
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="https"
android:host="api.nativeapptemplate.com"
android:path="/scan"
/>
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application>
</manifest>
</manifest>
9 changes: 4 additions & 5 deletions app/src/main/assets/item_tag.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@
"type": "item_tag",
"attributes": {
"shop_id": "5712F2DF-DFC7-A3AA-66BC-191203654A1A",
"queue_number": "A001",
"name": "A001",
"description": "",
"position": 1,
"state": "idled",
"scan_state": "unscanned",
"created_at": "2025-01-02T12:00:00.000Z",
"shop_name": "8th & Townsend",
"customer_read_at": "2025-01-02T12:00:01.000Z",
"completed_at": "2025-01-02T12:00:03.000Z",
"already_completed": false
"completed_at": "2025-01-02T12:00:03.000Z"
}
}
}
27 changes: 12 additions & 15 deletions app/src/main/assets/item_tags.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,41 @@
"type": "item_tag",
"attributes": {
"shop_id": "5712F2DF-DFC7-A3AA-66BC-191203654A1A",
"queue_number": "A001",
"name": "A001",
"description": "",
"position": 1,
"state": "idled",
"scan_state": "unscanned",
"created_at": "2025-01-02T12:00:00.000Z",
"shop_name": "8th & Townsend",
"customer_read_at": "2025-01-02T12:00:01.000Z",
"completed_at": "2025-01-02T12:00:03.000Z",
"already_completed": false
"completed_at": "2025-01-02T12:00:03.000Z"
}
},
{
"id": "9712F2DF-DFC7-A3AA-66BC-191203654A1",
"type": "shop",
"attributes": {
"shop_id": "5712F2DF-DFC7-A3AA-66BC-191203654A1A",
"queue_number": "A002",
"name": "A002",
"description": "",
"position": 2,
"state": "idled",
"scan_state": "unscanned",
"created_at": "2025-01-02T12:00:00.000Z",
"shop_name": "8th & Townsend",
"customer_read_at": "2025-01-02T12:00:01.000Z",
"completed_at": "2025-01-02T12:00:03.000Z",
"already_completed": false
"completed_at": "2025-01-02T12:00:03.000Z"
}
},
{
"id": "9712F2DF-DFC7-A3AA-66BC-191203654A1C",
"type": "shop",
"attributes": {
"shop_id": "5712F2DF-DFC7-A3AA-66BC-191203654A1A",
"queue_number": "A003",
"name": "A003",
"description": "",
"position": 3,
"state": "idled",
"scan_state": "unscanned",
"created_at": "2025-01-02T12:00:00.000Z",
"shop_name": "8th & Townsend",
"customer_read_at": "2025-01-02T12:00:01.000Z",
"completed_at": "2025-01-02T12:00:03.000Z",
"already_completed": false
"completed_at": "2025-01-02T12:00:03.000Z"
}
}
]
Expand Down
Loading
Loading