feat: profiles and /set profile, and id cards

* New functionality to set meta data on businesscards.
* Can develop a user profile.
* Business cards link to user profile.
This commit is contained in:
Evan Carroll 2026-01-25 10:50:10 -06:00
parent cd8dfb94a3
commit 710985638f
35 changed files with 4932 additions and 435 deletions

View file

@ -114,6 +114,7 @@ pub fn RealmSceneViewer(
let (context_menu_open, set_context_menu_open) = signal(false);
let (context_menu_position, set_context_menu_position) = signal((0.0_f64, 0.0_f64));
let (context_menu_target, set_context_menu_target) = signal(Option::<String>::None);
let (context_menu_username, set_context_menu_username) = signal(Option::<String>::None);
// Prop context menu state
let (prop_context_menu_open, set_prop_context_menu_open) = signal(false);
@ -258,13 +259,13 @@ pub fn RealmSceneViewer(
if hit_test_canvas(&canvas, client_x, client_y) {
if let Ok(member_id) = member_id_str.parse::<Uuid>() {
if my_user_id != Some(member_id) {
if let Some(name) = members.get().iter()
if let Some(member) = members.get().iter()
.find(|m| m.member.user_id == member_id)
.map(|m| m.member.display_name.clone())
{
ev.prevent_default();
set_context_menu_position.set((client_x, client_y));
set_context_menu_target.set(Some(name));
set_context_menu_target.set(Some(member.member.display_name.clone()));
set_context_menu_username.set(Some(member.member.username.clone()));
set_context_menu_open.set(true);
return;
}
@ -581,22 +582,40 @@ pub fn RealmSceneViewer(
open=Signal::derive(move || context_menu_open.get())
position=Signal::derive(move || context_menu_position.get())
header=Signal::derive(move || context_menu_target.get())
items=Signal::derive(move || vec![ContextMenuItem { label: "Whisper".to_string(), action: "whisper".to_string() }])
items=Signal::derive(move || vec![
ContextMenuItem { label: "View Profile".to_string(), action: "view_profile".to_string() },
ContextMenuItem { label: "Whisper".to_string(), action: "whisper".to_string() },
])
on_select=Callback::new({
let on_whisper_request = on_whisper_request.clone();
move |action: String| {
if action == "whisper" {
if let Some(target) = context_menu_target.get() {
if let Some(ref callback) = on_whisper_request {
callback.run(target);
match action.as_str() {
"view_profile" => {
if let Some(username) = context_menu_username.get() {
#[cfg(feature = "hydrate")]
{
let url = format!("/users/{}", username);
let _ = web_sys::window()
.unwrap()
.open_with_url_and_target(&url, "_blank");
}
}
}
"whisper" => {
if let Some(target) = context_menu_target.get() {
if let Some(ref callback) = on_whisper_request {
callback.run(target);
}
}
}
_ => {}
}
}
})
on_close=Callback::new(move |_: ()| {
set_context_menu_open.set(false);
set_context_menu_target.set(None);
set_context_menu_username.set(None);
})
/>
<ContextMenu