remove members by key

This commit is contained in:
Evan Carroll 2026-01-27 01:00:39 -06:00
parent ae210d5352
commit c945fb7c43
2 changed files with 49 additions and 23 deletions

View file

@ -663,13 +663,28 @@ pub fn Avatar(
// Effect to draw the avatar when canvas is ready or appearance changes // Effect to draw the avatar when canvas is ready or appearance changes
Effect::new(move |_| { Effect::new(move |_| {
// Subscribe to redraw trigger so this effect re-runs when images load // Subscribe to redraw trigger so this effect re-runs when images load
let _ = redraw_trigger.get(); let trigger_count = redraw_trigger.get();
// Get current values from signals // Get current values from signals
let m = member.get(); let m = member.get();
let layout = layout_memo.get(); let layout = layout_memo.get();
// Debug: log avatar redraw
web_sys::console::log_1(
&format!(
"[Avatar Redraw] user={} trigger={} pos=({:.1}, {:.1}) screen=({:.1}, {:.1})",
m.member.display_name,
trigger_count,
m.member.position_x,
m.member.position_y,
layout.canvas_screen_x,
layout.canvas_screen_y,
)
.into(),
);
let Some(canvas) = canvas_ref.get() else { let Some(canvas) = canvas_ref.get() else {
web_sys::console::log_1(&"[Avatar Redraw] canvas not ready".into());
return; return;
}; };
@ -716,7 +731,11 @@ pub fn Avatar(
// Set onload handler to trigger redraw when image loads // Set onload handler to trigger redraw when image loads
let trigger = set_redraw_trigger; let trigger = set_redraw_trigger;
let path_for_log = normalized_path.clone();
let onload = Closure::once(Box::new(move || { let onload = Closure::once(Box::new(move || {
web_sys::console::log_1(
&format!("[Avatar] Image loaded: {}", path_for_log).into(),
);
trigger.update(|v| *v += 1); trigger.update(|v| *v += 1);
}) as Box<dyn FnOnce()>); }) as Box<dyn FnOnce()>);
img.set_onload(Some(onload.as_ref().unchecked_ref())); img.set_onload(Some(onload.as_ref().unchecked_ref()));

View file

@ -468,14 +468,14 @@ pub fn RealmSceneViewer(
}) })
.into(); .into();
let members_by_key = Signal::derive(move || { // Create indexed members for z-ordering (index determines stacking)
sorted_members.get().into_iter().enumerate() let indexed_members = Signal::derive(move || {
.map(|(idx, m)| (member_key(&m), (idx, m))) sorted_members
.collect::<HashMap<_, _>>() .get()
}); .into_iter()
.enumerate()
let member_keys = Memo::new(move |_| { .map(|(idx, m)| (idx, m))
sorted_members.get().iter().map(member_key).collect::<Vec<_>>() .collect::<Vec<_>>()
}); });
let scene_name = scene.name.clone(); let scene_name = scene.name.clone();
@ -511,33 +511,40 @@ pub fn RealmSceneViewer(
</div> </div>
<div class="avatars-container absolute inset-0" style=format!("z-index: {}; pointer-events: none; overflow: visible;", Z_AVATARS_CONTAINER)> <div class="avatars-container absolute inset-0" style=format!("z-index: {}; pointer-events: none; overflow: visible;", Z_AVATARS_CONTAINER)>
<Show when=move || scales_ready.get() fallback=|| ()> <Show when=move || scales_ready.get() fallback=|| ()>
{move || { <For
member_keys.get().into_iter().map(|key| { each=move || indexed_members.get()
let member_signal = Signal::derive(move || { key=|(_, m)| member_key(m)
members_by_key.get().get(&key).map(|(_, m)| m.clone()).expect("member key should exist") children=move |(idx, initial_member)| {
let key = member_key(&initial_member);
let z_index = (idx as i32) + Z_AVATAR_BASE;
// Per-member Memo: only fires when THIS member's data changes
let member_memo: Memo<ChannelMemberWithAvatar> = Memo::new(move |_| {
sorted_members
.get()
.into_iter()
.find(|m| member_key(m) == key)
.unwrap_or_else(|| initial_member.clone())
}); });
let z_index_signal = Signal::derive(move || {
members_by_key.get().get(&key).map(|(idx, _)| (*idx as i32) + Z_AVATAR_BASE).unwrap_or(Z_AVATAR_BASE) // Bubble signal for this member
}); let bubble_signal: Signal<Option<ActiveBubble>> = Signal::derive(move || {
let z = z_index_signal.get_untracked();
// Derive bubble signal for this member
let bubble_signal = Signal::derive(move || {
active_bubbles.get().get(&key).cloned() active_bubbles.get().get(&key).cloned()
}); });
view! { view! {
<Avatar <Avatar
member=member_signal member=member_memo.into()
transform=scene_transform transform=scene_transform
prop_size=prop_size prop_size=prop_size
z_index=z z_index=z_index
text_em_size=text_em_size text_em_size=text_em_size
screen_bounds=screen_bounds screen_bounds=screen_bounds
active_bubble=bubble_signal active_bubble=bubble_signal
/> />
} }
}).collect_view() }
}} />
{move || { {move || {
let Some(fading_signal) = fading_members else { let Some(fading_signal) = fading_members else {
return Vec::new().into_iter().collect_view(); return Vec::new().into_iter().collect_view();