Support for uploading images in posts/comments

This commit is contained in:
Bnyro
2023-06-25 19:53:15 +02:00
parent bc488e7cd3
commit 7a293458b9
5 changed files with 124 additions and 6 deletions

47
src/api/image.rs Normal file
View File

@@ -0,0 +1,47 @@
use reqwest::blocking::multipart::Part;
use std::io::Read;
use std::fs::File;
use crate::settings;
use serde::Deserialize;
use rand::distributions::{Alphanumeric, DistString};
use super::CLIENT;
#[derive(Deserialize)]
pub struct UploadImageResponse {
#[allow(dead_code)]
msg: String,
files: Vec<UploadImageFile>,
}
#[derive(Deserialize)]
struct UploadImageFile {
pub file: String,
#[allow(dead_code)]
pub delete_token: String,
}
pub fn upload_image(
image: std::path::PathBuf,
) -> Result<String, reqwest::Error> {
let mime_type = mime_guess::from_path(image.clone()).first();
let file_name = Alphanumeric.sample_string(&mut rand::thread_rng(), 16);
let mut file = File::open(image).unwrap();
let mut data = Vec::new();
file.read_to_end(&mut data).unwrap();
let part = Part::bytes(data).file_name(file_name).mime_str(&mime_type.unwrap().essence_str())?;
let form = reqwest::blocking::multipart::Form::new().part("images[]", part);
let account = settings::get_current_account();
let base_url = account.instance_url;
let path = format!("{}/pictrs/image", base_url);
let res: UploadImageResponse = CLIENT
.post(&path)
.header("cookie", format!("jwt={}", account.jwt.unwrap().to_string()))
.multipart(form)
.send()?
.json()?;
Ok(format!("{}/pictrs/image/{}", base_url, res.files[0].file))
}

View File

@@ -12,6 +12,7 @@ pub mod auth;
pub mod moderation;
pub mod comment;
pub mod site;
pub mod image;
static API_VERSION: &str = "v3";

View File

@@ -1,6 +1,8 @@
use relm4::prelude::*;
use relm4::{prelude::*, gtk::{ResponseType, FileFilter}};
use gtk::prelude::*;
use crate::api;
#[derive(Debug, Clone, Default)]
pub struct EditorData {
pub name: String,
@@ -17,7 +19,8 @@ pub struct EditorDialog {
url_buffer: gtk::EntryBuffer,
body_buffer: gtk::TextBuffer,
// Optional field to temporarily store the post or comment id
id: Option<i32>
id: Option<i32>,
window: gtk::Window
}
#[derive(Debug, Clone, Copy)]
@@ -32,7 +35,10 @@ pub enum DialogMsg {
Hide,
UpdateType(EditorType, bool),
UpdateData(EditorData),
Okay
Okay,
ChooseImage,
UploadImage(std::path::PathBuf),
AppendBody(String)
}
#[derive(Debug)]
@@ -108,7 +114,15 @@ impl SimpleComponent for EditorDialog {
},
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_halign: gtk::Align::End,
set_hexpand: true,
gtk::Button {
set_label: "Upload image",
set_margin_end: 10,
connect_clicked => DialogMsg::ChooseImage,
},
gtk::Box {
set_hexpand: true,
},
gtk::Button {
set_label: "Cancel",
set_margin_end: 10,
@@ -136,7 +150,8 @@ impl SimpleComponent for EditorDialog {
let name_buffer = gtk::EntryBuffer::builder().build();
let url_buffer = gtk::EntryBuffer::builder().build();
let body_buffer = gtk::TextBuffer::builder().build();
let model = EditorDialog { type_: init, visible: false, is_new: true, name_buffer, url_buffer, body_buffer, id: None };
let window = root.toplevel_window().unwrap();
let model = EditorDialog { type_: init, visible: false, is_new: true, name_buffer, url_buffer, body_buffer, id: None, window };
let widgets = view_output!();
ComponentParts { model, widgets }
}
@@ -173,6 +188,37 @@ impl SimpleComponent for EditorDialog {
if let Some(url) = data.url { self.url_buffer.set_text(url.to_string()); }
self.body_buffer.set_text(&data.body.clone());
}
DialogMsg::ChooseImage => {
let buttons = [("_Cancel", ResponseType::Cancel), ("_Okay", ResponseType::Accept)];
let dialog = gtk::FileChooserDialog::new(Some("Upload image"), None::<&gtk::ApplicationWindow>, gtk::FileChooserAction::Open, &buttons);
dialog.set_transient_for(Some(&self.window));
let image_filter = FileFilter::new();
image_filter.add_pattern("image/*");
dialog.add_filter(&image_filter);
dialog.run_async(move |dialog, result | {
match result {
ResponseType::Accept => {
let path = dialog.file().unwrap().path();
sender.input(DialogMsg::UploadImage(path.unwrap()))
},
_ => dialog.hide(),
}
dialog.destroy();
});
}
DialogMsg::UploadImage(path) => {
std::thread::spawn(move || {
if let Ok(image_path) = api::image::upload_image(path) {
let new_text = format!("![]({})", image_path);
sender.input(DialogMsg::AppendBody(new_text));
}
});
}
DialogMsg::AppendBody(new_text) => {
let (start, end) = &self.body_buffer.bounds();
let body = self.body_buffer.text(start, end, true).to_string();
self.body_buffer.set_text(&format!("{}\n{}", body, new_text));
}
}
}
}