diff --git a/README.md b/README.md index c191d27..6ce739f 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,13 @@ [![TypeScript](https://img.shields.io/badge/TypeScript-types%20included-3178C6.svg)](https://www.typescriptlang.org/) [![License: MIT](https://img.shields.io/badge/license-MIT-111827.svg)](LICENSE) -SeatLayer is interactive seating chart software built for stadium scale. Platforms embed the white-label seat picker with their own checkout; organizers sell seated events on their own website with their own payment gateway. - -The official SeatLayer React Native and Expo SDK adds an interactive seating -chart and seat picker to iOS and Android ticketing apps. It combines live +The official SeatLayer React Native seat map SDK adds an interactive seating +chart and seat picker to React Native and Expo ticketing apps on iOS and Android. It combines live availability, best-available selection, temporary holds, and typed TypeScript APIs with composable React Native buyer controls around SeatLayer's version-pinned shared venue renderer; your trusted server completes booking. +SeatLayer is seating chart and reserved-seat ticketing software built for +venues up to stadium scale. [`@seatlayer/react-native` on npm](https://www.npmjs.com/package/@seatlayer/react-native) · [React Native seat-map documentation](https://docs.seatlayer.io/buyer-sdk/react-native/) · @@ -64,7 +64,7 @@ ships no custom native module of its own, so there is nothing else to link. Peer requirements are `react >= 18.2.0`, `react-native >= 0.72.0`, and `react-native-webview >= 13.0.0`. TypeScript declarations are published with the -package — `dist/index.d.ts` for ESM and `dist/index.d.cts` for CommonJS — so no +package (`dist/index.d.ts` for ESM and `dist/index.d.cts` for CommonJS), so no `@types/*` package is needed. ## Quick start: ready-made seat picker @@ -277,8 +277,8 @@ The customization layers have distinct ownership: `defaultChild`. Use a builder when structure or placement must change. An app that wants the picker set in its own typeface passes -`themeOptions.theme.fontFamily` and loads the face itself — with `expo-font`, -`react-native.config.js` asset linking, or whatever the app already uses — and +`themeOptions.theme.fontFamily` and loads the face itself (with `expo-font`, +`react-native.config.js` asset linking, or whatever the app already uses), and every line the picker draws then takes that family. Without it the picker draws the platform's own face, which is the right default: React Native has no inherited text style, so a picker that guessed at the surrounding typography @@ -311,7 +311,7 @@ Callbacks are optional observations, never a place the SDK waits: `onThemeResolved` · `onSectionFocused` · `onSeatSelected` · `onSeatRemoved` · `onSeatViewOpened` · `onSeatConfidence` · `onContinue` -`onBooked` fires once, when the handed-off hold settles to booked — never on the +`onBooked` fires once, when the handed-off hold settles to booked, never on the handoff itself, because a buyer on the way to pay has not paid. `onSeatConfidence` opens the seat's confidence passport; supplying it turns the 3D card's confidence teaser into a chip, and without it the teaser stays a @@ -390,9 +390,9 @@ venue-map ones. Reach it as `controller` from `useSeatLayerPicker()` inside a | --- | --- | | `setSelectionFocus(seatId \| null)` | Names the seat a host-drawn card is asking about, so the runtime paints it as the candidate. `null` clears the paint. | | `setBlockedRegions(regions \| null)` | Reports where native chrome lies over the map, in the map's own pixels, so the runtime swallows a touch that starts inside one. | -| `frameSeat(seatId, options)` | Pans — never zooms — so a seat rests in the band the reported insets leave clear. Answers `dy: 0` for a seat already in place, an unknown seat, insets that leave no band, or a stale gesture count. | +| `frameSeat(seatId, options)` | Pans (never zooms) so a seat rests in the band the reported insets leave clear. Answers `dy: 0` for a seat already in place, an unknown seat, insets that leave no band, or a stale gesture count. | | `focusAccessibilityFilter()` | Flies to the matches of the filter already on, leaving the filter alone. | -| `focusNextAccessibleSection(types?)` | Steps to the next section holding a free matching space, in chart order, wrapping. `null` — nothing matches — is an answer; `undefined` means the runtime does not offer the tour. | +| `focusNextAccessibleSection(types?)` | Steps to the next section holding a free matching space, in chart order, wrapping. `null` (nothing matches) is an answer; `undefined` means the runtime does not offer the tour. | | `subscribeSeatRetap(listener)` | A seat already in the selection tapped again. Subscribing never replays an earlier event. | | `subscribeBooked(listener)` | Fires once per sale, with the handoff that became it. | | `getCheckoutHandoff()` / `getBookedHandoff()` | The handoff this picker made, and the one whose hold settled to booked. | @@ -419,7 +419,7 @@ guarding after its control has gone. While the seat card is up, the map goes behind a veil with a feathered hole around the tapped seat, so the buyer can still see the seat they are being asked about. React Native has no blur of its own, and a native blur module -cannot be a hard dependency — a bundler resolves `require` statically, so an +cannot be a hard dependency: a bundler resolves `require` statically, so an app without the module could not build. An app that already has one installs it once, at start-up: @@ -438,8 +438,8 @@ transparency. ## Optional vector seat glyphs -A seat can say what it is — an accessible physical seat, an empty wheelchair -space, a restricted view, a premium seat — and each of those wears a drawing +A seat can say what it is (an accessible physical seat, an empty wheelchair +space, a restricted view, a premium seat), and each of those wears a drawing shared with every other SeatLayer SDK, so the same seat wears the same mark everywhere. With no drawing dependency the picker hands each glyph to `Image` as an SVG data URI. @@ -447,12 +447,12 @@ as an SVG data URI. **On iOS that fallback draws nothing.** `Image` decodes a data URI through the platform's own image decoders, and none of them reads SVG, so the seat-note bands on the seat card and the rows of the accessibility sheet keep their words -and lose the mark beside them. Nothing else changes — no gap, no broken box, -no error — but if you want the marks on iOS, install the renderer below. +and lose the mark beside them. Nothing else changes (no gap, no broken box, +no error), but if you want the marks on iOS, install the renderer below. `react-native-svg` is an **optional peer**. An app that already has it installs the built-in vector renderer once, at start-up, and every glyph is drawn -natively instead — scaling without resampling and taking each row's ink +natively instead, scaling without resampling and taking each row's ink directly: ```tsx @@ -465,7 +465,7 @@ installSeatLayerPickerSvgIcons({ Svg, Path, Circle }); You pass your own imports rather than the SDK requiring the module, for the same reason as the blur above: a bundler resolves `require` statically, so a guarded import here would put `react-native-svg` in every consumer's build -graph whether they wanted it or not. `Circle` is optional — without it the +graph whether they wanted it or not. `Circle` is optional; without it the circles in a glyph are drawn as paths. Pass `undefined` to go back to the data URI, and `setSeatLayerPickerSeatIconRenderer` to draw them some other way entirely. @@ -526,9 +526,9 @@ TypeScript controller whose contract matches the Web, iOS, and Flutter SDKs: See [the bridge contract](docs/bridge.md) for the wire-level details. A host may warm the runtime page before the picker is opened, with -`SeatLayerRuntimePrewarm`. On React Native this warms the *transport* — DNS, +`SeatLayerRuntimePrewarm`. On React Native this warms the *transport* (DNS, TLS, the CDN edge and the HTTP cache entry for the runtime document and, where -named, its bundles — rather than a live page, because a page belongs to the +named, its bundles) rather than a live page, because a page belongs to the component that rendered it. The warm entry lives on a short TTL and is dropped under memory pressure, and a picker that finds nothing warm simply starts cold. @@ -589,19 +589,19 @@ regions and focus handling. Two of those need something from the host app. ### iOS reading order needs a native feature flag -The picker walks a screen reader through the seat map in buyer order — event, -prices, map, then the tray — rather than in the order the views happen to be +The picker walks a screen reader through the seat map in buyer order (event, +prices, map, then the tray) rather than in the order the views happen to be painted. It declares that with React Native's own `experimental_accessibilityOrder`, naming the `nativeID` of each surface at the composition root. **On iOS that prop is only honoured when the app turns the native feature flag -on.** Without it, nothing breaks and nothing is announced twice — VoiceOver +on.** Without it, nothing breaks and nothing is announced twice: VoiceOver simply falls back to the paint order, in which the cart tray is reached before the map. On Android the order is honoured without a flag. Turn it on once, early in the app's native start-up, before the first React -Native view is created — in `AppDelegate`: +Native view is created, in `AppDelegate`: ```objc // AppDelegate.mm, above [super application:didFinishLaunchingWithOptions:] @@ -620,7 +620,7 @@ Expo apps reach the same file through a config plugin or a prebuild; a managed project that cannot run native code does not get the declared order, and the picker stays usable on the fallback. -Check your React Native version's release notes for the flag's exact name — it +Check your React Native version's release notes for the flag's exact name: it has been an experimental API, and the SDK deliberately spreads the prop rather than typing it, so a runtime that does not know it simply ignores it. @@ -640,7 +640,7 @@ settings: - the map is one named region with a hint naming the controls around it; - the seat card is a dialog, with custom actions, that hides the page beneath - it, and focus returns to the map when the card — or a toast's action — is + it, and focus returns to the map when the card (or a toast's action) is done with it; - a change that is news is a live region and nothing else is: where the buyer has arrived and how much room is left there, and the hold's countdown; @@ -671,7 +671,7 @@ covers lifecycle, commands, and events in depth. `SeatLayerView` is a React Native component with a typed TypeScript controller and a SeatLayer venue-map renderer. The package contains no custom native -module — no podspec, no Java, Kotlin, Swift, or Objective-C source — so +module (no podspec, no Java, Kotlin, Swift, or Objective-C source), so application code works through TypeScript commands, payloads, errors, and events. @@ -687,8 +687,8 @@ app that renders the SDK. When a buyer selects seats, the SDK creates a temporary hold that reserves the inventory against concurrent buyers for a limited window. The hold expires -automatically if checkout does not complete — the `holdExpired` event tells the -app to return the buyer to the map — and `extendHold` and `resumeHold` cover +automatically if checkout does not complete (the `holdExpired` event tells the +app to return the buyer to the map), and `extendHold` and `resumeHold` cover longer checkouts and app restarts. This prevents double-selling without locking seats forever. @@ -696,7 +696,7 @@ seats forever. Yes. SeatLayer never processes payment inside the seat map. The app hands the `holdId` to your backend, and your backend charges through any payment provider -you already use — Stripe, Adyen, Razorpay, or your own — before booking the hold +you already use (Stripe, Adyen, Razorpay, or your own) before booking the hold through the [server-side checkout flow](https://docs.seatlayer.io/buyer-sdk/holds-and-checkout/). diff --git a/design/picker-spec.md b/design/picker-spec.md index 81f0490..3c3d991 100644 --- a/design/picker-spec.md +++ b/design/picker-spec.md @@ -235,8 +235,8 @@ gate them, and the reference file. ### 3.1 Header -The header's hold pill is drawn for as long as a hold is live (owner call, -2026-09-05): it is the picker's ONE clock. There is no hold clock anywhere on +The header's hold pill is drawn for as long as a hold is live: it is the +picker's ONE clock. There is no hold clock anywhere on the cart sheet, in either state, and no "display buffer" on it — the pill shows the full server hold. This deliberately departs from the web's `data-peek-clock` rule, under which the pill stepped aside while a peek bar @@ -265,13 +265,13 @@ is the identity. `size.minimumHitTarget` centred on it. Copy `strings.close`. **Hold pill.** Drawn for as long as a live hold exists, whoever owns it -(owner call 2026-09-05: the clock is always in the header; a host that draws +(the clock is always in the header; a host that draws its own clock turns it off with the `showHoldPill` option). A true pill (`radius.pill`), a dot and `m:ss` in `type.pill`, tabular figures, with a `size.minimumHitTarget` reach. Resting look is the accent mixed lightly into the surface with accent- toned ink; while expiring it inverts to the full accent with `color.*.onAccent` and its dot pulses. Copy `strings.heldFor`. It is never hidden while the hold -lives — the countdown is said once, here (owner call, 2026-09-05). +lives: the countdown is said once, here. **Sales-closed pill.** Same pill geometry, deliberately neutral — the text colour on a lightly text-tinted surface, never the accent. Copy @@ -482,7 +482,7 @@ inset `size.mapAnchorInset` from the map's edges, with `size.mapAnchorGap` between members. Regions do not receive presses; their children do. Nothing free-floats. -**One column, bottom-right, on both compositions** (owner call, 2026-09-06). +**One column, bottom-right, on both compositions.** The ♿ filter disc heads it; the zoom discs follow. It used to stand alone in the bottom-left region — one control facing a stack of them, in the corner the floor rail already owns — and read as something the layout had forgotten. Who @@ -502,7 +502,7 @@ Every disc is `size.mapControlSize` across, the ♿ disc `size.zoomColumnGap`; the head disc's stepper rides beside it at `size.accessStepGap` (§3.4.1). -**There is no `−` disc on the phone** (owner call, 2026-09-06). It only ever +**There is no `−` disc on the phone.** It only ever had a rung of its own while the buyer was already among the seats: at a section's own frame the one step back is the whole venue, and that is the disc below it. Two discs for one move reads as a puzzle, and a disc that only @@ -1625,8 +1625,7 @@ Full entry: `components.md` › HoverCard. **Name** `SeatLayerCartSheet` · **slots** `sheetStyle`, `continueButtonStyle` · **file** `lib/src/picker/picker_cart_sheet.dart` -**THE COLLAPSED SHEET IS THE FOOTER BLOCK, AND NOTHING ELSE** (owner call -2026-09-06): the total line, the call to action and the by-line, in one set of +**THE COLLAPSED SHEET IS THE FOOTER BLOCK, AND NOTHING ELSE**: the total line, the call to action and the by-line, in one set of rules shared with the wide panel. **The cards wait behind the handle.** A list that unrolled itself every time a seat was added read as a panel the buyer had not opened, so the collapsed cart region has height zero (`_collapsedCart = 0`) @@ -1764,8 +1763,8 @@ seat (§3.10.2) leaves the sheet where the buyer put it. **Name** `SeatLayerCartList` / `SeatLayerCartCard` · **file** `lib/src/picker/picker_cart_list.dart` -ONE CARD PER TICKET, AND THE SAME CARD ON EVERY WIDTH (owner call 2026-09-06: -"cards should be the same design as desktop"). The phone used to draw a second +ONE CARD PER TICKET, AND THE SAME CARD ON EVERY WIDTH: the phone draws the same +card design as desktop. The phone used to draw a second cart — a bordered plate of `44` pt hairline-divided lines, with consecutive seats folded into runs behind a `+N more`. It saved real pixels and it cost the sheet its coherence: a bordered list on its own surface between a band of @@ -2358,7 +2357,7 @@ stepper, a range caption `strings.chooseMinMaxGuests`, and the action pair `strings.fewerGuests` / `strings.moreGuests`. Accessible name for the whole sheet: `strings.chooseTableGuests`. Capability `table-quantity-v1`. -#### 3.13.13 Seats already in checkout (N1, owner decision 2026-09-05: option B) +#### 3.13.13 Seats already in checkout The hold belongs to the host from the moment it is handed off, and the runtime refuses to grow or shrink it from the picker: `hold_owned_by_host` (a cart @@ -2728,7 +2727,7 @@ from the wrong one. ## 5. Capability index -| Capability | What it unlocks | +| Capability | What it enables | | --- | --- | | `native-chrome-contract-v1` | the runtime suppresses its own buyer chrome; native owns header, rail, dock, tray, prompts | | `viewport-insets-v1` | native reports the bands its chrome covers, so framing lands inside them | @@ -2757,7 +2756,7 @@ snapshot reports; their presence in the `hello` **command table** is the whole contract, and a runtime answering `unsupported_command` for one of them anyway leaves nothing on screen for the buyer to read (runtime 0.80.2+): -| Command | What it unlocks | +| Command | What it enables | | --- | --- | | `picker.setSelectionFocus { seatId \| null }` | the seat a card is asking about is painted as the candidate — thick double ring, halo, neighbours paled (§3.8.2) | | `picker.setBlockedRegions { rects }` | the runtime's own tap guard under native chrome over the map (§2.4) | diff --git a/src/picker/SeatLayerCartList.tsx b/src/picker/SeatLayerCartList.tsx index 2f853df..9229c7c 100644 --- a/src/picker/SeatLayerCartList.tsx +++ b/src/picker/SeatLayerCartList.tsx @@ -169,7 +169,7 @@ export function SeatLayerCartList(props: SeatLayerCartListProps): React.ReactEle /** * The map frames the seat at its resting place, and the SHEET STAYS OPEN. * - * Owner call, carried on both platforms: stepping the sheet down as well + * The same on both platforms: stepping the sheet down as well * answered a question the buyer had not asked. A tap on a cart card means * "where is this one?", and closing the list they were reading through to * answer it made checking a second seat cost a re-open every time. The card diff --git a/src/picker/SeatLayerDockBar.tsx b/src/picker/SeatLayerDockBar.tsx index 461b1fb..aa1550c 100644 --- a/src/picker/SeatLayerDockBar.tsx +++ b/src/picker/SeatLayerDockBar.tsx @@ -149,7 +149,7 @@ function MeasureText({ * Where the buyer is, and the two ways out, docked under the map. Rung 2 only, * sliding away when the map climbs a level. * - * THE DROP-IN MOUNTS NO DOCK ON ANY WIDTH (§3.6, owner call 2026-09-06): the + * THE DROP-IN MOUNTS NO DOCK ON ANY WIDTH (§3.6): the * pinch and the single stepped `-` control already walk a buyer back to the * venue, and the bar's height plus the home-indicator inset pushed every * bottom-corner control up the screen. A host that wants it sets diff --git a/src/picker/SeatLayerMapControls.tsx b/src/picker/SeatLayerMapControls.tsx index df372b0..2329ff9 100644 --- a/src/picker/SeatLayerMapControls.tsx +++ b/src/picker/SeatLayerMapControls.tsx @@ -144,7 +144,7 @@ export function seatLayerPickerMapCanStepBack(map: MapCameraReading): boolean { * §3.5 `\u2212`, which exists on the WIDE composition and in a host's own * composition only. Live once the buyer is in among the seats and the ladder * still has a rung: at a section's own frame the only step back is the whole - * venue, and that is a different control (owner, 2026-09-06). Two discs for + * venue, and that is a different control. Two discs for * one move read as a puzzle. */ export function seatLayerPickerMapCanStepOut(map: MapCameraReading): boolean { @@ -253,7 +253,7 @@ export function SeatLayerMapControls(props: SeatLayerMapControlsProps): React.Re props.showZoomControls === true && supports('picker.zoomIn', ['zoom']) && supports('picker.zoomOut', ['zoom']); // §3.5 phone column. `+` steps in and the framed dot puts the whole venue on - // screen from any depth; there is no `-` between them (owner, 2026-09-06): a + // screen from any depth; there is no `-` between them: a // `-` that only sometimes had a step to take read as a control that // sometimes worked. const zoomInSlotAvailable = snapshot !== undefined && buyerView === 'map' && compact && @@ -358,7 +358,7 @@ export function SeatLayerMapControls(props: SeatLayerMapControlsProps): React.Re /** * §3.5 — the map's floating controls, in their anchor regions. * - * ONE COLUMN, BOTTOM-RIGHT, ON BOTH COMPOSITIONS (owner, 2026-09-06). The + * ONE COLUMN, BOTTOM-RIGHT, ON BOTH COMPOSITIONS. The * accessibility disc HEADS it and the zoom discs follow: who can sit where is * an earlier question than how close the camera is, and the disc used to stand * alone in the corner the floor rail already owns, which read as something the diff --git a/src/picker/SeatLayerPickerMapButtons.tsx b/src/picker/SeatLayerPickerMapButtons.tsx index cd0b7a9..fb71b09 100644 --- a/src/picker/SeatLayerPickerMapButtons.tsx +++ b/src/picker/SeatLayerPickerMapButtons.tsx @@ -225,8 +225,8 @@ export function SeatLayerPickerZoomInButton(props: SeatLayerPickerMapControlButt } /** * The `\u2212` disc. It is drawn by the WIDE composition and by a host's own; - * the phone column has no `\u2212` between `+` and the whole-venue disc (owner, - * 2026-09-06) — pinch steps the camera out, and the disc below goes home. + * the phone column has no `\u2212` between `+` and the whole-venue disc: + * pinch steps the camera out, and the disc below goes home. * * Live only once the buyer is in among the seats and the ladder still has a * rung: at a section's own frame the only step back is the whole venue, and diff --git a/src/picker/header.tsx b/src/picker/header.tsx index 603282a..ec592d9 100644 --- a/src/picker/header.tsx +++ b/src/picker/header.tsx @@ -237,7 +237,7 @@ export function SeatLayerPickerHeaderView( setFailureVersion((value) => value + 1); }, []); // The pill is the picker's one clock and is drawn for as long as the hold - // lives (owner call, 2026-09-05); a hold handed to the host is the host's to + // lives; a hold handed to the host is the host's to // display (§4.8). const pillOwned = seatLayerHoldPillDrawn(hold, holdLapsed); const expiry = pillOwned ? hold!.expiresAt! : undefined; diff --git a/src/picker/holdCountdownAnnounce.ts b/src/picker/holdCountdownAnnounce.ts index 483a8c5..479064c 100644 --- a/src/picker/holdCountdownAnnounce.ts +++ b/src/picker/holdCountdownAnnounce.ts @@ -53,7 +53,7 @@ export function seatLayerHoldExpiring(remainingSeconds: unknown): boolean { /** * The pill is drawn for as long as a live hold exists, WHOEVER owns it - * (spec §3.1/§4.8, owner call 2026-09-05). The buyer's time is running on a + * (spec §3.1/§4.8). The buyer's time is running on a * hold handed to the host exactly as it is on the picker's own, and the header * is the one place the picker states it. Ownership still governs what the * native controls may DO with the hold — no extend, no release from a cart row diff --git a/src/picker/options.ts b/src/picker/options.ts index 48f35d1..43bf1eb 100644 --- a/src/picker/options.ts +++ b/src/picker/options.ts @@ -33,7 +33,7 @@ export interface SeatLayerPickerChromeOptions { readonly cartSheet?: boolean; /** * Whether the rung-2 section dock bar renders. **Default false on EVERY - * width** (§3.6, owner call 2026-09-06): pinch-out past the melt point and + * width** (§3.6): pinch-out past the melt point and * the single stepped `−` control already walk a buyer back to the venue, so * the prev/next arrows only bought a two-tap version of a gesture the finger * does better, and the per-section "N seats left" is gone from the phone. diff --git a/test/picker-adaptive-layout.test.ts b/test/picker-adaptive-layout.test.ts index 1d4e5fd..02dae27 100644 --- a/test/picker-adaptive-layout.test.ts +++ b/test/picker-adaptive-layout.test.ts @@ -168,7 +168,7 @@ describe('adaptive picker composition', () => { }); it('mounts no section dock on a WIDE layout either, unless the host asks (§3.6)', async () => { - // The bar is opt-in on every width now (owner call 2026-09-06): it used to + // The bar is opt-in on every width now: it used to // be auto-resolved wide-on, so a wide picker got a bar nobody asked for. state.width = 920; state.scope = scope({ diff --git a/test/picker-cart-lane-renderer.test.ts b/test/picker-cart-lane-renderer.test.ts index 2fe6824..a90275d 100644 --- a/test/picker-cart-lane-renderer.test.ts +++ b/test/picker-cart-lane-renderer.test.ts @@ -111,7 +111,7 @@ const headerProps = { describe('§3.1 header renderer', () => { it('sits on the picker ground at its generated height with a letter mark and a 26 pt close ring', async () => { const renderer = await render(React.createElement(SeatLayerPickerHeaderView, { - ...headerProps, brandName: 'Paiteq Live', onClose: () => {}, + ...headerProps, brandName: 'Riverside Hall', onClose: () => {}, })); const root = renderer.root.findByProps({ accessibilityRole: 'header' }); const flattened = (root.props.style as unknown[]).filter(Boolean) as Record[]; @@ -125,7 +125,7 @@ describe('§3.1 header renderer', () => { // The venue-and-date meta line is NOT drawn on a phone. const output = JSON.stringify(renderer.toJSON()); expect(output).toContain('Arena night'); - expect(output).toContain('"P"'); + expect(output).toContain('"R"'); const ring = renderer.root.findByProps({ testID: 'seatlayer-header-close' }) .findAll((node) => Array.isArray(node.props.style) && node.props.style.some((style: any) => style && style.width === seatLayerPickerTokens.size.headerCloseSize && @@ -152,7 +152,7 @@ describe('§3.1 header renderer', () => { expect(JSON.stringify(picker.toJSON())).toContain('10:05'); expect(pill.props.accessibilityLiveRegion).toBe('polite'); // The reference draws the clock on a hold handed to the host too: the - // buyer's time is running either way (§3.1, owner call 2026-09-05). + // buyer's time is running either way (§3.1). const host = await render(props('host')); expect(host.root.findAllByProps({ testID: 'seatlayer-header-hold-pill' })).toHaveLength(1); expect(JSON.stringify(host.toJSON())).toContain('10:05'); @@ -305,7 +305,7 @@ describe('§3.10.2 cart cards', () => { await act(async () => { renderer.root.findByProps({ testID: 'seatlayer-cart-card-face' }).props.onPress(); }); expect(framed[0]![0]).toBe('seat-1'); expect(framed[0]![1]).toMatchObject({ fraction: seatLayerSheetRestoreFraction }); - // Owner call, both platforms: the sheet stays. A tap on a cart card asks + // Both platforms: the sheet stays. A tap on a cart card asks // "where is this one?", and closing the list the buyer was reading through // to answer it made checking a second seat cost a re-open every time. expect(sheet).toEqual([]); diff --git a/test/picker-cart-lane.test.ts b/test/picker-cart-lane.test.ts index 24dafe7..72ab6c7 100644 --- a/test/picker-cart-lane.test.ts +++ b/test/picker-cart-lane.test.ts @@ -82,7 +82,7 @@ describe('§3.1 header', () => { }); it('takes the brand mark from the organizer letter, falling back to the event name', () => { - expect(seatLayerHeaderInitial('Paiteq Live', 'Some event')).toBe('P'); + expect(seatLayerHeaderInitial('Riverside Hall', 'Some event')).toBe('R'); expect(seatLayerHeaderInitial(undefined, 'arena night')).toBe('A'); expect(seatLayerHeaderInitial(' ', undefined)).toBe(''); // A grapheme, not a UTF-16 unit: an emoji brand still yields one mark. @@ -91,7 +91,7 @@ describe('§3.1 header', () => { it('draws the hold pill for as long as the hold lives, whoever owns it', () => { expect(seatLayerHoldPillDrawn({ active: true, expiresAt: 1, owner: 'picker' })).toBe(true); - // §3.1/§4.8, owner call 2026-09-05: the buyer's time runs on a host-owned + // §3.1/§4.8: the buyer's time runs on a host-owned // hold too, and the header is the one place the picker states it. What // ownership still governs is what the controls may DO with the hold. expect(seatLayerHoldPillDrawn({ active: true, expiresAt: 1, owner: 'host' })).toBe(true); diff --git a/test/picker-map-chrome-parity.test.ts b/test/picker-map-chrome-parity.test.ts index 7c9a6c7..2273f0f 100644 --- a/test/picker-map-chrome-parity.test.ts +++ b/test/picker-map-chrome-parity.test.ts @@ -265,7 +265,7 @@ describe('3.5 map corner controls', () => { }); it('heads the control column with the accessibility disc, on both compositions', async () => { - // Owner call 2026-09-06. It stood alone in the map's bottom-left corner — + // It used to stand alone in the map's bottom-left corner: // one control facing a stack of them, in the corner the floor rail owns. // Who can sit where is an earlier question than how close the camera is. for (const compact of [true, false]) {