Files
photos/frontend/src/components/home.rs

276 lines
9.3 KiB
Rust

use std::io::Cursor;
use super::BasePage;
use crate::gallery::Grid;
use crate::hooks::use_user_context;
use gloo_net::http::Request;
use js_sys::{Array, ArrayBuffer, Promise};
use serde::{Deserialize, Serialize};
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
use web_sys::{
DataTransferItemList, Element, File, FileReader, FileSystemDirectoryEntry, FileSystemEntry,
HtmlElement,
};
use weblog::console_log;
use yew::prelude::*;
use yew_hooks::prelude::*;
use common::OutputPicture;
pub struct MetaData {
width: u32,
height: u32,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct PhotosWrapper {
photos: Vec<String>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct PhotosUrlsWrapper {
photos: Vec<(String, String)>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct DetailPhoto {
key: String,
width: u32,
height: u32,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct DetailPhotoWrapper {
photos: Vec<DetailPhoto>,
}
#[function_component(Home)]
pub fn home() -> Html {
let user_ctx = use_user_context();
let node = use_node_ref();
let size = use_size(node.clone());
let pictures = use_state(std::vec::Vec::new);
{
let pictures = pictures.clone();
use_effect_with_deps(
move |_| {
wasm_bindgen_futures::spawn_local(async move {
let url = "/api/photos/get";
let fetched_pictures: Vec<OutputPicture> = Request::get(url)
.send()
.await
.unwrap()
.json()
.await
.unwrap();
pictures.set(fetched_pictures);
});
|| ()
},
(),
);
}
let drag_node = node.clone();
let ondrop = Callback::from(move |e: DragEvent| {
e.prevent_default();
if let Some(element) = drag_node.cast::<HtmlElement>() {
element.class_list().remove_1("dragover_highlight").unwrap();
}
let items = e.data_transfer().unwrap().items();
wasm_bindgen_futures::spawn_local(async move {
let promise = get_files_data_transfer_items(items);
let result = wasm_bindgen_futures::JsFuture::from(promise).await.unwrap();
console_log!(&result);
let traverse = |d: JsValue| {
let mut new_files: Vec<JsValue> = Vec::new();
let mut files: Vec<File> = Vec::new();
if let Ok(fse) = d.clone().dyn_into::<File>() {
console_log!(&fse);
files.push(fse.clone());
} else if let Ok(a) = d.clone().dyn_into::<js_sys::Array>() {
for i in 0..a.length() {
let f = a.get(i);
new_files.push(f);
}
} else if let Ok(o) = d.clone().dyn_into::<js_sys::Object>() {
if let Ok(subfolder) = js_sys::Reflect::get(&o, &JsValue::from_str("subfolder"))
{
new_files.push(subfolder);
}
}
(new_files, files)
};
let mut files: Vec<File> = Vec::new();
let mut new_files = vec![result];
loop {
console_log!(files.len());
let Some(current) = new_files.pop() else {
console_log!("break");
break;
};
let (news, fs) = traverse(current);
news.iter().for_each(|x| new_files.push(x.clone()));
fs.iter().for_each(|x| files.push(x.clone()));
}
let uiae: Vec<String> = files
.iter()
.map(|x| {
if let Ok(filepath) = js_sys::Reflect::get(x, &JsValue::from_str("filepath")) {
filepath.as_string().unwrap_or("".to_string())
} else {
"".to_string()
}
})
.collect();
console_log!("end", uiae.join("\n"));
let photos: PhotosUrlsWrapper = Request::post("/api/photos/upload")
.json(&PhotosWrapper {
photos: uiae.clone(),
})
.unwrap()
.send()
.await
.unwrap()
.json()
.await
.unwrap();
console_log!("{}", serde_json::to_string(&photos).unwrap());
let mut metadata: Vec<MetaData> = Vec::new();
let mut promises: Vec<Promise> = Vec::new();
for (file, (key, url)) in files.iter().zip(photos.photos) {
console_log!("uploading: ", &file.name(), &url);
let promise = read_file(file.clone());
if let Ok(content) = wasm_bindgen_futures::JsFuture::from(promise).await {
let buffer: ArrayBuffer = content.dyn_into().unwrap();
let typed_buffer: js_sys::Uint8Array = js_sys::Uint8Array::new(&buffer);
let mut buf = vec![0; typed_buffer.length() as usize];
typed_buffer.copy_to(&mut buf);
// console_log!(
// serde_json::to_string(&buf.len()).unwrap(),
// serde_json::to_string(&buf).unwrap()
// );
let mut md = MetaData {
width: 0,
height: 0,
};
let meta = image_meta::load_from_buf(&buf).unwrap();
console_log!(format!(
"dims: {}x{}",
meta.dimensions.width, meta.dimensions.height
));
console_log!(format!("animation: {:?}", meta.is_animation()));
console_log!(format!("format: {:?}", meta.format));
md.height = meta.dimensions.height;
md.width = meta.dimensions.width;
let exifreader = exif::Reader::new();
let mut cursor = Cursor::new(&buf);
if let Ok(exif) = exifreader.read_from_container(&mut cursor) {
for f in exif.fields() {
console_log!(format!(
"{} {} {}",
f.tag,
f.ifd_num,
f.display_value().with_unit(&exif)
));
}
}
metadata.push(md);
promises.push(upload(buffer, file.type_(), url.clone()));
}
}
for promise in promises {
match wasm_bindgen_futures::JsFuture::from(promise).await {
Ok(result) => console_log!(result),
Err(e) => console_log!("errooooor", e),
};
}
let photos: DetailPhotoWrapper = Request::post("/api/photos/upload/done")
.json(&DetailPhotoWrapper {
photos: uiae
.iter()
.zip(metadata)
.map(|(p, md)| DetailPhoto {
key: p.clone(),
width: md.width,
height: md.height,
})
.collect(),
})
.unwrap()
.send()
.await
.unwrap()
.json()
.await
.unwrap();
console_log!("all uploaded");
});
});
let drag_node = node.clone();
let ondragenter = Callback::from(move |e: DragEvent| {
e.prevent_default();
// e.data_transfer().unwrap().set_drop_effect("move");
if let Some(element) = drag_node.cast::<HtmlElement>() {
element.class_list().remove_1("dragover_highlight").unwrap();
}
});
let drag_node = node.clone();
let ondragleave = Callback::from(move |e: DragEvent| {
e.prevent_default();
if let Some(element) = drag_node.cast::<HtmlElement>() {
element.class_list().remove_1("dragover_highlight").unwrap();
}
});
let drag_node = node.clone();
let ondragover = Callback::from(move |e: DragEvent| {
e.prevent_default();
if let Some(element) = drag_node.cast::<HtmlElement>() {
element.class_list().add_1("dragover_highlight").unwrap();
}
});
let body = if user_ctx.is_authenticated() {
html! {
<Grid
pictures={(*pictures).clone()}
width={size.0}
/>
}
} else {
html! {}
};
html! {
<BasePage>
<div ref={node} {ondrop} {ondragenter} {ondragleave} {ondragover}>
{body}
</div>
</BasePage>
}
}
#[wasm_bindgen(module = "/src/components/home.js")]
extern "C" {
fn get_files_data_transfer_items(data_transfer_items: DataTransferItemList) -> js_sys::Promise;
fn read_file(file: File) -> js_sys::Promise;
fn upload(content: ArrayBuffer, content_type: String, url: String) -> js_sys::Promise;
}