Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Views in List view in Tab view not updating correctly #1441

Open
jtormey opened this issue Sep 11, 2024 · 8 comments
Open

[Bug]: Views in List view in Tab view not updating correctly #1441

jtormey opened this issue Sep 11, 2024 · 8 comments
Assignees
Labels
bug Something isn't working v0.3
Milestone

Comments

@jtormey
Copy link

jtormey commented Sep 11, 2024

What happened?

I noticed that if an event updates the order of items in a list and at the same time updates a property of a list item in a tab view, after navigating from another tab, then the list no longer behaves as expected after the first update.

Without reordering (working):

Simulator.Screen.Recording.-.iPhone.15.-.2024-09-11.at.16.39.27.mp4

With reordering (not working) after navigating from another tab:

Simulator.Screen.Recording.-.iPhone.15.-.2024-09-11.at.16.42.26.mp4

Note that the circular indicator does not change even as the list order changes.

Relevant code:

<TabView selection={@selection} phx-change={@on_change}>
  <VStack tag="..." style="tabItem(:item)">
    <Label template="item" systemImage="...">
      ...
    </Label>
    <List style="listStyle(.plain)">
      <Section>
        <Text template="header">
          Meat counter
        </Text>
        <HStack id={"item_" <> item.id} :for={item <- @items}>
          <Image
            systemName={if item.in_cart, do: "largecircle.fill.circle", else: "circle"}
            style="imageScale(.large); foregroundColor(.accentColor);"
            phx-click="toggle_completed"
            phx-value-id={item.id}
          />
          <Text>
            <%= item.title %>
          </Text>
          <LabeledContent>
            <%= item.detail %>
          </LabeledContent>
        </HStack>
      </Section>
    </List>
  </VStack>
</TabView>

Library Version

0.3.0

Xcode Version

15.4 (15F31d)

Swift Version

No response

On which device or simulator are you running into the problem?

iPhone

Target Device Operating System Version

17.2

Relevant log output

No response

@jtormey jtormey added the bug Something isn't working label Sep 11, 2024
@bcardarella
Copy link
Collaborator

@jtormey can you share the event handler for toggle_completed ?

@jtormey
Copy link
Author

jtormey commented Sep 18, 2024

Handler is as follows:

  def handle_event("toggle_completed", %{"id" => id}, socket) do
    items =
      Enum.map(socket.assigns.items, fn
        %{id: ^id} = item -> %{item | in_cart: not item.in_cart}
        item -> item
      end)
      # BUG: LVN behavior changes after sorting
      |> sort_items()

    {:noreply, assign(socket, :items, items)}
  end

  def sort_items(items) do
    Enum.sort_by(items, & &1.in_cart)
  end

@carson-katri
Copy link
Contributor

carson-katri commented Sep 19, 2024

I'm having trouble replicating this on main. Does this look correct to replicate it?

I added an animation to visualize the items moving more easily, but the behavior was the same with/without it for me.

Simulator.Screen.Recording.-.iPhone.16.Pro.-.2024-09-19.at.10.41.44.mp4

This is from an iOS 18 simulator, but I also tried on a iOS 17.5 simulator.

Code
<TabView>
  <VStack tag="home" style="tabItem(:item)">
    <Label template="item" systemImage="house">
      Home
    </Label>
    Tab 1
  </VStack>
  <VStack tag="list" style="tabItem(:item)">
    <Label template="item" systemImage="circle">
      List
    </Label>
    <List style='listStyle(.plain); animation(.snappy, value: attr("anim-value"))' anim-value={length(Enum.filter(@items, & &1.in_cart))}>
      <Section>
        <Text template="header">
          Meat counter
        </Text>
        <HStack id={"item_" <> item.id} :for={item <- @items}>
          <Image
            systemName={if item.in_cart, do: "largecircle.fill.circle", else: "circle"}
            style="imageScale(.large); foregroundColor(.accentColor);"
            phx-click="toggle_completed"
            phx-value-id={item.id}
          />
          <Text>
            <%= item.title %>
          </Text>
          <LabeledContent>
            <%= item.detail %>
          </LabeledContent>
        </HStack>
      </Section>
    </List>
  </VStack>
</TabView>
defmodule CoreIntegrationWeb.HomeLive do
  use CoreIntegrationWeb, :live_view
  use CoreIntegrationNative, :live_view

  def mount(_params, _session, socket) do
    {:ok, socket
      |> assign(:items, [
        %{ id: "0", in_cart: false, title: "Item 1", detail: "Detail" },
        %{ id: "1", in_cart: false, title: "Item 2", detail: "Detail" },
        %{ id: "2", in_cart: false, title: "Item 3", detail: "Detail" },
        %{ id: "3", in_cart: false, title: "Item 4", detail: "Detail" },
        %{ id: "4", in_cart: false, title: "Item 5", detail: "Detail" },
        %{ id: "5", in_cart: false, title: "Item 6", detail: "Detail" },
      ])}
  end

  def render(assigns) do
    ~H"""
    """
  end

  def handle_event("toggle_completed", %{"id" => id}, socket) do
    items =
      Enum.map(socket.assigns.items, fn
        %{id: ^id} = item -> %{item | in_cart: not item.in_cart}
        item -> item
      end)
      # BUG: LVN behavior changes after sorting
      |> sort_items()

    {:noreply, assign(socket, :items, items)}
  end

  def sort_items(items) do
    Enum.sort_by(items, & &1.in_cart)
  end
end

@jtormey
Copy link
Author

jtormey commented Sep 20, 2024

That looks correct, maybe I need to update my package versions.

@jtormey
Copy link
Author

jtormey commented Sep 20, 2024

@carson-katri I believe I've narrowed in on the issue, I'm only able to recreate this when TabView has a phx-change="swiftui_tab_changed" attribute, which I noticed your example doesn't have.

@carson-katri
Copy link
Contributor

That makes sense, I think the TabView is created differently if a phx-change is provided. I'll test that way

@carson-katri
Copy link
Contributor

carson-katri commented Oct 2, 2024

We finally tracked down the issue.

If you change the IDs of all the elements in the list when switching tabs, the images inside the elements will not update. I'm not sure if this is a bug in the SwiftUI client, or core's diff merging yet.

You can replicate it by changing the ids when the tab selection changes. In this handler, I'm just adding a 0 at the end of them.

def handle_event("tab_changed", %{ "selection" => selection }, socket) do
  {
    :noreply,
    socket
      |> assign(:tab, selection)
      |> assign(:items, Enum.map(socket.assigns.items, & Map.put(&1, :id, &1.id <> "0")))
  }
end

Simpler replication:

<List>
  <HStack
    id={"item_" <> item.id} :for={item <- @items}
    phx-click="toggle_completed"
    phx-value-id={item.id}
  >
    <Text
      content={if item.in_cart, do: "#{item.id} checked", else: "#{item.id} unchecked"}
    />
  </HStack>
</List>

<Button phx-click="replace_ids">Replace IDs</Button>
def handle_event("replace_ids", _params, socket) do
  {:noreply, assign(socket, :items, Enum.map(socket.assigns.items, & Map.put(&1, :id, &1.id <> "0")))}
end

@bcardarella
Copy link
Collaborator

Confirmed as still a bug. Pushing to 0.5.0 milestone

@bcardarella bcardarella added this to the 0.5.0 milestone Feb 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working v0.3
Projects
None yet
Development

No branches or pull requests

3 participants