Skip to content

Release 0.18.0 (rc2)#313

Closed
emilymclean wants to merge 37 commits intomainfrom
rc/0.18.0-2
Closed

Release 0.18.0 (rc2)#313
emilymclean wants to merge 37 commits intomainfrom
rc/0.18.0-2

Conversation

@emilymclean
Copy link
Copy Markdown
Owner

@emilymclean emilymclean commented Feb 21, 2026

  • Bump compose to stable
  • Refactor last departure code
  • Find content banners for routes/stops
  • Add countdown to arrival/departure as an option
  • Update location more frequently
  • Make whole checkbox lockup clickable

Summary by Sourcery

Update bottom sheet interaction, last departure selection, time handling, and preferences while upgrading tooling for the 0.18.0 release candidate.

New Features:

  • Add an optional countdown-style display for arrival and departure times with a user preference toggle.
  • Surface route- and stop-specific content banners via the alert repository using new content IDs.

Bug Fixes:

  • Adjust last departure selection logic to prefer after-midnight departures on the following day when appropriate and add coverage for this case.
  • Fix service date calculation in tests when targeting specific weekdays.
  • Resolve NaN offset handling and anchor API changes in the custom bottom sheet implementation.

Enhancements:

  • Refactor bottom sheet state and anchored draggable integrations to use custom sheet/anchor abstractions compatible with newer Compose APIs.
  • Improve time formatting and day comparison utilities with memoization and countdown helpers based on the new kotlin.time Clock.
  • Make checkbox lockups in preferences fully clickable via a dedicated horizontal preferences checkbox component and apply it across routing and units screens.
  • Increase current-location update rate and default accuracy on Android and iOS to provide more responsive positioning.

Build:

  • Upgrade Gradle wrapper, Android Gradle Plugin, Kotlin, Compose, Ktor, Room, and other dependencies, and opt in to kotlin.time APIs across modules.
  • Update KSP and Koin annotation processing wiring and ensure KSP metadata tasks run before other Kotlin compilation tasks.

CI:

  • Bump custom GitHub composite actions versions, move builds and tests to JDK 21, and propagate the Java version through templated workflows.

Tests:

  • Extend last-departure use case tests to cover after-midnight next-day departures and ensure new business rules are validated.

emilymclean and others added 30 commits May 13, 2025 17:56
Snapping behaviour is now strange, needs to be fixed before merge
I described the issue here: adrielcafe/voyager#541. Thanks to nvkleban for noting that the issue was not present in compose >1.9.0-alpha01
Cleans up the last departure code and attempts to resolve #275
Fix last departure times that end on the next day
…e-stop

Search for content banners matching stops/route
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Feb 21, 2026

Reviewer's Guide

Refactors the custom bottom sheet implementation to wrap Compose’s new draggable APIs, introduces a dedicated SinatraSheetState, adds optional countdown-style arrival/departure text and related preferences, refines last‑departure selection logic and tests, wires route/stop-specific content banners, increases location update frequency, and updates build tooling and dependencies for newer Kotlin/Compose/Gradle/Java versions.

Sequence diagram for bottom sheet drag and settle using SinatraAnchoredDraggableState

sequenceDiagram
    participant User as User
    participant Compose as SinatraBottomSheet
    participant Modifier as sinatraAnchoredDraggable
    participant State as SinatraAnchoredDraggableState
    participant Draggable as DraggableState

    User->>Compose: Drag sheet gesture
    Compose->>Modifier: Apply sinatraAnchoredDraggable(state, orientation,...)
    Modifier->>Draggable: drag(dragPriority) { dragBy(delta) }
    Draggable->>State: anchoredDrag(dragPriority) { dragScope.dragBy(delta) }
    State->>State: newOffsetForDelta(delta)
    State->>State: anchoredDragScope.dragTo(newOffset, lastVelocity)
    State->>State: update offset and lastVelocity

    User-->>Compose: Release gesture with velocity
    Compose->>State: settle(velocity)
    State->>State: computeTarget(offset, currentValue, velocity)
    alt confirmValueChange(targetValue)
        State->>State: animateTo(targetValue, velocity)
    else veto
        State->>State: animateTo(previousValue, velocity)
    end
    State->>State: anchoredDrag(targetValue) { animate from offset to targetOffset }
    State-->>Compose: currentValue updated at closest anchor
