ExtraChat/server/src/handlers/list.rs

160 lines
5.1 KiB
Rust

use std::str::FromStr;
use std::sync::Arc;
use anyhow::{Context, Result};
use tokio::sync::RwLock;
use uuid::Uuid;
use crate::{ClientState, State, types::protocol::{
channel::{
Channel,
ChannelMember,
Rank,
SimpleChannel,
},
ListRequest,
ListResponse,
}, util::send, World, WsStream};
use crate::util::RawMember;
pub async fn list(state: Arc<RwLock<State>>, client_state: Arc<RwLock<ClientState>>, conn: &mut WsStream, number: u32, req: ListRequest) -> Result<()> {
let lodestone_id = match &client_state.read().await.user {
Some(u) => u.lodestone_id,
None => return Ok(()),
};
let resp = match req {
ListRequest::All => ListResponse::All {
channels: get_full_channels(lodestone_id, &state).await?,
invites: get_full_invites(lodestone_id, &state).await?,
},
ListRequest::Channels => ListResponse::Channels(get_channels(lodestone_id, &state).await?),
ListRequest::Members(id) => ListResponse::Members {
id,
members: get_members(lodestone_id, &state, id).await?,
},
ListRequest::Invites => ListResponse::Invites(get_invites(lodestone_id, &state).await?),
};
send(conn, number, resp).await
}
async fn ids_to_channels(ids: &[&str], state: &RwLock<State>) -> Vec<Channel> {
let mut channels = Vec::with_capacity(ids.len());
for id in ids {
let id = match Uuid::from_str(id) {
Ok(id) => id,
Err(_) => continue,
};
let channel = match Channel::get(state, id).await {
Ok(Some(channel)) => channel,
_ => continue,
};
channels.push(channel);
}
channels
}
async fn get_full_channels(lodestone_id: u64, state: &RwLock<State>) -> Result<Vec<Channel>> {
let lodestone_id_i = lodestone_id as i64;
let channel_ids = sqlx::query!(
// language=sqlite
"select channel_id from user_channels where lodestone_id = ?",
lodestone_id_i,
)
.fetch_all(&state.read().await.db)
.await
.context("failed to fetch channel ids")?;
let ids: Vec<&str> = channel_ids
.iter()
.map(|id| id.channel_id.as_str())
.collect();
Ok(ids_to_channels(&ids, state).await)
}
async fn get_full_invites(lodestone_id: u64, state: &RwLock<State>) -> Result<Vec<Channel>> {
let lodestone_id_i = lodestone_id as i64;
let channel_ids = sqlx::query!(
// language=sqlite
"select channel_id from channel_invites where invited = ?",
lodestone_id_i,
)
.fetch_all(&state.read().await.db)
.await
.context("failed to fetch channel ids")?;
let ids: Vec<&str> = channel_ids
.iter()
.map(|id| id.channel_id.as_str())
.collect();
Ok(ids_to_channels(&ids, state).await)
}
async fn get_channels(lodestone_id: u64, state: &RwLock<State>) -> Result<Vec<SimpleChannel>> {
SimpleChannel::get_all_for_user(state, lodestone_id)
.await
.context("could not get channels for user")
}
async fn get_members(lodestone_id: u64, state: &RwLock<State>, channel_id: Uuid) -> Result<Vec<ChannelMember>> {
let lodestone_id_i = lodestone_id as i64;
let channel_id_str = channel_id.as_simple().to_string();
let users: Vec<RawMember> = sqlx::query_as!(
RawMember,
// language=sqlite
"select users.lodestone_id, users.name, users.world, user_channels.rank from user_channels inner join users on users.lodestone_id = user_channels.lodestone_id where user_channels.channel_id = ?",
channel_id_str,
)
.fetch_all(&state.read().await.db)
.await
.context("failed to get members")?;
let invited: Vec<RawMember> = sqlx::query_as!(
RawMember,
// language=sqlite
"select users.lodestone_id, users.name, users.world, cast(0 as int) as rank from channel_invites inner join users on users.lodestone_id = channel_invites.invited where channel_invites.channel_id = ?",
channel_id_str,
)
.fetch_all(&state.read().await.db)
.await
.context("failed to get invited members")?;
let mut found = false;
let mut members = Vec::with_capacity(users.len());
for user in users.into_iter().chain(invited.into_iter()) {
if user.lodestone_id == lodestone_id_i {
found = true;
}
let world = match World::from_str(&user.world) {
Ok(world) => world,
Err(_) => continue,
};
let online = state.read().await.clients.contains_key(&(user.lodestone_id as u64));
members.push(ChannelMember {
name: user.name,
world: crate::util::id_from_world(world),
rank: Rank::from_u8(user.rank as u8),
online,
});
}
if !found {
anyhow::bail!("user not in channel");
}
Ok(members)
}
async fn get_invites(lodestone_id: u64, state: &RwLock<State>) -> Result<Vec<SimpleChannel>> {
SimpleChannel::get_invites_for_user(state, lodestone_id)
.await
.context("could not get channels for user")
}