mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-08-25 07:01:08 +00:00
feat: add is_active conversaiton
This commit is contained in:
+63
-29
@@ -94,6 +94,7 @@ where
|
||||
pub client: ChatClient<T, R, S>,
|
||||
events: Receiver<Event>,
|
||||
pub state: AppState,
|
||||
is_active: bool,
|
||||
/// Ephemeral command output — not persisted, cleared on chat switch.
|
||||
command_output: Vec<DisplayMessage>,
|
||||
pub input: String,
|
||||
@@ -120,24 +121,29 @@ where
|
||||
let state = Self::load_state(&state_path);
|
||||
|
||||
let chat_count = state.chats.len();
|
||||
let status = if chat_count > 0 {
|
||||
format!(
|
||||
"Welcome back, {user_name}! {chat_count} chat(s) loaded. Type /help for commands."
|
||||
)
|
||||
} else {
|
||||
let status = if chat_count == 0 {
|
||||
format!("Welcome, {user_name}! Type /help for commands.")
|
||||
} else {
|
||||
format!(
|
||||
"Welcome back, {user_name}! {chat_count} chat(s) loaded — read-only from a previous \
|
||||
session; start a new /dm or /new to chat. Type /help."
|
||||
)
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
let mut app = Self {
|
||||
client,
|
||||
events,
|
||||
state,
|
||||
is_active: false,
|
||||
command_output: Vec::new(),
|
||||
input: String::new(),
|
||||
status,
|
||||
user_name: user_name.to_string(),
|
||||
state_path,
|
||||
})
|
||||
};
|
||||
app.state.active_chat = None;
|
||||
app.show_chats_list();
|
||||
Ok(app)
|
||||
}
|
||||
|
||||
fn load_state(path: &Path) -> AppState {
|
||||
@@ -172,6 +178,10 @@ where
|
||||
}
|
||||
|
||||
fn set_active_chat(&mut self, chat_id: Option<String>) {
|
||||
self.is_active = chat_id
|
||||
.as_deref()
|
||||
.map(|id| self.client.has_conversation(id))
|
||||
.unwrap_or(false);
|
||||
self.state.active_chat = chat_id;
|
||||
self.command_output.clear();
|
||||
}
|
||||
@@ -190,6 +200,39 @@ where
|
||||
self.set_active_chat(Some(chat_id));
|
||||
}
|
||||
|
||||
pub fn is_active(&self) -> bool {
|
||||
self.is_active
|
||||
}
|
||||
|
||||
/// Render the chat list; restored (dead) chats are flagged read-only.
|
||||
fn show_chats_list(&mut self) {
|
||||
self.command_output.clear();
|
||||
let sessions: Vec<_> = self.state.chats.values().cloned().collect();
|
||||
if sessions.is_empty() {
|
||||
self.add_system_message("No chats yet. Use /dm or /new to start one.");
|
||||
return;
|
||||
}
|
||||
self.add_system_message(&format!("── Your Chats ({}) ──", sessions.len()));
|
||||
for s in &sessions {
|
||||
let active = self.state.active_chat.as_deref() == Some(&s.chat_id);
|
||||
let read_only = !self.client.has_conversation(&s.chat_id);
|
||||
let mut tags = String::new();
|
||||
if active {
|
||||
tags.push_str(" (active)");
|
||||
}
|
||||
if read_only {
|
||||
tags.push_str(" (read-only)");
|
||||
}
|
||||
let label = format!(
|
||||
" • [{}] {} ({}){tags}",
|
||||
s.kind.badge(),
|
||||
s.display_name(),
|
||||
&s.chat_id[..8.min(s.chat_id.len())]
|
||||
);
|
||||
self.add_system_message(&label);
|
||||
}
|
||||
}
|
||||
|
||||
/// Find a chat_id by nickname (exact) or chat_id prefix.
|
||||
fn resolve_chat_id(&self, query: &str) -> Option<&str> {
|
||||
// Exact nickname match first.
|
||||
@@ -273,6 +316,13 @@ where
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow::anyhow!("No active chat. Use /dm or /new first."))?;
|
||||
|
||||
if !self.is_active {
|
||||
anyhow::bail!(
|
||||
"This conversation is from a previous session and can't receive messages yet \
|
||||
— chats don't persist across restart. Start a new one with /dm or /new."
|
||||
);
|
||||
}
|
||||
|
||||
self.client
|
||||
.send_message(&chat_id, content.as_bytes())
|
||||
.map_err(|e| anyhow::anyhow!("{e:?}"))?;
|
||||
@@ -473,29 +523,13 @@ where
|
||||
Ok(Some(format!("Nickname set to '{args}'")))
|
||||
}
|
||||
"/chats" => {
|
||||
let sessions: Vec<_> = self.state.chats.values().cloned().collect();
|
||||
if sessions.is_empty() {
|
||||
Ok(Some(
|
||||
"No chats yet. Use /dm or /new to start one.".to_string(),
|
||||
))
|
||||
self.show_chats_list();
|
||||
let n = self.state.chats.len();
|
||||
Ok(Some(if n == 0 {
|
||||
"No chats yet".to_string()
|
||||
} else {
|
||||
self.add_system_message(&format!("── Your Chats ({}) ──", sessions.len()));
|
||||
for s in &sessions {
|
||||
let marker = if self.state.active_chat.as_deref() == Some(&s.chat_id) {
|
||||
" (active)"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let label = format!(
|
||||
" • [{}] {} ({}){marker}",
|
||||
s.kind.badge(),
|
||||
s.display_name(),
|
||||
&s.chat_id[..8.min(s.chat_id.len())]
|
||||
);
|
||||
self.add_system_message(&label);
|
||||
}
|
||||
Ok(Some(format!("{} chat(s)", sessions.len())))
|
||||
}
|
||||
format!("{n} chat(s)")
|
||||
}))
|
||||
}
|
||||
"/switch" => {
|
||||
if args.is_empty() {
|
||||
|
||||
@@ -69,9 +69,14 @@ where
|
||||
let title = match app.current_session() {
|
||||
Some(session) => {
|
||||
let id = &session.chat_id[..8.min(session.chat_id.len())];
|
||||
let ro = if app.is_active() {
|
||||
""
|
||||
} else {
|
||||
" [read-only]"
|
||||
};
|
||||
match &session.nickname {
|
||||
Some(name) => format!(" 💬 Chat: {} ↔ {name} ({id}) ", app.user_name),
|
||||
None => format!(" 💬 Chat: {} ↔ ({id}) ", app.user_name),
|
||||
Some(name) => format!(" 💬 Chat: {} ↔ {name} ({id}){ro} ", app.user_name),
|
||||
None => format!(" 💬 Chat: {} ↔ ({id}){ro} ", app.user_name),
|
||||
}
|
||||
}
|
||||
None => format!(" 💬 {} — no active chat ", app.user_name),
|
||||
|
||||
@@ -325,6 +325,11 @@ impl<'a, S: ExternalServices + 'static> Core<S> {
|
||||
Ok(convos)
|
||||
}
|
||||
|
||||
/// Whether the conversation is loaded and usable.
|
||||
pub fn is_conversation_active(&self, convo_id: &str) -> bool {
|
||||
self.cached_convos.contains_key(convo_id)
|
||||
}
|
||||
|
||||
pub fn take_missing_messages(&self) -> Vec<MissingMessage> {
|
||||
self.services.causal.take_missing()
|
||||
}
|
||||
|
||||
@@ -273,6 +273,11 @@ where
|
||||
self.core.lock().list_conversations().map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Whether the conversation is live and usable this session.
|
||||
pub fn has_conversation(&self, convo_id: &str) -> bool {
|
||||
self.core.lock().is_conversation_active(convo_id)
|
||||
}
|
||||
|
||||
/// Encrypt and send `content` to an existing conversation. The core
|
||||
/// publishes the outbound envelope.
|
||||
pub fn send_message(&mut self, convo_id: &str, content: &[u8]) -> Result<(), ClientError> {
|
||||
|
||||
Reference in New Issue
Block a user