Loading

Class diagram for updated bottom sheet state and draggable infrastructure

classDiagram
    direction LR

    class SinatraAnchoredDraggableState_T_ {
        - MutatorMutex dragMutex
        - DraggableState draggableState
        - AnchoredDragScope anchoredDragScope
        - T currentValue
        - T targetValue
        - T closestValue
        - Float offset
        - Float lastVelocity
        - T dragTarget
        - DraggableAnchors_T_ anchors
        - fun requireOffset() Float
        - val isAnimationRunning Boolean
        - val progress Float
        - fun updateAnchors(newAnchors: DraggableAnchors_T_, newTarget: T)
        - suspend fun anchoredDrag(dragPriority: MutatePriority, block: suspend AnchoredDragScope.() -> Unit)
        - suspend fun anchoredDrag(targetValue: T, dragPriority: MutatePriority, block: suspend AnchoredDragScope.() -> Unit)
        - suspend fun animateTo(targetValue: T, velocity: Float)
        - suspend fun snapTo(targetValue: T)
        - suspend fun settle(velocity: Float)
        - fun newOffsetForDelta(delta: Float) Float
        - companion object Saver(animationSpec: () -> AnimationSpec_Float_, confirmValueChange: (T) -> Boolean, positionalThreshold: (Float) -> Float, velocityThreshold: () -> Float)
    }

    class SinatraSheetState {
        + Boolean skipPartiallyExpanded
        + Boolean skipHiddenState
        + SinatraSheetValue currentValue
        + SinatraSheetValue targetValue
        + Boolean isVisible
        + Boolean isAnimationRunning
        + SinatraAnchoredDraggableState_SinatraSheetValue_ anchoredDraggableState
        + AnimationSpec_Float_ anchoredDraggableMotionSpec
        + FiniteAnimationSpec_Float_ showMotionSpec
        + FiniteAnimationSpec_Float_ hideMotionSpec
        + fun requireOffset() Float
        + fun expand() suspend
        + fun partialExpand() suspend
        + fun halfExpand() suspend
        + fun show() suspend
        + fun hide() suspend
        + internal fun animateTo(targetValue: SinatraSheetValue, animationSpec: FiniteAnimationSpec_Float_, velocity: Float) suspend
        + internal fun snapTo(targetValue: SinatraSheetValue) suspend
        + internal fun settle(velocity: Float) suspend
        + companion object Saver(skipPartiallyExpanded: Boolean, positionalThreshold: () -> Float, velocityThreshold: () -> Float, confirmValueChange: (SinatraSheetValue) -> Boolean, skipHiddenState: Boolean)
    }

    class SinatraDraggableAnchors_T_ {
        - Map_T_Float_ anchors
        + fun positionOf(value: T) Float
        + fun hasPositionFor(value: T) Boolean
        + fun closestAnchor(position: Float) T
        + fun closestAnchor(position: Float, searchUpwards: Boolean) T
        + fun minPosition() Float
        + fun maxPosition() Float
        + val size Int
        + fun anchorAt(index: Int) T
        + fun positionAt(index: Int) Float
    }

    class SinatraDraggableAnchorsConfig_T_ {
        + MutableMap_T_Float_ anchors
        + infix fun T.at(position: Float)
    }

    class SinatraBottomSheet {
        <<Composable>>
        + fun SinatraBottomSheet(sheetState: SinatraSheetState, sheetSwipeEnabled: Boolean)
    }

    class SinatraSheetValue {
        <<enum>>
        Hidden
        PartiallyExpanded
        HalfExpanded
        Expanded
    }

    class DraggableState {
        <<interface>>
        + fun drag(dragPriority: MutatePriority, block: suspend DragScope.() -> Unit)
        + fun dispatchRawDelta(delta: Float)
    }

    class DragScope {
        <<interface>>
        + fun dragBy(pixels: Float)
    }

    class AnchoredDragScope {
        <<interface>>
        + fun dragTo(newOffset: Float, lastKnownVelocity: Float)
    }

    class DraggableAnchors_T_ {
        <<interface>>
    }

    class AnimationSpec_Float_ {
    }

    class FiniteAnimationSpec_Float_ {
    }

    class SinatraAnchoredDraggableModifier {
        <<extension>>
        + fun sinatraAnchoredDraggable(state: SinatraAnchoredDraggableState_T_, orientation: Orientation, enabled: Boolean, reverseDirection: Boolean, interactionSource: MutableInteractionSource?) Modifier
    }

    SinatraSheetState "1" *-- "1" SinatraAnchoredDraggableState_SinatraSheetValue_ : uses
    SinatraAnchoredDraggableState_T_ "1" o-- "1" DraggableState : exposes
    SinatraAnchoredDraggableState_T_ "1" *-- "1" AnchoredDragScope : uses
    SinatraAnchoredDraggableState_T_ "1" *-- "1" DraggableAnchors_T_ : anchors
    SinatraAnchoredDraggableState_T_ ..> AnimationSpec_Float_ : animationSpec
    SinatraSheetState ..> FiniteAnimationSpec_Float_ : showMotionSpec
    SinatraSheetState ..> SinatraSheetValue
    SinatraDraggableAnchors_T_ ..|> DraggableAnchors_T_
    SinatraBottomSheet ..> SinatraSheetState : parameter
    SinatraAnchoredDraggableModifier ..> SinatraAnchoredDraggableState_T_ : parameter
    SinatraAnchoredDraggableModifier ..> DraggableState : wraps
    SinatraAnchoredDraggableState_T_ ..> SinatraDraggableAnchors_T_ : default factory
    SinatraDraggableAnchorsConfig_T_ ..> SinatraDraggableAnchors_T_ : builds
