Perfect MediaPicker
This commit is contained in:
parent
fc2a2510ec
commit
89a7b99ed2
20
Cargo.lock
generated
20
Cargo.lock
generated
@ -795,6 +795,16 @@ version = "0.3.16"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mime_guess"
|
||||||
|
version = "2.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
|
||||||
|
dependencies = [
|
||||||
|
"mime",
|
||||||
|
"unicase",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minimal-lexical"
|
name = "minimal-lexical"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@ -1096,6 +1106,7 @@ dependencies = [
|
|||||||
"js-sys",
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
"mime",
|
"mime",
|
||||||
|
"mime_guess",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
@ -1575,6 +1586,15 @@ version = "0.2.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
|
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicase"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||||
|
dependencies = [
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi"
|
name = "unicode-bidi"
|
||||||
version = "0.3.11"
|
version = "0.3.11"
|
||||||
|
@ -23,7 +23,7 @@ js-sys = "0.3"
|
|||||||
web-sys = "0.3.61"
|
web-sys = "0.3.61"
|
||||||
|
|
||||||
# other libs
|
# other libs
|
||||||
reqwest = { version = "0.11.11", features = ["rustls-tls", "json", "blocking"], default-features = false }
|
reqwest = { version = "0.11.11", features = ["rustls-tls", "json", "blocking", "multipart"], default-features = false }
|
||||||
uuid = { version = "1.3.0", features = ["v4", "fast-rng", "macro-diagnostics", "serde", "js"] }
|
uuid = { version = "1.3.0", features = ["v4", "fast-rng", "macro-diagnostics", "serde", "js"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
@ -5,12 +5,13 @@ use jl_types::{
|
|||||||
dto::{
|
dto::{
|
||||||
filters::Filter, listing::Listing, payloads::contact::ContactPayload,
|
filters::Filter, listing::Listing, payloads::contact::ContactPayload,
|
||||||
project_card::ProjectCardDto,
|
project_card::ProjectCardDto,
|
||||||
|
item::Item,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use reqwest::Method;
|
use reqwest::Method;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use super::base::perform_request_without_client;
|
use super::base::{perform_request_without_client, perform_multipart_request_without_client};
|
||||||
|
|
||||||
const BASE_URL: &str = "http://localhost:8095/";
|
const BASE_URL: &str = "http://localhost:8095/";
|
||||||
|
|
||||||
@ -148,7 +149,16 @@ pub async fn create_new_contact_request(contact: ContactPayload) -> Result<(), e
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
pub async fn upload_image() -> Result<String, err::Error> {
|
pub async fn upload_image(item: Item, body: Vec<u8>) -> Result<String, err::Error> {
|
||||||
perform_request_without_client(BASE_URL.into(), Method::POST, format!("images/"), Some(), 200, Vec::new(), None).await
|
perform_multipart_request_without_client(
|
||||||
}*/
|
BASE_URL.into(),
|
||||||
|
Method::POST,
|
||||||
|
format!("admin/images/{item}"),
|
||||||
|
body,
|
||||||
|
200,
|
||||||
|
Vec::new(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
@ -60,3 +60,59 @@ pub async fn perform_request_without_client<B: Serialize, R: DeserializeOwned>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn perform_multipart_request_without_client<R: DeserializeOwned>(
|
||||||
|
base_url: String,
|
||||||
|
method: reqwest::Method,
|
||||||
|
path: String,
|
||||||
|
body: Vec<u8>,
|
||||||
|
expected_status_code: u16,
|
||||||
|
headers: Vec<(String, String)>,
|
||||||
|
params: Option<Vec<(String, String)>>,
|
||||||
|
) -> Result<R, Error> {
|
||||||
|
let client = Client::new();
|
||||||
|
let mut req_incomplete =
|
||||||
|
client.request(method, format!("{url}{path}", url = base_url, path = path));
|
||||||
|
|
||||||
|
for header in headers {
|
||||||
|
req_incomplete = req_incomplete.header(&header.0, &header.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(parameters) = params {
|
||||||
|
req_incomplete = req_incomplete.query(¶meters)
|
||||||
|
}
|
||||||
|
|
||||||
|
let part = reqwest::multipart::Part::bytes(body).file_name("image");
|
||||||
|
let form = reqwest::multipart::Form::new();
|
||||||
|
let form = form.part("image", part);
|
||||||
|
|
||||||
|
match req_incomplete.multipart(form).send().await {
|
||||||
|
// Error handling here
|
||||||
|
Ok(res) => {
|
||||||
|
// Request sent correctly
|
||||||
|
match res.status().as_u16() == expected_status_code {
|
||||||
|
true => {
|
||||||
|
match res.json::<R>().await {
|
||||||
|
Ok(resp_dto) => Ok(resp_dto), // Return correctly deserialized obj
|
||||||
|
Err(err) => Err(Error::Serde(MessageResource::from(err))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false => {
|
||||||
|
//If status code is any other than expected
|
||||||
|
Err(Error::UnexpectedStatusCode(
|
||||||
|
expected_status_code,
|
||||||
|
res.status().as_u16(),
|
||||||
|
match res.json::<Vec<MessageResource>>().await {
|
||||||
|
Ok(messages) => messages,
|
||||||
|
Err(e) => vec![MessageResource::from(e)],
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// Request couldn't be sent
|
||||||
|
Err(Error::Network(MessageResource::from(e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use jl_types::domain::media::{Media, MediaList};
|
use jl_types::domain::media::{Media, MediaList};
|
||||||
|
use js_sys::Uint8Array;
|
||||||
|
use wasm_bindgen_futures::JsFuture;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
use crate::components::textfield::get_value_from_input_event;
|
use crate::{components::textfield::{get_value_from_input_event, get_files_from_input_event}, api};
|
||||||
|
|
||||||
#[function_component(MediaPicker)]
|
#[function_component(MediaPicker)]
|
||||||
pub fn media_picker(props: &MediaPickerProps) -> Html {
|
pub fn media_picker(props: &MediaPickerProps) -> Html {
|
||||||
@ -9,7 +11,7 @@ pub fn media_picker(props: &MediaPickerProps) -> Html {
|
|||||||
<>
|
<>
|
||||||
<div class={{"textfield-label-required"}}>{"Media del proyecto"}</div>
|
<div class={{"textfield-label-required"}}>{"Media del proyecto"}</div>
|
||||||
<div class={"mediapicker-container"}>
|
<div class={"mediapicker-container"}>
|
||||||
<MediaListRendered medialist={props.value.clone()} onchange={props.onchange.clone()}/>
|
<MediaListRendered medialist={props.value.clone()} onchange={props.onchange.clone()} item={props.item.clone()}/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
@ -21,6 +23,7 @@ pub struct MediaPickerProps {
|
|||||||
pub onchange: Option<Callback<String>>,
|
pub onchange: Option<Callback<String>>,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub required: bool,
|
pub required: bool,
|
||||||
|
pub item: jl_types::dto::item::Item,
|
||||||
}
|
}
|
||||||
#[function_component(MediaListRendered)]
|
#[function_component(MediaListRendered)]
|
||||||
fn render_media_list(props: &MediaListProps) -> Html {
|
fn render_media_list(props: &MediaListProps) -> Html {
|
||||||
@ -90,15 +93,42 @@ fn render_media_list(props: &MediaListProps) -> Html {
|
|||||||
let onphoto_upload = {
|
let onphoto_upload = {
|
||||||
let media_handle = props.medialist.clone();
|
let media_handle = props.medialist.clone();
|
||||||
let onchange_cb = props.onchange.clone();
|
let onchange_cb = props.onchange.clone();
|
||||||
Callback::from(move |_: InputEvent| {
|
let item = props.item.clone();
|
||||||
|
Callback::from(move |e: InputEvent| {
|
||||||
|
|
||||||
match onchange_cb.clone() {
|
match onchange_cb.clone() {
|
||||||
Some(cb) => cb.emit(String::new()),
|
Some(cb) => cb.emit(String::new()),
|
||||||
None => {}
|
None => {}
|
||||||
};
|
};
|
||||||
//TODO: Upload picture, then push url into medialist
|
let files_opt = get_files_from_input_event(e);
|
||||||
let mut media = (*media_handle).clone();
|
match files_opt {
|
||||||
media.media_list.push(Media::Photo("".into()));
|
Some(filelist) => {
|
||||||
media_handle.set(media);
|
match filelist.get(0) {
|
||||||
|
Some(file) => {
|
||||||
|
let media_handle = media_handle.clone();
|
||||||
|
let item = item.clone();
|
||||||
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
|
let array_buf = JsFuture::from(file.array_buffer()).await;
|
||||||
|
let array = Uint8Array::new(&array_buf.unwrap());
|
||||||
|
let bytes: Vec<u8> = array.to_vec();
|
||||||
|
// Upload to backend and retrieve url
|
||||||
|
match api::backend::upload_image(item, bytes).await {
|
||||||
|
Ok(url) => {
|
||||||
|
let mut media = (*media_handle).clone();
|
||||||
|
media.media_list.push(Media::Photo(url));
|
||||||
|
media_handle.set(media);
|
||||||
|
},
|
||||||
|
Err(error) => log::error!("Error uploading image to backend: {error}"),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
None => {log::error!("No files in first element of filelist...")}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
log::error!("Something weird happened. No files after selecting files")
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -126,4 +156,5 @@ fn render_media_list(props: &MediaListProps) -> Html {
|
|||||||
pub struct MediaListProps {
|
pub struct MediaListProps {
|
||||||
pub medialist: UseStateHandle<MediaList>,
|
pub medialist: UseStateHandle<MediaList>,
|
||||||
pub onchange: Option<Callback<String>>,
|
pub onchange: Option<Callback<String>>,
|
||||||
|
pub item: jl_types::dto::item::Item,
|
||||||
}
|
}
|
||||||
|
@ -79,3 +79,9 @@ pub fn get_value_from_textarea_event(e: InputEvent) -> String {
|
|||||||
let target: HtmlTextAreaElement = event_target.dyn_into().unwrap_throw();
|
let target: HtmlTextAreaElement = event_target.dyn_into().unwrap_throw();
|
||||||
target.value()
|
target.value()
|
||||||
}
|
}
|
||||||
|
pub fn get_files_from_input_event(e: InputEvent) -> Option<web_sys::FileList> {
|
||||||
|
let event: Event = e.dyn_into().unwrap_throw();
|
||||||
|
let event_target = event.target().unwrap_throw();
|
||||||
|
let target: HtmlInputElement = event_target.dyn_into().unwrap_throw();
|
||||||
|
target.files()
|
||||||
|
}
|
||||||
|
@ -228,7 +228,7 @@ pub fn generate_fields_for_project(props: &ProjectFieldsProps) -> Html {
|
|||||||
<>
|
<>
|
||||||
<TextField label={"Ciudad"} value={location_city} required={true} onchange={ontype_cb.clone()}/>
|
<TextField label={"Ciudad"} value={location_city} required={true} onchange={ontype_cb.clone()}/>
|
||||||
<TextField label={"Distrito"} value={location_district} required={true} onchange={ontype_cb.clone()} />
|
<TextField label={"Distrito"} value={location_district} required={true} onchange={ontype_cb.clone()} />
|
||||||
<MediaPicker value={media} onchange={ontype_cb.clone()}/>
|
<MediaPicker value={media} onchange={ontype_cb.clone()} item={jl_types::dto::item::Item::Project}/>
|
||||||
{if (*agent).clone().is_none() {
|
{if (*agent).clone().is_none() {
|
||||||
html! {
|
html! {
|
||||||
<div class={"textfield-container"}>
|
<div class={"textfield-container"}>
|
||||||
|
Loading…
Reference in New Issue
Block a user