egui-test/src/ui/session.rs

180 lines
5.7 KiB
Rust

use std::{borrow::Cow, collections::HashMap};
use crossbeam_channel::Receiver;
use eframe::egui::{self, Color32, ScrollArea, Sense};
use matrix_sdk::{
deserialized_responses::SyncResponse,
room::Room,
ruma::{events::AnyRoomEvent, RoomId, UserId},
Client, RoomMember,
};
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc::UnboundedSender;
use crate::sync;
use super::Id;
/// Logged in application state
#[derive(Debug)]
pub struct App {
pub client: matrix_sdk::Client,
pub request: UnboundedSender<sync::Request>,
pub response: Receiver<sync::Response>,
pub sync_handle: Option<std::thread::JoinHandle<()>>,
pub error: Option<String>,
pub room_list: RoomList,
pub timelines: HashMap<RoomId, Timeline>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct RoomList {
pub selected_room: Option<matrix_sdk::ruma::identifiers::RoomId>,
pub room_name: HashMap<RoomId, String>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Timeline {
messages: Vec<AnyRoomEvent>,
#[serde(skip)]
member: HashMap<UserId, RoomMember>,
}
impl RoomList {
fn room_name<'a>(&'a self, room: &matrix_sdk::BaseRoom) -> Cow<'a, str> {
if let Some(name) = self.room_name.get(room.room_id()) {
name.into()
} else if let Some(name) = room.name() {
name.into()
} else if let Some(alias) = room.canonical_alias() {
String::from(alias).into()
} else {
room.room_id().to_string().into()
}
}
fn is_selected(&self, room_id: &RoomId) -> bool {
self.selected_room
.as_ref()
.map_or(false, |id| id == room_id)
}
fn selected_room(&self, client: &Client) -> Option<matrix_sdk::room::Room> {
match self.selected_room {
Some(ref selected) => client.get_room(selected),
None => None,
}
}
}
impl App {
pub fn new(
client: Client,
request: UnboundedSender<sync::Request>,
response: Receiver<sync::Response>,
sync_handle: std::thread::JoinHandle<()>,
) -> Self {
Self {
client,
request,
response,
sync_handle: Some(sync_handle),
room_list: RoomList::default(),
error: None,
timelines: HashMap::new(),
}
}
pub fn update(&mut self, ctx: &egui::CtxRef) {
if let Ok(response) = self.response.try_recv() {
self.handle_response(response);
}
egui::SidePanel::left(Id::RoomPanel)
.max_width(800.0)
.default_width(400.0)
.show(ctx, |ui| {
ui.add(egui::Label::new("Joined").strong());
for room in self.client.joined_rooms() {
let group = if self.room_list.is_selected(room.room_id()) {
egui::Frame::group(&Default::default())
} else {
egui::Frame::group(&Default::default()).fill(Color32::from_rgb(0, 0, 0))
};
let response = group.show(ui, |ui| {
ui.set_width(ui.available_width());
let name = self.room_list.room_name(&*room);
ui.label(&*name);
});
let response = response.response.interact(Sense::click());
if response.clicked() {
self.room_list.selected_room = Some(room.room_id().clone());
}
}
});
let room = match self.room_list.selected_room(&self.client) {
Some(room) => room,
_ => return,
};
egui::TopBottomPanel::top(Id::RoomSummary).show(ctx, |ui| {
ui.horizontal(|ui| {
ui.set_width(ui.available_width());
ui.heading(&*self.room_list.room_name(&room));
if let Some(ref target) = room.direct_target() {
ui.label(target.as_str());
} else if let Some(ref alias) = room.canonical_alias() {
ui.label(alias.as_str());
}
if let Some(ref topic) = room.topic() {
ui.label(topic);
}
})
});
egui::SidePanel::right(Id::MemberList).show(ctx, |ui| {
ui.heading("Members");
});
let room = match room {
Room::Joined(room) => room,
_ => return,
};
let timeline = self.timelines.entry(room.room_id().clone()).or_default();
egui::CentralPanel::default().show(ctx, |ui| {
ScrollArea::auto_sized().show(ui, |ui| {
for message in timeline.messages.iter() {
ui.label(message.sender().as_str());
}
})
});
}
fn handle_response(&mut self, response: sync::Response) {
match response {
sync::Response::RoomName(room, name) => {
self.room_list.room_name.insert(room, name);
}
sync::Response::Error(e) => {
self.error = Some(e.to_string());
}
sync::Response::Sync(sync) => self.handle_sync(sync),
};
}
fn handle_sync(&mut self, sync: SyncResponse) {
for (id, room) in sync.rooms.join {
let timeline = self.timelines.entry(id.clone()).or_default();
for event in room.timeline.events {
let event = match event.event.deserialize() {
Ok(event) => event,
Err(_) => continue,
};
timeline.messages.push(event.into_full_event(id.clone()));
}
}
}
}