use crate::users::{UserRecordEntry, UserStore};
use iced::Task;
use super::common::SingleLineEditorState;
use super::editor_widget::EditorAction;
pub(crate) fn user_store() -> Result<&'static UserStore, String> {
crate::users::USER_STORE
.get()
.ok_or_else(|| "User store not initialized".to_string())
}
pub(crate) async fn set_selected_workspace_field(
sender: String,
workspace: String,
) -> Result<(), String> {
let store = user_store()?;
let val = if workspace.is_empty() || crate::users::is_personal_workspace(&workspace) {
None
} else {
Some(workspace.as_str())
};
store
.set_selected_workspace(&sender, val)
.await
.map_err(|e| e.to_string())
}
#[derive(Debug, Clone)]
pub enum UsersMessage {
Refreshed(Vec<UserRecordEntry>),
RefreshError(String),
DeleteUser(String),
ConfirmDelete(String),
CancelDelete,
DeleteResult(Result<(), String>),
OpenBindInput(String),
CloseBindInput,
BindInputChanged(EditorAction),
SubmitBind(String),
UnbindChannel(String, String),
BindResult(Result<(), String>, String),
Escape,
Toast(super::ToastMessage),
}
pub struct UsersState {
pub(crate) users: Vec<UserRecordEntry>,
pub(crate) load_state: super::common::AsyncLoadState,
pub(crate) delete_target: Option<String>,
pub(crate) bind_target: Option<String>,
pub(crate) bind_input: SingleLineEditorState,
pub(crate) binding: bool,
}
impl UsersState {
#[must_use]
pub fn new() -> Self {
Self {
users: Vec::new(),
load_state: super::common::AsyncLoadState::new(),
delete_target: None,
bind_target: None,
bind_input: SingleLineEditorState::new(""),
binding: false,
}
}
#[allow(clippy::unused_self)]
pub fn refresh(&self) -> Task<UsersMessage> {
Task::perform(
async {
let store = user_store()?;
let users = store.list_users().await.map_err(|e| e.to_string())?;
Ok::<_, String>(users)
},
|res| match res {
Ok(users) => UsersMessage::Refreshed(users),
Err(e) => UsersMessage::RefreshError(e),
},
)
}
#[expect(clippy::too_many_lines)]
pub fn update(&mut self, msg: UsersMessage) -> Task<UsersMessage> {
match msg {
UsersMessage::Refreshed(users) => {
self.users = users;
self.load_state.clear_error();
Task::none()
}
UsersMessage::RefreshError(e) => {
self.load_state.fail(e);
Task::none()
}
UsersMessage::DeleteUser(sender) => {
self.delete_target = Some(sender);
Task::none()
}
UsersMessage::ConfirmDelete(sender) => {
self.delete_target = None;
Task::perform(
async move {
let store = user_store()?;
store.delete_user(&sender).await.map_err(|e| e.to_string())
},
UsersMessage::DeleteResult,
)
}
UsersMessage::CancelDelete | UsersMessage::Escape => {
self.delete_target = None;
self.bind_target = None;
self.bind_input.clear();
Task::none()
}
UsersMessage::DeleteResult(Ok(())) => {
self.load_state.clear_error();
self.refresh()
}
UsersMessage::DeleteResult(Err(e)) => {
self.load_state.fail(e.clone());
Task::done(UsersMessage::Toast(super::ToastMessage::Error(e)))
}
UsersMessage::Toast(_) => Task::none(),
UsersMessage::OpenBindInput(user_name) => {
self.bind_target = Some(user_name);
self.bind_input.clear();
self.delete_target = None;
Task::none()
}
UsersMessage::CloseBindInput => {
self.bind_target = None;
self.bind_input.clear();
Task::none()
}
UsersMessage::BindInputChanged(action) => {
if let Some(task) = super::common::focus_navigation_task(&action) {
return task;
}
self.bind_input.apply_action(action);
Task::none()
}
UsersMessage::SubmitBind(user_name) => {
self.binding = true;
let user_clone = user_name.clone();
let input = self.bind_input.text().clone();
Task::perform(
async move {
let store = user_store()?;
store
.bind_telegram(&user_clone, &input)
.await
.map(|_| ())
.map_err(|e| e.to_string())
},
move |res| UsersMessage::BindResult(res, user_name),
)
}
UsersMessage::UnbindChannel(user_name, identifier) => {
self.binding = true;
let user_clone = user_name.clone();
Task::perform(
async move {
let store = user_store()?;
store
.unbind_channel(&user_clone, "telegram", &identifier)
.await
.map_err(|e| e.to_string())
},
move |res| UsersMessage::BindResult(res, user_name),
)
}
UsersMessage::BindResult(Ok(()), _user_name) => {
self.binding = false;
self.bind_target = None;
self.bind_input.clear();
self.refresh()
}
UsersMessage::BindResult(Err(e), user_name) => {
self.binding = false;
if self.bind_target.as_deref() != Some(&user_name) {
self.load_state.fail(format!("Failed to unbind: {e}"));
}
Task::done(UsersMessage::Toast(super::ToastMessage::Error(e)))
}
}
}
}