Loading

Class diagram for preferences, countdown time helpers, and stop time text

classDiagram
    direction LR

    class Preference_T_ {
        <<sealed interface>>
    }

    class Preference_CountdownUntilArrival {
        <<object>>
    }

    class Preference_ShowAccessibilityIconsNavigation {
        <<object>>
    }

    class Preference_RequiresWheelchair {
        <<object>>
    }

    class Preference_RequiresBikes {
        <<object>>
    }

    class Preference_MaximumWalkingTime {
        <<object>>
    }

    class Preference_Use24HourUnits {
        <<object>>
    }

    class PreferencesRepository {
        - PreferencesUnit_Boolean_ countdownUntilArrival
        + fun preference(preference: Preference_T_) PreferencesUnit_T_
    }

    class PreferencesUnit_T_ {
        <<interface>>
        + val value Flow_T_
        + suspend fun set(value: T)
    }

    class TimeHelpers {
        <<file>>
        + fun scheduleStartOfDay() Instant
        + fun startOfDay(timeZone: TimeZone) Instant
        + fun rememberCountdown(instant: kotlin.time.Instant) Duration
        + fun Time.toTodayInstant() Instant
        + fun Time.isInPast() Boolean
        + fun Time.isNowish() Boolean
        + fun Instant.format() String
        + fun Instant.isSameDay(timeZone: TimeZone) Boolean
        + fun Time.format() String
        + fun countdown(time: kotlin.time.Instant, negative: Boolean) String
    }

    class LocalScheduleTimeZone {
        <<CompositionLocal TimeZone>>
    }

    class LocalClock {
        <<CompositionLocal kotlin.time.Clock>>
    }

    class StopStationTime {
        <<sealed interface>>
        + val stop: Stop
        + val stationTime: TimetableStationTime
        + val text String
    }

    class StopStationTime_Arrival {
        <<data>>
    }

    class StopStationTime_Departure {
        <<data>>
    }

    class TimetableStationTime {
        + val time Time
        + val approximate Boolean
    }

    class StationTime_Scheduled {
        + val approximate Boolean
    }

    class StationTime_Live {
        + val delay kotlin.time.Duration
    }

    class StopCard_TimeTextLogic {
        <<extension>>
        + val StopStationTime.text String
    }

    class HorizontalPreferencesCheckboxLockup {
        <<Composable>>
        + fun HorizontalPreferencesCheckboxLockup(preference: Preference_Boolean_, title: String, subtitle: String?, modifier: Modifier)
    }

    class UnitsPreferencesScreen {
        <<Composable screen>>
        + fun Preferences()
    }

    class RoutingPreferencesScreen {
        <<Composable screen>>
        + fun Preferences()
    }

    Preference_CountdownUntilArrival ..|> Preference_Boolean_
    Preference_ShowAccessibilityIconsNavigation ..|> Preference_Boolean_
    Preference_RequiresWheelchair ..|> Preference_Boolean_
    Preference_RequiresBikes ..|> Preference_Boolean_
    Preference_MaximumWalkingTime ..|> Preference_kotlin_time_Duration_
    Preference_Use24HourUnits ..|> Preference_Time24HSetting_

    PreferencesRepository ..> Preference_T_ : maps
    PreferencesRepository "1" *-- "1" PreferencesUnit_Boolean_ : countdownUntilArrival

    TimeHelpers ..> LocalClock : uses
    TimeHelpers ..> LocalScheduleTimeZone : uses

    StopStationTime <|-- StopStationTime_Arrival
    StopStationTime <|-- StopStationTime_Departure
    StopStationTime ..> TimetableStationTime : stationTime
    StopStationTime_Departure ..> StationTime_Live : may use delay
    StopCard_TimeTextLogic ..> StopStationTime : extension
    StopCard_TimeTextLogic ..> Preference_CountdownUntilArrival : reads
    StopCard_TimeTextLogic ..> TimeHelpers : uses countdown and isNowish

    HorizontalPreferencesCheckboxLockup ..> Preference_Boolean_ : binds
    UnitsPreferencesScreen ..> HorizontalPreferencesCheckboxLockup : uses
    RoutingPreferencesScreen ..> HorizontalPreferencesCheckboxLockup : uses
