Skip to content

Bug: FeedView cannot see in-place row updates — merges only unseen ids, keyed on count #105

Description

@Adron

Found while implementing #96 (see PR #103).

The problem

FeedView mirrors store.feedMessages into a private @State private var messages and reconciles
in .onChange(of: store.feedMessages.count) (Views/FeedView.swift:385). The merge adds only ids it
does not already hold:

let existingIds = Set(messages.map { $0.id })
let newMessages = store.feedMessages.filter { !existingIds.contains($0.id) }

So any change to an existing row is invisible to the feed:

  • the trigger is .count, which does not move when a row is edited in place, and
  • even if it fired, the merge skips ids already present.

The store and the persisted cache are correct; only the view's private copy goes stale, until a full
reload replaces it.

How it surfaced

#103 adds AppDataStore.applyLinkMetadata(_:toMessageId:) to backfill a just-posted row's link
preview. The store updates correctly and the test proves it — and the feed still renders the old row.

Why it is worth fixing properly

This is not specific to link metadata. Any future in-place mutation — a dig count corrected from the
server, an edited body, a moderation state change — hits the same wall, and each one will look like
its own mysterious bug. The dig path already works around it with a separate digStates dictionary
and a locallyToggled set, which is the same problem solved once, locally.

Build this

  • Make the reconciliation respond to content changes, not just .count. Options, roughly in
    increasing order of cost: observe the array itself rather than its count (Message is
    Hashable); or replace matched ids in place during the merge rather than skipping them; or
    drop the private @State mirror and render from the store directly.
  • Preserve what the mirror is currently buying — scroll position and the syncedFromStore
    first-load behaviour must not regress, and a change must not re-trigger a load.
  • Consider whether digStates / locallyToggled can then collapse into the same path. Do not do
    it in the same PR unless it falls out cleanly.

Acceptance criteria

  • Mutating one message in AppDataStore updates that row in the rendered feed without a reload.
  • No scroll-position regression, no extra fetches, no duplicate rows.
  • A test that mutates a store message and asserts the view's data source reflects it.

Blocks

#103 / #96 are inert until this and #104 land.

Files: Views/FeedView.swift, possibly Services/AppDataStore.swift.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingws:papercutsW8 — small parity papercuts

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions