fix(live-state): eliminate flash on poll by switching to innerHTML swap
outerHTML removes and re-inserts the section on each tick, causing a blank frame. Keeping the <section> as a stable DOM container and swapping only innerHTML means avatars and text update in-place without any teardown/reconstruct cycle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
096d18ac64
commit
175e4e653c
2 changed files with 59 additions and 64 deletions
|
|
@ -1,65 +1,60 @@
|
|||
<section class="panel live-state"
|
||||
hx-get="/servers/{{ server.id }}/live-state"
|
||||
hx-trigger="every {{ poll_seconds }}s"
|
||||
hx-swap="outerHTML">
|
||||
<h2 class="section-title">Live state</h2>
|
||||
{% if not snapshot or not snapshot_fresh %}
|
||||
<p class="muted">No data — server is not currently reporting.</p>
|
||||
{% else %}
|
||||
<p class="server-live-summary">
|
||||
{{ snapshot.players }}/{{ snapshot.max_players }}
|
||||
{% if snapshot.hibernating %}· idle{% endif %}
|
||||
· {{ snapshot.map }}
|
||||
<small class="muted">
|
||||
polled {{ ((now - snapshot.last_seen_at).total_seconds() | int) }}s ago
|
||||
</small>
|
||||
</p>
|
||||
{% endif %}
|
||||
<h2 class="section-title">Live state</h2>
|
||||
{% if not snapshot or not snapshot_fresh %}
|
||||
<p class="muted">No data — server is not currently reporting.</p>
|
||||
{% else %}
|
||||
<p class="server-live-summary">
|
||||
{{ snapshot.players }}/{{ snapshot.max_players }}
|
||||
{% if snapshot.hibernating %}· idle{% endif %}
|
||||
· {{ snapshot.map }}
|
||||
<small class="muted">
|
||||
polled {{ ((now - snapshot.last_seen_at).total_seconds() | int) }}s ago
|
||||
</small>
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if current_players %}
|
||||
<h3 class="section-subtitle">Current players</h3>
|
||||
<ul class="player-grid">
|
||||
{% for session, profile in current_players %}
|
||||
<li class="player-card">
|
||||
<a class="player-link"
|
||||
href="https://steamcommunity.com/profiles/{{ session.steam_id_64 }}"
|
||||
target="_blank" rel="noopener noreferrer">
|
||||
{% if profile and profile.avatar_url %}
|
||||
<img class="avatar" src="{{ profile.avatar_url }}" alt="" loading="lazy">
|
||||
{% else %}
|
||||
<span class="avatar placeholder" aria-hidden="true"></span>
|
||||
{% endif %}
|
||||
<span class="name">{{ (profile and profile.persona_name) or session.name_at_join }}</span>
|
||||
</a>
|
||||
<span class="meta">
|
||||
joined {{ ((now - session.joined_at).total_seconds() // 60) | int }}m ago
|
||||
· ping {{ session.min_ping }}-{{ session.max_ping }}ms
|
||||
</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% if current_players %}
|
||||
<h3 class="section-subtitle">Current players</h3>
|
||||
<ul class="player-grid">
|
||||
{% for session, profile in current_players %}
|
||||
<li class="player-card">
|
||||
<a class="player-link"
|
||||
href="https://steamcommunity.com/profiles/{{ session.steam_id_64 }}"
|
||||
target="_blank" rel="noopener noreferrer">
|
||||
{% if profile and profile.avatar_url %}
|
||||
<img class="avatar" src="{{ profile.avatar_url }}" alt="" loading="lazy">
|
||||
{% else %}
|
||||
<span class="avatar placeholder" aria-hidden="true"></span>
|
||||
{% endif %}
|
||||
<span class="name">{{ (profile and profile.persona_name) or session.name_at_join }}</span>
|
||||
</a>
|
||||
<span class="meta">
|
||||
joined {{ ((now - session.joined_at).total_seconds() // 60) | int }}m ago
|
||||
· ping {{ session.min_ping }}-{{ session.max_ping }}ms
|
||||
</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if recent_players %}
|
||||
<h3 class="section-subtitle">Recent players</h3>
|
||||
<ul class="player-grid recent">
|
||||
{% for row in recent_players %}
|
||||
<li class="player-card">
|
||||
<a class="player-link"
|
||||
href="https://steamcommunity.com/profiles/{{ row.steam_id_64 }}"
|
||||
target="_blank" rel="noopener noreferrer">
|
||||
{% if row.avatar_url %}
|
||||
<img class="avatar" src="{{ row.avatar_url }}" alt="" loading="lazy">
|
||||
{% else %}
|
||||
<span class="avatar placeholder" aria-hidden="true"></span>
|
||||
{% endif %}
|
||||
<span class="name">{{ row.persona_name or row.name_at_join }}</span>
|
||||
</a>
|
||||
<span class="meta">
|
||||
last seen {{ ((now - row.last_seen).total_seconds() // 60) | int }}m ago
|
||||
</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% if recent_players %}
|
||||
<h3 class="section-subtitle">Recent players</h3>
|
||||
<ul class="player-grid recent">
|
||||
{% for row in recent_players %}
|
||||
<li class="player-card">
|
||||
<a class="player-link"
|
||||
href="https://steamcommunity.com/profiles/{{ row.steam_id_64 }}"
|
||||
target="_blank" rel="noopener noreferrer">
|
||||
{% if row.avatar_url %}
|
||||
<img class="avatar" src="{{ row.avatar_url }}" alt="" loading="lazy">
|
||||
{% else %}
|
||||
<span class="avatar placeholder" aria-hidden="true"></span>
|
||||
{% endif %}
|
||||
<span class="name">{{ row.persona_name or row.name_at_join }}</span>
|
||||
</a>
|
||||
<span class="meta">
|
||||
last seen {{ ((now - row.last_seen).total_seconds() // 60) | int }}m ago
|
||||
</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
<section class="panel live-state"
|
||||
hx-get="/servers/{{ server.id }}/live-state"
|
||||
hx-trigger="load, every 5s"
|
||||
hx-swap="outerHTML">
|
||||
hx-swap="innerHTML">
|
||||
</section>
|
||||
|
||||
<h2 class="section-title">Files</h2>
|
||||
|
|
|
|||
Loading…
Reference in a new issue