diff --git a/web/src/at_record_web/effects.gleam b/web/src/at_record_web/effects.gleam index dc96d1b..6793d72 100644 --- a/web/src/at_record_web/effects.gleam +++ b/web/src/at_record_web/effects.gleam @@ -501,9 +501,8 @@ pub fn add_item(form: Form) -> Effect(Msg) { ) } -/// Same write as `add_item`, tagged with the barcode so the batch importer's -/// row can be found again when the response comes back; `on_done` is the msg -/// constructor to dispatch (the sequential batch loop). +/// Same write as `add_item`, tagged with the barcode so the batch importer +/// can find the row again when the response lands. pub fn add_scanned( barcode: String, form: Form, @@ -516,9 +515,8 @@ pub fn add_scanned( ) } -/// A network-matched scan row's import: the release is already known, so -/// this adopts it exactly like the browse grid's "I HAVE THIS" (owned -/// status), tagged with the barcode like `add_scanned`. +/// Adopts an already-known release (same write as the browse grid's +/// "I HAVE THIS"), tagged with the barcode like `add_scanned`. pub fn adopt_scanned( barcode: String, uri: String, diff --git a/web/src/at_record_web/ffi.mjs b/web/src/at_record_web/ffi.mjs index c4a13eb..a33d906 100644 --- a/web/src/at_record_web/ffi.mjs +++ b/web/src/at_record_web/ffi.mjs @@ -135,4 +135,3 @@ export function stopScanner() { scanStream = null; } } - diff --git a/web/src/at_record_web/model.gleam b/web/src/at_record_web/model.gleam index eaa0f6f..70ea69b 100644 --- a/web/src/at_record_web/model.gleam +++ b/web/src/at_record_web/model.gleam @@ -224,10 +224,8 @@ pub type ScanItem { FromNetwork(NetworkMatch) } -/// One scanned barcode's outcome, from lookup through commit. `NoMatch` carries -/// any "did you mean" suggestions (empty when there are none). `Failed` is a -/// batch-import attempt that errored, keeping its item so a RETRY can re-fire -/// the same write without re-running the barcode lookup. +/// One scanned barcode's outcome, from lookup through commit. `Failed` keeps +/// its item so RETRY can re-fire the write without re-running the lookup. pub type ScanOutcome { Pending Matched(result: DiscogsResult, owned: Bool) @@ -288,11 +286,9 @@ pub fn blank_scan() -> ScanState { ) } -/// Rows the bulk batch loop still cares about: matched candidates not yet -/// committed, or in flight. Drives the review page's "IMPORT N -> CRATE" -/// count and which rows a batch run picks up; a `Failed` row is deliberately -/// excluded from both - it's only ever retried via its own RETRY tap, never -/// automatically re-swept by the bulk button, so it can't loop on itself. +/// Rows a batch run picks up, and the "IMPORT N -> CRATE" count. `Failed` is +/// excluded so the bulk button can't loop on a failing row; RETRY is its only +/// path back in. pub fn scan_importable(rows: List(ScanRow)) -> List(ScanRow) { list.filter(rows, fn(row) { case row.outcome { diff --git a/web/src/at_record_web/pages/scan.gleam b/web/src/at_record_web/pages/scan.gleam index d28731b..1d5de81 100644 --- a/web/src/at_record_web/pages/scan.gleam +++ b/web/src/at_record_web/pages/scan.gleam @@ -141,7 +141,6 @@ fn tray_body(outcome: ScanOutcome) -> Element(Msg) { case outcome { Pending -> tray_label("looking up…") NoMatch(suggestions: []) -> tray_label("no match") - // A hint that review holds "did you mean?" candidates for this barcode. NoMatch(suggestions:) -> tray_label( "no match · " diff --git a/web/src/at_record_web/pages/scan_common.gleam b/web/src/at_record_web/pages/scan_common.gleam index 9b8d9c9..80e77a5 100644 --- a/web/src/at_record_web/pages/scan_common.gleam +++ b/web/src/at_record_web/pages/scan_common.gleam @@ -1,6 +1,4 @@ -//// Shared view helpers for the scan capture/review/done trio: pulling a -//// thumbnail/title out of either kind of `ScanItem`, and the barcode dismiss -//// control every row (whatever its outcome) offers. +//// Shared view helpers for the scan capture/review/done trio. import at_record_web/model.{type ScanItem, FromDiscogs, FromNetwork} import at_record_web/msg.{type Msg, RemoveScanRow} diff --git a/web/src/at_record_web/pages/scan_done.gleam b/web/src/at_record_web/pages/scan_done.gleam index 8c22554..1519ee3 100644 --- a/web/src/at_record_web/pages/scan_done.gleam +++ b/web/src/at_record_web/pages/scan_done.gleam @@ -1,6 +1,5 @@ -//// The post-import ceremony: how many records just landed, and the crate's -//// old -> new count. The count-up is pure CSS (see 18-scan-batch.css); the -//// view only supplies --count-from/--count-to. +//// The post-import ceremony. The count-up is pure CSS (18-scan-batch.css); +//// the view only supplies --count-from/--count-to. import at_record_web/model.{type Model, Crate, Scan} import at_record_web/msg.{type Msg} diff --git a/web/src/at_record_web/pages/scan_review.gleam b/web/src/at_record_web/pages/scan_review.gleam index 6160fa4..925cc78 100644 --- a/web/src/at_record_web/pages/scan_review.gleam +++ b/web/src/at_record_web/pages/scan_review.gleam @@ -1,7 +1,5 @@ -//// The batch review page ("review your haul"): scanned rows bucketed into -//// MATCHED (importable), already-in-your-crate duplicates (excluded), and -//// NEEDS REVIEW (no Discogs match), with a single CTA that runs the import -//// sequentially over the matched bucket. +//// The batch review page: rows bucketed into MATCHED / ALREADY IN YOUR CRATE / +//// NEEDS REVIEW, with one CTA that imports the matched bucket sequentially. import at_record_web/model.{ type Model, type ScanRow, type Suggestion, Adding, Failed, Matched, @@ -185,9 +183,8 @@ fn needs_review_row(row: ScanRow, importing: Bool) -> Element(Msg) { ]) } -/// "Did you mean?" rows under a no-match barcode: at most two -/// MusicBrainz-seeded Discogs candidates. ACCEPT fires no write; it promotes -/// the row into the MATCHED bucket so it imports with the batch. +/// ACCEPT fires no write; it promotes the row into MATCHED so it imports +/// with the batch. fn suggestions_view(row: ScanRow, importing: Bool) -> Element(Msg) { case row.outcome { NoMatch(suggestions: [_, ..] as suggestions) -> diff --git a/web/src/at_record_web/update.gleam b/web/src/at_record_web/update.gleam index 38c4b22..6b50edf 100644 --- a/web/src/at_record_web/update.gleam +++ b/web/src/at_record_web/update.gleam @@ -66,9 +66,7 @@ pub fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) { disarm_confirms(), ]), ) - // Entering the done page needs the pre-navigation model (the run's - // old/added counts) to size the count-up, so it gets its own arm rather - // than folding into the generic case below. + // Own arm: the done page must keep the run's old/added counts intact. OnRouteChange(ScanDone) -> { let updated = Model( @@ -88,10 +86,6 @@ pub fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) { // Add refreshes Discogs connection status; scan starts/stops the camera; // browse reloads the shared catalog; the edit inbox loads its proposals // only on entry (never at login) and clears any stale in-flight add. - // Scan's own row state is handled by `scan_for_route`: a fresh entry - // (from outside the scan/review/done trio) starts a blank session, while - // moving within the trio preserves it so the review page survives - // navigation. OnRouteChange(route) -> { let scan = scan_for_route(model.scan, model.route, route) #( @@ -814,8 +808,7 @@ pub fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) { Model(..model, scan: model.ScanState(..model.scan, photo_hint: None)), effect.none(), ) - // Accepting a "did you mean?" candidate fires no write: it just promotes - // the NoMatch row to a regular unowned match, so it imports with the batch. + // Fires no write: promotes the NoMatch row to a match so it imports with the batch. AcceptSuggestion(barcode, release) -> case find_row(model.scan, barcode) { Some(model.ScanRow(outcome: model.NoMatch(_), ..)) -> #( @@ -831,9 +824,7 @@ pub fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) { ) _ -> #(model, effect.none()) } - // Kicks off the sequential batch import: one write in flight at a time, - // picked up again by BatchItemDone until nothing importable remains. - // Guarded against double-taps while a run is already going. + // One write in flight at a time; BatchItemDone chains the rest. Double-tap guarded. StartBatchImport -> case model.scan.importing, next_pending(model.scan.rows) { False, Some(#(barcode, item)) -> { @@ -1084,9 +1075,8 @@ fn scan_outcome( } } -/// An unstarted row the batch loop will pick up next. `Failed` is -/// deliberately excluded - retrying it is RETRY's job, not the loop's, -/// otherwise a failure would just get re-fired forever. +/// An unstarted row's item. `Failed` is excluded so the loop can't re-fire a +/// failure forever; that's RETRY's job. fn pending_item(outcome: model.ScanOutcome) -> Option(model.ScanItem) { case outcome { model.Matched(result:, owned: False) -> Some(model.FromDiscogs(result)) @@ -1108,8 +1098,7 @@ fn next_pending( |> option.from_result } -/// Fire one row's write and flip it to `Adding`; shared by the batch loop's -/// first tap and a per-row RETRY. +/// Fire one row's write and flip it to `Adding` (batch loop and RETRY share this). fn start_batch_item( model: Model, barcode: String, @@ -1139,8 +1128,7 @@ fn advance_batch(model: Model) -> #(Model, Effect(Msg)) { } } -/// Nothing left to attempt: clear the running flag, and if the run landed at -/// least one success, refresh the shelf and hand off to the done page. +/// Clear the running flag; any successes refresh the shelf and go to done. fn finish_batch(model: Model) -> #(Model, Effect(Msg)) { let scan = model.ScanState(..model.scan, importing: False) let updated = Model(..model, scan:) @@ -1156,10 +1144,8 @@ fn finish_batch(model: Model) -> #(Model, Effect(Msg)) { } } -/// Scan session state across a route change: a fresh entry into the -/// scan/review/done trio starts a blank session; moving within it (camera <-> -/// review) preserves rows; SCAN MORE (done -> scan) clears imported rows but -/// keeps unresolved ones. +/// A fresh entry into the scan/review/done trio starts a blank session; moving +/// within it preserves rows; done -> scan drops imported rows, keeps the rest. fn scan_for_route( scan: model.ScanState, previous: model.Route, diff --git a/web/test/scan_test.gleam b/web/test/scan_test.gleam index bb147d0..fbd7f19 100644 --- a/web/test/scan_test.gleam +++ b/web/test/scan_test.gleam @@ -117,8 +117,7 @@ pub fn scan_page_renders_no_match_row_without_did_you_mean_test() { ]), ) let html = model |> scan.view |> element.to_string - // The tray never renders the suggestion cards themselves; it only hints - // that the review page holds candidates for this barcode. + // The tray only hints at suggestions; the cards render on the review page. assert !string.contains(html, "DID YOU MEAN?") assert string.contains(html, "no match · 1 suggestions") } @@ -286,8 +285,7 @@ pub fn accept_suggestion_ignores_a_row_that_is_not_no_match_test() { assert model.scan.rows == seeded.scan.rows } -// Accepting stays a pure state change even mid-run (the UI disables the -// button instead); the promoted row simply joins the loop's pending set. +// Accepting mid-run stays pure; the promoted row joins the loop's pending set. pub fn accept_suggestion_still_promotes_while_a_run_is_active_test() { let release = discogs_result(9) let seeded =