This commit is contained in:
Mikhail Kilin
2026-01-18 14:49:31 +03:00
parent d701464fde
commit b6d9291864
29 changed files with 3920 additions and 833 deletions

View File

@@ -1,201 +1,118 @@
use crate::telegram::{Chat, Message};
mod state;
pub use state::AppScreen;
use ratatui::widgets::ListState;
use crate::tdlib::client::{ChatInfo, MessageInfo};
use crate::tdlib::TdClient;
#[derive(Debug)]
pub struct App {
pub tabs: Vec<String>,
pub selected_tab: usize,
pub chats: Vec<Chat>,
pub selected_chat: Option<usize>,
pub messages: Vec<Message>,
pub input: String,
pub search_query: String,
pub screen: AppScreen,
pub td_client: TdClient,
// Auth state
pub phone_input: String,
pub code_input: String,
pub password_input: String,
pub error_message: Option<String>,
pub status_message: Option<String>,
// Main app state
pub chats: Vec<ChatInfo>,
pub chat_list_state: ListState,
pub selected_chat_id: Option<i64>,
pub current_messages: Vec<MessageInfo>,
pub message_input: String,
pub message_scroll_offset: usize,
pub folders: Vec<String>,
pub selected_folder: usize,
pub is_loading: bool,
}
impl App {
pub fn new() -> Self {
Self {
tabs: vec![
"All".to_string(),
"Personal".to_string(),
"Work".to_string(),
"Bots".to_string(),
],
selected_tab: 0,
chats: Self::mock_chats(),
selected_chat: Some(0),
messages: Self::mock_messages(),
input: String::new(),
search_query: String::new(),
}
}
pub fn new() -> App {
let mut state = ListState::default();
state.select(Some(0));
pub fn select_tab(&mut self, index: usize) {
if index < self.tabs.len() {
self.selected_tab = index;
App {
screen: AppScreen::Loading,
td_client: TdClient::new(),
phone_input: String::new(),
code_input: String::new(),
password_input: String::new(),
error_message: None,
status_message: Some("Инициализация TDLib...".to_string()),
chats: Vec::new(),
chat_list_state: state,
selected_chat_id: None,
current_messages: Vec::new(),
message_input: String::new(),
message_scroll_offset: 0,
folders: vec!["All".to_string()],
selected_folder: 0,
is_loading: true,
}
}
pub fn next_chat(&mut self) {
if !self.chats.is_empty() {
self.selected_chat = Some(
self.selected_chat
.map(|i| (i + 1) % self.chats.len())
.unwrap_or(0),
);
self.load_messages();
if self.chats.is_empty() {
return;
}
let i = match self.chat_list_state.selected() {
Some(i) => {
if i >= self.chats.len() - 1 {
0
} else {
i + 1
}
}
None => 0,
};
self.chat_list_state.select(Some(i));
}
pub fn previous_chat(&mut self) {
if !self.chats.is_empty() {
self.selected_chat = Some(
self.selected_chat
.map(|i| if i == 0 { self.chats.len() - 1 } else { i - 1 })
.unwrap_or(0),
);
self.load_messages();
if self.chats.is_empty() {
return;
}
let i = match self.chat_list_state.selected() {
Some(i) => {
if i == 0 {
self.chats.len() - 1
} else {
i - 1
}
}
None => 0,
};
self.chat_list_state.select(Some(i));
}
pub fn select_current_chat(&mut self) {
if let Some(i) = self.chat_list_state.selected() {
if let Some(chat) = self.chats.get(i) {
self.selected_chat_id = Some(chat.id);
}
}
}
pub fn open_chat(&mut self) {
self.load_messages();
pub fn close_chat(&mut self) {
self.selected_chat_id = None;
self.current_messages.clear();
self.message_input.clear();
self.message_scroll_offset = 0;
}
fn load_messages(&mut self) {
self.messages = Self::mock_messages();
pub fn select_first_chat(&mut self) {
if !self.chats.is_empty() {
self.chat_list_state.select(Some(0));
}
}
fn mock_chats() -> Vec<Chat> {
vec![
Chat {
name: "Saved Messages".to_string(),
last_message: "My notes...".to_string(),
unread_count: 0,
is_pinned: true,
is_online: false,
},
Chat {
name: "Mom".to_string(),
last_message: "Отлично, захвати хлеба.".to_string(),
unread_count: 2,
is_pinned: false,
is_online: true,
},
Chat {
name: "Boss".to_string(),
last_message: "Meeting at 3pm".to_string(),
unread_count: 0,
is_pinned: false,
is_online: false,
},
Chat {
name: "Rust Community".to_string(),
last_message: "Check out this crate...".to_string(),
unread_count: 0,
is_pinned: false,
is_online: false,
},
Chat {
name: "Durov".to_string(),
last_message: "Privacy matters".to_string(),
unread_count: 0,
is_pinned: false,
is_online: false,
},
Chat {
name: "News Channel".to_string(),
last_message: "Breaking news...".to_string(),
unread_count: 0,
is_pinned: false,
is_online: false,
},
Chat {
name: "Spam Bot".to_string(),
last_message: "Click here!!!".to_string(),
unread_count: 0,
is_pinned: false,
is_online: false,
},
Chat {
name: "Wife".to_string(),
last_message: "Don't forget the milk".to_string(),
unread_count: 0,
is_pinned: false,
is_online: false,
},
Chat {
name: "Team Lead".to_string(),
last_message: "Code review please".to_string(),
unread_count: 0,
is_pinned: false,
is_online: false,
},
Chat {
name: "DevOps Chat".to_string(),
last_message: "Server is down!".to_string(),
unread_count: 9,
is_pinned: false,
is_online: false,
},
]
pub fn get_selected_chat_id(&self) -> Option<i64> {
self.selected_chat_id
}
fn mock_messages() -> Vec<Message> {
vec![
Message {
sender: "Mom".to_string(),
text: "Привет! Ты покормил кота?".to_string(),
time: "14:20".to_string(),
is_outgoing: false,
read_status: 0,
},
Message {
sender: "You".to_string(),
text: "Да, конечно. Купил ему корм.".to_string(),
time: "14:22".to_string(),
is_outgoing: true,
read_status: 2,
},
Message {
sender: "You".to_string(),
text: "Скоро буду дома.".to_string(),
time: "14:22".to_string(),
is_outgoing: true,
read_status: 2,
},
Message {
sender: "Mom".to_string(),
text: "Отлично, захвати хлеба.".to_string(),
time: "14:23".to_string(),
is_outgoing: false,
read_status: 0,
},
Message {
sender: "You".to_string(),
text: "Ок.".to_string(),
time: "14:25".to_string(),
is_outgoing: true,
read_status: 1,
},
]
}
pub fn get_current_chat_name(&self) -> String {
self.selected_chat
.and_then(|i| self.chats.get(i))
.map(|chat| {
if chat.is_online {
format!("👤 {} (online)", chat.name)
} else {
format!("👤 {}", chat.name)
}
})
.unwrap_or_default()
}
}
impl Default for App {
fn default() -> Self {
Self::new()
pub fn get_selected_chat(&self) -> Option<&ChatInfo> {
self.selected_chat_id
.and_then(|id| self.chats.iter().find(|c| c.id == id))
}
}

6
src/app/state.rs Normal file
View File

@@ -0,0 +1,6 @@
#[derive(PartialEq, Clone)]
pub enum AppScreen {
Loading,
Auth,
Main,
}