diff --git a/Cargo.toml b/Cargo.toml index ff0a51c..3e906c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ features = [ "DragEvent", "HtmlImageElement", "Headers", + "Navigator", "Request", "RequestInit", "RequestMode", @@ -31,7 +32,7 @@ features = [ "Url", "Blob", "BlobPropertyBag", - "Window" + "Window", ] [profile.release] diff --git a/assets/index.html b/assets/index.html index bb38d9d..e61853c 100644 --- a/assets/index.html +++ b/assets/index.html @@ -12,6 +12,7 @@

Pecahkan puzzle nya dan dapatkan kata inggris nya

+

Seret dan lepas untuk menukar gambar

diff --git a/assets/style.css b/assets/style.css index de61e0d..a2bc6b9 100644 --- a/assets/style.css +++ b/assets/style.css @@ -47,6 +47,11 @@ h1 { font-size: 1rem; } +p { + text-align: center; + opacity: 0.8; +} + details { text-align: center; } @@ -96,6 +101,7 @@ img { border: 2px solid gray; margin: 2px; width: 12vw; + touch-action: none; } fieldset { diff --git a/compose.yaml b/compose.yaml index 3f64dac..e6971de 100644 --- a/compose.yaml +++ b/compose.yaml @@ -13,4 +13,4 @@ services: command: firebase emulators:start -P $COMPOSE_PROJECT_NAME volumes: - ./firebase.json:/home/node/firebase.json - - ./assets:/home/node/assets \ No newline at end of file + - ./assets:/home/node/assets diff --git a/src/display.rs b/src/display.rs index e1682b2..0b016c3 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,8 +1,16 @@ use wasm_bindgen::JsCast; -use web_sys::{DragEvent, HtmlImageElement}; +use web_sys::{DragEvent, Element, Event, HtmlImageElement}; use crate::util; +pub fn change_message(msg: &str) { + let document = web_sys::window().unwrap().document().unwrap(); + document + .get_element_by_id("message") + .unwrap() + .set_text_content(Some(msg)); +} + pub fn toggle_loading(display: bool) { let document = web_sys::window().unwrap().document().unwrap(); @@ -51,34 +59,67 @@ pub fn compare_rand_urls(urls: &[String]) -> bool { urls.eq(&random_urls) } -pub fn send_src_url(e: DragEvent) { - let el = e.target().unwrap().dyn_into::().unwrap(); +pub fn send_img_src_on_drag(e: DragEvent) { + let t_id = e + .target() + .unwrap() + .dyn_into::() + .unwrap() + .id(); e.data_transfer().unwrap().clear_data().unwrap(); e.data_transfer() .unwrap() - .set_data("text/plain", &el.id()) + .set_data("text/plain", &t_id) .unwrap(); } -pub fn swap_url(e: DragEvent) { - let window = web_sys::window().unwrap(); - let document = window.document().unwrap(); - +pub fn swap_img_src_on_drag(e: DragEvent) { e.prevent_default(); - let el = e.target().unwrap().dyn_into::().unwrap(); - let src = el.src(); + + let document = web_sys::window().unwrap().document().unwrap(); + + let target = e.target().unwrap().dyn_into::().unwrap(); let id = e.data_transfer().unwrap().get_data("text/plain").unwrap(); - let dst_el = document + let source = document .get_element_by_id(&id) .unwrap() .dyn_into::() .unwrap(); - let dst_src = dst_el.src(); + let temp = target.src(); + + target.set_src(&source.src()); + source.set_src(&temp); +} + +pub fn swap_img_src_on_touch(e: Event) { + let document = web_sys::window().unwrap().document().unwrap(); + let clicked = document.query_selector(r#"[data-touched="ok"]"#).unwrap(); + + let t_id = e + .current_target() + .unwrap() + .dyn_into::() + .unwrap() + .id(); + + let target = document + .get_element_by_id(&t_id) + .unwrap() + .dyn_into::() + .unwrap(); - dst_el.set_src(&src); - el.set_src(&dst_src); + if let Some(clicked) = clicked { + let clicked = clicked.dyn_into::().unwrap(); + + let temp = target.src(); + target.set_src(&clicked.src()); + clicked.set_src(&temp); + clicked.remove_attribute("data-touched").unwrap() + } else { + target.set_attribute("data-touched", "ok").unwrap(); + } } pub fn display_img_url(id: usize, url: &str) { diff --git a/src/puzzle.rs b/src/puzzle.rs index 6548d4d..afe05e3 100644 --- a/src/puzzle.rs +++ b/src/puzzle.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use wasm_bindgen::closure::Closure; use wasm_bindgen::JsCast; -use web_sys::{DragEvent, Element, HtmlImageElement, HtmlLabelElement}; +use web_sys::{DragEvent, Element, Event, HtmlImageElement, HtmlLabelElement}; use crate::{data::Data, display, util}; @@ -10,12 +10,12 @@ pub fn puzzle_handler(urls: Vec, data: Data) { let document = web_sys::window().unwrap().document().unwrap(); let imgs = document.query_selector_all("figure img").unwrap(); - let drag_over_handler = - Closure::new(Box::new(|e: DragEvent| e.prevent_default()) as Box); - let urls = Rc::new(urls); let data = Rc::new(data); + let drag_over_handler = + Closure::new(Box::new(|e: DragEvent| e.prevent_default()) as Box); + for i in 0..imgs.length() { let img = imgs .item(i) @@ -23,13 +23,39 @@ pub fn puzzle_handler(urls: Vec, data: Data) { .dyn_into::() .unwrap(); + let urls = Rc::clone(&urls); + let data = Rc::clone(&data); + + if util::is_mobile() && !util::support_drag_drop() { + display::change_message("Tekan 2 gambar untuk menukarnya"); + + let click_handler = Closure::new(Box::new(move |e: Event| { + display::swap_img_src_on_touch(e); + + let ok = display::compare_rand_urls(&urls); + if ok { + success_handler(&data); + } + }) as Box); + + img.add_event_listener_with_callback( + "touchstart", + click_handler.as_ref().unchecked_ref(), + ) + .unwrap(); + + click_handler.forget(); + continue; + } + img.add_event_listener_with_callback( "dragover", drag_over_handler.as_ref().unchecked_ref(), ) .unwrap(); - let drag_start_handler = Closure::new(Box::new(display::send_src_url) as Box); + let drag_start_handler = + Closure::new(Box::new(display::send_img_src_on_drag) as Box); img.add_event_listener_with_callback( "dragstart", @@ -37,11 +63,8 @@ pub fn puzzle_handler(urls: Vec, data: Data) { ) .unwrap(); - let urls = Rc::clone(&urls); - let data = Rc::clone(&data); - let drop_handler = Closure::new(Box::new(move |e: DragEvent| { - display::swap_url(e); + display::swap_img_src_on_drag(e); let ok = display::compare_rand_urls(&urls); if ok { diff --git a/src/util.rs b/src/util.rs index 0ccb6cf..c095e04 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,11 +1,33 @@ use std::fmt::Display; -use wasm_bindgen::JsValue; +use wasm_bindgen::{JsCast, JsValue}; use web_sys::{ js_sys::{Array, Math, Uint8Array}, - Blob, BlobPropertyBag, + Blob, BlobPropertyBag, HtmlElement, }; +pub fn is_mobile() -> bool { + let window = web_sys::window().unwrap(); + + let touch_start_exists = window.ontouchstart().is_some(); + let touch_points = window.navigator().max_touch_points(); + touch_start_exists || touch_points > 0 +} + +pub fn support_drag_drop() -> bool { + let document = web_sys::window().unwrap().document().unwrap(); + let div = document + .create_element("div") + .unwrap() + .dyn_into::() + .unwrap(); + + div.draggable() + && div.ondragstart().is_some() + && div.ondrop().is_some() + && div.ondragover().is_some() +} + pub fn js_buffer_to_bytes(js_buff: &JsValue) -> Vec { let js_bytes = Uint8Array::new(js_buff); js_bytes.to_vec()