Loading

Class diagram for LastDepartureForStopUseCase and related data

classDiagram
    direction LR

    class LastDepartureForStopUseCase {
        - MetadataRepository metadataRepository
        - ServicesAndTimesForStopUseCase servicesAndTimesForStopUseCase
        - RemoteConfigRepository remoteConfigRepository
        - PreferencesRepository preferencesRepository
        - Clock clock
        - companion object CUTOFF_TIME kotlin.time.Duration
        + operator fun invoke(stopId: StopId, routeId: RouteId?) Flow_List_IStopTimetableTime__
    }

    class ServicesAndTimesForStopUseCase {
        + operator fun invoke(stopId: StopId): Flow_ServicesAndTimes_
    }

    class ServicesAndTimes {
        + val services List_Service_
        + val times List_StopTimetableTime_
    }

    class Service {
        + val id String
        + fun active(instant: Instant, scheduleTimeZone: TimeZone): Boolean
    }

    class StopTimetableTime {
        + val serviceId String
        + val routeId RouteId
        + val heading String
        + val departureTime Time
        + val last Boolean
        + fun withTimeReference(startOfDay: Instant) StopTimetableTime
    }

    class IStopTimetableTime {
        <<interface>>
        + val serviceId String
        + val routeId RouteId
        + val departureTime Time
    }

    class MetadataRepository {
        + fun timeZone(): TimeZone
    }

    class RemoteConfigRepository {
        + fun showSchoolServices(): Flow_Boolean_
    }

    class PreferencesRepository_LD {
        + fun preference(preference: Preference_T_): PreferencesUnit_T_
    }

    class Clock {
        + fun now(): Instant
    }

    class RouteAndHeading {
        + val routeId RouteId
        + val heading String
    }

    LastDepartureForStopUseCase ..> MetadataRepository : uses
    LastDepartureForStopUseCase ..> ServicesAndTimesForStopUseCase : uses
    LastDepartureForStopUseCase ..> RemoteConfigRepository : uses
    LastDepartureForStopUseCase ..> PreferencesRepository_LD : uses
    LastDepartureForStopUseCase ..> Clock : uses
    LastDepartureForStopUseCase ..> ServicesAndTimes : aggregates
    LastDepartureForStopUseCase ..> RouteAndHeading : groups

    ServicesAndTimes ..> Service : contains
    ServicesAndTimes ..> StopTimetableTime : contains

    StopTimetableTime ..|> IStopTimetableTime

    ServiceTest ..> LastDepartureForStopUseCase : tests
Loading

File-Level Changes

Change Details Files
Refactor anchored bottom sheet drag/animation to use a custom SinatraAnchoredDraggableState and SinatraSheetState around modern Draggable APIs.
  • Replace direct AnchoredDraggable usage with a Modifier.sinatraAnchoredDraggable helper backed by an internal DraggableState
  • Rework SinatraAnchoredDraggableState to remove decay-animation path, compute targets via positional/velocity thresholds, and expose closestValue/progress using new DraggableAnchors APIs
  • Introduce SinatraSheetState as a thin wrapper around SinatraAnchoredDraggableState with explicit expand/halfExpand/partialExpand/show/hide APIs and motion specs
  • Move SinatraDraggableAnchors into its own file and update it to new DraggableAnchors signatures (hasPositionFor, minPosition/maxPosition, anchorAt/positionAt stubs)
  • Adjust SinatraBottomSheet and related scaffold code to use the new state/anchors API and remove density wiring
ui/src/commonMain/kotlin/cl/emilym/sinatra/ui/widgets/bottomsheet/SinatraAnchoredDraggableState.kt
ui/src/commonMain/kotlin/cl/emilym/sinatra/ui/widgets/bottomsheet/SinatraBottomSheet.kt
ui/src/commonMain/kotlin/cl/emilym/sinatra/ui/widgets/bottomsheet/SinatraBottomSheetScaffold.kt
ui/src/commonMain/kotlin/cl/emilym/sinatra/ui/widgets/bottomsheet/SinatraSheetDefaults.kt
ui/src/commonMain/kotlin/cl/emilym/sinatra/ui/widgets/bottomsheet/SinatraSheetState.kt
ui/src/commonMain/kotlin/cl/emilym/sinatra/ui/widgets/bottomsheet/SinatraDraggableAnchors.kt
Add optional countdown-style arrival/departure display with new time helpers and preferences, simplifying delay wording strings.
  • Introduce Preference.CountdownUntilArrival and wire it into PreferencesRepository and UnitsPreferencesScreen via HorizontalPreferencesCheckboxLockup
  • Extend StopStationTime.text to generate either absolute time or relative countdown strings, including new generic_countdown and scheduled_delay strings
  • Add reactive time helpers (rememberCountdown, Time.isNowish, composable Time.isInPast using countdown, cached formatting) in TimeHelpers and TimeFormats
  • Update localized string resources (en, zh) to support countdown wording and consolidate early/late phrasing into scheduled_delay_* strings
  • Make checkbox lockup rows whole-row clickable via new HorizontalPreferencesCheckboxLockup and noRippleClickable
