Finish login
This commit is contained in:
parent
eed07706df
commit
22981ea573
1788
Cargo.lock
generated
1788
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -3,6 +3,7 @@ name = "retrix"
|
|||
authors = ["Amanda Graven <amanda@amandag.net>"]
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
resolver = "2"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -10,14 +11,15 @@ edition = "2018"
|
|||
directories = "3.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
url = { version = "2.2", features = ["serde"] }
|
||||
|
||||
[dependencies.iced]
|
||||
git = "https://github.com/hecrj/iced"
|
||||
rev = "a08e4eb"
|
||||
features = ["image", "svg", "debug"]
|
||||
rev = "378a135"
|
||||
features = ["image", "svg", "debug", "tokio"]
|
||||
|
||||
[dependencies.matrix-sdk]
|
||||
git = "https://github.com/matrix-org/matrix-rust-sdk"
|
||||
rev = "1fd1570"
|
||||
rev = "9e1024f"
|
||||
default-features = false
|
||||
features = ["encryption", "sled_state_store", "sled_cryptostore", "rustls-tls", "require_auth_for_profile_requests"]
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
trivial_casts,
|
||||
trivial_numeric_casts,
|
||||
unused_extern_crates,
|
||||
unused_allocation,
|
||||
unused_qualifications
|
||||
unused_allocation
|
||||
)]
|
||||
|
||||
use config::Config;
|
||||
|
@ -18,6 +17,8 @@ use crate::{config::Session, ui::Retrix};
|
|||
extern crate directories as dirs;
|
||||
|
||||
pub mod config;
|
||||
pub mod matrix;
|
||||
pub mod style;
|
||||
pub mod ui;
|
||||
|
||||
fn main() {
|
||||
|
|
90
src/matrix.rs
Normal file
90
src/matrix.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
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 async 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 response = client.login(user, password, None, None).await?;
|
||||
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)
|
||||
}
|
24
src/style.rs
Normal file
24
src/style.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
//! Style definitions for various elements
|
||||
|
||||
/// Style definitions for [`iced::Container`]
|
||||
pub mod container {
|
||||
use iced::{
|
||||
container::{Style, StyleSheet},
|
||||
Color,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Style for a container displaying an error message
|
||||
pub struct Error;
|
||||
|
||||
impl StyleSheet for Error {
|
||||
fn style(&self) -> Style {
|
||||
iced::container::Style {
|
||||
background: Color::from_rgb(1.0, 0.0, 0.0).into(),
|
||||
text_color: Some(Color::from_rgb(1.0, 1.0, 1.0)),
|
||||
border_radius: 2.0,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
16
src/ui.rs
16
src/ui.rs
|
@ -5,6 +5,7 @@ use iced::Command;
|
|||
use crate::config;
|
||||
|
||||
pub mod login;
|
||||
pub mod session;
|
||||
|
||||
/// Data for the running application
|
||||
#[derive(Debug)]
|
||||
|
@ -20,6 +21,8 @@ pub struct Retrix {
|
|||
pub enum View {
|
||||
/// The login prompt
|
||||
Login(login::Login),
|
||||
/// The main view
|
||||
Main(session::View),
|
||||
}
|
||||
|
||||
/// A message notifying application state should change
|
||||
|
@ -36,7 +39,7 @@ pub enum Message {
|
|||
pub struct Flags {
|
||||
/// The application configuration
|
||||
pub config: config::Config,
|
||||
/// The session data if we've loggen in
|
||||
/// The session data if we've logged in
|
||||
pub session: Option<config::Session>,
|
||||
}
|
||||
|
||||
|
@ -54,12 +57,12 @@ impl iced::Application for Retrix {
|
|||
String::from("Retrix")
|
||||
}
|
||||
|
||||
fn update(
|
||||
&mut self,
|
||||
message: Self::Message,
|
||||
_clipboard: &mut iced::Clipboard,
|
||||
) -> Command<Self::Message> {
|
||||
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
|
||||
match (&mut self.view, message) {
|
||||
(_, Message::Login(login::Message::LoggedIn(client))) => {
|
||||
self.view = View::Main(session::View::with_client(client));
|
||||
Command::none()
|
||||
}
|
||||
(View::Login(ref mut view), Message::Login(message)) => view.update(message),
|
||||
_ => {
|
||||
eprint!("WARN: Received a message for an inactive view");
|
||||
|
@ -71,6 +74,7 @@ impl iced::Application for Retrix {
|
|||
fn view(&mut self) -> iced::Element<'_, Self::Message> {
|
||||
match self.view {
|
||||
View::Login(ref mut login) => login.view(),
|
||||
View::Main(ref mut view) => view.view(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@ use iced::{
|
|||
Button, Column, Command, Container, Element, Length, Space, Text, TextInput, Toggler,
|
||||
};
|
||||
|
||||
use self::Message::{InputHomeserver, InputPassword, InputUser, LoginFailed, TogglePassword};
|
||||
use self::Message::*;
|
||||
use crate::{matrix, style};
|
||||
|
||||
/// Data for the login prompt
|
||||
#[derive(Debug, Default)]
|
||||
|
@ -20,6 +21,8 @@ pub struct Login {
|
|||
homeserver: String,
|
||||
/// Whether we're waiting for a response to a login attempt
|
||||
waiting: bool,
|
||||
/// The text of an error message
|
||||
error: Option<String>,
|
||||
|
||||
/// Widget state
|
||||
state: State,
|
||||
|
@ -49,10 +52,14 @@ pub enum Message {
|
|||
InputHomeserver(String),
|
||||
/// The "show password" toggle has been switched.
|
||||
TogglePassword(bool),
|
||||
/// Triggers login
|
||||
/// Triggers login.
|
||||
Login,
|
||||
/// A login attempt failed
|
||||
/// A login attempt failed.
|
||||
LoginFailed(String),
|
||||
/// Login completed.
|
||||
LoggedIn(matrix_sdk::Client),
|
||||
/// Hide the error message.
|
||||
ResetError,
|
||||
}
|
||||
|
||||
impl Login {
|
||||
|
@ -63,23 +70,41 @@ impl Login {
|
|||
InputPassword(input) => self.password = input,
|
||||
InputHomeserver(input) => self.homeserver = input,
|
||||
TogglePassword(toggle) => self.show_password = toggle,
|
||||
Message::Login => {
|
||||
Login => {
|
||||
self.waiting = true;
|
||||
let command = async {
|
||||
std::thread::sleep_ms(1000);
|
||||
super::Message::from(LoginFailed(String::from("Not implemented :(")))
|
||||
let homeserver = self.homeserver.clone();
|
||||
let user = self.user.clone();
|
||||
let password = self.password.clone();
|
||||
let command = async move {
|
||||
let client = match matrix::login(&homeserver, &user, &password).await {
|
||||
Ok(client) => client,
|
||||
Err(e) => return LoginFailed(e.to_string()),
|
||||
};
|
||||
return command.into();
|
||||
LoggedIn(client)
|
||||
};
|
||||
return Command::perform(command, super::Message::from);
|
||||
}
|
||||
LoginFailed(error) => {
|
||||
self.waiting = false;
|
||||
self.error = Some(error);
|
||||
}
|
||||
LoggedIn(_) => (),
|
||||
ResetError => self.error = None,
|
||||
};
|
||||
Command::none()
|
||||
}
|
||||
|
||||
/// Generate widgets for this view
|
||||
pub fn view(&mut self) -> Element<super::Message> {
|
||||
let error_message: iced::Element<_> = match self.error {
|
||||
Some(ref error) => Container::new(Text::new(error.clone()))
|
||||
.center_x()
|
||||
.width(Length::Fill)
|
||||
.padding(5)
|
||||
.style(style::container::Error)
|
||||
.into(),
|
||||
None => Space::new(0.into(), 0.into()).into(),
|
||||
};
|
||||
let user_input =
|
||||
TextInput::new(&mut self.state.user, "alice", &self.user, |i| InputUser(i).into())
|
||||
.padding(5);
|
||||
|
@ -110,7 +135,7 @@ impl Login {
|
|||
let login_button = Button::new(
|
||||
&mut self.state.login,
|
||||
Text::new("Log in")
|
||||
.horizontal_alignment(iced::HorizontalAlignment::Center)
|
||||
.horizontal_alignment(iced::alignment::Horizontal::Center)
|
||||
.width(Length::Fill),
|
||||
)
|
||||
.on_press(Message::Login.into())
|
||||
|
@ -118,6 +143,7 @@ impl Login {
|
|||
|
||||
let mut column = Column::new()
|
||||
.width(500.into())
|
||||
.push(error_message)
|
||||
.push(Text::new("User name"))
|
||||
.push(user_input)
|
||||
.push(Space::with_height(10.into()))
|
||||
|
@ -130,7 +156,7 @@ impl Login {
|
|||
.push(login_button);
|
||||
|
||||
if self.waiting {
|
||||
column = column.push(Text::new("Loggin in"));
|
||||
column = column.push(Text::new("Logging in"));
|
||||
}
|
||||
|
||||
Container::new(column).center_x().center_y().width(Length::Fill).height(Length::Fill).into()
|
||||
|
|
53
src/ui/session.rs
Normal file
53
src/ui/session.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
//! View for a logged in session
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use iced::{Element, Row, Space};
|
||||
use matrix_sdk::{ruma::RoomId, Client};
|
||||
|
||||
/// The main view, for a logged in session
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct View {
|
||||
client: matrix_sdk::Client,
|
||||
|
||||
/// List of known rooms
|
||||
room_list: RoomList,
|
||||
|
||||
/// Widget state
|
||||
state: State,
|
||||
}
|
||||
|
||||
/// Widget state.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct State {}
|
||||
|
||||
/// The list of rooms
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct RoomList {
|
||||
names: HashMap<RoomId, String>,
|
||||
}
|
||||
|
||||
impl RoomList {
|
||||
fn view(&mut self) -> Element<super::Message> {
|
||||
Space::new(0.into(), 0.into()).into()
|
||||
}
|
||||
}
|
||||
|
||||
/// State change notification
|
||||
#[derive(Debug)]
|
||||
pub enum Message {
|
||||
/// The name for a room has been recalculated.
|
||||
RoomName(RoomId, String),
|
||||
}
|
||||
|
||||
impl View {
|
||||
/// Create a new view.
|
||||
pub fn with_client(client: Client) -> Self {
|
||||
Self { client, room_list: RoomList::default(), state: State::default() }
|
||||
}
|
||||
|
||||
/// Generate widgets
|
||||
pub fn view(&mut self) -> Element<super::Message> {
|
||||
Row::new().push(self.room_list.view()).into()
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue