commit
245fe8e6f5
8 changed files with 4152 additions and 0 deletions
@ -0,0 +1 @@ |
|||
/target |
File diff suppressed because it is too large
@ -0,0 +1,20 @@ |
|||
[package] |
|||
name = "egui-test" |
|||
version = "0.1.0" |
|||
edition = "2018" |
|||
resolver = "2" |
|||
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
|||
|
|||
[dependencies] |
|||
eframe = { version = "0.14", features = ["persistence", "http"] } |
|||
futures = "0.3" |
|||
serde = { version = "1.0", features = ["derive"] } |
|||
tokio = { version = "*", features = ["full"] } |
|||
url = { version = "2.2", features = ["serde"] } |
|||
|
|||
[dependencies.matrix-sdk] |
|||
git = "https://github.com/matrix-org/matrix-rust-sdk/" |
|||
rev = "9e1024f" |
|||
default-features = false |
|||
features = ["encryption", "qrcode", "sled_cryptostore", "sled_state_store", "require_auth_for_profile_requests", "rustls-tls"] |
@ -0,0 +1,56 @@ |
|||
pub mod matrix; |
|||
pub mod ui; |
|||
|
|||
use eframe::NativeOptions; |
|||
|
|||
#[cfg(not(target_arch = "wasm32"))] |
|||
fn main() { |
|||
let app = ui::App::default(); |
|||
let options = NativeOptions::default(); |
|||
eframe::run_native(Box::new(app), options); |
|||
} |
|||
|
|||
/*#[derive(Clone, Debug, Default)]
|
|||
struct Login { |
|||
username: String, |
|||
password: String, |
|||
} |
|||
|
|||
impl epi::App for Login { |
|||
fn name(&self) -> &str { |
|||
"retrix" |
|||
} |
|||
|
|||
fn update(&mut self, ctx: &egui::CtxRef, _frame: &mut epi::Frame<'_>) { |
|||
egui::CentralPanel::default().show(ctx, |ui| { |
|||
ui.style_mut().body_text_style = egui::TextStyle::Button; |
|||
ui.add_space(ui.available_height() / 3.0); |
|||
ui.vertical_centered(|ui| { |
|||
//ui.style_mut() .visuals .widgets .noninteractive .bg_stroke .color = egui::Color32::TRANSPARENT;
|
|||
ui.group(|ui| { |
|||
//ui.reset_style();
|
|||
ui.set_max_width(300.0); |
|||
ui.vertical(|ui| { |
|||
ui.heading("Test"); |
|||
ui.label("Username"); |
|||
ui.text_edit_singleline(&mut self.username); |
|||
ui.label("Password:"); |
|||
ui.text_edit_singleline(&mut self.password); |
|||
}) |
|||
}) |
|||
}); |
|||
/*let mut ui = ui.child_ui(
|
|||
egui::Rect::from_center_size( |
|||
(ui.available_width() / 2.0, ui.available_height() / 2.0).into(), |
|||
(300.0, 500.0).into(), |
|||
), |
|||
egui::Layout::top_down(egui::Align::Min), |
|||
); |
|||
ui.heading("Test"); |
|||
ui.label("Username"); |
|||
ui.text_edit_singleline(&mut self.username); |
|||
ui.label("Password:"); |
|||
ui.text_edit_singleline(&mut self.password);*/ |
|||
}); |
|||
} |
|||
}*/ |
@ -0,0 +1,96 @@ |
|||
use std::path::{Path, PathBuf}; |
|||
|
|||
use matrix_sdk::{ |
|||
config::ClientConfig, |
|||
reqwest::Url, |
|||
ruma::{DeviceIdBox, UserId}, |
|||
Client, |
|||
}; |
|||
use serde::{Deserialize, Serialize}; |
|||
|
|||
#[derive(Debug, Clone, Serialize, Deserialize)] |
|||
pub struct Session { |
|||
/// The homeserver URL
|
|||
pub homeserver: Url, |
|||
/// Access token for authentication
|
|||
pub access_token: String, |
|||
/// The user's mxid.
|
|||
pub user_id: UserId, |
|||
/// The user's device ID.
|
|||
pub device_id: DeviceIdBox, |
|||
} |
|||
|
|||
/// Create a matrix client and log it in to the server at the given URL with the
|
|||
/// given credentials.
|
|||
pub fn login(url: &str, user: &str, password: &str) -> Result<Client, LoginError> { |
|||
let url: Url = if !url.contains("://") { |
|||
format!("https://{}", url).parse() |
|||
} else { |
|||
url.parse() |
|||
}?; |
|||
let client = Client::new_with_config(url.clone(), config()?)?; |
|||
let rt = tokio::runtime::Runtime::new().unwrap(); |
|||
let response = rt.block_on(client.login(user, password, None, None))?; |
|||
let session = Session { |
|||
homeserver: url, |
|||
access_token: response.access_token, |
|||
user_id: response.user_id, |
|||
device_id: response.device_id, |
|||
}; |
|||
Ok(client) |
|||
} |
|||
|
|||
/// Errors that can happen when logging in
|
|||
#[derive(Debug)] |
|||
pub enum LoginError { |
|||
/// Invalid URL
|
|||
Url(url::ParseError), |
|||
/// Matrix SDK error
|
|||
Sdk(matrix_sdk::Error), |
|||
/// I/O error
|
|||
Io(std::io::Error), |
|||
} |
|||
|
|||
impl std::fmt::Display for LoginError { |
|||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
|||
match self { |
|||
LoginError::Url(_) => write!(f, "Invalid homeserver address"), |
|||
LoginError::Sdk(e) => write!(f, "{}", e), |
|||
LoginError::Io(e) => write!(f, "Filesystem error: {}", e), |
|||
} |
|||
} |
|||
} |
|||
|
|||
impl std::error::Error for LoginError {} |
|||
|
|||
impl From<url::ParseError> for LoginError { |
|||
fn from(e: url::ParseError) -> Self { |
|||
LoginError::Url(e) |
|||
} |
|||
} |
|||
|
|||
impl From<matrix_sdk::Error> for LoginError { |
|||
fn from(e: matrix_sdk::Error) -> Self { |
|||
LoginError::Sdk(e) |
|||
} |
|||
} |
|||
|
|||
impl From<std::io::Error> for LoginError { |
|||
fn from(e: std::io::Error) -> Self { |
|||
LoginError::Io(e) |
|||
} |
|||
} |
|||
|
|||
/// Configuration for `Clients`.
|
|||
fn config() -> Result<ClientConfig, std::io::Error> { |
|||
Ok(ClientConfig::new().store_path(&path()?)) |
|||
} |
|||
|
|||
/// The path the the sdk store should be put in.
|
|||
fn path() -> Result<PathBuf, std::io::Error> { |
|||
let path = Path::new(&std::env::var_os("HOME").unwrap()) |
|||
.join(".config") |
|||
.join("retrix"); |
|||
std::fs::create_dir_all(&path)?; |
|||
Ok(path) |
|||
} |
@ -0,0 +1,59 @@ |
|||
use eframe::{egui, epi}; |
|||
|
|||
use crate::matrix; |
|||
|
|||
pub mod login; |
|||
pub mod session; |
|||
|
|||
/// Application state
|
|||
#[derive(Debug, Default)] |
|||
pub struct App { |
|||
view: View, |
|||
} |
|||
|
|||
impl epi::App for App { |
|||
fn name(&self) -> &str { |
|||
"retrix" |
|||
} |
|||
|
|||
fn update(&mut self, ctx: &egui::CtxRef, _frame: &mut epi::Frame<'_>) { |
|||
match self.view { |
|||
View::Login(ref mut login) => { |
|||
if login.update(ctx) { |
|||
let client = |
|||
match matrix::login(&login.homeserver, &login.username, &login.password) { |
|||
Ok(client) => client, |
|||
Err(e) => { |
|||
login.error = Some(e.to_string()); |
|||
return; |
|||
} |
|||
}; |
|||
self.view = View::Main(session::App::with_client(client.clone())); |
|||
std::thread::spawn(move || { |
|||
let rt = tokio::runtime::Runtime::new().unwrap(); |
|||
rt.block_on(client.sync(Default::default())) |
|||
}); |
|||
} |
|||
} |
|||
View::Main(ref mut view) => view.update(ctx), |
|||
}; |
|||
} |
|||
} |
|||
|
|||
/// Which view is currectly open
|
|||
#[derive(Debug)] |
|||
pub enum View { |
|||
Login(login::Login), |
|||
Main(session::App), |
|||
} |
|||
|
|||
impl Default for View { |
|||
fn default() -> Self { |
|||
View::Login(login::Login::default()) |
|||
} |
|||
} |
|||
|
|||
#[derive(Debug, Clone, Copy, Hash)] |
|||
pub enum Id { |
|||
RoomPanel, |
|||
} |
@ -0,0 +1,44 @@ |
|||
use eframe::egui::{self, Color32, TextEdit}; |
|||
|
|||
#[derive(Clone, Debug, Default)] |
|||
pub struct Login { |
|||
pub homeserver: String, |
|||
pub username: String, |
|||
pub password: String, |
|||
pub error: Option<String>, |
|||
} |
|||
|
|||
impl Login { |
|||
pub fn update(&mut self, ctx: &egui::CtxRef) -> bool { |
|||
let mut update = false; |
|||
egui::CentralPanel::default().show(ctx, |ui| { |
|||
ui.add_space(ui.available_height() / 3.0); |
|||
ui.vertical_centered(|ui| { |
|||
//ui.style_mut() .visuals .widgets .noninteractive .bg_stroke .color = egui::Color32::TRANSPARENT;
|
|||
ui.group(|ui| { |
|||
//ui.reset_style();
|
|||
if let Some(ref error) = self.error { |
|||
ui.colored_label(Color32::from_rgb(255, 0, 0), error); |
|||
} |
|||
ui.set_max_width(300.0); |
|||
ui.vertical(|ui| { |
|||
ui.heading("Log in"); |
|||
ui.label("Homeserver:"); |
|||
ui.add( |
|||
TextEdit::singleline(&mut self.homeserver) |
|||
.hint_text("https://example.net"), |
|||
); |
|||
ui.label("Username:"); |
|||
ui.add(TextEdit::singleline(&mut self.username).hint_text("alice")); |
|||
ui.label("Password:"); |
|||
ui.add(TextEdit::singleline(&mut self.password).password(true)); |
|||
if ui.button("Log in").clicked() { |
|||
update = true; |
|||
} |
|||
}) |
|||
}) |
|||
}); |
|||
}); |
|||
update |
|||
} |
|||
} |
@ -0,0 +1,43 @@ |
|||
use eframe::egui::{self, Sense}; |
|||
use futures::executor::block_on; |
|||
use matrix_sdk::Client; |
|||
|
|||
use super::Id; |
|||
|
|||
/// Logged in application state
|
|||
#[derive(Debug)] |
|||
pub struct App { |
|||
client: matrix_sdk::Client, |
|||
selected_room: Option<matrix_sdk::ruma::identifiers::RoomId>, |
|||
} |
|||
|
|||
impl App { |
|||
pub fn with_client(client: Client) -> Self { |
|||
Self { |
|||
client, |
|||
selected_room: None, |
|||
} |
|||
} |
|||
|
|||
pub fn update(&mut self, ctx: &egui::CtxRef) { |
|||
egui::SidePanel::left(Id::RoomPanel) |
|||
.max_width(800.0) |
|||
.show(ctx, |ui| { |
|||
ui.add(egui::Label::new("Joined").strong()); |
|||
for room in self.client.joined_rooms() { |
|||
let response = ui.group(|ui| { |
|||
if let Some(name) = room.name() { |
|||
ui.label(name.to_string()); |
|||
} |
|||
if let Some(alias) = room.canonical_alias() { |
|||
ui.label(alias.to_string()); |
|||
} |
|||
}); |
|||
let response = response.response.interact(Sense::click()); |
|||
if response.clicked() { |
|||
self.selected_room = Some(room.room_id().clone()); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
} |
Loading…
Reference in new issue