shared/src/commonMain/kotlin/cl/emilym/sinatra/data/repository/PreferencesRepository.kt
ui/src/commonMain/kotlin/cl/emilym/sinatra/ui/presentation/screens/preferences/UnitsPreferencesScreen.kt
ui/src/commonMain/kotlin/cl/emilym/sinatra/ui/widgets/StopCard.kt
ui/src/commonMain/kotlin/cl/emilym/sinatra/ui/localization/TimeHelpers.kt
ui/src/commonMain/kotlin/cl/emilym/sinatra/ui/localization/TimeFormats.kt
ui/src/commonMain/kotlin/cl/emilym/sinatra/ui/widgets/form/PreferencesCheckbox.kt
ui/src/commonMain/composeResources/values/strings.xml
ui/src/commonMain/composeResources/values-zh/strings.xml
Refine last departure selection logic across day boundaries and add coverage for early-morning next-day departures.
  • Change LastDepartureForStopUseCase to use kotlin.time.Clock and treat the next day specially: only consider departures before a configured cutoff (3am) and prefer them over same-day future departures in some cases
  • Add helper CUTOFF_TIME constant and adjust day iteration ordering and selection logic (yesterday/today/tomorrow resolution)
  • Introduce a new android unit test to assert that a 1am tomorrow departure is preferred over a 23:00 today departure under the new rules
shared/src/commonMain/kotlin/cl/emilym/sinatra/domain/LastDepartureForStopUseCase.kt
shared/src/androidUnitTest/kotlin/cl/emilym/sinatra/domain/LastDepartureForStopUseCaseTest.kt
Surface route- and stop-specific content banners through AlertRepository and ContentRepository.
  • Add ROUTE_BANNER_ID and STOP_BANNER_ID templates in ContentRepository
  • Update AlertRepository.alerts(flow) to resolve ContentRepository banners when the context is a route or a stop, returning a single banner when available
shared/src/commonMain/kotlin/cl/emilym/sinatra/data/repository/ContentRepository.kt
shared/src/commonMain/kotlin/cl/emilym/sinatra/data/repository/AlertRepository.kt
Increase location update frequency for current-location flows on Android and iOS, and tighten default accuracy.
  • Change default LocationAccuracy in currentLocation() to HIGH
  • Shorten high-accuracy update interval on Android from 30s to 5s and on iOS from 10s to 5s to get more frequent updates
ui/src/commonMain/kotlin/cl/emilym/sinatra/ui/widgets/CurrentLocation.kt
ui/src/androidMain/kotlin/cl/emilym/sinatra/ui/widgets/CurrentLocation.android.kt
ui/src/iosMain/kotlin/cl/emilym/sinatra/ui/widgets/CurrentLocation.ios.kt
Upgrade toolchain/dependencies and align with kotlin.time.Clock plus Java 21/Gradle 8.13.
  • Update Gradle wrapper to 8.13 and switch CI workflows (build, lint, test) and shared GitHub action configs to Java 21 and newer shared action versions
  • Bump Kotlin, Compose, Ktor, Koin, Room, SQLite, Coil, kotlinx-serialization, kotlinx-datetime and various emily* libraries in libs.versions.toml; add emily-standardbutton
  • Switch from kotlinx.datetime.Clock/Instant to kotlin.time.Clock/Instant in shared code (MemoryCache, SharedModule, RealtimeInformationImpl, time helpers, tests) and opt in to ExperimentalTime in shared, ui, and androidApp Kotlin compiler options
  • Adjust KSP wiring and compilation task dependencies in ui/shared Gradle scripts to ensure common KSP metadata runs before platform compilations
