Basic login screen
This commit is contained in:
commit
245fe8e6f5
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
3833
Cargo.lock
generated
Normal file
3833
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
20
Cargo.toml
Normal file
20
Cargo.toml
Normal file
|
@ -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"]
|
56
src/main.rs
Normal file
56
src/main.rs
Normal file
|
@ -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);*/
|
||||
});
|
||||
}
|
||||
}*/
|
96
src/matrix.rs
Normal file
96
src/matrix.rs
Normal file
|
@ -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)
|
||||
}
|
59
src/ui.rs
Normal file
59
src/ui.rs
Normal file
|
@ -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,
|
||||
}
|
44
src/ui/login.rs
Normal file
44
src/ui/login.rs
Normal file
|
@ -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
|
||||
}
|
||||
}
|
43
src/ui/session.rs
Normal file
43
src/ui/session.rs
Normal file
|
@ -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 a new issue