Skip to content

Commit

Permalink
GH-72: Add Settings for URL preview
Browse files Browse the repository at this point in the history
  • Loading branch information
SetZero committed Jul 17, 2023
1 parent 0c0dced commit 61112c8
Show file tree
Hide file tree
Showing 12 changed files with 457 additions and 254 deletions.
177 changes: 5 additions & 172 deletions src-tauri/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,22 @@
#![allow(clippy::used_underscore_binding)]

mod helper;
pub mod settings_cmd;
pub mod web_cmd;
pub mod zip_cmd;

use std::{
borrow::BorrowMut,
collections::HashMap,
io::{Seek, SeekFrom, Write},
};
use std::{borrow::BorrowMut, collections::HashMap};

use crate::{
connection::{traits::Shutdown, Connection},
errors::string_convertion::ErrorString,
manager::user::UpdateableUserState,
protocol::message_transmitter::MessageTransmitter,
utils::{
audio::device_manager::AudioDeviceManager, constants::get_project_dirs, server::Server,
},
utils::audio::device_manager::AudioDeviceManager,
};
use base64::{engine::general_purpose, Engine};
use serde_json::json;
use tauri::State;
use tokio::sync::Mutex;
use tracing::{error, info, trace};
use webbrowser::{Browser, BrowserOptions};

use self::helper::OpenGraphCrawler;

pub struct ConnectionState {
pub connection: Mutex<Option<Connection>>,
Expand Down Expand Up @@ -82,91 +74,6 @@ pub async fn connect_to_server(
Ok(())
}

#[tauri::command]
pub fn save_server(
description: &str,
server_host: &str,
server_port: u16,
username: &str,
) -> Result<(), String> {
info!("Saving server: {server_host}:{server_port}");
let project_dirs = get_project_dirs().ok_or("Unable to load project dir")?;

let data_dir = project_dirs.config_dir();

// create config dir if it doesn't exist
std::fs::create_dir_all(data_dir).map_err(|e| format!("{e:?}"))?;

// open server.json or create it if it doesn't exist
let mut server_file = std::fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(data_dir.join("server.json"))
.map_err(|e| format!("Error opening file: {e:?}"))?;

// read the json content using serde and append the new server
let mut server_list =
serde_json::from_reader::<&std::fs::File, Vec<Server>>(&server_file).unwrap_or_default();

// check if the server is already in the list
for server in &server_list {
if server.host == server_host && server.port == server_port {
return Err("Server already exists".to_string());
}
}

server_list.push(Server {
description: description.to_string(),
host: server_host.to_string(),
port: server_port,
username: username.to_string(),
});

trace!("Server list: {:#?}", server_list);

// write the new json content
server_file
.seek(SeekFrom::Start(0))
.map_err(|e| format!("{e:?}"))?;
server_file
.write_all(
serde_json::to_string_pretty(&server_list)
.map_err(|e| format!("{e:?}"))?
.as_bytes(),
)
.map_err(|e| format!("{e:?}"))?;

Ok(())
}

#[tauri::command]
pub fn get_server_list() -> Result<Vec<Server>, String> {
info!("Getting server list");
let project_dirs = get_project_dirs().ok_or("Unable to load project dir")?;

let data_dir = project_dirs.config_dir();

// create config dir if it doesn't exist
std::fs::create_dir_all(data_dir).map_err(|e| format!("{e:?}"))?;

// open server.json or create it if it doesn't exist
let server_file = std::fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(data_dir.join("server.json"))
.map_err(|e| format!("Error opening file: {e:?}"))?;

// read the json content using serde
let server_list =
serde_json::from_reader::<&std::fs::File, Vec<Server>>(&server_file).unwrap_or_default();

trace!("Server list: {:#?}", server_list);

Ok(server_list)
}

#[tauri::command]
pub async fn send_message(
chat_message: String,
Expand Down Expand Up @@ -289,77 +196,3 @@ pub async fn get_audio_devices(

Err(ErrorString("Failed to get audio devices".to_string()))
}

#[tauri::command]
pub fn zip_data_to_utf8(data: &str, quality: u32) -> Result<String, String> {
trace!("zipping data {:?}", data);

let mut buffer = Vec::new();
let lg_windows_size = 22;

{
let cursor = std::io::Cursor::new(&mut buffer);
let mut writer = brotli::CompressorWriter::new(cursor, 4096, quality, lg_windows_size);
writer
.write_all(data.as_bytes())
.map_err(|e| e.to_string())?;
writer.flush().map_err(|e| e.to_string())?;
}

let encoded = general_purpose::STANDARD.encode(buffer);
Ok(encoded)
}

#[tauri::command]
pub fn unzip_data_from_utf8(data: &str) -> Result<String, String> {
let decoded_data = general_purpose::STANDARD
.decode(data)
.map_err(|e| e.to_string())?;
let mut writer = brotli::DecompressorWriter::new(Vec::new(), 4096);
writer.write_all(&decoded_data).map_err(|e| e.to_string())?;
let output = writer.into_inner().map_err(|_| "Decompress Error")?;

let result = String::from_utf8(output).map_err(|e| e.to_string())?;
Ok(result)
}

#[tauri::command]
pub fn open_browser(url: &str) -> Result<(), String> {
if let Err(e) = webbrowser::open_browser_with_options(
Browser::Default,
url,
BrowserOptions::new().with_suppress_output(false),
) {
return Err(format!("{e:?}"));
}

Ok(())
}

pub struct CrawlerState {
pub crawler: Mutex<Option<OpenGraphCrawler>>,
}

#[tauri::command]
pub async fn get_open_graph_data_from_website(
state: State<'_, CrawlerState>,
url: &str,
) -> Result<String, String> {
// setup crawler if not already done
let result = {
let mut client = state.crawler.lock().await;
if client.is_none() {
*client = OpenGraphCrawler::try_new();
}

client
.as_ref()
.ok_or_else(|| "Failed to read website body".to_string())?
.crawl(url)
.await
};

let result = json!(result);

Ok(result.to_string())
}
138 changes: 138 additions & 0 deletions src-tauri/src/commands/settings_cmd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
use std::io::{Read, Seek, SeekFrom, Write};

use tracing::{info, trace};

use crate::utils::{constants::get_project_dirs, server::Server};

const SERVER_SETTINS_FILE: &str = "server.json";
const FRONTEND_SETTINS_FILE: &str = "frontend_settings.json";

pub fn get_settings_file(file_name: &str) -> Result<std::fs::File, String> {
let project_dirs = get_project_dirs().ok_or("Unable to load project dir")?;
let data_dir = project_dirs.config_dir();
std::fs::create_dir_all(data_dir).map_err(|e| format!("{e:?}"))?;
let settings_file = std::fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(data_dir.join(file_name))
.map_err(|e| format!("Error opening file: {e:?}"))?;
Ok(settings_file)
}

#[tauri::command]
pub fn save_server(
description: &str,
server_host: &str,
server_port: u16,
username: &str,
) -> Result<(), String> {
info!("Saving server: {server_host}:{server_port}");
let mut server_file = get_settings_file(SERVER_SETTINS_FILE)?;

// read the json content using serde and append the new server
let mut server_list =
serde_json::from_reader::<&std::fs::File, Vec<Server>>(&server_file).unwrap_or_default();

// check if the server is already in the list
for server in &server_list {
if server.host == server_host && server.port == server_port {
return Err("Server already exists".to_string());
}
}

server_list.push(Server {
description: description.to_string(),
host: server_host.to_string(),
port: server_port,
username: username.to_string(),
});

trace!("Server list: {:#?}", server_list);

// write the new json content
server_file
.seek(SeekFrom::Start(0))
.map_err(|e| format!("{e:?}"))?;
server_file
.write_all(
serde_json::to_string_pretty(&server_list)
.map_err(|e| format!("{e:?}"))?
.as_bytes(),
)
.map_err(|e| format!("{e:?}"))?;

Ok(())
}

#[tauri::command]
pub fn get_server_list() -> Result<Vec<Server>, String> {
info!("Getting server list");
let project_dirs = get_project_dirs().ok_or("Unable to load project dir")?;

let data_dir = project_dirs.config_dir();

// create config dir if it doesn't exist
std::fs::create_dir_all(data_dir).map_err(|e| format!("{e:?}"))?;

// open server.json or create it if it doesn't exist
let server_file = std::fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(data_dir.join(SERVER_SETTINS_FILE))
.map_err(|e| format!("Error opening file: {e:?}"))?;

// read the json content using serde
let server_list =
serde_json::from_reader::<&std::fs::File, Vec<Server>>(&server_file).unwrap_or_default();

trace!("Server list: {:#?}", server_list);

Ok(server_list)
}

#[derive(serde::Deserialize, serde::Serialize, Debug)]
pub struct LinkPreview {
enabled: bool,
allow_all: bool,
urls: Vec<String>,
}

#[allow(clippy::needless_pass_by_value)] // LinkPreview needs to be deserialized
#[tauri::command]
pub fn save_frontend_settings(settings_name: &str, data: LinkPreview) -> Result<(), String> {
info!("Saving frontend settings: {settings_name}");
let mut settings_file = get_settings_file(&format!("{settings_name}_{FRONTEND_SETTINS_FILE}"))?;

trace!("Settings data: {:#?}", data);

// write the new json content
settings_file
.seek(SeekFrom::Start(0))
.map_err(|e| format!("{e:?}"))?;
settings_file
.write_all(
serde_json::to_string_pretty(&data)
.map_err(|e| format!("{e:?}"))?
.as_bytes(),
)
.map_err(|e| format!("{e:?}"))?;

Ok(())
}

#[tauri::command]
pub fn get_frontend_settings(settings_name: &str) -> Result<String, String> {
info!("Getting frontend settings: {settings_name}");
let mut settings_file = get_settings_file(&format!("{settings_name}_{FRONTEND_SETTINS_FILE}"))?;

let mut settings_data = String::new();
settings_file
.read_to_string(&mut settings_data)
.map_err(|e| format!("{e:?}"))?;

trace!("Settings data: {:#?}", settings_data);

Ok(settings_data)
}
47 changes: 47 additions & 0 deletions src-tauri/src/commands/web_cmd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use serde_json::json;
use tauri::State;
use tokio::sync::Mutex;
use webbrowser::{Browser, BrowserOptions};

use super::helper::OpenGraphCrawler;

#[tauri::command]
pub fn open_browser(url: &str) -> Result<(), String> {
if let Err(e) = webbrowser::open_browser_with_options(
Browser::Default,
url,
BrowserOptions::new().with_suppress_output(false),
) {
return Err(format!("{e:?}"));
}

Ok(())
}

pub struct CrawlerState {
pub crawler: Mutex<Option<OpenGraphCrawler>>,
}

#[tauri::command]
pub async fn get_open_graph_data_from_website(
state: State<'_, CrawlerState>,
url: &str,
) -> Result<String, String> {
// setup crawler if not already done
let result = {
let mut client = state.crawler.lock().await;
if client.is_none() {
*client = OpenGraphCrawler::try_new();
}

client
.as_ref()
.ok_or_else(|| "Failed to read website body".to_string())?
.crawl(url)
.await
};

let result = json!(result);

Ok(result.to_string())
}
Loading

0 comments on commit 61112c8

Please sign in to comment.