Skip to content

Stability improvements requiring source/binary breaks (queued for next major) #67

Description

@pushpalroy

Collecting follow-ups found during the audit in #66 that were intentionally deferred because they change the public API of the library. These should ship together in the next major version (e.g. 5.0) so consumers only migrate once.

1. JetLimeEventStyle.position / pointPlacement should be val

Current: both are public var properties on an @Immutable class and are mutated via setPosition / setPointPlacement / JetLimeEventDefaults.eventStyle(...).apply { ... }.

Problem: @Immutable annotation is dishonest; Compose compiler trusts it, but the mutable backing fields are observable through public setters. Any consumer that hoists a style and calls .position = X directly is relying on that mutability.

Proposed fix:

  • Move position and pointPlacement into the primary constructor as val.
  • Keep setPosition / setPointPlacement but have them return a new instance (they already document "returns a JetLimeEventStyle instance with the updated value").
  • Update JetLimeEventDefaults.eventStyle(...) to pass values through the constructor instead of .apply { this.position = ... }.

Break: source and binary break for any caller that writes style.position = X directly.

2. ItemsList backed by ImmutableList<T>

Current: class ItemsList<T>(val items: List<T>) — the Compose compiler treats List<T> as unstable; @Immutable on the class papers over it.

Proposed fix:

  • Change items property type to kotlinx.collections.immutable.ImmutableList<T>.
  • Add a secondary convenience constructor ItemsList(list: List<T>) that wraps via toImmutableList(), and / or a varargs factory.

Break: source break for anyone reading itemsList.items as a List<T> in a non-covariant context; binary break via getter signature change.

3. EventPointType.equals over Painter

Current: equals / hashCode include icon: Painter?. Painter does not override equals by content — it falls back to referential identity. Two different painter instances with identical content are considered "not equal", defeating stability if the caller doesn't hoist / remember the painter.

Proposed fix (pick one):

  • Drop icon from equals / hashCode and document that callers must remember the painter upstream.
  • Or use a small content key (e.g. a user-supplied id) for equality.

Break: behavior change that could alter recomposition-skipping semantics for existing users.

4. Convert EventPosition to enum class

Current: class EventPosition internal constructor(val name: String) with three singletons in the companion (START private, MIDDLE private, END internal) and a dynamic(index, listSize) factory.

Proposed fix:

enum class EventPosition {
  START, MIDDLE, END;
  fun isNotEnd() = this != END
  fun isNotStart() = this != START
  companion object {
    fun dynamic(index: Int, listSize: Int): EventPosition = when (index) {
      listSize - 1 -> END
      0 -> START
      else -> MIDDLE
    }
  }
}

Benefits: real compiler-level stability, removes the fragile string-literal comparisons, simpler equals / hashCode.

Break: widens visibility of START / MIDDLE / END (now public). Class → enum also changes compiled bytecode flags; reflective users may break.

5. Per-item eventStyle(...) should be remembered

Current: the sample and README call JetLimeEventDefaults.eventStyle(position = position) fresh inside every lazy item on every recomposition, allocating a new JetLimeEventStyle. With the stability fixes above, value equality would still let the item skip — but the allocation is wasted.

Proposed fix:

  • Update the sample and README snippets to remember(position) { eventStyle(position) } where appropriate.
  • Consider adding an overload or helper in JetLimeEventDefaults that memoizes by position.

Break: documentation / sample only; no library break.


Tracking

Target: next major version.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions