Skip to content

Conversation Sidebar

The conversation sidebar adds an icon tab strip and expandable panel to /messaging/:contact_id. You can view and edit contact details, notes, activity history, and todos alongside the active conversation.

The conversation view uses a three-section layout:

SectionWidthContent
Contact list25%Searchable contact sidebar
Feedflex-1Messages + calls timeline
Sidebar40px icon strip + 320px panelContact info, notes, activity, todos

The panel only renders when a tab is active. When closed, only the 40px icon strip is visible and the feed expands to fill the space.

src/mods/messaging/types/sidebar_tab_type.rs
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SidebarTab {
ContactInfo, // Editable contact details
Notes, // Contact notes
Activity, // Call/note/todo timeline
Todos, // Contact todos
}

State is managed in MessagingContactView as a Signal<Option<SidebarTab>>. None means the panel is closed.

The strip renders four buttons vertically, each mapped to a SidebarTab:

IconTabComponent Rendered
UserContactInfoContactSidebar
StickyNoteNotesContactRightPanel (notes tab)
HistoryActivityContactRightPanel (activity tab)
ListTodoTodosContactRightPanel (todos tab)

Clicking an active tab closes the panel. Clicking an inactive tab switches to it.

onclick: move |_| {
if active_tab() == Some(sidebar_tab) {
active_tab.set(None);
} else {
active_tab.set(Some(sidebar_tab));
}
}

ContactRightPanel uses Dioxus Tabs internally, which only reads default_value on mount. To switch between Notes, Activity, and Todos correctly, the sidebar sets a key prop that forces a full remount:

ContactRightPanel {
key: "{inner_tab}",
default_tab: inner_tab.to_string(),
// ...
}

Without this, switching from Notes → Activity would keep Notes visible because Tabs ignores default_value changes after mount.

File: src/mods/messaging/components/conversation_sidebar_component.rs

This component accepts the active_tab signal and all props needed by both ContactSidebar and ContactRightPanel. It renders the icon strip unconditionally and the panel content conditionally based on the active tab.

ContactInfo tab renders ContactSidebar with full contact CRUD — edit fields, manage phones, emails, addresses, tags, and user assignments.

Notes/Activity/Todos tabs render ContactRightPanel with the appropriate default_tab value.

MessagingContactView loads eight resources in parallel on mount:

ResourceAPI Call
contact_resourceget_contact_api(id)
calls_resourceget_contact_calls_api(id)
messages_resourceget_contact_messages_api(uuid)
phones_resourceget_phones_api()
notes_resourceget_contact_notes_api(id)
todos_resourceget_contact_todos_api(id)
tags_resourceget_contact_tags_api()
org_members_resourceget_org_members_api()

Every mutation (save, delete, create) triggers a resource restart to refresh data.

Contact edits in the sidebar follow a status state machine:

Idle → Saving → Saved → (2s delay) → Idle
↘ Error("message")

The SaveStatus enum drives UI indicators so the user sees feedback during save operations.

  1. Add a variant to SidebarTab in sidebar_tab_type.rs
  2. Add the icon and label to the tabs array in ConversationSidebarPanel
  3. Add icon rendering in the match block
  4. Add panel content rendering for the new tab
FilePurpose
src/mods/messaging/types/sidebar_tab_type.rsSidebarTab enum
src/mods/messaging/components/conversation_sidebar_component.rsSidebar panel + icon strip
src/mods/messaging/views/messaging_contact_view.rsParent view with state + handlers
src/mods/contact/components/contact_right_panel_component.rsNotes/activity/todos panel (added default_tab prop)