Skip to content

Commit e65811e

Browse files
dadachiclaude
andauthored
Substrate v2: Generic CRUD app foundation (#47)
* Phase 2A-1: Port ItemTag schema update from paid iOS Ports nativeapptemplate/NativeAppTemplate-iOS#49 - ItemTag model: queueNumber → name; add description/position; remove scanState/customerReadAt/alreadyCompleted - ItemTagAdapter: parse name/description/position; drop legacy attrs - PermissionsRequest: maximum_queue_number_length meta now optional with default 256 (full removal deferred to 2A-3) - SessionController / App.swift null stub: maximumQueueNumberLength default 0 → 256 - ViewModels: queueNumber → name; validateQueueNumberLength → validateNameLength; hasInvalidDataQueueNumber → hasInvalidDataName - Drop 5 production call sites of the deleted fields (ShowTagInfoScanResultView, ScanViewModel, ShopDetailViewModel, ShopDetailCardView, MainViewModel) — each flagged // TODO: removed in Phase 2A-2 - Shop model: drop displayShopServerPath / displayShopServerUrl; drop "Open Server Number Tags Webpage" link from ShopDetailView; delete NumberTagsWebpageListView/ViewModel and the Shop Settings section that linked to it - Tests: update helpers and adapter fixtures for the new schema; delete ScanViewModelTest (queue/NFC-specific, goes away in 2A-2), completeTagWhenAlreadyCompleted test (asserted the removed branch), and NumberTagsWebpageListViewModelTest Main target and test target both compile. Some tests may fail at runtime (deferred to Phase 2A-4 per plan). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Phase 2A-2: Remove NFC / QR / Scan Ports nativeapptemplate/NativeAppTemplate-iOS#50 to the Free iOS app. Drops all NFC, QR-code, and Scan-tab functionality; ItemTagDetailView becomes a placeholder until Phase 2A-3 lands the full generic CRUD UI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * remove NativeAppTemplate.xcsheme * Restore item-tag swipe actions on ShopDetailView; rename reset → idle Reverts the upstream PR's removal of the complete/idle swipe actions on ShopDetailView so users can still mark items completed or send them back to idled without going through Settings. Renames the user-facing "reset" terminology to "idle" since the action just transitions a completed tag back to the idled state: - ItemTag UI/ViewModel/strings: resetTag → idleTag, isResetting → isIdling, itemTagReset[Error] → itemTagIdled[Error], button label "Reset" → "Idle". - ItemTag repository layer: ItemTagRepositoryProtocol.reset → idle, ItemTagRepository / DemoItemTagRepository / TestItemTagRepository updated. - ItemTag network layer: ResetItemTagRequest → IdleItemTagRequest, ItemTagsService.resetItemTag → idleItemTag, HTTP path /shopkeeper/item_tags/:id/reset → /shopkeeper/item_tags/:id/idle. Backend must expose PATCH /shopkeeper/item_tags/:id/idle. Shop repository / network layer left as `reset` (no UI surfaces it). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Silence success toasts on item-tag complete/idle in ShopDetailView Ports nativeapptemplate/NativeAppTemplate-iOS#52. The swipe action already reflects the state change via ShopDetailCardView's re-render after reload(); the extra success toast was redundant noise. Errors still post to the message bus so users still see feedback when something actually fails. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Phase 2A-3: Generic CRUD UI for ItemTag Ports nativeapptemplate/NativeAppTemplate-iOS#53 to the Free iOS app. Replaces the Phase 2A-2 ItemTagDetailView placeholder with a full generic CRUD UI; relaxes Create/Edit validation; adds a description field; makes ItemTag.position non-optional. Schema: - ItemTag.position: Int? -> Int = 0; toJson() excludes position (server auto-assigns). - ItemTagAdapter strict-guards position; throws invalidOrMissingAttributes when missing. - Mock ItemTag(position: nil) -> position: 1 across tests. Rename: - maximumQueueNumberLength -> maximumNameLength across SessionController, protocol, App.swift NullSessionController, TestSessionController, PermissionsRequest. Default fallback 256 -> 100. - PermissionsRequest reads meta["maximum_name_length"]. Create + Edit: - Validation: name 1-100 chars (any string), description 0-1000 chars. Drops alphanumeric and count >= 2 checks; standard keyboard. - New description field with multi-line TextEditor. - Edit's hasInvalidData also requires name OR description changed. Detail view full rewrite: - State badge (IdlingTag / CompletedTag), description display, completedAt timestamp. - Toggle button (Mark as completed / Mark as idled) calling ItemTagRepository.complete(id:) / idle(id:). - New isToggling, completeItemTag(), idleItemTag(); failure posts error toast, success silently re-renders (matching PR #52). ItemTagListCardView: name + description preview (2 lines) + state badge + completedAt. Constants: removed tagNumber/tagNumberIsInvalid; added nameLabel, descriptionLabel, itemTagNamePlaceholder, itemTagNameIsInvalid, itemTagDescriptionIsInvalid, completedAtLabel, markAsCompleted, markAsIdled, itemTagNameHelp/itemTagDescriptionHelp helpers, and NativeAppTemplateConstants.maximumItemTagDescriptionLength = 1_000. Deviations from upstream PR #53: - Skipped permission gates (canUpdateShops/canDeleteShops/canCreateShops) - Free repo has no Permission system. Edit/delete/toggle/create are unconditionally available. Skipped the upstream permissionChecking parameterized tests for the same reason. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Bump TestSessionController.maximumNameLength default 4 → 100 The new hasInvalidDataChangeDetection test in ItemTagEditViewModelTest sets name = "Updated" (7 chars), which tripped the old default of 4 (carried over from the queue-number-length era). Match upstream Paid PR's default of 100. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * remove scan folder * Display completedAt as yyyy/MM/dd HH:mm regardless of locale Ports nativeapptemplate/NativeAppTemplate-iOS#54. ItemTagDetailView's completed-timestamp row was using Text(completedAt.formatted()), which is locale-dependent — same instant rendered as 4/26/2026, 10:30 AM (en_US), 26/04/2026, 10:30 (en_GB), or 2026/04/26 10:30 (ja_JP). Non-Gregorian calendars (Buddhist, Japanese imperial) could shift the year value entirely. Introduces Date.cardDateTimeString that stitches existing cardDateString + cardTimeString format constants, and pins Locale(identifier: "en_US_POSIX") on the shared DateFormatter.formatter(for:) factory so every fixed-format formatter is calendar/locale-stable. - DateFormatter+Extensions.swift: - cardDateString: "MMM dd yyyy" → "yyyy/MM/dd" (the MMM token was locale-aware; the constant had zero callers). - formatter(for:) factory: pins Locale(identifier: "en_US_POSIX"). - Date+Extensions.swift: new cardDateTimeString computed property. - ItemTagDetailView, ShopDetailCardView, ItemTagListCardView: switched to cardDateTimeString. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Remove "How To Use" link from Support section in SettingsView Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Remove NativeAppTemplate.entitlements The associated-domains entitlement is no longer needed; the entitlements file is now empty and dropped. CODE_SIGN_ENTITLEMENTS unset for both Debug and Release configs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Drop maximum_name_length from permissions; move to Constants Ports nativeapptemplate/NativeAppTemplate-iOS#59. Two-step cleanup of maximumNameLength: 1. Stop reading maximum_name_length from /shopkeeper/permissions. The client already tolerated its absence via a "?? 100" fallback, so this is dead plumbing. Drops the field from PermissionsResponse, the meta read in PermissionsRequest.handle, and the assignment in SessionController.fetchPermissions. 2. Move maximumNameLength from SessionController to Constants. Now that it's a fixed value, it's just a constant — like maximumItemTagDescriptionLength. Adds NativeAppTemplateConstants.maximumItemTagNameLength = 100, drops maximumNameLength from SessionControllerProtocol / SessionController / NullSessionController / TestSessionController. ItemTagCreateViewModel and ItemTagEditViewModel now read the constant directly and no longer take sessionController. Updates the call sites in ItemTagListView / ItemTagDetailView. Tests rewritten to drop sessionController field and update truncation tests to use 100+ char strings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add client-side length caps + truncation for Shop name/description Ports nativeapptemplate/NativeAppTemplate-iOS#60. Mirror the ItemTag pattern from PR #49 for Shop. Server has no caps on Shop name/description; this is a client-only UX guard. - Constants: maximumShopNameLength = 100, maximumShopDescriptionLength = 1_000. New strings: shopNameIsInvalid, shopDescriptionIsInvalid, plus shopNameHelp(maximumLength:) / shopDescriptionHelp(maximumLength:) parametric helpers. - ShopCreateViewModel + ShopBasicSettingsViewModel: split hasInvalidData into hasInvalidDataName + hasInvalidDataDescription, expose maximumNameLength / maximumDescriptionLength, add validateNameLength() / validateDescriptionLength(). - ShopCreateView + ShopBasicSettingsView: wire .onChange truncation on Name and Description; switch to two-line footer (always-visible help + conditional red "is invalid" text), matching ItemTagCreateView. - Tests: added maximumNameLength, maximumDescriptionLength, parametric nameValidation / descriptionValidation, and the two truncation tests on both viewmodel test suites; replaced the old simple hasInvalidData(name:) parametric test in ShopCreateViewModelTest. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Drop unused tags-scanned count and howToUse; tighten ItemTag labels - Drop scannedItemTagsCount from Shop model, adapter, demo repo, and tests; remove the "tags scanned by customers" stat from ShopListCardView. - Replace ShopDetailView "Learn More" link with a left-aligned shopDetailInstruction ("Swipe an item tag to change its status."). - Swap the OnboardingView "How to use" link for the Support Website link; trim onboarding from 13 to 8 descriptions and drop unused String(localized:) wrap. - Normalize ItemTag string keys: editTag/addTag/deleteTag/etc. -> editItemTag/addItemTag/deleteItemTag/etc. - Drop unused constants: howToUseUrl, learnMore, createShops, createTags. * Wrap String constants in enum Strings namespace Convert the large `extension String` block in `Constants.swift` to a caseless `enum Strings` namespace, matching the existing `NativeAppTemplateConstants` idiom in the same file. Inline the two `cardDateString`/`cardTimeString` literals in `DateFormatter+Extensions.swift` and remove the small extension they lived in. Update call sites across the app and tests from `String.foo` to `Strings.foo`, including dot-shorthand uses for `message:`, `text:`, `buttonTitle:`, and `?.message ==` parameters. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Replace onboarding image assets with new set; add hero asset Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Slim onboarding to 4 slides; ImageOrientation enum; hero asset Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Drop onboarding reload ceremony Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add unit tests for untested Models and Date extension Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Ignore .claude/scheduled_tasks.lock Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Remove vestigial code from NFC/Scan removal Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * README: drop stale NFC/QR feature lines Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Remove dead code: ImageSaver, composited(), unused colors and fonts Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Cleanup: dead Date helper, swiftier String predicates, README env vars - Drop unused cardTimeAgoInWordsDateString and its timeAgoInWordsDateFormatter - Move isBlank and validateEmail from Utility static helpers to String computed-property extensions (isBlank, isValidEmail) and update all call sites in production and tests - Fold UtilityTest cases into StringExtensionsTest and delete UtilityTest - README: clarify that Xcode is in practice the only NATEMPLATE_API_* env var injector, so the hardcoded fallbacks are what keep Debug builds working when launched from SpringBoard or after Xcode disconnects Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Remove Beta build configuration The Beta config has no shared scheme and isn't referenced by CI, README, or any xcconfig — drop it from the pbxproj so the project only ships Debug and Release. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Remove unused String helpers: image(withAttributes:size:), isAlphanumeric The String.image(...) renderer and both isAlphanumeric() overloads have no production callers — only their own tests reference them. Drop the helpers and their tests; UIKit/Foundation imports collapse to Foundation since the remaining isBlank / isValidEmail need only that. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * add docs-private to gitignore * phase2-prestep-number-tag-rename.md removed * Normalize substrate naming: drop NATI-/NATEMPLATE_API_ abbreviations - Env vars: NATEMPLATE_API_{SCHEME,DOMAIN,PORT} -> NATIVEAPPTEMPLATE_API_* in Constants.swift and README.md - Error codes: NATI-XXXX -> NATIVEAPPTEMPLATE-XXXX across production and test Swift files, plus CHANGELOG.md - CLAUDE.md: error-code section reframed as a shared iOS+Android prefix; dropped the stale NFCError.swift / NATIVEAPPTEMPLATE-3xxx row (file no longer present in this fork) Mirrors upstream PR nativeapptemplate/NativeAppTemplate-iOS#71. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add CHANGELOG entry for substrate naming normalization Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Update CHANGELOG for 3.2.0 release Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Bump version to 3.2.0 (build 10) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d67e78e commit e65811e

224 files changed

Lines changed: 1911 additions & 5054 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.

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,7 @@ secrets.*.xcconfig
8989
.claude/logs/
9090
.claude/tmp/
9191
.claude/*.log
92+
.claude/scheduled_tasks.lock
93+
94+
# Private docs
95+
docs-private/

CHANGELOG.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [3.2.0] - 2026-05-02
11+
12+
### Removed
13+
- NFC, QR code, and Scan functionality (Phase 2A-2)
14+
- `tagsScanned` count and `howToUse` fields from ItemTag
15+
- `maximum_name_length` from server permissions; moved to client `Constants`
16+
- Onboarding reload ceremony
17+
- Dead code: `ImageSaver`, `composited()`, unused colors and fonts, dead `Date` helper
18+
19+
### Changed
20+
- Renamed "Number Tag" to "Item Tag" across labels and identifiers
21+
- Ported ItemTag schema and generic CRUD UI from paid iOS (Phase 2A-1, 2A-3)
22+
- Display `completedAt` as `yyyy/MM/dd HH:mm` regardless of locale
23+
- Added client-side length caps and truncation for Shop name/description
24+
- Wrapped String constants in `enum Strings` namespace
25+
- Slimmed onboarding to 4 slides; introduced `ImageOrientation` enum
26+
- Tightened ItemTag labels and silenced success toasts on complete/idle
27+
- Read API endpoint from env vars in Debug builds
28+
- Renamed error code prefix `NATI-` to `NATIVEAPPTEMPLATE-` and env var prefix `NATEMPLATE_API_` to `NATIVEAPPTEMPLATE_API_`
29+
- Swiftier String predicates; replaced custom helpers with stdlib equivalents
30+
31+
### Added
32+
- Unit tests for previously untested Models and `Date` extension
33+
- `CONTRIBUTING.md` and `CODE_OF_CONDUCT.md`
34+
35+
### Fixed
36+
- Onboarding flow and asset cleanup
37+
- README: dropped stale NFC/QR feature lines and updated env var references
38+
1039
## [3.1.1] - 2026-04-06
1140

1241
### Changed
@@ -17,7 +46,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1746

1847
### Added
1948
- Pagination support for item tags list
20-
- CodedError system with `NATI-XXXX` prefixed error codes
49+
- CodedError system with `NATIVEAPPTEMPLATE-XXXX` prefixed error codes
2150
- App version display in settings
2251
- Design system constants (spacing, animation, glass, layout, corner radius)
2352
- GlassButtonStyle and GlassCard components with glassmorphism styling

CLAUDE.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,18 +107,17 @@ NativeAppTemplate/
107107
```
108108

109109
### Error Handling (CodedError System)
110-
All errors use the `CodedError` protocol in `NativeAppTemplate/Common/Errors/`. Error codes use the `NATI-XXXX` prefix (NativeAppTemplate iOS) to distinguish from Android (`NATA-XXXX`).
110+
All errors use the `CodedError` protocol in `NativeAppTemplate/Common/Errors/`. Error codes share the `NATIVEAPPTEMPLATE-XXXX` prefix across iOS and Android.
111111

112112
| Range | Type | File |
113113
|-------|------|------|
114-
| NATI-1xxx | App/general errors | `AppError.swift` |
115-
| NATI-2xxx | API/network errors | `NativeAppTemplateAPIError.swift` |
116-
| NATI-3xxx | NFC/scan errors | `NFCError.swift` |
114+
| NATIVEAPPTEMPLATE-1xxx | App/general errors | `AppError.swift` |
115+
| NATIVEAPPTEMPLATE-2xxx | API/network errors | `NativeAppTemplateAPIError.swift` |
117116

118117
- New error types must conform to `CodedError` and be placed in `Common/Errors/`
119118
- Use `error.codedDescription` (not `error.localizedDescription`) in all error messages
120119
- Use `Message(error: error)` convenience to post errors to `MessageBus`
121-
- Error code numbers must match across iOS and Android (only the prefix differs)
120+
- Error code numbers must match across iOS and Android
122121

123122
### Dependencies (Swift Package Manager)
124123
- KeychainAccess (4.2.2) - Secure credential storage

NativeAppTemplate.xcodeproj/project.pbxproj

Lines changed: 6 additions & 212 deletions
Large diffs are not rendered by default.

NativeAppTemplate.xcodeproj/xcshareddata/xcschemes/NativeAppTemplate.xcscheme

Lines changed: 0 additions & 95 deletions
This file was deleted.

NativeAppTemplate/App.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,9 @@ private final class NullSessionController: SessionControllerProtocol {
3333
}
3434

3535
var shouldPopToRootView: Bool = false
36-
var didBackgroundTagReading: Bool = false
37-
var completeScanResult = CompleteScanResult()
38-
var showTagInfoScanResult = ShowTagInfoScanResult()
3936
var shouldUpdateApp: Bool = false
4037
var shouldUpdatePrivacy: Bool = false
4138
var shouldUpdateTerms: Bool = false
42-
var maximumQueueNumberLength: Int = 0
4339
var shopLimitCount: Int = 0
4440
var shopkeeper: Shopkeeper?
4541
var hasPermissions: Bool {

NativeAppTemplate/AppSingletons.swift

Lines changed: 0 additions & 17 deletions
This file was deleted.

NativeAppTemplate/Common/Errors/AppError.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ enum AppError: CodedError {
1616
var errorCode: String {
1717
switch self {
1818
case .unexpected:
19-
"NATI-1001"
19+
"NATIVEAPPTEMPLATE-1001"
2020
}
2121
}
2222

NativeAppTemplate/Common/Errors/CodedError.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
// NativeAppTemplate
44
//
55

6-
// Error codes use the `NATI-XXXX` prefix (NativeAppTemplate iOS).
7-
// Android uses `NATA-XXXX`.
8-
// Ranges: 1xxx App errors, 2xxx API errors, 3xxx NFC errors.
6+
// Error codes share the `NATIVEAPPTEMPLATE-XXXX` prefix across iOS and Android.
7+
// Ranges: 1xxx App errors, 2xxx API errors.
98

109
import Foundation
1110

NativeAppTemplate/Common/Errors/NFCError.swift

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)