<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="180539" data-post-id="180539">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tomekowal" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/tomekowal/120/2847_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tomekowal
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Some questions to make sure I understand the solution proposed here. Let’s work on the counter example to have something concrete.<br>
We have two DOM elements: the button that emits click event and the counter.</p>
<p>Where do I use the <code>this.syncPushEvent</code>? Do I attach it to <code>onClick</code> of that button inside <code>mounted</code> section of that hook? Will that prevent sending regular event?<br>
Where do I put the code that updates the counter? in the <code>updated</code> section of the button hook? How do we know we should not update the counter in the meantime?</p>
<p>I think that the solution LiveView currently uses for buttons can work only because the event and state update are for the same DOM element. In case of optimistic UI, we should be able to change multiple parts of the page with a single event.</p>
<p>I was thinking about a different solution using client and server clocks.</p>
<ol>
<li>JS part has the vector clock <code>client.clock</code>. The server has its own, let’s call it <code>server.clock</code></li>
<li>Server increments its own clock every time it sends new state (e.g. an event may originate in the server from PubSub). That part is really important and AFAIU is not part of the current solution with <code>view.ref</code>.</li>
<li>I am marking UI elements that I think should be lazily updated e.g: <code>&lt;p phx-update="optimistic"&gt;&lt;%= @counter %&gt;&lt;/p&gt;</code>.</li>
<li>I am attaching hook to emitted LiveView event. E.g. <code>&lt;button phx-click="inc" phx-hook="Button"&gt;+&lt;/button&gt;</code></li>
<li>In the hook, there needs to be a section for running this custom code right after event gets sent to the server:</li>
</ol>
<pre data-code-wrap="elixir"><code class="lang-elixir">Hooks.Button = {
  onEvent("inc") { //should be called right after "inc" event is sent
    counter = getElementById("counter")
    mark_updated(counter) //add something like `phx-clock="#{client.clock}"` to the counter
    ... //increment the counter to achieve instant update
  }
}
</code></pre>
<ol start="6">
<li>When state update comes from the server, it has the server.clock.</li>
</ol>
<pre data-code-wrap="elixir"><code class="lang-elixir">if server.clock &gt; client.clock, do: client.clock = server.clock

// when updating elements
if element.phx-update=="lazy" &amp;&amp; server.clock &gt;= element.phx-clock do
  normally_patch_the_DOM()
else
  ignore()
end
</code></pre>
<p>I believe that solution is much more general because it takes into consideration both events started by the browser and server.<br>
It can’t replace the current solution for buttons with <code>view.ref</code> because you need to precisely know which client event that button is handling.</p>
<p>Also without separate clocks, you can’t handle optimistic updates originating on the server.</p>
<p>Does that make sense?</p>
<p>The machinery might seem complicated but I wanted it to work on examples like this:</p>
<ol>
<li>User quickly increments counter twice. JS optimistically sets it two. Counter = 2 Client clock = 2.</li>
<li>Server processes first event. Sends Counter = 1, Server clock = 1. Since Server clock is lower than client clock, it doesn’t do the update (so the counter doesn’t go back)</li>
<li>Server processes the second event. Sends Counter = 2 Server clock = 2. Now the counters are the same so we replace the number 2 with number 2 (everything is fine)</li>
</ol>
<p>This works even if server-side increments the counter a bunch of times.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="180539" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/optimistic-uis-in-liveview/32536/12">Post #11</a>
	                </div>
	            </div>
              <div id="likers-container-180539" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="180539"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #11"></div>
  </section>
</div>
    <div class="postbit" id="180540" data-post-id="180540">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="josevalim" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/josevalim/120/1787_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  josevalim
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Elixir</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>The clock example will definitely work with what we have in mind. The trick though is that, because you have to update the client, you won’t be using phx-click when you first click on the button, but rather a hook, so you can update the client and then push the event.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="180540" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/optimistic-uis-in-liveview/32536/13">Post #12</a>
	                </div>
	            </div>
              <div id="likers-container-180540" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="180540"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #12"></div>
  </section>
</div>
    <div class="postbit" id="180541" data-post-id="180541">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tomekowal" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/tomekowal/120/2847_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tomekowal
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Yes, We would need to make sure that the hook is closely tied to the event for two reasons:</p>
<ul>
<li>we need client.clock tied to sending event to mark the DOM</li>
<li>it would be easier to work with if we use the same event name in JS and Elixir (both implementations should do similar thing, but the one from server should eventually overwrite the one client)</li>
</ul>
<p>So I would even send the event before running the hook.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="180541" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/optimistic-uis-in-liveview/32536/14">Post #13</a>
	                </div>
	            </div>
              <div id="likers-container-180541" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="180541"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #13"></div>
  </section>
</div>
    <div class="postbit" id="180740" data-post-id="180740">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tomekowal" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/tomekowal/120/2847_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tomekowal
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I fiddled around with existing clocks and references.<br>
I think we need a completely new one for the purpose.</p>
<p>I can’t reuse view.ref because server initiated events would mess up current button references when synchronizing the clocks.</p>
<p>I also can’t reuse underlying transport numbering because keep-alive messages could mess up the clocks.</p>
<p>So I believe, I would have to add the clock to the payload.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="180740" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/optimistic-uis-in-liveview/32536/15">Post #14</a>
	                </div>
	            </div>
              <div id="likers-container-180740" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="180740"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-last-post cat-last-post" title="Last post!"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <span class="all-loaded">— All posts loaded —</span>
</div></template></turbo-stream>