gradle/libs.versions.toml
gradle/wrapper/gradle-wrapper.properties
.github/workflows/build.yml
.github/workflows/test.yml
.github/workflows/lint.yml
.github/workflows/build.pkl
.github/workflows/test.pkl
.github/workflows/lint.pkl
shared/src/commonMain/kotlin/cl/emilym/sinatra/SharedModule.kt
shared/src/commonMain/kotlin/cl/emilym/sinatra/lib/MemoryCache.kt
shared/src/commonMain/kotlin/cl/emilym/sinatra/data/models/Realtime.kt
ui/build.gradle.kts
shared/build.gradle.kts
androidApp/build.gradle.kts
Miscellaneous fixes and cleanups around services tests and bottom sheet utilities.
  • Fix ServiceTest import mix between java.time and kotlinx.datetime, using java.time.DayOfWeek explicitly
  • Remove BottomSheetDefaults UI helpers from SinatraSheetDefaults in favor of upstream Material defaults
  • Expose PositionalThreshold constant for bottom sheets and minor state wiring tweaks (e.g., offset NaN handling)
shared/src/androidUnitTest/kotlin/cl/emilym/sinatra/data/models/ServiceTest.kt
ui/src/commonMain/kotlin/cl/emilym/sinatra/ui/widgets/bottomsheet/SinatraSheetDefaults.kt
ui/src/commonMain/kotlin/cl/emilym/sinatra/ui/widgets/bottomsheet/SinatraBottomSheet.kt

Possibly linked issues

  • #⭐ Update to compose 1.8.0: PR upgrades Compose and substantially rewrites the Sinatra bottom sheet state/drag logic, addressing the janky sheet behavior issue.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • In SinatraDraggableAnchors, anchorAt and positionAt are left as TODO("Not yet implemented"), which will crash at runtime if the framework starts calling these methods; either implement them (e.g., via a stable ordered backing list) or throw a more explicit UnsupportedOperationException with justification, and add a test to ensure they’re not accidentally invoked.
  • You’ve switched various clocks and instants over to kotlin.time.Clock/kotlin.time.Instant (e.g., LocalClock, MemoryCache, SharedModule) while other APIs still use kotlinx.datetime.Instant and extensions like startOfDay; it would be safer to centralize on a single time abstraction or provide clear conversion helpers to avoid subtle type/extension mismatches later.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `SinatraDraggableAnchors`, `anchorAt` and `positionAt` are left as `TODO("Not yet implemented")`, which will crash at runtime if the framework starts calling these methods; either implement them (e.g., via a stable ordered backing list) or throw a more explicit `UnsupportedOperationException` with justification, and add a test to ensure they’re not accidentally invoked.
- You’ve switched various clocks and instants over to `kotlin.time.Clock`/`kotlin.time.Instant` (e.g., `LocalClock`, `MemoryCache`, `SharedModule`) while other APIs still use `kotlinx.datetime.Instant` and extensions like `startOfDay`; it would be safer to centralize on a single time abstraction or provide clear conversion helpers to avoid subtle type/extension mismatches later.

## Individual Comments

### Comment 1
<location> `ui/src/commonMain/kotlin/cl/emilym/sinatra/ui/widgets/bottomsheet/SinatraDraggableAnchors.kt:48-49` </location>
<code_context>
+        return anchors == other.anchors
+    }
+
+    override fun anchorAt(index: Int): T? {
+        TODO("Not yet implemented")
+    }
+
</code_context>

<issue_to_address>
**issue (bug_risk):** Implement `anchorAt`/`positionAt` instead of leaving TODOs to avoid runtime crashes.

Because the `DraggableAnchors` gesture system will call `anchorAt`/`positionAt`, leaving them as `TODO` will cause a runtime crash whenever they’re used. Please implement them using the backing `anchors` map (e.g., via `entries.toList()[index]`) or refactor storage to maintain a stable ordered list so these lookups are O(1) and safe to call.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +48 to +49
override fun anchorAt(index: Int): T? {
TODO("Not yet implemented")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Implement anchorAt/positionAt instead of leaving TODOs to avoid runtime crashes.

Because the DraggableAnchors gesture system will call anchorAt/positionAt, leaving them as TODO will cause a runtime crash whenever they’re used. Please implement them using the backing anchors map (e.g., via entries.toList()[index]) or refactor storage to maintain a stable ordered list so these lookups are O(1) and safe to call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant