Bring OLX categories, listing signals and the flips ledger to iOS #22

Merged
kubus merged 3 commits from feat/ios-reseller into feat/flip-ledger 2026-10-01 12:38:50 +00:00
Owner

Stacked on #21 (which is stacked on #20). Merge #20, then #21, then switch this PR's base to main.

The iOS side of #18, #20 and #21.

ScoutKit

  • Listing: postedAt, refreshedAt, promoted, sellerType, firstSeenAt. All optional, so older servers still decode.
  • Watch/WatchDraft: olxCategory, sellerType, ignorePromoted. They are always sent, as null when unset, so an edit can clear them. The category is dropped when OLX isn't a source.
  • MarketWatch/MarketWatchDraft/MarketWatchPatch: olxCategory as criteria, compared by id like the server, so relabelling it doesn't start a new series.
  • Client calls for OLX category counts and the flips API.
  • Flips.swift: a port of src/profit.ts (fees, net, estimate, quarterly limit, DAC7, sales record and CSV) plus FlipsSummary, matching the web page.
  • Demo mode: category and flips routes with seed flips, plus listing signals on the demo feed.

App

  • OLX category picker (loads OLX's counts for the query) in the watch editor, research-watch editor and search filters.
  • Seller and Skip promoted listings in the watch editor. The settings are shown on the watch and research detail screens.
  • Listing rows and search results show Posted 14 mo ago · bumped 2 h ago, Promoted and Business. Listing details add Posted and Seller.
  • Listing details → Flip: resale estimate after the chosen platform's fee, and I bought this.
  • Market → Flips (a third segment):
    • summary with the quarterly limit bar and a "sold but still listed" warning
    • unsold and sold lists with swipe actions (Sold, Edit, Unsell, Delete)
    • add/edit sheet, and a mark-sold sheet with the delist checklist and a live net
    • DAC7 counts
    • the sales record for any quarter, shared as a CSV file
    • fee presets
  • New flips screenshot screen. Live refresh on the flips event.

Verification

  • ScoutKit builds and tests on Linux with Swift 6.2: swift test passes 62/62, 12 of them new.
  • ScoutKit against a real server: a throwaway XCTest (not committed) ran on a temporary server with live OLX. It covered:
    • category counts (Karty graficzne · 83)
    • a watch with category, seller and skip-promoted, then an edit clearing the seller
    • search scoped to the category: 49/49 OLX results had posting times and seller type
    • research-watch category removal
    • the full flip cycle: sale fee 63,70 zł stored, sale before purchase rejected with 400, unsell, presets, delete
  • SwiftUI app target: it can't be compiled on Linux, so every app file was only checked with swiftc -parse, and every ScoutKit symbol it uses was checked against the public API. The macOS CI build is the real check.

🤖 Generated with Claude Code

**Stacked on #21** (which is stacked on #20). Merge #20, then #21, then switch this PR's base to `main`. The iOS side of #18, #20 and #21. ## ScoutKit - `Listing`: `postedAt`, `refreshedAt`, `promoted`, `sellerType`, `firstSeenAt`. All optional, so older servers still decode. - `Watch`/`WatchDraft`: `olxCategory`, `sellerType`, `ignorePromoted`. They are always sent, as null when unset, so an edit can clear them. The category is dropped when OLX isn't a source. - `MarketWatch`/`MarketWatchDraft`/`MarketWatchPatch`: `olxCategory` as criteria, compared by id like the server, so relabelling it doesn't start a new series. - Client calls for OLX category counts and the flips API. - `Flips.swift`: a port of `src/profit.ts` (fees, net, estimate, quarterly limit, DAC7, sales record and CSV) plus `FlipsSummary`, matching the web page. - Demo mode: category and flips routes with seed flips, plus listing signals on the demo feed. ## App - **OLX category picker** (loads OLX's counts for the query) in the watch editor, research-watch editor and search filters. - **Seller** and **Skip promoted listings** in the watch editor. The settings are shown on the watch and research detail screens. - **Listing rows and search results** show *Posted 14 mo ago · bumped 2 h ago*, *Promoted* and *Business*. **Listing details** add Posted and Seller. - **Listing details → Flip:** resale estimate after the chosen platform's fee, and **I bought this**. - **Market → Flips** (a third segment): - summary with the quarterly limit bar and a "sold but still listed" warning - unsold and sold lists with swipe actions (Sold, Edit, Unsell, Delete) - add/edit sheet, and a mark-sold sheet with the delist checklist and a live net - DAC7 counts - the sales record for any quarter, shared as a CSV file - fee presets - New `flips` screenshot screen. Live refresh on the `flips` event. ## Verification - **ScoutKit** builds and tests on Linux with Swift 6.2: `swift test` passes 62/62, 12 of them new. - **ScoutKit against a real server:** a throwaway XCTest (not committed) ran on a temporary server with live OLX. It covered: - category counts (Karty graficzne · 83) - a watch with category, seller and skip-promoted, then an edit clearing the seller - search scoped to the category: 49/49 OLX results had posting times and seller type - research-watch category removal - the full flip cycle: sale fee 63,70 zł stored, sale before purchase rejected with 400, unsell, presets, delete - **SwiftUI app target:** it can't be compiled on Linux, so every app file was only checked with `swiftc -parse`, and every ScoutKit symbol it uses was checked against the public API. The macOS CI build is the real check. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
ScoutKit:
- Listing posted/refreshed/promoted/seller fields; watch OLX category,
  seller type and skip-promoted settings (sent as null so edits clear them);
  research-watch category as criteria, compared by id like the server.
- Client calls for OLX category counts and the flips ledger, plus a Swift
  port of src/profit.ts (fees, net, estimates, quarterly limit, DAC7,
  sales record and CSV) and a FlipsSummary matching the web page.
- Demo routes for categories and flips, demo listing signals, and 12 new
  tests (62 total, run on Linux with Swift 6.2).

App:
- OLX category picker (loads OLX's counts for the query) in the watch,
  research-watch and search filters; Seller and "Skip promoted listings"
  in the watch editor; settings shown on detail screens.
- "Posted 14 mo ago · bumped 2 h ago", Promoted and Business on listing
  rows and search results; Posted and Seller in listing details.
- Listing details: resale estimate after the chosen platform's fee and
  "I bought this".
- Market → Flips: summary with the quarterly limit bar, unsold and sold
  lists with swipe actions, add/edit, mark sold with the delist checklist,
  DAC7 counts, the sales record shared as CSV, and fee presets. New
  "flips" screenshot screen.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Merge branch 'feat/flip-ledger' into feat/ios-reseller
Some checks failed
ci / verify (pull_request) Has been cancelled
52ca3f95a1
Merge feat/flip-ledger (with main) into feat/ios-reseller
Some checks failed
ci / verify (pull_request) Has been cancelled
36d88d1463
Main replaced the iOS refresh-on-any-event set with LiveInvalidation scopes,
so 'flips' gets its own scope and AppModel.flipsToken, which FlipsView keys
its load on. The Market switch keeps Flips next to the store-backed
Research and Analytics views.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
kubus merged commit 00f8d56450 into feat/flip-ledger 2026-10-01 12:38:50 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
kubus/scout!22
No description provided.